基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?
当前回答
这些短语/概念最初是在JS创建之前很久定义的,它们没有准确描述javascript的语义。我认为尝试将它们应用于JS会导致更多的困惑。
所以不要被“通过引用/值传递”挂断。
考虑以下事项:
变量是指向值的指针。重新分配变量只会将指针指向新值。重新分配变量不会影响指向同一对象的其他变量,因为每个变量都有自己的指针。
所以如果我必须给它起个名字,我会说“passbypointer”——我们不处理JS中的指针,但底层引擎会处理。
// 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'
一些最终意见:
短语“传递值/引用”仅用于描述语言的行为,不一定是实际的底层实现。由于这种抽象,对于正确解释至关重要的关键细节丢失了,这不可避免地导致了当前的情况,在没有额外信息的情况下,单个术语无法充分描述实际行为。人们很容易认为原语是由特殊规则强制执行的,而对象不是,但原语只是指针链的末端。作为最后一个示例,请考虑为什么清除数组的常见尝试不能按预期工作。
变量a=[1,2];变量b=a;a=[];console.log(b);//[1,2]//不起作用,因为“b”仍指向原始数组
其他回答
这里有一些关于JavaScript中使用术语“通过引用传递”的讨论,但要回答您的问题:
对象通过引用自动传递,无需特别声明
(摘自上述文章。)
如果您想要像其他语言一样的(正常)函数参数行为(传递值的副本)然后在传递到函数之前克隆对象:
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();
观察:如果观察者无法检查引擎的底层内存,则无法确定是否复制了不可变值或传递了引用。
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”})。术语“引用”有时也出现在规范中的不相关章节中。
变量不“保存”对象;它有一个参考。您可以将该引用分配给另一个变量,现在两者都引用同一个对象。它总是按值传递(即使该值是引用…)。
无法更改作为参数传递的变量所持有的值,如果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