JavaScript по ссылке или по значению

Я ищу хороший исчерпывающий материал для чтения о том, когда JavaScript передает что-то по значению, а когда по ссылке и когда изменение переданного элемента влияет на значение вне функции, а когда нет. Меня также интересует, когда присвоение другой переменной осуществляется по ссылке или по значению, и следует ли это каким-либо другим правилам, кроме передачи в качестве параметра функции.

Я провел много поисков и нашел множество конкретных примеров (многие из них здесь, на SO), из которых я могу начать собирать воедино настоящие правила, но я еще не нашел ни одного хорошо написанного документа, который описывает все это.

Кроме того, есть ли в языке способы управления передачей чего-либо по ссылке или по значению?

Вот некоторые типы вопросов, которые я хочу понять. Это всего лишь примеры - на самом деле я хочу понять правила языка, а не просто ответы на конкретные примеры. Но вот несколько примеров:

function f(a,b,c) {
   a = 3;
   b.push("foo");
   c.first = false;
}

var x = 4;
var y = ["eeny", "miny", "mo"];
var z = {first: true};
f(x,y,z);

Когда содержимое x, y и z изменяется вне области f для всех различных типов?

function f() {
    var a = ["1", "2", "3"];
    var b = a[1];
    a[1] = "4";
    // what is the value of b now for all possible data types that the array in "a" might hold?
}

function f() {
    var a = [{yellow: "blue"}, {red: "cyan"}, {green: "magenta"}];
    var b = a[1];
    a[1].red = "tan";
    // what is the value of b now and why?
    b.red = "black";
    // did the value of a[1].red change when I assigned to b.red?
}

Если я хочу сделать полностью независимую копию объекта (без каких-либо ссылок), как лучше всего это сделать?


person jfriend00    schedule 07.07.2011    source источник


Ответы (4)


Насколько я понимаю, это на самом деле очень просто:

  • Javascript всегда передается по значению, но когда переменная ссылается на объект (включая массивы), «значение» является ссылкой на объект.
  • Изменение значения переменной никогда не изменяет базовый примитив или объект, оно просто указывает переменной на новый примитив или объект.
  • Однако изменение свойства объекта, на который ссылается переменная, действительно изменяет базовый объект.

Итак, чтобы проработать некоторые из ваших примеров:

function f(a,b,c) {
    // Argument a is re-assigned to a new value.
    // The object or primitive referenced by the original a is unchanged.
    a = 3;
    // Calling b.push changes its properties - it adds
    // a new property b[b.length] with the value "foo".
    // So the object referenced by b has been changed.
    b.push("foo");
    // The "first" property of argument c has been changed.
    // So the object referenced by c has been changed (unless c is a primitive)
    c.first = false;
}

var x = 4;
var y = ["eeny", "miny", "mo"];
var z = {first: true};
f(x,y,z);
console.log(x, y, z.first); // 4, ["eeny", "miny", "mo", "foo"], false

Пример 2:

var a = ["1", "2", {foo:"bar"}];
var b = a[1]; // b is now "2";
var c = a[2]; // c now references {foo:"bar"}
a[1] = "4";   // a is now ["1", "4", {foo:"bar"}]; b still has the value
              // it had at the time of assignment
a[2] = "5";   // a is now ["1", "4", "5"]; c still has the value
              // it had at the time of assignment, i.e. a reference to
              // the object {foo:"bar"}
console.log(b, c.foo); // "2" "bar"
person nrabinowitz    schedule 07.07.2011
comment
Хотя технически это правда, я предпочитаю говорить, что JavaScript - это Pass By Object Sharing. Он избегает такой путаницы и переходит к высокоуровневому обзору. - person ; 07.07.2011
comment
@pst - это имеет смысл, но обратите внимание на проблемы, поднятые на указанной странице Википедии - большинство людей не используют этот термин, и (большое) сообщество Java называет это передачей по значению. Тем не менее, я согласен, что это немного сбивает с толку. - person nrabinowitz; 07.07.2011
comment
Какое замешательство вы имеете в виду? Для меня передача по стоимости совершенно ясна. - person MEMark; 24.06.2014
comment
В примере в этом вопросе, который повторяется в этом вопросе, показан еще более интересный случай: c = {first: '5'}. После выполнения f(x, y, z) z останется {first: true}. - person Dan Dascalescu; 26.10.2014
comment
Changing the value of a variable never changes the underlying primitive or object. However, changing a property of an object referenced by a variable does change the underlying object. Эти два предложения вместе устраняют так много сомнений. Спасибо! - person Amit Tomar; 18.01.2016
comment
исходящий из c, это глупо и раздражает, состояние состояние состояние ... - person Rafael; 27.04.2016
comment
var users = [1,2,3,4]; var x_users = users; x_users.push(5); теперь пользователи и x_users совпадают, поскольку они передаются по ссылке. Один из способов исправить это - var x_users = users.slice(0); x_users.push(6);. Теперь пользователи и x_users разные, поскольку x_users не относится к пользователям. Мне потребовалось время, чтобы понять это :-) Надеюсь, это может кому-то помочь. - person ralixyle; 28.08.2017
comment
Что забавно, потому что в JavaScript все считается объектом ... ['hi','ho'].length, (4).toString(), 'Hi'.length - person StanE; 26.10.2017
comment
Итак, все примитивы JavaScript неизменяемы, и все значения используются совместно (как указано @ user166390). Итак, для примитивов это вообще не имеет значения, но я предполагаю, что передача всего по ссылке более эффективна с точки зрения производительности (поскольку материал не копируется каждый раз), а объекты, очевидно, не дублируются во время назначения, не могли бы мы тогда заявить что все в JavaScript по ссылке (или по совместному использованию)? Я знаю, что это полная противоположность тому, что вы утверждаете, но концепция по сути та же, и это кажется более верным, учитывая, что примитивы неизменяемы. - person doubleOrt; 13.02.2018
comment
продолжение см. эту страницу Википедии, на которой указано, что все значения JavaScript являются справочными типы (а также взгляните на источник спецификации для этого оператора). Но тогда как это может сосуществовать с тем фактом, что иногда значения хранятся в стеке, а не в куче? - person doubleOrt; 13.02.2018
comment
Исходя из C, кажется, что во всех вызовах передаются указатели на определенную память (объект). - person Hritik; 24.11.2018
comment
передача по копии ссылки - это наиболее точный способ описать, как непримитивы передаются в функцию. Это означает, что исходная ссылочная переменная (извне функции) недоступна внутри функции ... но есть копия этой ссылочной переменной. Вот почему исходная ссылочная переменная не может быть изменена изнутри функции, чтобы указывать на другой объект, но свойства исходного объекта можно редактировать, потому что наша скопированная ссылочная переменная все еще указывает на тот же объект. - person JWess; 25.10.2019

Javascript всегда передает значение. Однако, если вы передаете объект функции, «значение» на самом деле является ссылкой на этот объект, поэтому функция может изменять свойства этого объекта , но не заставляет переменную вне функции указывать на какой-либо другой объект .

Пример:

function changeParam(x, y, z) {
  x = 3;
  y = "new string";
  z["key2"] = "new";
  z["key3"] = "newer";

  z = {"new" : "object"};
}

var a = 1,
    b = "something",
    c = {"key1" : "whatever", "key2" : "original value"};

changeParam(a, b, c);

// at this point a is still 1
// b is still "something"
// c still points to the same object but its properties have been updated
// so it is now {"key1" : "whatever", "key2" : "new", "key3" : "newer"}
// c definitely doesn't point to the new object created as the last line
// of the function with z = ...
person nnnnnn    schedule 07.07.2011
comment
Array - это объект, поэтому он также изменится. - person shyammakwana.me; 13.04.2016
comment
За исключением того, что почти все в JavaScript является объектом. - person Hritik; 24.11.2018
comment
@Hritik - За исключением всех примитивных значений, которые не являются объектами. - person nnnnnn; 02.12.2018

Да, Javascript всегда передается по значению, но в массиве или объекте значение является ссылкой на него, поэтому вы можете «изменить» содержимое.

Но, я думаю, вы уже читали это на SO; здесь у вас есть необходимая документация:

http://snook.ca/archives/javascript/javascript_pass

person Edgar Villegas Alvarado    schedule 07.07.2011
comment
Хотя технически это правда, я предпочитаю говорить, что JavaScript - это Pass By Object Sharing. Он избегает такой путаницы и переходит к высокоуровневому обзору. - person ; 07.07.2011
comment
Я сделал скрипку, чтобы немного поиграть с этим: jsfiddle.net/tkane2000/7weKS/1 - person tkane2000; 22.05.2014

  1. Переменные примитивного типа, такие как строка, число, всегда передаются как передаваемые по значению.
  2. Массив и объект передаются по ссылке или по значению в зависимости от этих двух условий.

    • если вы меняете значение этого объекта или массива на новый объект или массив, то оно передается по значению.

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

    здесь вы назначаете новый объект или массив старому. вы не меняете значение свойства старого объекта. поэтому он передается по значению.

    • если вы изменяете значение свойства объекта или массива, оно передается по ссылке.

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

    здесь вы меняете значение свойства старого объекта. вы не назначаете новый объект или массив старому. поэтому он передается по ссылке.

Код

    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
person Mukund Kumar    schedule 28.10.2014
comment
Неа. Всегда все проходит по цене. - person newacct; 28.10.2014
comment
просто введите приведенный выше код в консоль и посмотрите ... значение изменилось .. - person Mukund Kumar; 28.10.2014
comment
Существует освященная временем дискуссия о терминологии о вызове того, что Javascript передает по ссылке. Я предпочитаю обойти эту дискуссию и называть то, что JS делает для объектов и массивов, передается по указателю. Массивы и объекты всегда передаются по указателю. Если вы измените то, что было передано (доступ к указателю), оригинал будет изменен. Если вы назначаете другой массив или объект переменной-указателю, то оригинал не изменяется, потому что теперь ваша переменная указывает на другой массив или объект. По большей части это споры о терминологии, потому что не ведется споров о том, что происходит на самом деле. - person jfriend00; 28.10.2014
comment
@newacct - А для тех, кто любит утверждать, что все передается по значению, это никоим образом не помогает новичкам понять проблему, независимо от того, насколько технически правильным вы можете подумать, что находитесь на каком-то уровне. Любое хорошее объяснение должно объяснять разницу между тем, как передаются объекты и примитивы, так, чтобы новичок понимал разницу в практическом использовании, поскольку цель в вопросе и ответе, подобном этому, является четким объяснением, которое можно использовать для тех, кто этого не делает. • разбираться в деталях реализации или техническом значении терминов. - person jfriend00; 28.10.2014
comment
@ jfriend00: Дело в том, что в прохождении нет ничего особенного. Он работает так же, как присвоение или любая другая операция. Как только вы поймете, что все значения являются либо примитивами, либо указателями на объекты, и не будете говорить об объектах как о самих значениях, тогда не возникнет путаницы. Кроме того, семантика передачи и назначения в JavaScript идентична семантике в Java. И все, что передается по значению, в значительной степени похоже на то, как Java обычно описывается в StackOverflow, в сочетании с объяснением того, что все непримитивы являются указателями на объекты. - person newacct; 30.10.2014
comment
@newacct - суть здесь в том, чтобы объяснить людям, которые не являются опытными разработчиками и говорят, что все проходит по значению без дополнительных объяснений, просто недостаточно. Он не объясняет разницу в том, как передаются или присваиваются примитив и массив. Вы можете утверждать, что это технически правильно, но это недостаточное объяснение для неискушенных разработчиков. Сам ваш комментарий, который, как только вы поймете ... показывает, что этого недостаточно, пока вы не поймете другие вещи - поэтому вы не можете просто сказать, что все передано новичку и готово. - person jfriend00; 30.10.2014
comment
@ jfriend00: Но я здесь не отвечаю на вопрос. Я комментирую заявление, сделанное в ответе. Если бы я писал ответ, в нем было бы много содержания. Но здесь я просто указываю на утверждение, которое считаю неправильным, не включая тонны вещей, которые могут не относиться к теме. - person newacct; 30.10.2014