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; здесь у вас есть документация, которую вы хотите:
- Переменная типа примитива, такая как строка, число всегда передается как передаваемое значение.
Массив и Объект передаются как передача по ссылке или передача по значению на основе этих двух условий.
если вы меняете значение этого объекта или массива с помощью нового объекта или массива, то оно передается по значению.
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