基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
当前回答
函数内部的简单值不会改变函数外部的值(通过值传递),而复杂值会(通过引用传递)。
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
其他回答
我在AirBNB风格指南中找到了最简洁的解释:
基本体:访问基本体类型时,直接处理其价值一串数字布尔型无效的未定义
例如。:
var foo = 1,
bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
复杂:访问复杂类型时,需要处理对其值的引用对象大堆作用
例如。:
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
也就是说,基本类型是通过值传递的,复杂类型是通过引用传递的。
它总是按值传递,但对于对象,变量的值是一个引用。因此,当您传递一个对象并更改其成员时,这些更改将在函数之外持续存在。这使得它看起来像是通过引用传递。但如果您实际更改了对象变量的值,您将看到更改不会持续,这证明它确实是通过值传递的。
例子:
函数changeObject(x){x={member:“bar”};console.log(“in changeObject:”+x.member);}函数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);/*更改没有持续*/console.log(“changeMember之前:”+x.member);changeMember(x);console.log(“after changeMember:”+x.member);/*更改持续存在*/
输出:
before changeObject: foo
in changeObject: bar
after changeObject: foo
before changeMember: foo
in changeMember: bar
after changeMember: bar
观察:如果观察者无法检查引擎的底层内存,则无法确定是否复制了不可变值或传递了引用。
JavaScript或多或少与底层内存模型无关。没有参考²。JavaScript有值。两个变量可以保持相同的值(或者更准确:两个环境记录可以绑定相同的值)。唯一可以变异的值类型是通过抽象[[Get]]和[[Set]]操作的对象。如果您忘记了计算机和内存,这就是描述JavaScript行为所需的全部内容,它可以让您理解规范。
let a = { prop: 1 };
let b = a; // a and b hold the same value
a.prop = "test"; // The object gets mutated, can be observed through both a and b
b = { prop: 2 }; // b holds now a different value
现在你可能会问自己,两个变量如何在计算机上保持相同的值。然后,您可能会查看JavaScript引擎的源代码,很可能会发现引擎所用语言的程序员会调用引用。
所以事实上,你可以说JavaScript是“值传递”,而值可以被共享,你可以称JavaScript是“引用传递”,这对于低级语言的程序员来说可能是一个有用的逻辑抽象,或者你可以将行为称为“共享调用”。
由于JavaScript中没有引用,所有这些都没有错,也没有切中要害。因此,我不认为搜索答案特别有用。
²规范中的术语“参考”不是传统意义上的参考。它是一个对象和属性名称的容器,并且是一个中间值(例如,a.b计算为Reference{value=a,name=“b”})。术语“引用”有时也出现在规范中的不相关章节中。
在低级语言中,如果要通过引用传递变量,则必须在创建函数时使用特定语法:
int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
*age = *age + 1;
}
&age是对myAge的引用,但如果您需要该值,则必须使用*age转换引用。
JavaScript是一种高级语言,可以为您进行这种转换。
因此,尽管对象是通过引用传递的,但语言将引用参数转换为值。您不需要在函数定义上使用&来通过引用传递它,也不需要在功能体上使用*来将引用转换为值,JavaScript会为您执行此操作。
这就是为什么当您试图通过替换函数中的对象的值(即age={value:5})来更改该对象时,更改不会持久,但如果您更改了它的财产(即age.value=5),更改就会持久。
了解更多信息
如果您想要像其他语言一样的(正常)函数参数行为(传递值的副本)然后在传递到函数之前克隆对象:
function run()
{
var test = [];
test.push(1);
console.log('before: '+test); // 1
changeVariable(_.clone(test)); // (Note: I am using lodash _.clone() function)
console.log('after: '+test); // 1
}
function changeVariable(test2) {
var test1 = test2;
test1.push(2);
console.log('inside func:', test1); // inside func: [1,2]
}
run();