JavaScript 是按引用传递还是按值传递语言?
Posted
技术标签:
【中文标题】JavaScript 是按引用传递还是按值传递语言?【英文标题】:Is JavaScript a pass-by-reference or pass-by-value language? 【发布时间】:2009-02-05 21:23:56 【问题描述】:原始类型(数字、字符串等)按值传递,但对象是未知的,因为它们都可以按值传递(如果我们认为持有对象的变量实际上是引用到对象)和通过引用传递(当我们认为对象的变量包含对象本身时)。
虽然最后并不重要,但我想知道呈现参数传递约定的正确方法是什么。是否有 javascript 规范的摘录,它定义了与此相关的语义?
【问题讨论】:
我认为您不小心翻转了按值传递和按引用传递的定义...“按值传递(如果我们认为持有对象的变量实际上是对象的引用)和按引用传递(当我们认为对象的变量包含对象本身时)" 是的。无论语法如何,在任何编程语言的任何函数调用中,按引用传递意味着与传递的变量关联的数据在传递给函数时不会被复制,因此函数对传递的变量所做的任何修改都将被保留在函数调用终止后的程序中。按值传递意味着与变量关联的数据在传递给函数时实际上被复制,并且当函数返回时变量超出函数体的范围时,该函数对此类变量所做的任何修改都将丢失。 这个老问题有点有毒,因为它的大量支持的答案是不正确的。 JavaScript is strictly pass-by-value. @DanailNachev 令人遗憾的是,术语令人困惑。问题是,“按值传递”和“按引用传递”是许多更现代的编程语言功能出现之前的术语。 “值”和“引用”这两个词专门指的是出现在函数调用表达式中的参数。 JavaScript 总是在调用函数之前计算函数调用参数列表中的每个表达式,因此参数总是值。令人困惑的部分是对对象的引用是常见的 JavaScript 值。然而,这并不能使它成为一种“通过引用传递”的语言。 @DanailNachev“通过引用传递”具体意味着如果你有var x=3, y=x; f(x); alert(y === x);
,那么函数f()
可以使警报报告false
而不是true
。在 JavaScript 中,这是不可能的,所以它不是按引用传递。可以将引用传递给可修改的对象,这很好,但这不是“通过引用传递”的意思。正如我所说,令人遗憾的是,术语如此混乱。
【参考方案1】:
在 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
根本不是引用,那么更改obj1.item
不会影响函数外部的obj1
。
如果参数是正确的引用,那么一切都会改变。 num
是 100
,obj2.item
是 "changed"
。相反,num
保持 10
和 obj2.item
保持 "unchanged
"。
相反,情况是传入的item是按值传递的。但是按值传递的项目本身是一个引用。 从技术上讲,这称为call-by-sharing。
实际上,这意味着如果您更改参数本身(如num
和obj2
),不会影响输入参数的项目。但是,如果您更改参数的 internals,它将传播回来(与 obj1
一样)。
【讨论】:
这与 C# 完全相同(或至少在语义上)。对象有两种类型:值(原始类型)和引用。 我想这在Java中也有使用:reference-by-value。 真正的原因是在changeStuff 中,num、obj1 和obj2 是引用。当您更改 obj1 引用的对象的item
属性时,您正在更改最初设置为“未更改”的 item 属性的值。当您为 obj2 分配 item: "changed" 的值时,您正在更改对新对象的引用(当函数退出时,它立即超出范围)。如果您将函数参数命名为 numf、obj1f 和 obj2f 之类的东西,那么发生的事情就会变得更加明显。然后您会看到参数隐藏了外部变量名称。
@BartoNaz 不是真的。您想要的是按引用传递引用,而不是按值传递引用。但是 JavaScript 总是按值传递引用,就像它按值传递其他所有内容一样。 (相比之下,C# 具有类似于 JavaScript 和 Java 的按值传递行为,但允许您使用 ref
关键字指定按引用传递。)通常您只需让函数返回新对象,并在调用函数的位置进行赋值。例如,foo = GetNewFoo();
而不是 GetNewFoo(foo);
虽然这个答案是最受欢迎的,但它可能会有点令人困惑,因为它声明“如果它是纯按值传递”。 JavaScript 是纯值传递。但是传递的值是一个引用。这根本不限于参数传递。您可以简单地通过 var obj1 = item: 'unchanged' ; var obj2 = obj1; obj2.item = 'changed';
复制变量,并观察到与您的示例相同的效果。因此我个人参考了 Tim Goodman 的回答【参考方案2】:
它总是按值传递,但对于对象,变量的值是一个引用。因此,当您传递一个对象并更改其成员时,这些更改会在函数之外持续存在。这使得它看起来像通过引用传递。但是如果你真的改变了对象变量的值,你会发现改变并没有持续,证明它真的是按值传递。
例子:
function changeObject(x)
x = member: "bar" ;
console.log("in changeObject: " + x.member);
function changeMember(x)
x.member = "bar";
console.log("in changeMember: " + x.member);
var x = member: "foo" ;
console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */
console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */
输出:
before changeObject: foo
in changeObject: bar
after changeObject: foo
before changeMember: foo
in changeMember: bar
after changeMember: bar
【讨论】:
@daylight:其实你错了;如果它是由 const ref 传递的,试图做 changeObject 会导致错误,而不仅仅是失败。尝试为 C++ 中的 const 引用分配一个新值,编译器会拒绝它。用用户的话来说,这就是按值传递和按常量引用传递的区别。 @daylight:这不是恒定的参考。在changeObject
中,我更改了x
以包含对新对象的引用。 x = member:"bar";
等同于 x = new Object(); x.member = "bar";
顺便说一句,我所说的 C# 也是如此。
@daylight:对于C#,你可以从函数外部看到这一点,如果你使用ref
关键字你可以通过引用传递引用(而不是默认的通过值传递引用) ,然后指向new Object()
的更改将持续存在。
@adityamenon 很难回答“为什么”,但我注意到 Java 和 C# 的设计者做出了类似的选择;这不仅仅是一些 JavaScript 的怪异之处。确实,它非常一致地按值传递,让人们感到困惑的是,值可以作为参考。这与在 C++ 中传递一个指针(按值)然后取消引用它以设置成员并没有太大区别。没有人会对这种变化持续存在感到惊讶。但是因为这些语言抽象出指针并默默地为您解引用,所以人们会感到困惑。
换句话说,这里令人困惑的不是按值传递/按引用传递。一切都是按值传递,句号。令人困惑的是,您不能传递对象,也不能将对象存储在变量中。每次你认为你正在这样做,你实际上传递或存储对该对象的引用。但是当你去访问它的成员时,会发生一个静默的取消引用,这会延续你的变量持有实际对象的虚构。【参考方案3】:
变量不“持有”对象;它有一个参考。您可以将该引用分配给另一个变量,现在两者都引用同一个对象。它总是按值传递(即使该值是引用...)。
没有办法改变作为参数传递的变量所持有的值,如果 JavaScript 支持通过引用传递,这将是可能的。
【讨论】:
这让我有点困惑。不是按引用传递引用吗? 作者的意思是,通过传递引用,你传递的是一个引用值(另一种思考方式是传递内存地址的值)。所以这就是为什么如果你重新声明对象,原来的对象不会改变,因为你在不同的内存位置创建了一个新对象。如果你改变了一个属性,原来的对象也会改变,因为你在原来的内存位置(没有重新分配)改变了它。 “按值传递引用”这句话似乎不必要地令人困惑和多余。当传递一个引用时,当然一些值必须被传递。虽然在技术上是正确的,但大多数人可能默认假设任何东西都是按值传递的,除非另有说明。所以当然引用是按值传递的,除非它本身是按引用传递的(有点像 C 中指向指针的指针),但在这种情况下,Javascript 甚至不支持,所以我认为它无助于使概念更清晰 与 JavaScript 混淆的地方在于它没有提供任何选择,@geg:复杂类型将总是被间接处理,简单类型总是 i> 直接。没有办法获得对整数的引用,也无法阻止传递对元组的引用。这……有时会很尴尬。 简单地说,在十年之后,引用是按值复制的。【参考方案4】:我的两分钱...这是我理解的方式。 (如有错误欢迎指正)
是时候扔掉你所知道的关于按值/引用传递的一切了。
因为在 JavaScript 中,它是通过值传递还是通过引用传递或其他方式都无关紧要。 重要的是突变与传递给函数的参数的分配。
好的,让我尽力解释我的意思。假设您有几个对象。
var object1 = ;
var object2 = ;
我们所做的是“赋值”...我们已将 2 个单独的空对象分配给变量“object1”和“object2”。
现在,假设我们更喜欢 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;
我们最喜爱的String 和 string1 变量都分配给了“Hello world”。现在,如果我们想改变我们的 favoriteString 怎么办???会发生什么???
favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'
呃哦....发生了什么事。我们无法通过更改 favoriteString 来更改 string1... 为什么?因为我们没有更改我们的字符串object。我们所做的只是将喜爱的字符串变量“重新分配”到一个新字符串。这实质上将它与 string1 断开了连接。在前面的例子中,当我们重命名我们的对象时,我们没有分配任何东西。 (嗯,不是变量本身,......但是,我们确实将 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 中将参数传递给函数 也意味着赋值。 它们是相同的,并且 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'
【讨论】:
对于任何 C 程序员来说,想想 char*。foo(char *a)a="hello";
什么都不做,但如果你这样做 foo(char *a)a[0]='h';a[1]='i';a[2]=0;
它会在外部更改,因为 a
是一个通过引用字符串(char 数组)的值传递的内存位置。允许在 C 中按值传递结构(类似于 js 对象),但不推荐。 JavaScript 只是强制执行这些最佳实践并隐藏不必要的和通常不受欢迎的杂物……而且它确实使阅读更容易。
这是正确的 - 术语 pass-by-value 和 pass-by-reference 在编程语言设计中具有含义,而这些含义具有与对象突变完全无关。这都是关于函数参数如何工作的。
现在我明白了 obj1 = obj2 意味着 obj1 和 obj2 现在都指向同一个引用位置,如果我修改 obj2 的内部结构,引用 obj1 将暴露相同的内部结构。如何复制一个对象,以便当我执行 source = "id":"1"; copy = source /*this is wrong*/; copy.id="2"
时该源仍然是 "id":"1"?
我用传统定义发布了另一个答案,希望能减少混淆。 “按值传递”和“按引用传递”的传统定义是在自动取消引用之前的内存指针时代定义的。对象变量的值实际上是内存指针位置,而不是对象,这一点非常好理解。尽管您对分配与变异的讨论可能有用,但没有必要抛弃传统术语或其定义。变异、赋值、传值、传引用等不能相互矛盾。
“数字”是否也是“不可变的”?【参考方案5】:
这些短语/概念最初是在 JS 创建之前很久就定义的,它们不能准确地描述 javascript 的语义。我认为尝试将它们应用于 JS 会导致更多的混乱。
所以不要纠结于“通过引用/值传递”。
考虑以下几点:
-
变量是指向值的指针。
重新分配变量只是将该指针指向一个新值。
重新分配变量永远不会影响指向同一对象的其他变量,因为每个变量都有自己的指针。
所以如果我必须给它起一个名字,我会说 "pass-by-pointer" -- 我们不处理 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'
一些最终的cmets:
短语“按值/引用传递”仅用于描述语言的行为,不一定是实际的底层实现。这种抽象的结果是丢失了对一个体面的解释至关重要的关键细节,这不可避免地导致了当前的情况,即在没有额外信息的情况下,单个术语无法充分描述实际行为。 很容易认为 primitives 是由特殊规则强制执行的,而 objects 不是,但primitives 只是指针链的末端。 作为最后一个示例,请考虑为什么清除数组的常见尝试无法按预期工作。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
【讨论】:
额外学分的后续问题 ;) 垃圾收集是如何工作的?如果我通过一百万个'George', 1
值循环一个变量,但一次只使用其中一个,那么如何管理其他变量?当我将一个变量分配给另一个变量的值时会发生什么?然后我是指向一个指针,还是指向右操作数的指针? var myExistingVar = "blah", 42; var obj = myExistingVar;
是否导致 obj
指向 "blah", 42
或 myExistingVar
?
@MichaelHoffmann 这些应该有自己的 SO 问题,并且可能已经得到了比我能管理的更好的答案。话虽如此,1)
我在浏览器开发工具中运行了一个内存配置文件,用于循环功能,例如您描述的循环功能,并在整个循环过程中看到内存使用高峰。这似乎表明在循环的每次迭代中确实正在创建新的相同对象。当尖峰突然下降时,垃圾收集器只是清理了一组这些未使用的对象。
@MichaelHoffmann 2)
关于var a = b
之类的东西,javascript 没有提供使用指针的机制,因此变量永远不能指向指针(就像在 C 中一样),尽管底层javascript 引擎无疑会使用它们。所以...var a = b
将指向a
“指向右操作数的指针”
我在这里问了问题 #1(特别是关于 Chrome,因为每个浏览器的实现可能不同)***.com/q/42778439/539997 我仍在尝试思考如何表达问题 #2。任何帮助表示赞赏。
没有必要忘记“按引用/值传递”!这些术语具有准确描述您试图描述的内容的历史意义。如果我们抛弃历史术语和定义,懒得去了解它们的原始含义,那么我们就失去了在几代人之间进行有效沟通的能力。讨论不同语言和系统之间的差异是没有好方法的。相反,新程序员需要学习和理解传统术语以及它们的来源和来源。否则,我们会集体失去知识和理解。【参考方案6】:
函数外的对象通过引用外部对象传递给函数。
当您使用该引用来操作其对象时,外部的对象会因此受到影响。但是,如果在函数内部您决定将引用指向其他对象,则根本不会影响外部对象,因为您所做的只是将引用重新指向其他对象。
【讨论】:
【参考方案7】:这样想:它总是按值传递。但是,对象的值不是对象本身,而是对该对象的引用。
这里是一个例子,传递一个数字(原始类型)
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
【讨论】:
【参考方案8】:“JavaScript: The Definitive Guide”一书中的this chapter 中有关于通过值和引用进行复制、传递和比较的非常详细的说明。
在我们离开主题之前 操作对象和数组 参考,我们需要澄清一点 命名法。
短语“路过 参考”可以有多种含义。 对一些读者来说,这句话指的是 一种函数调用技术 允许函数分配新值 它的论点并拥有那些 修改后的值在外部可见 功能。这不是术语的方式 本书使用。
在这里,我们的意思是 只是对对象的引用 或数组——不是对象本身—— 被传递给一个函数。一个函数 可以使用参考来修改 对象或元素的属性 的数组。但是如果函数 用 a 覆盖引用 引用一个新的对象或数组, 该修改不可见 在函数之外。
读者 熟悉的另一个意思 这个词可能更愿意说 对象和数组通过 值,但传递的值是 实际上是参考而不是 对象本身。
【讨论】:
哇,这令人难以置信的混乱。有谁会在他们正确的头脑中定义一个公认的术语来表示完全相反,然后这样使用它?难怪这里有这么多关于这个问题的答案如此混乱。 这个答案是理解其他人在这个主题上写了什么的关键。大多数人没有意识到“通过引用”一词有两种定义,因此当您阅读他们的解释时,您必须对他们使用的术语的含义做出有根据的猜测。此答案中链接的书籍章节对于更深入地理解该主题也很有用。【参考方案9】:JavaScript 总是 按值传递;一切都是值类型。
对象是值,对象的成员函数本身就是值(请记住,函数是 JavaScript 中的一等对象)。另外,关于 JavaScript 中的一切都是 object 的概念;这是错误的。字符串、符号、数字、布尔值、空值和未定义值是原语。
有时他们可以利用从其基本原型继承的一些成员函数和属性,但这只是为了方便。这并不意味着它们本身就是对象。请尝试以下方法以供参考:
x = "test";
console.log(x.foo);
x.foo = 12;
console.log(x.foo);
在console.log
中,您都会发现值是undefined
。
【讨论】:
-1,它并不总是按值传递。来自 MDC:“如果您将对象(即非原始值,例如数组或用户定义的对象)作为参数传递,则对该对象的引用将传递给函数。” @Nick:总是按值传递。时期。对对象的引用按值传递给函数。这不是通过引用传递。 “通过引用传递”几乎可以被认为是传递变量本身,而不是它的值; any 函数对参数所做的更改(包括完全用不同的对象替换它!)将反映在调用者中。最后一点在 JS 中是不可能的,因为 JS 不通过引用传递 -- 它通过值传递引用。这种区别很微妙,但对于理解其局限性非常重要。 对于未来的堆垛机...关于你的这个参考:x = "teste"; x.foo = 12;
等等。仅仅因为一个属性不是持久的,并不意味着它不是一个对象。正如 MDN 所说:在 JavaScript 中,几乎所有东西都是对象。除 null 和 undefined 之外的所有原始类型都被视为对象。它们可以被分配属性(某些类型的分配属性不是持久的),并且它们具有对象的所有特性。 link
MDN 是一个用户编辑的 wiki,它在那里是错误的。规范性参考是 ECMA-262。请参阅第 8 节“引用规范类型”,它解释了如何解析引用,以及 8.12.5“[[Put]]”,它用于解释对引用的赋值表达式,以及用于对象强制的 9.9 ToObject。对于原始值,Michael 已经解释了 ToObject 的作用,如规范中所述。但另见 s。 4.3.2 原始值。
@WonderLand:不,他不是。从未能够通过引用传递的人可能永远不会理解通过引用传递和通过值传递引用之间的区别。但它们就在那里,而且它们很重要。我不想误导人们只是因为这听起来更容易。【参考方案10】:
在 JavaScript 中,值的类型仅控制该值是由 value-copy 还是由 reference-copy 分配。
原始值总是由 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]
在上面的 sn-p 中,因为2
是一个标量原语,a
拥有该值的一个初始副本,而b
被分配了该值的另一个副本。更改b
时,您绝不会更改a
中的值。
但是c
和d
都是对同一个共享值[1,2,3]
的单独引用,这是一个复合值。需要注意的是,c
和 d
都没有更多地“拥有”[1,2,3]
值——两者都只是对该值的同等对等引用。因此,当使用任一引用修改 (.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
引用的副本分配给x
。 x
和 a
是指向相同 [1,2,3]
值的单独引用。现在,在函数内部,我们可以使用该引用来改变值本身(push(4)
)。但是当我们分配x = [4,5,6]
时,这绝不会影响初始引用a
指向的位置——仍然指向(现在修改的)[1,2,3,4]
值。
要通过值复制有效地传递复合值(如array
),您需要手动复制它,以便传递的引用不会仍然指向原始值。例如:
foo( a.slice() );
reference-copy可以传递的复合值(对象、数组等)
function foo(wrapper)
wrapper.a = 42;
var obj =
a: 2
;
foo( obj );
obj.a; // 42
这里,obj
充当标量原始属性 a
的包装器。当传递给foo(..)
时,obj
引用的副本被传入并设置为wrapper
参数。我们现在可以使用wrapper
引用来访问共享对象,并更新其属性。函数完成后,obj.a
将看到更新后的值42
。
Source
【讨论】:
您首先声明“复合值始终由引用副本分配/传递”,然后您声明“将 a 引用的 副本 分配给 x”。在您所谓的“复合值”的情况下,实际变量值是引用(即内存指针)。正如您所解释的,引用被复制...所以变量值被复制,再次强调引用就是值。这意味着 JavaScript 对所有类型都是按值传递的。按值传递意味着传递变量值的副本。值是对对象/数组的引用并不重要。 您引入了新的术语(值复制/引用复制),这只会让事情变得更加复杂。只有副本,期间。如果传递原始数据,则传递实际原始数据的副本,如果传递对象,则传递对象内存位置的副本。这就是你需要说的。更多的话只会让人们更加困惑。【参考方案11】:嗯,它是关于“性能”和“速度”以及编程语言中的简单单词“内存管理”。
在 javascript 中,我们可以将值放在两层:type1-objects
和 type2-所有其他类型的值,例如 string
& boolean
&等等
如果您将内存想象为下面的正方形,每个正方形中只能保存一个 type2-value:
每个 type2-value(绿色)是一个单个正方形,而 type1-value(蓝色)是一个它们的组:
关键是,如果你想指示一个 type2-value,地址是简单的,但如果你想对 type1-value 做同样的事情,那根本不容易! :
还有一个更复杂的故事:
所以这里 references 可以拯救我们:
这里的绿色箭头是典型变量,紫色是对象变量,所以因为绿色箭头(典型变量)只有一个任务(也就是代表典型值)我们不需要将它的值与它分开,因此我们将绿色箭头与它的值一起移动到任何地方以及所有分配、函数等中......
但我们不能用紫色箭头做同样的事情,我们可能想在这里移动“约翰”单元格或许多其他东西......所以紫色箭头会粘在它的位置,只是分配给的典型箭头它会移动......
一个非常令人困惑的情况是你无法意识到你引用的变量是如何变化的,让我们看一个非常好的例子:
let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];
【讨论】:
【参考方案12】:这是关于按值传递和按引用传递 (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 中的所有基本类型(字符串、数字、布尔值、未定义和空值)。
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"
equal(=) 运算符设置新的内存空间或地址
【讨论】:
在您所谓的特殊情况下,导致分配内存空间的不是赋值运算符,而是object literal 本身。大括号符号会导致创建一个新对象。属性c
设置为新对象引用的副本。
这不是通过引用传递的。这是传值,谁的值恰好是一个引用。【参考方案13】:
语义!!设置具体的定义必然会使某些答案和 cmets 不兼容,因为即使使用相同的单词和短语,它们也不会描述相同的事物,但克服混淆至关重要(尤其是对于新程序员而言)。
首先,似乎不是每个人都能掌握多个抽象层次。学习过第 4 代或第 5 代语言的新程序员可能难以围绕汇编或 C 程序员所熟悉的概念,而不是由指向指针的指针分阶段。引用传递不仅仅意味着能够使用函数参数变量来更改被引用对象。
变量:符号的组合概念,它引用内存中特定位置的值。这个术语通常过于冗长,无法单独用于讨论细节。
符号:用于引用变量的文本字符串(即变量的名称)。
值:存储在内存中并使用变量符号引用的特定位。
内存位置:存储变量值的位置。 (位置本身由一个数字表示,与存储在该位置的值不同。)
函数参数:在函数定义中声明的变量,用于引用传递给函数的变量。
函数参数:函数外的变量,由调用者传递给函数。
对象变量:其基本基础值不是“对象”本身的变量,而是它的值是指向存储对象实际数据的内存中另一个位置的指针(内存位置值)。在大多数高代语言中,“指针”方面通过在各种上下文中的自动取消引用有效地隐藏了。
原始变量:其值为实际值的变量。甚至这个概念也可以通过各种语言的自动装箱和类似对象的上下文来复杂化,但一般的想法是变量的值是变量符号表示的实际值,而不是指向另一个内存位置的指针。
函数实参和形参不是一回事。此外,变量的值不是变量的对象(正如很多人已经指出的那样,但显然被忽略了)。这些区别对于正确理解至关重要。
按值传递或按共享调用(对于对象):函数参数的值被复制到函数参数符号引用的另一个内存位置(无论它是否打开堆栈或堆)。换句话说,函数参数收到了传递参数值的副本......并且(关键)参数的值永远不会被调用函数更新/更改/更改。请记住,对象变量的值不是对象本身,而是指向对象的指针,因此按值传递对象变量会将指针复制到函数参数变量。函数参数的值指向内存中完全相同的对象。对象数据本身可以通过函数参数直接更改,但函数参数的值永远不会更新,因此它会在整个过程中甚至在函数调用之后继续指向 same 对象(即使它的对象的数据已更改,或者函数参数完全分配了不同的对象)。仅仅因为被引用的对象可以通过函数参数变量进行更新,就得出函数参数是通过引用传递的结论是不正确的。
调用/传递引用:函数参数的值可以/将由相应的函数参数直接更新。如果有帮助,函数参数将成为参数的有效“别名”——它们有效地引用相同内存位置的相同值。如果函数参数是对象变量,则更改对象数据的能力与按值传递的情况没有什么不同,因为函数参数仍将指向与参数相同的对象。但是在对象变量的情况下,如果函数参数被设置为完全不同的对象,那么参数同样也会指向不同的对象——这在传值的情况下不会发生。
JavaScript 不通过引用传递。如果您仔细阅读,您会发现所有相反的意见都误解了按值传递的含义,并且错误地得出结论,通过函数参数更新对象数据的能力与“按值传递”同义。
对象克隆/复制:创建一个新对象并复制原始对象的数据。这可以是深拷贝或浅拷贝,但重点是创建了一个新对象。创建对象的副本是与传递值不同的概念。一些语言区分类对象和结构(或类似的),并且对于传递不同类型的变量可能有不同的行为。但是 JavaScript 在传递对象变量时不会自动执行类似的操作。但是没有自动对象克隆并不能转化为传递引用。
【讨论】:
【参考方案14】:分享我对 JavaScript 引用的了解
在 JavaScript 中,将对象赋值给变量时,赋值给变量的值是对该对象的引用:
var a =
a: 1,
b: 2,
c: 3
;
var b = a;
// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4
【讨论】:
这是一个过于简单化的答案,它没有说明早期的答案没有更好地解释。我很困惑为什么您将数组称为特殊情况。 “对象存储为引用”具有误导性。我认为您的意思是,将对象分配给变量时,分配给变量的值是对该对象的引用。 这并没有解决在函数内部更新对象而不更新函数外部对象的问题。这就是它似乎作为价值而不是参考的整体情况。因此-1 @amaster 感谢您指出这一点!你能suggest an edit, please? 哈哈,我试过了...我建议的编辑更改太多 amd 是不允许的【参考方案15】:JavaScript 通过值传递原始类型,通过引用传递对象类型
现在,人们喜欢无休止地争论是否“通过引用” 是描述 Java 等的正确方法。实际上做。重点 这是:
传递对象不会复制该对象。 传递给函数的对象可以通过函数修改其成员。 传递给函数的原始值不能被函数修改。已制作副本。
在我的书中称为通过引用传递。
——Brian Bi - Which programming languages are pass by reference?
更新
对此进行反驳:
There is no "pass by reference" available in JavaScript.
【讨论】:
@Amy 因为那是在描述按值传递,而不是按引用传递。这个答案是一个很好的显示差异的答案:***.com/a/3638034/3307720 @nasch 我理解其中的区别。 #1 和 #2 描述了 pass-by-ref 语义。 #3 描述了按值传递的语义。 @Amy 1、2、3都与传值一致。要通过引用传递,您还需要 4:将引用分配给函数内部的新值(使用 = 运算符)也会重新分配函数外部的引用。 Javascript 不是这种情况,它只能按值传递。传递对象时,传递一个指向该对象的指针,然后按值传递该指针。 这通常不是“通过引用”的意思。你已经满足了我的要求,我不同意你的看法。谢谢。 “在我的书中,这被称为通过引用传递。” – 在每一本编译器书籍、解释器书籍、编程语言理论书籍和计算机科学书籍中,都不是。【参考方案16】:观察:如果观察者无法检查引擎的底层内存,则无法确定是复制不可变值还是传递了引用。
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 中没有引用这样的东西,所有这些都没有错,也没有正确的意义。因此,我认为答案对搜索不是特别有用。
² 规范中的术语Reference并不是传统意义上的引用。它是一个对象和属性名称的容器,它是一个中间值(例如,a.b
的计算结果为 Reference value = a, name = "b"
)。 reference 一词有时也会出现在规范中不相关的部分。
【讨论】:
【参考方案17】:我理解这个的简单方法......
调用函数时,您传递的是内容(引用或 value) 的参数变量,而不是变量本身。
var var1 = 13;
var var2 = prop: 2 ;
//13 and var2's content (reference) are being passed here
foo(var1, var2);
在函数内部,参数变量inVar1
和inVar2
接收被传递的内容。
function foo(inVar1, inVar2)
//changing contents of inVar1 and inVar2 won't affect variables outside
inVar1 = 20;
inVar2 = prop: 7 ;
由于inVar2
收到 prop: 2
的引用,你可以改变对象的属性值。
function foo(inVar1, inVar2)
inVar2.prop = 7;
【讨论】:
你清楚地表达了我的理解。主要需要注意的是我们正在传递内容(引用或值)【参考方案18】:在 JavaScript 中向函数传递参数类似于传递 C中的指针值参数:
/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.
function changeStuff(num, obj1, obj2)
num = num * 10;
obj1.item = "changed";
obj2 = 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);
This produces the output:
10
changed
unchanged
*/
#include <stdio.h>
#include <stdlib.h>
struct obj
char *item;
;
void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
// make pointer point to a new memory location
// holding the new integer value
int *old_num = num;
num = malloc(sizeof(int));
*num = *old_num * 10;
// make property of structure pointed to by pointer
// point to the new value
obj1->item = "changed";
// make pointer point to a new memory location
// holding the new structure value
obj2 = malloc(sizeof(struct obj));
obj2->item = "changed";
free(num); // end of scope
free(obj2); // end of scope
int num = 10;
struct obj obj1 = "unchanged" ;
struct obj obj2 = "unchanged" ;
int main()
// pass pointers by value: the pointers
// will be copied into the argument list
// of the called function and the copied
// pointers will point to the same values
// as the original pointers
changeStuff(&num, &obj1, &obj2);
printf("%d\n", num);
puts(obj1.item);
puts(obj2.item);
return 0;
【讨论】:
我不认为在 JavaScript 中是这样的:```javascript var num = 5; @DanailNachev:虽然这在技术上可能是正确的,但只有在 ECMAScript 原语不是的可变对象中才能观察到差异。【参考方案19】:对于编程语言律师,我已经阅读了 ECMAScript 5.1 的以下部分(比最新版本更容易阅读),并在 ECMAScript 邮件列表中找到asking。
TL;DR:一切都是按值传递的,但对象的属性是引用,标准中对对象的定义令人毛骨悚然。
参数列表的构造
第 11.2.4 节“参数列表”在生成仅包含 1 个参数的参数列表时说如下:
产生式 ArgumentList : AssignmentExpression 的评估如下:
设 ref 为评估 AssignmentExpression 的结果。 设 arg 为 GetValue(ref)。 返回唯一项为 arg 的 List。
该部分还列举了参数列表具有 0 或 >1 个参数的情况。
因此,一切都是通过引用传递的。
对象属性的访问
第 11.2.1 节“属性访问器”
产生式 MemberExpression : MemberExpression [ Expression ] 计算如下:
让 baseReference 成为评估 MemberExpression 的结果。 设 baseValue 为 GetValue(baseReference)。 让 propertyNameReference 成为计算 Expression 的结果。 让 propertyNameValue 为 GetValue(propertyNameReference)。 调用 CheckObjectCoercible(baseValue)。 让 propertyNameString 为 ToString(propertyNameValue)。 如果正在评估的语法产生包含在严格模式代码中,则让 strict 为真,否则让 严格为假。 返回一个 Reference 类型的值,其基值为 baseValue,其引用名称为 propertyNameString,其严格模式标志为严格。
因此,对象的属性始终可以作为参考。
关于参考
在第 8.7 节“引用规范类型”中描述,引用不是语言中的真实类型 - 它们仅用于描述删除、typeof 和赋值运算符的行为。
“对象”的定义
5.1 版本中定义“对象是属性的集合”。因此,我们可以推断,对象的值就是集合,但是关于集合的值是什么,规范中没有定义,需要一点effort才能理解。
【讨论】:
令我惊讶的是,有多少人对按值传递的参数、按引用传递的参数、对整个对象的操作以及对其属性的操作之间的区别感到困惑。 1979 年,我没有获得计算机科学学位,而是选择在我的 MBA 课程中增加 15 小时左右的 CS 选修课。尽管如此,我很快就明白,我对这些概念的掌握至少与我的任何拥有计算机科学或数学学位的同事一样好。研究一下Assembler,就会很清楚了。 规范中的引用与所讨论的行为无关。这是解释为什么a.b = 1
能够知道属性(b
)设置在哪个对象(a
)上的中间构造(因为a.b
计算为Reference a, "b"
)。【参考方案20】:
MDN 文档解释得很清楚,不会太冗长:
函数调用的参数是函数的参数。 参数按值传递给函数。如果功能改变 参数的值,此更改不会反映在全局或 调用函数。但是,对象引用也是值,并且 它们是特殊的:如果函数改变了引用对象的 属性,该更改在函数外部可见,(...)
来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
【讨论】:
【参考方案21】:一切都是按值传递的。
基本类型按值传递(即,将实际变量值的新副本传递给函数)。
复杂类型(对象)作为“指向对象的指针”传递。所以你传递的实际东西是一个按值传递的指针(它是一个地址,一个像其他任何东西一样的数值)。显然,如果您尝试在函数内部修改对象的属性,即使在该函数之外,修改也会反映出来。那是因为您通过指向该属性的唯一副本的指针访问该属性。
这里的混淆出现在“通过值传递指针”和“通过引用传递对象”。
【讨论】:
【参考方案22】:我找到的最简洁的解释在AirBNB style guide:
基元:当您访问一个基元类型时,您直接在其上工作 价值
字符串 号码 布尔值 空 未定义例如:
var foo = 1,
bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
复杂:当您访问复杂类型时,您会处理对其值的引用
对象 数组 功能例如:
var foo = [1, 2],
bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
即实际上,原始类型是按值传递的,而复杂类型是按引用传递的。
【讨论】:
不,一切都是按值传递的。它仅取决于您传递的内容(值或引用)。见this。【参考方案23】:我已经多次阅读这些答案,但直到我了解了 Barbara Liskov 所说的 "Call by sharing" 的技术定义后才真正明白
共享调用的语义不同于引用调用,因为调用者看不到函数内函数参数的赋值(与引用语义不同)[需要引用],例如如果传递了一个变量,则无法在调用者的范围内模拟对该变量的赋值。但是,由于该函数可以访问与调用者相同的对象(不进行复制),因此如果对象是可变的,则对这些对象的突变对调用者是可见的,这可能看起来与按值调用不同语义。函数中可变对象的突变对调用者是可见的,因为该对象没有被复制或克隆——它是共享的。
也就是说,如果您访问参数值本身,参数引用是可以更改的。另一方面,对参数的赋值将在评估后消失,并且函数调用者无法访问。
【讨论】:
不,对象是否可变并不是真正的问题。一切总是按价值传递。它仅取决于您传递的内容(值或引用)。见this。 她描述的是传递参考 BY-VALUE。没有理由引入新术语。【参考方案24】:在低级语言中,如果要通过引用传递变量,则必须在创建函数时使用特定的语法:
int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age)
*age = *age + 1;
&age
是对myAge
的引用,但如果您想要该值,则必须使用*age
转换引用。
JavaScript 是一种高级语言,可以为您进行这种转换。
因此,尽管对象是通过引用传递的,但语言会将引用参数转换为值。您不需要在函数定义上使用&
来通过引用传递它,也不需要在函数体上使用*
来将引用转换为值,JavaScript 会为您完成。
这就是为什么当您尝试更改函数内部的对象时,通过替换它的值(即age = value:5
),更改不会持续,但如果您更改它的属性(即age.value = 5
),它会持续。
Learn more
【讨论】:
不,没有必要(或不允许)在 C++ 中取消引用引用。要么是被取消引用的指针,要么是没有被取消引用的引用。 这个答案实际上是错误的。 JavaScript 没有这种转换。在 JavaScript 中不可能通过引用传递。 “按引用传递”的全部意义在于让函数能够更改myAge
的值。你不能在 JavaScript 中做到这一点。您可以更改对象 myAge
引用的属性,但不能不更改 myAge
变量本身。这就是“通过引用传递”的意思,能够在函数外部更改变量的值。【参考方案25】:
如果你想要(正常的)函数参数行为,就像其他语言一样(传递值的副本) 然后在传递给函数之前克隆对象:
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();
【讨论】:
【参考方案26】:我发现Underscore.js library 中的extend method 在我想将对象作为参数传入时非常有用,该参数可以被修改或完全替换。
function replaceOrModify(aObj)
if (modify)
aObj.setNewValue('foo');
else
var newObj = new MyObject();
// _.extend(destination, *sources)
_.extend(newObj, aObj);
【讨论】:
【参考方案27】:我会说这是通过复制 -
考虑参数和变量对象是在函数调用开始时创建的执行上下文期间创建的对象 - 传递给函数的实际值/引用只是存储在此参数 + 变量对象中。
简单来说,对于原始类型,值在函数调用开始时被复制,对于对象类型,引用被复制。
【讨论】:
"pass-by-copy" === 传值【参考方案28】:-
像字符串、数字这样的原始类型变量总是作为传递传递
按价值计算。
数组和对象根据这两个条件作为引用传递或值传递来传递。
如果你用新的对象或数组改变那个对象或数组的值,那么它是按值传递的。
object1 = item: "car";
array1=[1,2,3];
在这里您将新对象或数组分配给旧对象。您没有更改属性的值 旧对象。所以它是按值传递的。
如果您要更改对象或数组的属性值,则它是通过引用传递的。
object1.key1= "car";
array1[0]=9;
在这里您正在更改旧对象的属性值。您没有将新对象或数组分配给旧对象。因此它是通过引用传递的。
代码
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
【讨论】:
赋值运算符不要与函数调用混淆。当您将新数据分配给现有变量时,旧数据的引用计数会减少,并且新数据与旧变量相关联。基本上,变量最终指向新数据。属性变量也是如此。由于这些赋值不是函数调用,它们与按值传递或按引用传递无关。 不,一切都是按值传递的。它仅取决于您传递的内容(值或引用)。见this。【参考方案29】:判断某事物是否“通过引用传递”的一个简单方法是您是否可以编写“交换”函数。例如,在 C 中,您可以这样做:
void swap(int *i, int *j)
int t;
t = *i;
*i = *j;
*j = t;
如果你不能在 JavaScript 中做同样的事情,那就不是“通过引用”。
【讨论】:
这并不是真正的引用传递。您正在将指针传递给函数,并且这些指针是按值传递的。一个更好的例子是 C++ 的 & 运算符或 C# 的“ref”关键字,它们都是真正的按引用传递。 更简单的是,一切都在 JavaScript 中按值传递。 ***.com/questions/42045586/…【参考方案30】:-
基元(数字、布尔值等)按值传递。
字符串是不可变的,因此对它们来说并不重要。
对象按引用传递(引用按值传递)。
【讨论】:
不,一切都是按值传递的。它仅取决于您传递的内容(值或引用)。见this。 你的第二个说法自相矛盾。以上是关于JavaScript 是按引用传递还是按值传递语言?的主要内容,如果未能解决你的问题,请参考以下文章