Saltar al contenido principal
07Referencias y Prototipos

Valor y Referencia

Clase 1 de 56 min de lectura

Copias un objeto en otra variable, cambias la copia… y el original también ha cambiado. No es un bug: es que JavaScript no copia igual un número que un objeto.

Según el tipo de dato, al asignar una variable o pasar un argumento a una función, JavaScript usa una de dos estrategias: paso por valor o paso por referencia. Entender la diferencia te va a ahorrar muchas horas buscando por qué tu código hace cosas raras con objetos y arrays.

Tipos Primitivos: Paso por Valor#

Cuando asignas un primitivo a una variable, JavaScript crea una copia independiente de ese valor. Es como hacer una fotocopia: puedes pintarrajearla todo lo que quieras, que el original ni se entera.

Fíjate en qué le pasa a score cuando cambiamos playerScore:

let score = 100let playerScore = score // Se copia el valor 100 a playerScoreplayerScore = 150 // Modificamos la copiaconsole.log(score) // -> 100console.log(playerScore) // -> 150

Nada. playerScore recibió su propia copia del 100, así que al reasignarla el valor de score sigue intacto. Eso es el paso por valor.

Infografía
Los primitivos se copian por valor
4.Cada variable primitiva tiene su propia copia: lo que le pase a una no afecta a la otra.
Paso 4 de 4

¿Y con las funciones? Exactamente igual. El parámetro es una variable local que recibe una copia del valor:

function increaseScore(currentScore) {  currentScore = currentScore + 10 // Modificamos la copia local de la función  console.log('Dentro de la función:', currentScore) // -> 110}// Creamos el valor originallet initialScore = 100// Al ser un primitivo, la función recibe una copiaincreaseScore(initialScore)console.log('Fuera de la función:', initialScore) // -> 100

Dentro de la función el valor sube a 110, pero fuera initialScore sigue valiendo 100. La función solo ha tocado su copia.

Tipos No Primitivos: Paso por Referencia#

Aquí cambian las reglas. Los tipos no primitivos son los objetos, y eso incluye object, array, function y el resto de objetos complejos.

Cuando asignas un objeto a otra variable o lo pasas como argumento, no se crea una copia del objeto. Lo que se copia es la referencia: la dirección de memoria donde vive ese objeto.

Piensa en un documento compartido. No le envías a tu compañero una copia del documento, le pasas el enlace. Si lo edita, tú también ves los cambios, porque es el mismo documento para los dos.

Mira qué pasa con player1 cuando modificamos player2:

let player1 = { name: 'midu', hp: 100 }let player2 = player1 // player2 apunta al *mismo* objeto que player1player2.hp = 50 // Modificamos el objeto a través de player2console.log(player1.hp) // -> 50console.log(player2.hp) // -> 50

El original ha cambiado. No hay dos objetos: hay uno solo y dos variables que apuntan a él.

Infografía
Los objetos se copian por referencia
4.Por eso player1.hp también es 50: el cambio se ve desde todas las variables que apuntan a ese objeto.
Paso 4 de 4

Los arrays también son objetos, así que se comportan igual:

let team1 = ['midu', 'dani']let team2 = team1 // team2 apunta al *mismo* array que team1team2.push('luffy') // Modificamos el array a través de team2console.log(team1) // -> ['midu', 'dani', 'luffy']console.log(team2) // -> ['midu', 'dani', 'luffy']

Y lo mismo ocurre al pasar un objeto a una función. El parámetro no recibe una copia, sino la referencia al objeto original:

function addMember(teamArray, member) {  teamArray.push(member) // Modificamos el array original a través de su referencia  console.log('Dentro de la función:', teamArray)}let myTeam = ['SpiderMan', 'IronMan']addMember(myTeam, 'Hulk') // ['SpiderMan', 'IronMan', 'Hulk']console.log('Fuera de la función:', myTeam)// -> ['SpiderMan', 'IronMan', 'Hulk']

La función ha añadido a Hulk… y myTeam, que está fuera, también lo tiene.

Tipos primitivos dentro de objetos

Otro despiste muy habitual: olvidar que un primitivo sigue siendo un primitivo aunque viva dentro de un objeto. Si a una función le pasas una propiedad que es un primitivo, recibe una copia de su valor, no un trozo del objeto.

Aquí tenemos un objeto character y le pasamos a la función solo su propiedad level:

let character = { name: 'Hero', level: 5 }function updateLevel(level) {  // actualizamos el valor de level  // y lo devolvemos  level = level + 1  return level}updateLevel(character.level)console.log('Fuera de la función:', character.level)// -> 5

El nivel sigue siendo 5. Aunque level sea una propiedad de character, es un número, y por lo tanto se pasa por valor.

Ahora fíjate en qué ocurre si haces lo mismo pasando el objeto completo:

let character = { name: 'Hero', level: 5 }function updateCharacter(character) {  character.level = character.level + 1  return character.level}updateCharacter(character)console.log('Fuera de la función:', character)// -> { name: 'Hero', level: 6 }

Esta vez sí cambia: la función ha recibido la referencia al objeto y ha modificado el original.

En resumen

  • Los primitivos se pasan por valor: al asignarlos o pasarlos a una función se crea una copia independiente.
  • Los objetos, arrays y funciones se pasan por referencia: se copia la dirección de memoria, no el contenido.
  • Si modificas un objeto desde cualquiera de sus referencias, cambia para todas, porque es el mismo objeto.
  • Un primitivo que es propiedad de un objeto sigue pasándose por valor.