题 JavaScript是传递引​​用还是按值传递语言?


原始类型(Number,String等)按值传递,但是对象是未知的,因为它们可以是值传递的(如果我们认为持有对象的变量实际上是对象的引用)并且通过引用传递(当我们认为对象的变量保存对象本身时)。

虽然最后并不重要,但我想知道提交传递约定的参数的正确方法是什么。是否有JavaScript规范的摘录,它定义了与此相关的语义?


1138
2018-02-05 21:23


起源


我认为你不小心翻了你的值传递和传递引用的定义......“按值传递(如果我们认为持有对象的变量实际上是对象的引用)并通过-by-reference(当我们认为对象的变量保存对象本身时)“ - Niko Bellic
是。无论语法如何,在任何编程语言的任何函数调用中,传递引用意味着传递给变量的数据在传递给函数时不会被复制,因此函数对传递的变量所做的任何修改都将被保留在函数调用终止后的程序中。按值传递意味着与变量关联的数据在传递给函数时实际上被复制,当函数返回时,当变量超出函数体的范围时,此函数对此变量所做的任何修改都将丢失。 - John Sonderson
@MjrKusanagi我不认为 Object.create 可以认为是适当的克隆技术,即如果更改对象的原始副本,它将影响克隆,只有克隆的更改不会影响原始对象。当然,它有它的用途,JavaScript是围绕这个概念设计的(原型继承),但我认为这是误导性的 Object.create 克隆对象的唯一机制。 - Danail Nachev
这个老问题有点有毒,因为它的反对意见很大。 JavaScript是严格的 通过按值。 - Pointy
@DanailNachev这个术语令人遗憾地令人困惑。问题是,“按值传递”和“按引用传递”是早于许多更现代的编程语言功能的术语。 “值”和“参考”一词是指 特别 到函数调用表达式中出现的参数。 JavaScript始终评估函数调用参数列表中的每个表达式 之前 调用函数,所以参数总是值。令人困惑的部分是对对象的引用是常见的JavaScript值。然而,这并不能使它成为“通过参考传递”的语言。 - Pointy


答案:


它在Javascript中很有趣。考虑这个例子:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

这会产生输出:

10
changed
unchanged
  • 如果它是纯粹的价值传递,那么改变 obj1.item 对...没有影响 obj1 功能之外。
  • 如果它是纯粹的参考传递,那么一切都会改变。 num 将会 100,和 obj2.item 会读 "changed"

相反,情况是传入的项目是按值传递的。但是通过值传递的项目是 本身 一个参考。 从技术上讲,这称为 呼叫由共享

实际上,这意味着如果您更改参数本身(如同 num 和 obj2),这不会影响送入参数的项目。 但如果你改变了 INTERNALS 参数,将传播回来(如同 obj1)。


1361
2018-03-15 16:38



这与C#完全相同(或至少在语义上)。对象有两种类型:Value(基本类型)和Reference。 - Peter Lee
我认为这也用在Java中:按值引用。 - Jpnh
真正的原因是在changeStuff中,num,obj1和obj2是引用。当你改变了 item obj1引用的对象的属性,您正在更改最初设置为“未更改”的项属性的值。当您为obj2指定值{item:“changed”}时,您正在更改对新对象的引用(当函数退出时,它立即超出范围)。如果你将函数命名为numf,obj1f和obj2f这样的函数,那么会发生什么变得更加明显。然后你看到params隐藏了外部var名称。 - jinglesthula
@BartoNaz不是真的。你想要的是通过引用传递引用,而不是通过值传递引用。但JavaScript总是按值传递引用,就像它按值传递其他所有内容一样。 (为了进行比较,C#具有类似于JavaScript和Java的pass-reference-by-value行为,但允许您指定pass-reference-by-reference with the ref 通常你只需让函数返回新对象,然后在调用函数的位置进行赋值。例如。, foo = GetNewFoo(); 代替 GetNewFoo(foo); - Tim Goodman
虽然这个答案是最受欢迎的,但它可能会有些混乱,因为它表明“如果这是纯粹的价值传递”。 JavaScript的 是 纯粹的传值。但传递的值是参考。这根本不限于参数传递。您可以简单地复制变量 var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed'; 并会观察到与你的例子相同的效果。因此,我亲自参考蒂姆古德曼的答案 - chiccodoro


它总是按值传递,但对于对象,变量的值是引用。因此,当您传递一个对象并更改它时 会员,这些变化在功能之外持续存在。这样做  喜欢通过引用传递。但是,如果您实际更改了对象变量的值,您将看到更改不会持续存在,从而证明它确实是通过值传递的。

例:

function changeObject(x) {
  x = {member:"bar"};
  alert("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  alert("in changeMember: " + x.member);
}

var x = {member:"foo"};

alert("before changeObject: " + x.member);
changeObject(x);
alert("after changeObject: " + x.member); /* change did not persist */

alert("before changeMember: " + x.member);
changeMember(x);
alert("after changeMember: " + x.member); /* change persists */

输出:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar

376
2018-02-05 21:37



@daylight:其实,你错了;如果它是由const ref传递的,那么尝试执行changeObject会导致错误,而不仅仅是失败。尝试在C ++中为const引用分配一个新值,编译器拒绝它。在用户术语中,这是传递值和传递const引用之间的差异。 - deworde
@daylight:这不是常数。在 changeObject,我改变了 x 包含对新对象的引用。 x = {member:"bar"}; 相当于 x = new Object(); x.member = "bar";  顺便说一句,我所说的C#也是如此。 - Tim Goodman
@daylight:对于C#,你可以从函数外部看到这个,如果你使用的话 ref 关键字你可以通过引用传递引用(而不是通过值传递引用的默认值),然后将更改指向a new Object()  将 坚持。 - Tim Goodman
@adityamenon很难回答“为什么”,但我会注意到Java和C#的设计者做出了类似的选择;这不仅仅是一些JavaScript的怪异。实际上,它非常一致地传递价值,让人们感到困惑的是价值可以作为参考。它与在C ++中传递指针(通过值)然后解除引用以设置成员没有多大区别。没有人会对这种变化持续存在感到惊讶。但是因为这些语言抽象了指针并默默地为你解除引用,人们会感到困惑。 - Tim Goodman
换句话说,这里令人困惑的事情不是通过值传递/传递引用。一切都是价值传递,完全停止。令人困惑的是,您无法传递对象,也无法将对象存储在变量中。每次你 认为 你这样做,你是 其实 传递或存储对该对象的引用。但是当你去访问它的成员时,会发生一个静默的解除引用,这会使你的变量保持实际对象的虚构成为可能。 - Tim Goodman


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

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


132
2017-08-04 11:06



这让我感到困惑。是不是通过参考传递参考?
作者意味着通过传递引用,您传递的是一个引用值(另一种方式是认为它传递了内存地址的值)。因此,如果您重新声明对象,原始版本不会更改,因为您在不同的内存位置创建新对象。如果更改属性,原始对象将更改,因为您在原始内存位置(未重新分配)更改了它。 - Huy-Anh Hoang


我的2分......这是我理解的方式。 (如果我错了,请随意纠正我)

是时候抛弃你所知道的关于通过值/参考传递的所有内容了。

因为在JavaScript中,无论是通过值传递还是通过引用或其他任何方式传递都无关紧要。 重要的是突变与传递给函数的参数的赋值。

好的,让我尽力解释我的意思。假设你有几个对象。

var object1 = {};
var object2 = {};

我们所做的是“赋值”......我们已经为变量“object1”和“object2”分配了2个单独的空对象。

现在,让我们说我们更喜欢object1 ......所以,我们“分配”一个新的变量。

var favoriteObject = object1;

接下来,无论出于何种原因,我们都认为我们更喜欢对象2。所以,我们只是做一点重新分配。

favoriteObject = object2;

object1或object2没有任何反应。我们根本没有改变任何数据。我们所做的就是重新分配我们最喜欢的对象。重要的是要知道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变量都分配给'Hello world'。现在,如果我们想改变我们最喜欢的字符串怎么办?会发生什么???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

呃哦......发生了什么事。我们无法通过更改favoriteString更改string1 ...为什么?因为字符串是不可变的,我们没有改变它。我们所做的只是“RE ASSIGN”favoriteString到一个新的字符串。这基本上与string1断开了连接。在前面的示例中,当我们重命名对象时,我们没有分配任何内容。 (好吧,实际上......我们做了,我们将name属性分配给一个新字符串。)相反,我们只是改变了保持2个变量和底层对象之间连接的对象。

现在,转到函数和传递参数....当你调用一个函数,并传递一个参数时,你实际上做的是“赋值”到一个新变量,它的工作方式与你简单地使用等号(=)。

拿这些例子。

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
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates 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 no longer mutates 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中的工作原理。无论是通过引用还是通过值传递的东西都没关系......重要的是赋值与变异。

每次将变量传递给函数时,您都可以“分配”参数变量的名称,就像使用等号(=)一样。

永远记住,等号(=)表示赋值。 永远记住,将参数传递给函数也意味着赋值。 它们是相同的,2个变量以完全相同的方式连接。

修改变量影响不同变量的唯一时间是基础对象发生变异时。

在对象和基元之间进行区分是没有意义的,因为它的工作方式与您没有函数并使用等号分配给新变量的方式相同。

唯一的问题是当您传递给函数的变量名称与函数参数的名称相同时。当发生这种情况时,你必须将函数内部的参数视为一个私有函数的全新变量(因为它是)

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'

84
2017-09-26 04:06



@Rimbuaj - 我确实有一个博客..无论如何我现在都做。一个包含有关编码的讨论。我刚刚发布了关于编码的第一篇文章。 constalink.com/blog - Ray Perea
对于任何C程序员,请考虑char *。 foo(char *a){a="hello";}  什么都不做,但如果你这样做 foo(char *a){a[0]='h';a[1]='i';a[2]=0;} 因为改变了 a 是一个通过值传递的内存位置,它引用一个字符串(char数组)。允许使用C中的值传递结构(类似于js对象),但不建议这样做。 JavaScript只是强制执行这些最佳实践,并隐藏了不必要的,通常不受欢迎的错误......它确实使阅读更容易。 - technosaurus
这是正确的 - 条款 通过按值 和 通过按引用 在编程语言设计中有意义,这些意义与对象变异毫无关系。这都是关于函数参数如何工作的。 - Pointy
现在我明白了obj1 = obj2意味着obj1和obj2现在都指向同一个参考位置,如果我修改obj2的内部,引用obj1将暴露相同的内部。如何在我这样做的时候复制一个对象 source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2" 该来源仍然是{“id”:“1”}? - Machtyn
我发布了传统定义的另一个答案,希望能够减少混淆。传递的“按值传递”和“传递引用”的定义是在自动解除引用之前的内存指针的日期定义的。完全可以理解,对象变量的值实际上是内存指针位置,而不是对象。虽然您对分配与变异的讨论可能有用,但没有必要抛弃传统术语及其定义。变异,赋值,按值传递,按引用传递等不得相互矛盾。 - C Perkins


考虑以下:

  1. 变量是 指针记忆中的价值观。
  2. 重新分配变量只会将指针指向新值。
  3. 重新分配变量永远不会影响指向同一对象的其他变量

所以, 把...忘了吧 “通过引用/值传递” 不要挂断“通过引用/值传递”,因为:

  1. 这些术语仅用于描述 行为 语言,不一定是实际的底层实现。由于这种抽象,对于正确解释至关重要的关键细节将丢失,这不可避免地导致当前情况,即单个术语无法充分描述实际行为,必须提供补充信息
  2. 这些概念最初并不是为了特别描述javascript而定义的,因此当他们只是加入混乱时我不觉得有必要使用它们。

回答你的问题:指针被传递。


// 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'

一些最终评论:

  • 想到这一点很诱人 原语 由特殊规则强制执行 对象 不是,但原语只是指针链的末端。
  • 作为最后一个例子,考虑为什么清除数组的常见尝试不能按预期工作。


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array

57
2018-06-02 23:04



额外信用的后续问题;)垃圾收集如何工作?如果我循环变量一百万 {'George', 1} 值,但一次只使用其中一个,那么其他人如何管理?当我将变量赋值给另一个变量的值时会发生什么?我是指向指针,还是指向右操作数的指针?是否 var myExistingVar = {"blah", 42}; var obj = myExistingVar; 导致 obj 指向 {"blah", 42}, 或者 myExistingVar? - Michael Hoffmann
@MichaelHoffmann这些值得他们自己的SO问题,并且可能已经得到了比我能管理得更好的答案。话虽如此, 1) 我在浏览器开发工具中运行了一个内存配置文件,用于循环功能,例如您描述的循环功能,并在整个循环过程中看到内存使用量的峰值。这似乎表明在循环的每次迭代中确实创建了新的相同对象。当尖峰突然下降时,垃圾收集器只清理了一组这些未使用的对象。 - geg
@MichaelHoffmann 2) 关于类似的事情 var a = b,javascript没有提供使用指针的机制,因此变量永远不会指向指针(就像在C中一样),尽管底层的javascript引擎无疑会使用它们。所以...var a = b 会指出 a“对于右操作数的指针” - geg
我在这里问了问题#1(特别是关于Chrome的问题,因为每个浏览器的实现可能都不同) stackoverflow.com/q/42778439/539997 而且我还在想着如何写出问题#2。任何帮助表示赞赏。 - Michael Hoffmann
@CPerkins Fair点。我试着澄清一下。 - geg


函数外部的对象通过提供对外部obejct的引用而传递给函数。当您使用该引用来操纵其对象时,外部对象因此受到影响。但是,如果在函数内部您决定将引用指向其他内容,则根本不会影响外部对象,因为您所做的只是将引用重定向到其他内容。


23
2018-02-13 11:00





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

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

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}

16
2018-02-05 21:49





Javascript总是如此 通过按值,一切都是有价值的。对象是值,对象的成员函数本身就是值(请记住,函数是Javascript中的第一类对象)。此外,关于Javascript中的所有内容都是一个概念 目的这是错的。字符串,符号,数字,布尔值,空值和未定义是 原语。有时他们可以利用从基础原型继承的一些成员函数和属性,但这只是为了方便,并不意味着它们本身就是对象。请尝试以下内容以供参考

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

在两个警报中,您都会发现未定义的值。


14
2017-12-30 02:20



-1,它并不总是按值传递。从MDC:“如果传递一个对象(即非原始值,如Array或用户定义的对象)作为参数,则对该对象的引用将传递给该函数。” - Nick
@Nick:总是按值传递。期。传递对象的引用 按价值 到功能。这不是通过引用传递的。 “通过引用传递”几乎可以被认为是传递变量本身,而不是它的价值; 任何 更改函数对参数的更改(包括完全用不同的对象替换它!)将反映在调用者中。最后一点在JS中是不可能的,因为 JS不通过引用传递  - 它按值传递引用。区别是微妙的,但对于理解其局限性非常重要。 - cHao
对于未来的堆垛机......关于你的这个参考: x = "teste"; x.foo = 12; 仅仅因为属性不是持久性的,并不意味着它不是一个对象。正如MDN所说: 在JavaScript中,几乎所有东西都是对象。除null和undefined之外的所有基本类型都被视为对象。可以为它们分配属性(某些类型的已分配属性不是持久性的),并且它们具有对象的所有特征。  链接 - slacktracer
MDN是用户编辑的维基,它在那里是错误的。规范性参考文献是ECMA-262。参见S. 8“参考规范类型”,它解释了如何解析引用,以及8.12.5“[[Put]]”,它用于解释AssignmentExpression到Reference,以及对象coersion 9.9 ToObject。对于原始值,Michael已经解释了ToObject的功能,如规范中所述。但另见s。 4.3.2原始价值。 - Garrett
@WonderLand:不,他不是。从来没有能够通过引用传递的人可能永远不会理解通过引用传递和通过值传递引用之间的差异。但他们在那里,他们很重要。我不在乎误导人,只是让它听起来更容易。 - cHao


在JavaScript中,值的类型 独自 控制是否将分配该值 值拷贝 或者 参考拷贝

始终通过value-copy分配/传递原始值

  • null
  • undefined
  • 布尔
  • 符号 ES6

复合值始终由引用副本分配/传递

  • 对象
  • 阵列
  • 功能

例如

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

在上面的片段中,因为 2 是一个标量原语, a 持有该值的一个初始副本,和 b 被赋予该值的另一​​个副本。改变时 b,你绝不会改变它的价值 a

但两者都有 c 和 d 是对同一共享值的单独引用 [1,2,3],这是一个复合值。重要的是要注意两者都不是 c 也不 d 更“拥有”了 [1,2,3] value - 两者都是对值的等同对等引用。所以,当使用任何一个引用来修改(.push(4))实际共享 array 值本身,它只影响一个共享值,两个引用都将引用新修改的值 [1,2,3,4]

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

当我们完成任务时 b = [4,5,6],我们绝对不会影响到哪里 a 仍在引用([1,2,3])。要做到这一点, b 必须是一个指针 a 而不是参考 array  - 但JS中没有这样的功能!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

当我们传递参数时 a,它指定了一份副本 a 参考 xx 和 a 是指向相同的单独引用 [1,2,3] 值。现在,在函数内部,我们可以使用该引用来改变值本身(push(4))。但是当我们完成任务时 x = [4,5,6],这绝不会影响初始参考的位置 a 指向 - 仍然指向(现在修改) [1,2,3,4] 值。

有效传递复合值(如 array)通过值复制,您需要手动复制它,以便传递的引用仍然不指向原始引用。例如:

foo( a.slice() );

可以通过引用副本传递的复合值(对象,数组等)

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

这里, obj 充当标量原始属性的包装器 a。传递给 foo(..),副本 obj 引用传入并设置为 wrapper参数。我们现在可以用了 wrapper 引用访问共享对象,并更新其属性。功能完成后, obj.a 将看到更新的值 42

资源


12
2018-05-12 22:20



您首先声明“复合值始终通过引用副本分配/传递”,然后您声明“分配一个 复制 在你所谓的“复合值”的情况下,实际变量值是引用(即内存指针)。正如你所解释的那样,引用被复制...所以变量 值被复制,再次强调参考是有价值的。这意味着JavaScript是所有类型的pass-by-value。按值传递意味着传递变量值的副本。值是对象/数组的引用并不重要。 - C Perkins
您引入了新的术语(值复制/引用副本),这使得事情变得更加复杂。只有副本,期间。如果传递一个原语,则传递了实际原始数据的副本,如果传递一个对象,则传递该对象的内存位置的副本。这就是你需要说的。任何事情都会让人们更加困惑。 - Scott Marcus


关于按价值和参考进行复制,传递和比较的非常详细的解释 本章 “JavaScript:The Definitive Guide”一书。

在我们离开主题之前   通过操纵对象和数组   参考,我们需要澄清一点   命名法。短语“经过   参考“可以有几个含义。   对于一些读者来说,这句话指的是   一种函数调用技术   允许函数分配新值   它的论点和拥有它们   修改后的值可见   功能。这不是这个词的方式   在本书中使用。我们的意思是   只是对对象的引用   或数组 - 不是对象本身 -   被传递给一个函数。一个功能   可以使用引用来修改   对象或元素的属性   数组。但如果功能   用a覆盖引用   引用新对象或数组,   该修改不可见   功能之外。读者   熟悉其他含义   这个词可能更喜欢这样说   对象和数组传递   值,但传递的值是   实际上是一个参考而不是   对象本身。

有一点我还是想不通。检查下面的代码。有什么想法吗?

function A() {}
A.prototype.foo = function() {
    return 'initial value';
}


function B() {}
B.prototype.bar = A.prototype.foo;

console.log(A.prototype.foo()); //initial value
console.log(B.prototype.bar()); //initial value

A.prototype.foo = function() {
    return 'changed now';
}

console.log(A.prototype.foo()); //changed now
console.log(B.prototype.bar()); //Why still 'initial value'???

10
2018-02-05 21:36



我已经解释了你已经完成了什么以及它意味着什么,但这些评论不允许我发布超过600个字符或其他什么,所以我在下面发布了它作为答案。请检查并评论。 thx~daylight - raddevus
所以似乎B.prototype.bar = A.prototype.foo;通过引用分配,但后面的A.prototype.foo = function(){...}正在改变A.prototype的foo属性的值并打破引用。 B.prototype.bar仍然保持对同一个第一个函数的引用。 - igor
将一个函数设置为等于另一个函数不是通过引用的赋值。 'foo'和'bar'只是作为对象A和B成员的函数 - Greg
这是一个将函数实现为单个数组成员的演示。数组将通过引用进行复制,但函数将按值复制。 jsfiddle.net/ug04ks33 - Greg


有一些关于在JS中使用“通过引用传递”一词的讨论 这里,但要回答你的问题:

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

(从上面提到的文章。)


3
2017-11-01 12:43



链接的文章不再包含该语句,并且完全避免使用“通过引用传递”。 - C Perkins