O JavaScript é uma linguagem de passagem por referência ou de passagem por valor?

1405

Os tipos primitivos (número, string, etc.) são passados ​​por valor, mas os objetos são desconhecidos, porque podem ser passados ​​por valor (no caso de considerarmos que uma variável que contém um objeto é de fato uma referência ao objeto ) e passado por referência (quando consideramos que a variável para o objeto mantém o próprio objeto).

Embora isso realmente não importe no final, quero saber qual é a maneira correta de apresentar os argumentos que passam pelas convenções. Existe um trecho da especificação JavaScript, que define qual deve ser a semântica em relação a isso?

Danail Nachev
fonte
2
Eu acho que você acidentalmente inverteu suas definições de passado por valor e passado por referência ... "passado por valor (no caso de considerarmos que uma variável que contém um objeto é de fato uma referência ao objeto) e passado -de-referência (quando consideramos que a variável para o objeto contém o objeto em si)"
Niko Bellic
5
Sim. Independentemente da sintaxe, em qualquer chamada de função em qualquer linguagem de programação, passagem por referência significa que os dados associados à variável passada não são copiados quando passados ​​para a função e, portanto, quaisquer modificações feitas pela função na variável passada serão mantidas. no programa após o término da chamada de função. Passagem por valor significa que os dados associados à variável são realmente copiados quando passados ​​para a função e quaisquer modificações feitas por essa função em tal variável serão perdidas quando a variável sair do escopo do corpo da função quando a função retornar.
John Sonderson
5
Essa pergunta antiga é um tanto tóxica porque sua resposta altamente votada está incorreta. JavaScript é estritamente passado por valor .
Pointy
6
@DanailNachev A terminologia é lamentavelmente confusa. O problema é que "passar por valor" e "passar por referência" são termos que antecedem muitos recursos da linguagem de programação mais modernos. As palavras "valor" e "referência" se referem especificamente ao parâmetro como ele aparece na expressão de chamada de função. O JavaScript sempre avalia cada expressão em uma lista de parâmetros de chamada de função antes de chamar a função, portanto, os parâmetros são sempre valores. A parte confusa é que as referências a objetos são valores comuns do JavaScript. Isso não a torna uma linguagem de "passagem por referência", no entanto.
Pointy
2
@DanailNachev "passar por referência" significa especificamente que, se você tiver var x=3, y=x; f(x); alert(y === x);, a função f()poderá fazer o relatório de alerta falsee não true. No JavaScript, isso não é possível, portanto, não é passado por referência. É bom que seja possível passar referências a objetos modificáveis, mas não é isso que significa "passar por referência". Como eu disse, é uma pena que a terminologia seja tão confusa.
Pointy

Respostas:

1599

É interessante em JavaScript. Considere este exemplo:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Isso produz a saída:

10
changed
unchanged
  • Se obj1não fosse uma referência, a alteração obj1.itemnão teria efeito no obj1exterior da função.
  • Se o argumento fosse uma referência adequada, tudo teria mudado. numseria 100, e obj2.itemiria ler "changed".

Em vez disso, a situação é que o item passado é passado por valor. Mas o item que é passado por valor é em si uma referência. Tecnicamente, isso é chamado de chamada por compartilhamento .

Em termos práticos, isso significa que, se você alterar o próprio parâmetro (como com nume obj2), isso não afetará o item que foi alimentado no parâmetro. Mas se você alterar os INTERNOS do parâmetro, isso será propagado de volta (como em obj1).

deworde
fonte
32
É exatamente o mesmo (ou pelo menos semanticamente) que o C #. O objeto tem dois tipos: Valor (tipos primitivos) e Referência.
Peter Lee
53
Eu acho que isso também é usado em Java: referência por valor.
Jpnh
296
a verdadeira razão é que dentro de changeStuff, num, obj1 e obj2 são referências. Quando você altera a itempropriedade do objeto referenciado por obj1, está alterando o valor da propriedade do item que foi originalmente definida como "inalterada". Quando você atribui ao obj2 um valor de {item: "alterado"}, você está alterando a referência para um novo objeto (que fica imediatamente fora do escopo quando a função sai). Torna-se mais evidente o que está acontecendo se você nomear a função params, como numf, obj1f e obj2f. Então você vê que os parâmetros estavam escondendo os nomes de var externos.
precisa saber é o seguinte
13
@BartoNaz Na verdade não. O que você deseja é passar a referência por referência, em vez de passar a referência por valor. Mas o JavaScript sempre passa a referência por valor, assim como passa todo o resto por valor. (Para comparação, o C # possui um comportamento de referência por valor semelhante ao JavaScript e Java, mas permite especificar referência por referência com a refpalavra - chave.) Normalmente, você apenas faria com que a função retornasse o novo objeto e a atribuição no ponto em que você chama a função. Por exemplo, em foo = GetNewFoo();vez deGetNewFoo(foo);
Tim Goodman
56
Embora essa resposta seja a mais popular, ela pode ser um pouco confusa porque afirma "Se foi pura passagem por valor". JavaScript é pura passagem por valor. Mas o valor que é passado é uma referência. Isso não é restrito à passagem de parâmetros. Você pode simplesmente copiar a variável var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';e observar o mesmo efeito que no seu exemplo. Portanto, eu pessoalmente refiro a resposta de Tim Goodman
chiccodoro
476

É sempre transmitido por valor, mas para objetos o valor da variável é uma referência. Por esse motivo, quando você passa um objeto e altera seus membros , essas alterações persistem fora da função. Isso faz com que pareça passar por referência. Mas se você realmente alterar o valor da variável de objeto, verá que a alteração não persiste, provando que é realmente transmitida por valor.

Exemplo:

function changeObject(x) {
  x = { member: "bar" };
  console.log("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  console.log("in changeMember: " + x.member);
}

var x = { member: "foo" };

console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */

Resultado:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar
Tim Goodman
fonte
14
@ daylight: Na verdade, você está errado; se fosse passado por const ref, tentando fazer changeObject causaria um erro, em vez de apenas falhar. Tente atribuir um novo valor a uma referência const em C ++ e o compilador o rejeita. Em termos de usuário, essa é a diferença entre passar por valor e passar por const.
deworde
5
@ daylight: Não é constante ref. Em changeObject, mudei xpara conter uma referência ao novo objeto. x = {member:"bar"};é equivalente ao x = new Object(); x.member = "bar"; que estou dizendo também é verdade em C #, a propósito.
Tim Goodman
2
@daylight: Para C #, você pode ver isso de fora da função, se você usar a refpalavra-chave que você pode passar a referência por referência (em vez do padrão de passar a referência por valor), e então a mudança para apontar para um new Object() vai persistir .
Tim Goodman
11
@adityamenon É difícil responder "por que", mas eu observaria que os designers de Java e C # fizeram uma escolha semelhante; isso não é apenas uma esquisitice do JavaScript. Realmente, é muito consistente passar por valor, o que torna confuso para as pessoas é que um valor pode ser uma referência. Não é muito diferente de passar um ponteiro (por valor) em C ++ e desreferenciar a referência para definir os membros. Ninguém ficaria surpreso que essa mudança persista. Mas, como essas linguagens abstraem o ponteiro e silenciosamente fazem o cancelamento de referência para você, as pessoas ficam confusas.
Tim Goodman
41
Em outras palavras, a coisa confusa aqui não é passar por valor / passar por referência. Tudo passa por valor, ponto final. A coisa confusa é que você não pode passar um objeto, nem pode armazenar um objeto em uma variável. Toda vez que você pensa que está fazendo isso, na verdade está passando ou armazenando uma referência a esse objeto. Mas quando você acessa seus membros, ocorre uma desreferenciação silenciosa que perpetua a ficção de que sua variável continha o objeto real.
22713 Tim Timmanman
150

A variável não "mantém" o objeto; contém uma referência. Você pode atribuir essa referência a outra variável e agora ambas fazem referência ao mesmo objeto. É sempre passar por valor (mesmo quando esse valor é uma referência ...).

Não há como alterar o valor mantido por uma variável passada como parâmetro, o que seria possível se o JavaScript suportasse a passagem por referência.

Shog9
fonte
2
Isso me confunde um pouco. A passagem de referência não é passada por referência?
8
O autor significa que, ao passar uma referência, você está passando um valor de referência (outra maneira de pensar nisso é passar o valor do endereço de memória). Por isso, se você redeclarar o objeto, o original não será alterado, porque você estará criando um novo objeto em um local de memória diferente. Se você alterar uma propriedade, o objeto original será alterado porque você o alterou no local da memória original (que não foi reatribuído).
Huy-Anh Hoang 10/10
113

Meus dois centavos ... É assim que eu entendo. (Sinta-se livre para me corrigir se eu estiver errado)

É hora de jogar fora tudo o que você sabe sobre passar por valor / referência.

Porque em JavaScript, não importa se é passado por valor ou por referência ou o que for. O que importa é a mutação vs a atribuição dos parâmetros passados ​​para uma função.

OK, deixe-me fazer o meu melhor para explicar o que quero dizer. Digamos que você tenha alguns objetos.

var object1 = {};
var object2 = {};

O que fizemos foi "atribuição" ... Atribuímos 2 objetos vazios separados às variáveis ​​"objeto1" e "objeto2".

Agora, digamos que gostamos mais do objeto1 ... Então, "atribuímos" uma nova variável.

var favoriteObject = object1;

Em seguida, por qualquer motivo, decidimos que gostamos mais do objeto 2. Então, simplesmente fazemos uma pequena re-atribuição.

favoriteObject = object2;

Nada aconteceu ao objeto1 ou ao objeto2. Não alteramos nenhum dado. Tudo o que fizemos foi redesignar qual é o nosso objeto favorito. É importante saber que object2 e favoriteObject estão atribuídos ao mesmo objeto. Podemos mudar esse objeto através de qualquer uma dessas variáveis.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

OK, agora vamos ver primitivas como cadeias de caracteres, por exemplo

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Mais uma vez, escolhemos um favorito.

var favoriteString = string1;

Nossas variáveis ​​favoriteString e string1 são atribuídas a 'Hello world'. Agora, e se quisermos mudar nossa string favorita ??? O que vai acontecer???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Uh oh .... O que aconteceu. Não foi possível alterar a string1 alterando favoriteString ... Por que ?? Porque não mudamos nosso objeto string . Tudo o que fizemos foi "RE ASSIGN" a variável favoriteString para uma nova string. Isso basicamente o desconectou da string1. No exemplo anterior, quando renomeamos nosso objeto, não atribuímos nada. (Bem, não para a variável em si , ... no entanto, atribuímos a propriedade name a uma nova string.) Em vez disso, simplesmente modificamos o objeto que mantém as conexões entre as 2 variáveis ​​e os objetos subjacentes. (Mesmo se quiséssemos modificar ou alterar o próprio objeto string, não poderíamos ter, porque as strings são realmente imutáveis ​​em JavaScript.)

Agora, para funções e passagem de parâmetros .... Quando você chama uma função e passa um parâmetro, o que você está fazendo essencialmente é uma "atribuição" a uma nova variável, e funciona exatamente da mesma forma como se você tivesse simplesmente atribuído usando o sinal de igual (=).

Veja estes exemplos.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

Agora, a mesma coisa, mas com uma função

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

OK, agora vamos dar alguns exemplos usando objetos ... primeiro, sem a função

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Agora, a mesma coisa, mas com uma chamada de função

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

OK, se você ler esta postagem inteira, talvez agora tenha uma melhor compreensão de como as chamadas de função funcionam em JavaScript. Não importa se algo é passado por referência ou por valor ... O que importa é atribuição versus mutação.

Toda vez que você passa uma variável para uma função, você está "Atribuindo" a qualquer que seja o nome da variável de parâmetro, como se você usasse o sinal de igual (=).

Lembre-se sempre de que o sinal de igual (=) significa atribuição. Lembre-se sempre de que passar um parâmetro para uma função em JavaScript também significa atribuição. Eles são iguais e as 2 variáveis ​​são conectadas exatamente da mesma maneira (ou seja, não são, a menos que você conte que elas foram atribuídas ao mesmo objeto).

O único momento em que "modificar uma variável" afeta uma variável diferente é quando o objeto subjacente é alterado (nesse caso, você não modificou a variável, mas o próprio objeto.

Não faz sentido fazer uma distinção entre objetos e primitivas, porque funciona da mesma maneira exata como se você não tivesse uma função e apenas usasse o sinal de igual para atribuir a uma nova variável.

A única pegadinha é quando o nome da variável que você passa para a função é igual ao nome do parâmetro da função. Quando isso acontece, você deve tratar o parâmetro dentro da função como se fosse uma variável totalmente nova privada à função (porque é)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'
Ray Perea
fonte
2
Para qualquer programador C, pense em char *. foo(char *a){a="hello";} não faz nada, mas se você o fizer, foo(char *a){a[0]='h';a[1]='i';a[2]=0;}ele será alterado fora porque aé um local de memória transmitido por valor que faz referência a uma string (matriz de caracteres). Passar estruturas (semelhantes aos objetos js) pelo valor em C é permitido, mas não recomendado. O JavaScript simplesmente impõe essas práticas recomendadas e oculta a quantidade desnecessária e geralmente indesejada ... e com certeza facilita a leitura.
Technosaurus # 26/15
2
Isso está correto - os termos passagem por valor e passagem por referência têm significados no design da linguagem de programação, e esses significados não têm nada a ver com mutação de objeto. É tudo sobre como os parâmetros de função funcionam.
Pointy
2
Agora que entendi que obj1 = obj2 significa que ambos obj1 e obj2 estão apontando para o mesmo local de referência e, se eu modificar os internos de obj2, a referência a obj1 exporá os mesmos internos. Como copiar um objeto para que, quando eu faço, source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2"essa fonte ainda seja {"id": "1"}?
Machtyn 22/02
1
Postei outra resposta com definições tradicionais para reduzir a confusão. As definições tradicionais de "passagem por valor" e "passagem por referência" foram definidas no dia dos ponteiros de memória antes da desreferenciação automática. Entendeu-se perfeitamente que o valor de uma variável de objeto era na verdade o local do ponteiro de memória, não o objeto. Embora sua discussão sobre atribuição versus mutação seja talvez útil, não é necessário descartar os termos tradicionais nem suas definições. Mutação, atribuição, passagem por valor, passagem por referência, etc. não devem se contradizer.
C Perkins
"Number" também é "imutável"?
ebram khalil
72

Considere o seguinte:

  1. Variáveis ​​são indicadores de valores na memória.
  2. A reatribuição de uma variável apenas aponta esse ponteiro para um novo valor.
  3. A reatribuição de uma variável nunca afetará outras variáveis ​​que estavam apontando para o mesmo objeto

Portanto, esqueça "passar por referência / valor" , não fique desligado em "passar por referência / valor" porque:

  1. Os termos são usados ​​apenas para descrever o comportamento de uma linguagem, não necessariamente a implementação subjacente real. Como resultado dessa abstração, detalhes críticos essenciais para uma explicação decente são perdidos, o que inevitavelmente leva à situação atual em que um único termo não descreve adequadamente o comportamento real e informações adicionais devem ser fornecidas
  2. Esses conceitos não foram originalmente definidos com a intenção de descrever o javascript em particular e, portanto, não me sinto obrigado a usá-los quando eles apenas aumentam a confusão.

Para responder sua pergunta: os ponteiros são passados.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Alguns comentários finais:

  • É tentador pensar que as primitivas são impostas por regras especiais enquanto os objetos não são, mas as primitivas são simplesmente o fim da cadeia de ponteiros.
  • Como exemplo final, considere por que uma tentativa comum de limpar uma matriz não funciona conforme o esperado.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array
geg
fonte
Perguntas de acompanhamento para crédito extra;) Como funciona a coleta de lixo? Se eu alternar uma variável por um milhão de {'George', 1}valores, mas usar apenas uma por vez, como as outras serão gerenciadas? E o que acontece quando atribuo uma variável ao valor de outra variável? Estou então apontando para um ponteiro ou apontando para o pontapé do operando certo? O var myExistingVar = {"blah", 42}; var obj = myExistingVar;resultado é objapontar para {"blah", 42}ou para myExistingVar?
Michael Hoffmann
@MichaelHoffmann Eles merecem suas próprias perguntas de SO e provavelmente já foram respondidas melhor do que eu posso gerenciar. Dito isso, 1)executei um perfil de memória nas ferramentas de desenvolvimento do navegador para uma função de loop, como a que você descreveu e viu picos no uso de memória durante todo o processo de loop. Isso parece indicar que novos objetos idênticos estão realmente sendo criados em cada iteração do loop. Quando os picos caem repentinamente, o coletor de lixo acaba de limpar um grupo desses objetos não utilizados.
GEG
1
@MichaelHoffmann 2)Em relação a algo como var a = b, o javascript não fornece um mecanismo para o uso de ponteiros e, portanto, uma variável nunca pode apontar para um ponteiro (como você pode em C), embora o mecanismo javascript subjacente, sem dúvida, os utilize. Então ... var a = bapontará a"para o pontapé do operando certo"
geg 14/03/17
Fiz a pergunta nº 1 aqui (especificamente sobre o Chrome, porque a implementação provavelmente é diferente em todos os navegadores) stackoverflow.com/q/42778439/539997 e ainda estou tentando pensar em como expressar a pergunta nº 2. Qualquer ajuda é apreciada.
Michael Hoffmann
1
Não há necessidade de esquecer "passar por referência / valor" ! Esses termos têm significados históricos que descrevem exatamente o que você tenta descrever. Se jogarmos fora os termos e definições históricos e ficarmos com preguiça de aprender o que eles originalmente queriam dizer, perderemos a capacidade de nos comunicarmos efetivamente entre gerações. Não haveria uma boa maneira de discutir as diferenças entre diferentes idiomas e sistemas. Em vez disso, novos programadores precisam aprender e entender os termos tradicionais e por que e de onde vieram. Caso contrário, perdemos coletivamente o conhecimento e a compreensão.
C Perkins
24

Um objeto fora de uma função é passado para uma função, fornecendo uma referência ao objeto externo.

Quando você usa essa referência para manipular seu objeto, o objeto externo é afetado. No entanto, se dentro da função você decidiu apontar a referência para outra coisa, você não afetou o objeto de fora, porque tudo o que você fez foi redirecionar a referência para outra coisa.

user779764
fonte
20

Pense assim: sempre passa por valor. No entanto, o valor de um objeto não é o objeto em si, mas uma referência a esse objeto.

Aqui está um exemplo, passando um número (um tipo primitivo)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Repetir isso com um objeto gera resultados diferentes:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Mais um exemplo:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}
Phil Mander
fonte
19

Uma explicação muito detalhada sobre como copiar, transmitir e comparar por valor e por referência está neste capítulo do livro "JavaScript: The Definitive Guide" .

Antes de deixarmos o tópico de manipulação de objetos e matrizes por referência, precisamos esclarecer um ponto de nomenclatura.

A frase "passar por referência" pode ter vários significados. Para alguns leitores, a frase se refere a uma técnica de chamada de função que permite que uma função atribua novos valores a seus argumentos e tenha esses valores modificados visíveis fora da função. Não é assim que o termo é usado neste livro.

Aqui, queremos dizer simplesmente que uma referência a um objeto ou matriz - não ao próprio objeto - é passada para uma função. Uma função pode usar a referência para modificar propriedades do objeto ou elementos da matriz. Mas se a função substitui a referência por uma referência a um novo objeto ou matriz, essa modificação não é visível fora da função.

Os leitores familiarizados com o outro significado desse termo podem preferir dizer que objetos e matrizes são passados ​​por valor, mas o valor que é passado é na verdade uma referência e não o próprio objeto.

igor
fonte
Uau, isso é incrivelmente confuso. Quem em sã consciência definiria um termo bem estabelecido para significar exatamente o oposto e depois o usaria dessa maneira? Não é de admirar que tantas respostas aqui sobre esta questão sejam tão confusas.
Jörg W Mittag
16

O JavaScript é sempre transmitido por valor ; tudo é do tipo valor.

Objetos são valores e funções-membro de objetos são valores próprios (lembre-se de que funções são objetos de primeira classe em JavaScript). Além disso, com relação ao conceito de que tudo em JavaScript é um objeto ; isto está errado. Sequências, símbolos, números, booleanos, nulos e indefinidos são primitivos .

Ocasionalmente, eles podem aproveitar algumas funções-membro e propriedades herdadas de seus protótipos de base, mas isso é apenas por conveniência. Isso não significa que eles próprios sejam objetos. Tente o seguinte para referência:

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

Nos dois alertas, você encontrará o valor indefinido.

Michael Roberts
fonte
12
-1, nem sempre é transmitido por valor. No MDC: "Se você passar um objeto (ou seja, um valor não primitivo, como Array ou um objeto definido pelo usuário) como parâmetro, uma referência ao objeto será passada para a função."
Nick
37
@ Nick: É sempre passar por valor. Período. Uma referência ao objeto é passada por valor para a função. Isso não está passando por referência. "Passagem por referência" poderia quase ser pensado como passando a própria variável, e não seu valor; quaisquer alterações que a função faça no argumento (incluindo substituí-lo por um objeto completamente diferente!) seriam refletidas no chamador. Esse último bit não é possível no JS, porque o JS não passa por referência - ele passa referências por valor. A distinção é sutil, mas importante para entender suas limitações.
cHao
1
Para futuros empilhadores ... Sobre esta referência sua: x = "teste"; x.foo = 12;etc. Só porque uma propriedade não é persistente, não significa que não seja um objeto. Como a MDN diz: No JavaScript, quase tudo é um objeto. Todos os tipos primitivos, exceto nulo e indefinido, são tratados como objetos. Eles podem ter propriedades atribuídas (propriedades atribuídas de alguns tipos não são persistentes) e possuem todas as características dos objetos. link
slacktracer 16/08/12
9
MDN é um wiki editado pelo usuário e está errado lá. A referência normativa é ECMA-262. Veja S. 8 "O Tipo de Especificação de Referência", que explica como as referências são resolvidas, e também 8.12.5 "[[Put]]", que é usado para explicar a Expressão de Atribuição em uma Referência e, para obter a coerção de objeto 9.9 ToObject. Para valores primitivos, Michael já explicou o que o ToObject faz, como na especificação. Mas veja também s. 4.3.2 valor primitivo.
Garrett
1
@ WonderLand: Não, ele não é. As pessoas que nunca foram capazes de passar por referência podem nunca entender as diferenças entre passar por referência e passar uma referência por valor. Mas eles estão lá e são importantes. Não me importo de desinformar as pessoas, apenas porque parece mais fácil.
cHao 5/05
12

Em JavaScript, o tipo do valor controla apenas se esse valor será atribuído por cópia de valor ou cópia de referência .

Os valores primitivos são sempre atribuídos / transmitidos por cópia de valor :

  • null
  • undefined
  • corda
  • número
  • boleano
  • símbolo em ES6

Os valores compostos são sempre atribuídos / transmitidos por cópia de referência

  • objetos
  • matrizes
  • função

Por exemplo

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

No trecho acima, por 2ser uma primitiva escalar, amantém uma cópia inicial desse valor e brecebe outra cópia do valor. Ao alterar b, você não altera o valor em a.

Mas ambos ce dsão referências separadas para o mesmo valor compartilhado [1,2,3], que é um valor composto. É importante observar que cnem dmais "possui" o [1,2,3]valor - ambos são apenas referências iguais ao valor. Portanto, ao usar qualquer referência para modificar ( .push(4)) o próprio arrayvalor compartilhado real , ele afeta apenas um valor compartilhado, e as duas referências fazem referência ao valor recém-modificado [1,2,3,4].

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

Quando fazemos a tarefa b = [4,5,6], não estamos fazendo absolutamente nada para afetar onde aainda está fazendo referência ( [1,2,3]). Para fazer isso, bteria que ser um ponteiro para, em avez de uma referência ao array- mas esse recurso não existe no JS!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

Quando passamos o argumento a, ele atribui uma cópia da areferência a x. xe asão referências separadas apontando para o mesmo [1,2,3]valor. Agora, dentro da função, podemos usar essa referência para alterar o próprio valor ( push(4)). Porém, quando fazemos a atribuição x = [4,5,6], isso não afeta de maneira alguma para onde a referência inicial aestá apontando - ainda aponta para o valor (agora modificado) [1,2,3,4].

Para passar efetivamente um valor composto (como um array) por cópia de valor, é necessário fazer uma cópia manualmente, para que a referência passada ainda não aponte para o original. Por exemplo:

foo( a.slice() );

Valor composto (objeto, matriz, etc.) que pode ser passado por cópia de referência

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Aqui, objatua como um invólucro para a propriedade primitiva escalar a. Quando passado para foo(..), uma cópia doobj referência é passada e definida como o wrapperparâmetro Agora podemos usar a wrapperreferência para acessar o objeto compartilhado e atualizar sua propriedade. Depois que a função terminar, obj.avocê verá o valor atualizado 42.

Fonte

zangw
fonte
Você primeiro declara "Os valores compostos são sempre atribuídos / transmitidos por cópia de referência" e, em seguida, declara "atribui uma cópia da referência a x". No caso do que você chama de "valor composto", o valor real da variável É a referência (ou seja, o ponteiro da memória). Assim como você explicou, a referência é copiada ... então o valor das variáveis é copiado , enfatizando novamente que a REFERÊNCIA É O VALOR. Isso significa que o JavaScript é transmitido por valor para todos os tipos. Passar por valor significa passar uma cópia do valor das variáveis. Não importa que o valor seja uma referência a um objeto / matriz.
C Perkins
Você introduz uma nova terminologia (value-copy / reference-copy) e isso apenas torna as coisas mais complexas. Existem apenas cópias, ponto final. Se você passa uma primitiva, passa uma cópia dos dados primitivos reais; se passa um objeto, passa uma cópia da localização da memória do objeto. É tudo o que você precisa dizer. Qualquer coisa a mais confunde ainda mais as pessoas.
Scott Marcus
9

bem, trata-se de 'desempenho' e 'velocidade' e na palavra simples 'gerenciamento de memória' em uma linguagem de programação.

em javascript, podemos colocar valores em duas camadas: type1 - objectse type2 - todos os outros tipos de valor, como string&boolean & etc

se você imaginar a memória como quadrados abaixo, os quais em cada um deles apenas um valor type2 pode ser salvo:

insira a descrição da imagem aqui

todo valor type2 (verde) é um único quadrado, enquanto um valor type1 (azul) é um grupo deles :

insira a descrição da imagem aqui

o ponto é que, se você deseja indicar um valor type2, o endereço é simples, mas se você deseja fazer a mesma coisa com o valor type1, isso não é nada fácil! :

insira a descrição da imagem aqui

e em uma história mais complicada:

insira a descrição da imagem aqui

então aqui as referências podem nos resgatar: insira a descrição da imagem aqui

enquanto a seta verde aqui é uma variável típica, a roxa é uma variável de objeto, portanto, porque a seta verde (variável típica) tem apenas uma tarefa (e isso indica um valor típico), não precisamos separar seu valor de então movemos a seta verde com o valor disso para onde for e em todas as atribuições, funções e assim por diante ...

mas não podemos fazer o mesmo com a seta roxa, podemos mover a célula 'john' para cá ou muitas outras coisas ..., para que a seta roxa se mantenha no seu lugar e apenas as flechas típicas atribuídas ...

uma situação muito confusa é onde você não pode perceber como a variável referenciada muda, vamos dar uma olhada em um exemplo muito bom:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

insira a descrição da imagem aqui insira a descrição da imagem aqui

ashkan nasirzadeh
fonte
8

Isso é um pouco mais de explicação para passagem por valor e passagem por referência (JavaScript). Nesse conceito, eles estão falando sobre passar a variável por referência e passar a variável por referência.

Passar por valor (tipo primitivo)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • aplica-se a todo tipo primitivo em JavaScript (sequência, número, Booleano, indefinido e nulo).
  • a está alocada em uma memória (digamos 0x001) eb cria uma cópia do valor na memória (digamos 0x002).
  • Portanto, alterar o valor de uma variável não afeta a outra, pois ambas residem em dois locais diferentes.

Passar por referência (objetos)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • O mecanismo JavaScript atribui o objeto à variável ce aponta para alguma memória, digamos (0x012).
  • Quando d = c, nesta etapa, daponte para o mesmo local (0x012).
  • Alterando o valor de qualquer valor de alteração para a variável.
  • Funções são objetos

Caso especial, passar por referência (objetos)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • O operador equal (=) configura um novo espaço ou endereço de memória
Ashish Singh Rawat
fonte
No seu chamado caso especial, não é o operador de atribuição que causa a alocação de espaço na memória, é o próprio literal do objeto . A notação de colchete causa a criação de um novo objeto. A propriedade cestá configurada para uma cópia da referência do novo objeto.
georgeawg
6

compartilhando o que sei das referências em JavaScript

Em JavaScript, ao atribuir um objeto a uma variável, o valor atribuído à variável é uma referência ao objeto:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4

xameeramir
fonte
1
Esta é uma resposta excessivamente simplista que não diz nada que as respostas anteriores não tenham explicado melhor. Estou confuso sobre o motivo de você chamar matrizes como um caso especial.
Quentin
1
" objetos são armazenados como referências " é enganoso. O que eu acho que você quer dizer é que, ao atribuir um objeto a uma variável, o valor atribuído à variável é uma referência ao objeto.
RobG 27/07/19
isso não soluciona o problema de atualizar um objeto dentro de uma função que não atualiza o objeto fora da função. Essa é toda a imagem em que parece funcionar como valores em vez de referência. Portanto -1
amaster 11/04
@ amaster Obrigado por apontar isso! Você pode sugerir uma edição, por favor?
xameeramir 11/04
Haha, eu tentei ... minha edição sugerida mudou muito amd não era permitido
amaster 13/04
4

Semântica!! Definir definições concretas necessariamente tornará algumas respostas e comentários incompatíveis, pois eles não estão descrevendo a mesma coisa, mesmo ao usar as mesmas palavras e frases, mas é fundamental superar a confusão (especialmente para novos programadores).

Primeiro de tudo, existem vários níveis de abstração que nem todos parecem entender. Programadores mais novos que aprenderam em linguagens de quarta ou quinta geração podem ter dificuldade em compreender conceitos familiares para assembly ou programadores em C que não são colocados em fases por ponteiros para ponteiros para ponteiros. Passagem por referência não significa simplesmente a capacidade de alterar um objeto referenciado usando uma variável de parâmetro de função.

Variável : conceito combinado de um símbolo que faz referência a um valor em um local específico na memória. Esse termo geralmente é carregado demais para ser usado sozinho na discussão de detalhes.

Símbolo : sequência de texto usada para se referir à variável (ou seja, nome da variável).

Valor : bits específicos armazenados na memória e referenciados usando o símbolo da variável.

Localização da memória : onde o valor de uma variável é armazenado. (O local em si é representado por um número separado do valor armazenado no local.)

Parâmetro da função : variável declarada em uma definição de função, usada para referenciar variáveis ​​passadas para a função.

Argumento da função : variável fora da função que é passada para a função pelo chamador.

Variável de objeto : variável cujo valor subjacente básico não é o "objeto" em si, mas seu valor é um ponteiro (valor do local da memória) para outro local na memória onde os dados reais do objeto são armazenados. Na maioria das linguagens de alta geração, o aspecto "ponteiro" é efetivamente oculto pela des-referência automática em vários contextos.

Variável primitiva : variável cujo valor é o valor real. Mesmo esse conceito pode ser complicado pelo auto-boxe e contextos semelhantes a objetos de várias linguagens, mas as idéias gerais são que o valor da variável É o valor real representado pelo símbolo da variável em vez de um ponteiro para outro local da memória.

Argumentos e parâmetros de função não são a mesma coisa. Além disso, o valor de uma variável não é o objeto da variável (como já apontado por várias pessoas, mas aparentemente ignorado). Essas distinções são críticas para o entendimento adequado.

Passagem por valor ou Chamada por compartilhamento (para objetos): O valor do argumento da função é COPIADO para outro local da memória que é referenciado pelo símbolo de parâmetro da função (independentemente de estar na pilha ou na pilha). Em outras palavras, o parâmetro function recebeu uma cópia do valor do argumento passado ... E (crítico) o valor do argumento NUNCA É ATUALIZADO / ALTERADO / ALTERADO pela função de chamada. Lembre-se de que o valor de uma variável de objeto NÃO é o objeto em si, é o ponteiro para o objeto, portanto, passar uma variável de objeto por valor copia o ponteiro para a variável de parâmetro de função. O valor do parâmetro da função aponta exatamente para o mesmo objeto na memória. Os dados do objeto em si podem ser alterados diretamente através do parâmetro function, MAS o valor do argumento da função NUNCA É ATUALIZADO, portanto, ele continuará apontando para o parâmetro mesmoobjeto durante e mesmo após a chamada da função (mesmo que os dados do objeto tenham sido alterados ou se o parâmetro da função tiver um objeto diferente totalmente atribuído). É incorreto concluir que o argumento da função foi passado por referência apenas porque o objeto referenciado é atualizável por meio da variável de parâmetro da função.

Chamada / Passagem por referência : o valor do argumento da função pode / será atualizado diretamente pelo parâmetro de função correspondente. Se ajudar, o parâmetro de função se torna um "alias" eficaz para o argumento - eles se referem efetivamente ao mesmo valor no mesmo local de memória. Se um argumento de função for uma variável de objeto, a capacidade de alterar os dados do objeto não será diferente do caso de passagem por valor, pois o parâmetro de função ainda apontará para o mesmo objeto que o argumento. Mas, no caso da variável de objeto, se o parâmetro de função estiver definido para um objeto completamente diferente, o argumento também apontará para o objeto diferente - isso não acontece no caso de passagem por valor.

JavaScript não passa por referência. Se você ler atentamente, perceberá que todas as opiniões contrárias interpretam mal o que se entende por passagem por valor e concluem falsamente que a capacidade de atualizar os dados de um objeto por meio do parâmetro function é sinônimo de "passagem por valor".

Clone / cópia de objeto: Um novo objeto é criado e os dados do objeto original são copiados. Pode ser uma cópia profunda ou superficial, mas o ponto é que um novo objeto é criado. Criar uma cópia de um objeto é um conceito separado da passagem por valor. Algumas linguagens distinguem entre objeto de classe e estruturas (ou similares) e podem ter um comportamento diferente para passar variáveis ​​dos diferentes tipos. Mas o JavaScript não faz nada assim automaticamente ao passar variáveis ​​de objeto. Mas a ausência de clonagem automática de objetos não se traduz em passagem por referência.

C Perkins
fonte
4

JavaScript passa tipos primitivos por valor e tipos de objeto por referência

Agora, as pessoas gostam de discutir incessantemente se "passar por referência" é a maneira correta de descrever o que Java et al. realmente fazer. O ponto é este:

  1. Passar um objeto não o copia.
  2. Um objeto passado para uma função pode ter seus membros modificados pela função.
  3. Um valor primitivo passado para uma função não pode ser modificado pela função. É feita uma cópia.

No meu livro, isso é chamado de passagem por referência.

- Brian Bi - Quais linguagens de programação são passadas por referência?


Atualizar

Aqui está uma refutação a isso:

Não há "passagem por referência" disponível em JavaScript.

georgeawg
fonte
@ Amy Porque está descrevendo passagem por valor, não por referência. Esta resposta é boa e mostra a diferença: stackoverflow.com/a/3638034/3307720
Nasch
@nasch eu entendo a diferença. Os números 1 e 2 estão descrevendo a semântica de passagem por referência. # 3 está descrevendo a semântica de passagem por valor.
Amy
@Amy 1, 2 e 3 são consistentes com a passagem por valor. Para passar por referência, você também precisará de 4: atribuir a referência a um novo valor dentro da função (com o operador =) também reatribui a referência fora da função. Este não é o caso do Javascript, fazendo com que ele passe exclusivamente por valor. Ao passar um objeto, você passa um ponteiro para o objeto e passa esse ponteiro por valor.
Nasch
Isso não é geralmente o que se entende por "passagem por referência". Você atendeu minha pergunta e eu discordo de você. Obrigado.
27719 Amy
"No meu livro, isso é chamado de passagem por referência." - Em cada livro de compilador, livro de intérpretes, livro de teoria da linguagem de programação e livro de ciência da computação já escrito, não é.
Jörg W Mittag
3

Minha maneira simples de entender isso ...

  • Ao chamar uma função, você está transmitindo o conteúdo (referência ou valor) das variáveis ​​de argumento, não as próprias variáveis.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • Dentro da função, as variáveis ​​de parâmetro inVar1e inVar2recebem o conteúdo que está sendo passado.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Desde que inVar2recebeu a referência de { prop: 2 }, você pode alterar o valor da propriedade do objeto.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
dpp
fonte
3

Passar argumentos para uma função em JavaScript é análogo a passar parâmetros pelo valor do ponteiro em C:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}
John Sonderson
fonte
1
Eu não acho que este seja o caso no JavaScript: `` `javascript var num = 5;
Danail Nachev 07/04
@DanailNachev: Embora isso possa ser tecnicamente verdadeiro, a diferença é observável apenas para objetos mutáveis ​​que os primitivos do ECMAScript não são.
Jörg W Mittag
3

Para os advogados de linguagem de programação, eu examinei as seções a seguir do ECMAScript 5.1 (que é mais fácil de ler do que a edição mais recente), e cheguei ao ponto de perguntar na lista de correspondência do ECMAScript.

TL; DR : Tudo é passado por valor, mas as propriedades de Objetos são referências e a definição de Objeto é assustadoramente ausente no padrão.

Construção de listas de argumentos

A Seção 11.2.4 "Listas de argumentos" diz o seguinte sobre a produção de uma lista de argumentos que consiste em apenas 1 argumento:

A produção ArgumentList: AssignmentExpression é avaliada da seguinte maneira:

  1. Deixe ref ser o resultado da avaliação de AssignmentExpression.
  2. Seja arg GetValue (ref).
  3. Retorna uma lista cujo único item é arg.

A seção também enumera casos em que a lista de argumentos possui 0 ou> 1 argumentos.

Assim, tudo é passado por referência.

Acesso às propriedades do objeto

Seção 11.2.1 "Acessadores de propriedades"

A produção MemberExpression: MemberExpression [Expression] é avaliada da seguinte maneira:

  1. Permita que baseReference seja o resultado da avaliação de MemberExpression.
  2. Deixe baseValue ser GetValue (baseReference).
  3. Permita que propertyNameReference seja o resultado da avaliação de Expressão.
  4. Permita que propertyNameValue seja GetValue (propertyNameReference).
  5. Chame CheckObjectCoercible (baseValue).
  6. Permita que propertyNameString seja ToString (propertyNameValue).
  7. Se a produção sintática que está sendo avaliada estiver contida no código de modo estrito, deixe estrito ser verdadeiro, caso contrário, estrito seja falso.
  8. Retorne um valor do tipo Referência cujo valor base é baseValue e cujo nome referenciado é propertyNameString e cujo sinalizador de modo estrito é estrito.

Assim, as propriedades dos objetos estão sempre disponíveis como referência.

Na Referência

É descrito na seção 8.7 "O Tipo de especificação de referência", que as referências não são tipos reais no idioma - elas são usadas apenas para descrever o comportamento dos operadores de exclusão, tipo e tipos de atribuição.

Definição de "Objeto"

É definido na edição 5.1 que "Um Objeto é uma coleção de propriedades". Portanto, podemos inferir que o valor do objeto é a coleção, mas qual é o valor da coleção é mal definido na especificação e requer um pouco de esforço para entender.

DannyNiu
fonte
Nunca deixa de me surpreender quantas pessoas ficam confusas com as distinções entre argumentos passados ​​por valor, argumentos passados ​​por referência, operações em objetos inteiros e operações em suas propriedades. Em 1979, não me formei em ciência da computação, optando por adicionar 15 horas ou mais de disciplinas eletivas de CS ao meu programa de MBA. No entanto, logo ficou claro para mim que minha compreensão desses conceitos era pelo menos tão boa quanto a de qualquer um dos meus colegas que tinham formação em ciências da computação ou matemática. Estude Assembler, e tudo ficará bem claro.
David A. Gray
3

Os documentos MDN explicam isso claramente, sem ser muito detalhado:

Os parâmetros de uma chamada de função são os argumentos da função . Os argumentos são passados ​​para funções por valor . Se a função alterar o valor de um argumento, essa alteração não será refletida globalmente ou na função de chamada. No entanto, as referências a objetos também são valores e são especiais: se a função altera as propriedades do objeto referido, essa alteração é visível fora da função, (...)

Fonte: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description

miguelr
fonte
1

Em um idioma de baixo nível, se você deseja passar uma variável por referência, é necessário usar uma sintaxe específica na criação da função:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

O &ageé uma referência a myAge, mas se você deseja o valor, precisa converter a referência, usando *age.

Javascript é uma linguagem de alto nível que faz essa conversão para você. Portanto, embora os objetos sejam passados ​​por referência, o idioma converte o parâmetro de referência no valor. Você não precisa usar &, na definição da função, para passá-la por referência, nem *no corpo da função, para converter a referência no valor, JS faz isso por você.

É por isso que quando você tenta alterar um objeto dentro de uma função, substituindo seu valor (ou seja age = {value:5}), a alteração não persiste, mas se você alterar suas propriedades (ou seja,age.value = 5 ), ela .

Saber mais

Narayon
fonte
1

Eu li essas respostas várias vezes, mas REALMENTE não as recebi até aprender sobre a definição técnica de "Chamada por compartilhamento", conforme denominado por Barbara Liskov

A semântica da chamada compartilhando difere da chamada por referência, pois as atribuições para argumentos de função dentro da função não são visíveis para o chamador (ao contrário da semântica de referência) [citação necessária], portanto, por exemplo, se uma variável foi passada, não é possível para simular uma atribuição nessa variável no escopo do chamador. No entanto, como a função tem acesso ao mesmo objeto que o chamador (nenhuma cópia é feita), as mutações nesses objetos, se os objetos forem mutáveis, dentro da função são visíveis para o chamador, o que pode parecer diferente da chamada por valor semântica. Mutações de um objeto mutável dentro da função são visíveis para o chamador porque o objeto não é copiado ou clonado - ele é compartilhado.

Ou seja, as referências dos parâmetros são alteráveis ​​se você acessar o próprio valor do parâmetro. Por outro lado, a atribuição a um parâmetro desaparecerá após a avaliação e não poderá ser acessada pelo chamador da função.

steviejay
fonte
Não, se um objeto é mutável ou não não é realmente o problema. Tudo é sempre passado por valor. Depende apenas do que você está passando (um valor ou uma referência). Veja isso .
21418 Scott
O que ela está descrevendo é passar uma referência BY-VALUE. Não há razão para introduzir uma nova terminologia.
Sanjeev
1

Maneira mais simples

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 
Asad Raza
fonte
JSON.parse( JSON.stringify( obj ) )é uma maneira horrível de clonar objetos profundamente. Não apenas não é apenas lento, mas também pode causar perda de dados.
D. Pardal
0

Eu achei o método de extensão da biblioteca Underscore.js muito útil quando quero passar um objeto como um parâmetro que pode ser modificado ou substituído inteiramente.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}
Jack
fonte
0

A explicação mais sucinta que encontrei foi no guia de estilo do AirBNB :

  • Primitivas : ao acessar um tipo primitivo, você trabalha diretamente em seu valor

    • corda
    • número
    • boleano
    • nulo
    • Indefinido

Por exemplo:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complexo : ao acessar um tipo complexo, você trabalha em uma referência ao seu valor

    • objeto
    • matriz
    • função

Por exemplo:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Ou seja, tipos primitivos efetivamente são passados ​​por valor e tipos complexos são passados ​​por referência.

tampa
fonte
Não, tudo é sempre passado por valor. Depende apenas do que você está passando (um valor ou uma referência). Veja isso .
Scott Marcus
-1

Eu diria que é passagem por cópia -

Considere que argumentos e objetos variáveis ​​são objetos criados durante o contexto de execução criado no início da chamada da função - e seu valor / referência real passado para a função é armazenado nesses argumentos + objetos variáveis.

Simplificando, para tipos primitivos, os valores são copiados no início da chamada de função, para tipo de objeto, a referência é copiada.

lyslim
fonte
1
"passagem por cópia" === passagem por valor
Scott Marcus
-1

Há alguma discussão sobre o uso do termo "passagem por referência" em JavaScript aqui , mas para responder sua pergunta:

Um objeto é passado automaticamente por referência, sem a necessidade de especificá-lo

(Do artigo mencionado acima.)

Jack Sleight
fonte
7
O artigo vinculado não inclui mais essas declarações e evita o uso de "passagem por referência".
C Perkins
O valor é uma referência
-2

Uma maneira fácil de determinar se algo é "passado por referência" é se você pode escrever uma função "swap". Por exemplo, em C, você pode fazer:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Se você não pode fazer o equivalente ao JavaScript, não é "passar por referência".

Ross
fonte
21
Isso não é realmente passar por referência. Você está passando ponteiros para a função e esses ponteiros estão sendo transmitidos por valor. Um exemplo melhor seria C ++ 's & operator ou a palavra-chave "ref" de C #, ambas são realmente passadas por referência.
Matt Greer
Ainda mais fácil é que tudo é passado por valor em JavaScript.
21418 Marc Scott Marcus
-3
  1. variável de tipo primitivo como string, number são sempre passados ​​como passados ​​por valor.
  2. Matriz e Objeto são passados ​​como passagem por referência ou passagem por valor com base nessas duas condições.

    • se você estiver alterando o valor desse objeto ou matriz com o novo objeto ou matriz, ele será passado por valor.

      object1 = {item: "car"}; array1=[1,2,3];

    aqui você está atribuindo um novo objeto ou matriz ao antigo. você não está alterando o valor da propriedade do objeto antigo. portanto, é transmitido por valor.

    • se você estiver alterando um valor de propriedade de um objeto ou matriz, ele será passado por Referência.

      object1.key1= "car"; array1[0]=9;

    aqui você está alterando um valor de propriedade do objeto antigo. você não está atribuindo um novo objeto ou matriz ao antigo. então é passado por referência.

Código

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10
Mukund Kumar
fonte
1
O operador de atribuição não deve ser confundido com uma chamada de função. Quando você atribui novos dados a uma variável existente, a contagem de referência dos dados antigos diminui e novos dados são associados à variável antiga. Basicamente, a variável acaba apontando para os novos dados. O mesmo vale para variáveis ​​de propriedade. Como essas atribuições não são chamadas de função, elas não têm nada a ver com passagem por valor ou passagem por referência.
John Sonderson
1
Não, tudo é sempre passado por valor. Depende apenas do que você está passando (um valor ou uma referência). Veja isso .
21418 Scott
-3
  1. Primitivas (número, booleano etc.) são passadas por valor.
    • As cordas são imutáveis, então isso realmente não importa para elas.
  2. Os objetos são passados ​​por referência (a referência é passada por valor).
uriz
fonte
Não, tudo é sempre passado por valor. Depende apenas do que você está passando (um valor ou uma referência). Veja isso .
Scott Marcus
Sua segunda afirmação está se contradizendo.
Jörg W Mittag
-5

Valores simples dentro de funções não alteram esses valores fora da função (eles são passados ​​por valor), enquanto valores complexos mudam (eles são passados ​​por referência).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000
Olivera Kovacevic
fonte
isso é ridículo, você mudará por causa do escopo do nível funcional; ele é içado, não porque é passado por referência.
Parijat Kalia 10/11/2015
Não, tudo é sempre passado por valor. Depende apenas do que você está passando (um valor ou uma referência). Veja isso .
Scott Marcus