基元类型(数字、字符串等)是通过值传递的,但对象是未知的,因为它们既可以通过值传递(如果我们认为持有对象的变量实际上是对对象的引用),也可以通过引用传递(当我们认为对象的变量持有对象本身)。

虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?


当前回答

变量不“保存”对象;它有一个参考。您可以将该引用分配给另一个变量,现在两者都引用同一个对象。它总是按值传递(即使该值是引用…)。

无法更改作为参数传递的变量所持有的值,如果JavaScript支持通过引用传递,这是可能的。

其他回答

确定某个对象是否“通过引用传递”的一个简单方法是您是否可以编写“交换”函数。例如,在C中,您可以执行以下操作:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

如果你不能在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'

基本体(数字、布尔值等)按值传递。字符串是不可变的,所以对它们来说并不重要。对象通过引用传递(引用通过值传递)。

我已经多次阅读了这些答案,但直到我了解了Barbara Liskov所称的“通过共享呼叫”的技术定义,我才真正理解

通过共享调用的语义与通过引用调用的语义不同,因为对函数内函数参数的赋值对调用方不可见(与引用语义不同)[需要引用],因此例如,如果传递了变量,则无法在调用方的范围内模拟对该变量的赋值。然而,由于函数可以访问与调用方相同的对象(不进行复制),因此如果对象是可变的,则调用方可以看到函数中这些对象的突变,这可能与逐值调用语义不同。调用方可以看到函数中可变对象的变体,因为该对象未被复制或克隆,而是共享的。

也就是说,如果您访问参数值本身,参数引用是可变的。另一方面,对参数的赋值将在求值后消失,函数调用方无法访问。

这是对值传递和引用传递(JavaScript)的更多解释。在这个概念中,他们讨论的是通过引用传递变量和通过引用传递该变量。

传递值(基本类型)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3

应用于JavaScript中的所有基元类型(字符串、数字、布尔值、未定义和null)。a被分配一个存储器(例如0x001),b在存储器中创建该值的拷贝(例如0x002)。因此,更改一个变量的值不会影响另一个变量,因为它们都位于两个不同的位置。


通过引用(对象)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }

JavaScript引擎将对象分配给变量c,并指向某个内存,例如(0x012)。当d=c时,在该步骤中,d指向相同的位置(0x012)。更改任何变量的值都会更改这两个变量的值。函数是对象


特殊情况,通过引用传递(对象)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }

等号(=)运算符设置新的内存空间或地址