基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。
虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有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
其他回答
字符串、数字等基本类型变量始终作为传递按价值计算。基于这两个条件,数组和对象作为引用传递或值传递。如果要使用新的Object或array更改该Object或array的值,则它将通过value传递。object1={item:“car”};数组1=[1,2,3];此处是将新对象或数组分配给旧对象或数组。您没有更改属性的值旧对象的。因此它是按值传递的。如果要更改对象或数组的属性值,则会通过引用传递。object1.key1=“汽车”;阵列1[0]=9;此处您正在更改旧对象的属性值。您没有将新对象或数组分配给旧对象或数组。因此它是通过引用传递的。
Code
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
确定某个对象是否“通过引用传递”的一个简单方法是您是否可以编写“交换”函数。例如,在C中,您可以执行以下操作:
void swap(int *i, int *j)
{
int t;
t = *i;
*i = *j;
*j = t;
}
如果你不能在JavaScript中做到这一点,那就不是“通过引用传递”。
观察:如果观察者无法检查引擎的底层内存,则无法确定是否复制了不可变值或传递了引用。
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中使用术语“通过引用传递”的讨论,但要回答您的问题:
对象通过引用自动传递,无需特别声明
(摘自上述文章。)
我的两分钱。。。这是我理解它的方式。(如果我错了,请随时纠正我)
是时候抛弃你所知道的关于传递值/引用的一切了。
因为在JavaScript中,它是通过值传递还是通过引用传递都不重要。重要的是变异与传递给函数的参数赋值。
好吧,让我尽力解释一下我的意思。假设您有几个对象。
var object1 = {};
var object2 = {};
我们所做的是“任务”。。。我们为变量“object1”和“object2”分配了两个单独的空对象。
现在,假设我们更喜欢对象1。。。因此,我们“分配”一个新变量。
var favoriteObject = object1;
接下来,无论出于什么原因,我们决定更喜欢对象2。所以,我们做了一点重新分配。
favoriteObject = object2;
对象1或对象2未发生任何情况。我们根本没有改变任何数据。我们所做的只是重新分配我们最喜欢的对象。重要的是要知道object2和favoriteObject都分配给了同一个对象。我们可以通过这些变量中的任何一个来改变这个对象。
object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe
好了,现在让我们看一下像字符串这样的原语
var string1 = 'Hello world';
var string2 = 'Goodbye world';
再次,我们选择一个最喜欢的。
var favoriteString = string1;
我们的favoriteString和string1变量都分配给“Helloworld”。现在,如果我们想更改我们的收藏夹字符串???会发生什么???
favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'
噢。。。。发生了什么。我们无法通过更改收藏夹字符串来更改字符串1。。。为什么?因为我们没有更改字符串对象。我们所做的只是将favoriteString变量“RE ASSIGN”为一个新字符串。这实际上将其与字符串1断开。在前面的示例中,当我们重命名对象时,我们没有分配任何内容。(好吧,不是变量本身,……但是,我们确实将name属性分配给了一个新字符串。)相反,我们对保持2个变量和基础对象之间连接的对象进行了变异。(即使我们想修改或变异字符串对象本身,我们也不能这样做,因为字符串在JavaScript中实际上是不可变的。)
现在,转到函数和传递参数。。。。当你调用一个函数并传递一个参数时,你所做的基本上是对一个新变量的“赋值”,它的工作方式与你使用等号(=)赋值的方式完全相同。
举这些例子。
var myString = 'hello';
// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment
console.log(myString); // Logs 'hello'
console.log(param1); // Logs 'world'
现在,同样的事情,但有一个函数
function myFunc(param1) {
param1 = 'world';
console.log(param1); // Logs 'world'
}
var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);
console.log(myString); // logs 'hello'
好的,现在让我们举几个使用对象的例子。。。首先,没有函数。
var myObject = {
firstName: 'Joe',
lastName: 'Smith'
};
// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;
// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'
// Now, let's reassign the variable
otherObj = {
firstName: 'Jack',
lastName: 'Frost'
};
// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';
现在,同样的事情,但有一个函数调用
function myFunc(otherObj) {
// Let's mutate our object
otherObj.firstName = 'Sue';
console.log(otherObj.firstName); // Logs 'Sue'
// Now let's re-assign
otherObj = {
firstName: 'Jack',
lastName: 'Frost'
};
console.log(otherObj.firstName); // Logs 'Jack'
// Again, otherObj and myObject are assigned to 2 very different objects
// And mutating one object doesn't magically mutate the other
}
var myObject = {
firstName: 'Joe',
lastName: 'Smith'
};
// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);
console.log(myObject.firstName); // Logs 'Sue', just like before
好的,如果你通读了这篇文章,也许你现在对JavaScript中函数调用的工作方式有了更好的理解。不管什么是通过引用还是通过值传递。。。重要的是赋值与变异。
每次将变量传递给函数时,都会“赋值”参数变量的名称,就像使用等号(=)一样。
始终记住等号(=)表示赋值。始终记住,在JavaScript中向函数传递参数也意味着赋值。它们是相同的,并且这两个变量以完全相同的方式连接(也就是说它们不是,除非你认为它们被分配给了同一个对象)。
“修改变量”影响不同变量的唯一时间是基础对象发生变化时(在这种情况下,您没有修改变量,而是修改对象本身)。
区分对象和基元是没有意义的,因为它的工作方式与没有函数的情况完全相同,只是使用等号分配给一个新变量。
唯一的错误是当传递到函数中的变量的名称与函数参数的名称相同时。当发生这种情况时,您必须将函数内的参数视为该函数专用的一个全新变量(因为它是)
function myFunc(myString) {
// myString is private and does not affect the outer variable
myString = 'hello';
}
var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';
myFunc(myString);
console.log(myString); // Logs 'test'