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

虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有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'

我发现Undercore.js库的extend方法非常有用,当我想将一个对象作为一个参数传递时,它可以被修改或完全替换。

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

这里有一些关于JavaScript中使用术语“通过引用传递”的讨论,但要回答您的问题:

对象通过引用自动传递,无需特别声明

(摘自上述文章。)

这样想:它总是通过价值传递。然而,对象的值不是对象本身,而是对该对象的引用。

下面是一个例子,传递一个数字(一个原始类型)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

对对象重复此操作会产生不同的结果:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

再举一个例子:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}

我会说这是通过复印件传递的-

考虑参数和变量对象是在函数调用开始时创建的执行上下文中创建的对象,传递到函数中的实际值/引用只存储在这个参数+变量对象中。

简单地说,对于基元类型,值在函数调用开始时被复制,对于对象类型,引用被复制。