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?
}

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

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"

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 = ...

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

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

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

  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
Другие вопросы по тегам