JavaScript 是一种按引用传递还是按值传递的语言?
基本类型(数字、字符串等)按值传递。 尽管如此,对象仍然是未知的,因为它们既可以按值传递(在这种情况下,我们认为保存对象的变量是对该对象的引用),也可以按引用传递(当我们认为对象的变量保存对象本身)。
尽管最终并不重要,但我想知道通过约定呈现参数的正确方法是什么。 JavaScript 规范是否有摘录,它定义了与此相关的语义?
The primitive types (number, string, etc.) are passed by value. Still, objects are unknown because they can be both passed by value (in which case we consider that a variable holding an object is a reference to the object) and passed-by-reference (when we consider that the variable to the object holds the object itself).
Although it doesn't matter in the end, I want to know what is the correct way to present the arguments passing conventions. Is there an excerpt from the JavaScript specification, which defines what should be the semantics regarding this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
我理解这一点的简单方法...
调用函数时,您正在传递内容(引用或
value)的参数变量,而不是变量本身。
在函数内部,参数变量
inVar1
和inVar2
接收传递的内容。由于
inVar2
收到了{ prop: 2 }
的引用,因此可以更改对象属性的值。My simple way to understand this...
When calling a function, you are passing the content (reference or
value) of the argument variables, not the the variables themselves.
Inside the function, parameter variables,
inVar1
andinVar2
, receive the contents being passed.Since
inVar2
received the reference of{ prop: 2 }
, you can change the value of the object's property.JavaScript 按值传递基本类型,按引用传递对象类型
— Brian Bi - 哪些编程语言是通过引用传递的?
更新
这是对此的反驳:
JavaScript 中没有可用的“通过引用传递”。
JavaScript passes primitive types by value and object types by reference
— Brian Bi - Which programming languages are pass by reference?
Update
Here is a rebuttal to this:
There is no "pass by reference" available in JavaScript.
在 JavaScript 中将参数传递给函数类似于传递
C 中指针值的参数:
Passing arguments to a function in JavaScript is analogous to passing
parameters by pointer value in C:
对于编程语言律师,我已经阅读了 ECMAScript 5.1 的以下部分(比最新版本更容易阅读),并一直阅读 在 ECMAScript 邮件列表上询问。
TL;DR:一切都是按值传递的,但对象的属性是引用,并且标准中令人毛骨悚然地缺乏对象的定义。
参数列表的构造
第 11.2.4 节“参数列表”对于生成仅包含 1 个参数的参数列表进行了以下说明:
本节还列举了参数列表具有 0 或 >1 个参数的情况。
因此,一切都是通过引用传递的。
对象属性的访问
第 11.2.1 节“属性访问器”
因此,对象的属性始终可用作参考。
关于引用
第 8.7 节“引用规范类型”中描述了引用不是语言中的真实类型 - 它们仅用于描述删除、typeof 和赋值运算符的行为。
“对象”的定义
5.1 版中定义“对象是属性的集合”。 因此,我们可以推断,对象的值就是集合,但是集合的值是什么,在规范中没有明确定义,需要一点 努力去理解。
For programming language lawyers, I've went through the following sections of ECMAScript 5.1 (which is easier to read than the latest edition), and go as far as asking it on the ECMAScript mailing list.
TL;DR: Everythings're passed by value, but properties of Objects are references, and the definition of Object is creepily lacking in the standard.
Construction of Argument Lists
Section 11.2.4 "Argument Lists" says the following on producing a argument list consisting of only 1 argument:
The section also enumerate cases where argument list has 0 or >1 arguments.
Thus, everything's are passed by reference.
Access of Object Properties
Section 11.2.1 "Property Accessors"
Thus, properties of Objects are always available as reference.
On Reference
It is described in section 8.7 "The Reference Specification Type", that references are not real types in the language - they're only used to describe the behavior of the delete, the typeof, and the assignment operators.
Definition of "Object"
It is defined in 5.1 edition that "An Object is a collection of properties". Therefore, we can infer, that the value of the object is the collection, but as to what is the value of the collection is poorly defined in the spec, and requires a bit of effort to understand.
引用提问者的话:
这不是真的。 对象作为参数传递的方式只有一种。 尽管其他答案也区分了原始值和非原始值,但这会分散注意力并且无关紧要。
无按引用传递
考虑此代码,它不关心
a
是原语还是对象:如果函数
alter
有某种方法可以使第二个输出false
,那么我们可以说传递引用,因为arg
将是一个别名 代表a
。 但这在 JavaScript 中是不可能的。 原语的概念与此无关:JavaScript 中没有引用传递。对象
在 JavaScript 中,对象是对属性(和槽)集合的引用。 当使用对象作为参数调用函数时,不会创建新的属性集合。 函数的参数变量(局部变量)将接收相同的值(即对属性集合的引用)。
变量赋值与对象突变
调用者的数据结构可能会发生突变,也可以通过函数调用进行突变。 显然,这仅适用于可变值(根据定义),而在 JavaScript 中,这是通过将值分配给属性来实现的。 有两点需要区分:
Quoting the asker:
This is not true. There is only one way that objects are passed as argument. Although also other answers make a distinction between primitive and non-primitive values, this is a distraction and irrelevant.
No pass-by-reference
Consider this code, which doesn't care whether
a
is intended to be a primitive or an object:If the function
alter
had some way to make that second outputfalse
, then we could speak of pass-by-reference, as thenarg
would have been an alias fora
. But this is just impossible in JavaScript. The concept of primitive is irrelevant to this: there is no pass-by-reference in JavaScript.Objects
In JavaScript an object is a reference to a collection of properties (and slots). When a function is called with an object as argument, then this does not create a new collection of properties. The function's parameter variable (which is a local variable), will receive the same value (i.e. the reference to the property collection).
Variable assignment versus object mutation
A caller's data structure may be mutated, also by a function call. This is obviously only true for mutable values (by definition), and in JavaScript this happens by assigning a value to a property. Two things to distinguish:
我发现的最简洁的解释是在 AirBNB 风格指南中:
Primitives< /strong>:当您访问原始类型时,您可以直接对其进行操作
值
例如:
Complex:当您访问复杂类型时,您将处理对其值的引用
的引用,例如:
即有效地基本类型按值传递,复杂类型按引用传递。
The most succinct explanation I found was in the AirBNB style guide:
Primitives: When you access a primitive type you work directly on its
value
E.g.:
Complex: When you access a complex type you work on a reference to its value
E.g.:
I.e. effectively primitive types are passed by value, and complex types are passed by reference.
我已经多次阅读这些答案,但直到我了解 Barbara Liskov 所说的“共享通话”
也就是说,如果您访问参数值本身,参数引用是可以更改的。 另一方面,对参数的赋值将在求值后消失,并且函数调用者无法访问。
I've read through these answers multiple times, but didn't REALLY get it until I learned about the technical definition of "Call by sharing" as termed by Barbara Liskov
That is, parameter references are alterable if you go and access the parameter value itself. On the other hand, assignment to a parameter will disappear after evaluation, and is non-accessible to the function caller.
Java 脚本变量是按值传递的。 例如:
但是java脚本对象是通过引用传递的。 例如:
输出:
要将对象传递给函数作为按值传递,您可以使用扩展运算符,如下所示
输出:
希望能够澄清。
Java script variable are pass by value. For example:
But java script objects are pass by reference. For example:
Output:
To pass object to function as pass by value you can use spread operator as below
Output:
Hope that clarifies.
我找到了 扩展方法当我想将一个对象作为参数传递(可以完全修改或替换)时,Underscore.js 库非常有用。
I have found the extend method of the Underscore.js library very useful when I want to pass in an object as a parameter which may either be modified or replaced entirely.
在低级语言中,如果要通过引用传递变量,则必须在创建函数时使用特定语法:
&age
是对myAge 的引用
,但如果您想要该值,则必须使用*age
转换引用。JavaScript 是一种高级语言,可以为您完成此转换。
因此,尽管对象是通过引用传递的,但语言会将引用参数转换为值。 您不需要在函数定义上使用
&
来通过引用传递它,也不需要在函数主体上使用*
将引用转换为值,JavaScript 会为你做这件事。这就是为什么当您尝试通过替换其值(即age = {value:5})来更改函数内的对象时,更改不会持续存在,但如果您更改它的属性(即
age.value = 5
),确实如此。了解详情
In a low-level language, if you want to pass a variable by reference, you have to use a specific syntax in the creation of the function:
The
&age
is a reference tomyAge
, but if you want the value you have to convert the reference, using*age
.JavaScript is a high level language that does this conversion for you.
So, although objects are passed by reference, the language converts the reference parameter to the value. You don't need to use
&
, on the function definition, to pass it by reference, neither*
, on the function body, to convert the reference to the value, JavaScript does it for you.That's why when you try to change an object inside a function, by replacing it's value (i.e.
age = {value:5}
), the change doesn't persist, but if you change it's properties (i.e.age.value = 5
), it does.Learn more
如果您想要像其他语言一样的(正常)函数参数行为(传递值的副本)
然后在传递给函数之前克隆该对象:
If you want (normal) function parameter behavior like in other languages (passing copy of a value)
then just clone the object before passing into a function:
我想说这是传递复制 -
考虑参数和变量对象是在函数调用开始时创建的执行上下文期间创建的对象 - 并且传递到函数中的实际值/引用只是存储在这个参数 + 变量对象中。
简单来说,对于基本类型,值在函数调用开始时被复制,对于对象类型,引用被复制。
I would say it is pass-by-copy -
Consider arguments and variable objects are objects created during the execution context created in the beginning of function invocation - and your actual value/reference passed into the function just get stored in this arguments + variable objects.
Simply speaking, for primitive types, the values get copied in the beginning of function call, for object type, the reference get copied.
通过给出对外部对象的引用,将函数外部的对象传递到函数中。
当您使用该引用来操作其对象时,外部对象就会受到影响。 但是,如果在函数内部您决定将引用指向其他内容,则根本不会影响外部对象,因为您所做的只是将引用重新定向到其他内容。
An object outside a function is passed into a function by giving a reference to the outside object.
When you use that reference to manipulate its object, the object outside is thus affected. However, if inside the function you decided to point the reference to something else, you did not affect the object outside at all, because all you did was re-direct the reference to something else.
可以这样想:它总是按值传递。 然而,对象的值不是对象本身,而是对该对象的引用。
这是一个示例,传递一个数字(基本类型)
对对象重复此操作会产生不同的结果:
再一个示例:
Think of it like this: It's always pass by value. However, the value of an object is not the object itself, but a reference to that object.
Here is an example, passing a number (a primitive type)
Repeating this with an object yields different results:
One more example:
关于按值和按引用复制、传递和比较的非常详细的说明位于本章“JavaScript:权威指南”一书。
A very detailed explanation about copying, passing and comparing by value and by reference is in this chapter of the "JavaScript: The Definitive Guide" book.
JavaScript 总是按值传递; 一切都是值类型。
对象是值,对象的成员函数本身就是值(请记住,函数是 JavaScript 中的第一类对象)。 另外,关于 JavaScript 中的一切都是对象的概念; 这是错误的。 字符串、符号、数字、布尔值、空值和未定义是基元。
有时,他们可以利用从其基本原型继承的一些成员函数和属性,但这只是为了方便。 这并不意味着它们本身就是物体。 尝试以下方法供参考:
在
console.log
中,您会发现该值均为undefined
。JavaScript is always pass-by-value; everything is of value type.
Objects are values, and member functions of objects are values themselves (remember that functions are first-class objects in JavaScript). Also, regarding the concept that everything in JavaScript is an object; this is wrong. Strings, symbols, numbers, booleans, nulls, and undefineds are primitives.
On occasion they can leverage some member functions and properties inherited from their base prototypes, but this is only for convenience. It does not mean that they are objects themselves. Try the following for reference:
In both
console.log
you will find the value to beundefined
.在 JavaScript 中,值的类型仅控制该值是由值复制分配还是由引用复制分配。
原始值总是通过值复制来赋值/传递:
null
undefined
ES6
中的>复合值总是通过引用复制
分配/传递例如
在上面的代码片段中,因为
2
是一个标量基元,a
持有一个该值的初始副本,并且b
被分配该值的另一个副本。 更改b
时,您绝不会更改a
中的值。但
c
和d
都是对同一共享值[1,2,3]
的单独引用,该共享值是一个复合值。 值得注意的是,c
和d
都不“拥有”[1,2,3]
值——两者只是平等的对等体对值的引用。 因此,当使用任一引用修改 (.push(4)
) 实际共享array
值本身时,它只会影响一个共享值,并且两个引用都将引用新修改的值[1,2,3,4]
。当我们进行赋值
b = [4,5,6]
时,我们绝对没有做任何事情来影响a
仍在引用的位置 ([1,2, 3]
)。 为此,b
必须是指向a
的指针,而不是对array
的引用——但 JS 中不存在这样的功能!当我们传入参数
a
时,它会将a
引用的副本分配给x
。x
和a
是指向相同[1,2,3]
值的单独引用。 现在,在函数内部,我们可以使用该引用来改变值本身 (push(4)
)。 但是当我们进行赋值x = [4,5,6]
时,这绝不会影响初始引用a
指向的位置——仍然指向(现已修改)[1,2,3,4]
值。要通过值复制有效地传递复合值(例如
数组
),您需要手动创建它的副本,以便传递的引用不再指向原始值。 例如:可以通过引用复制传递的复合值(对象、数组等)
这里,
obj
充当标量基元属性a
的包装器。 当传递给foo(..)
时,obj
引用的副本将被传入并设置为wrapper
参数。 我们现在可以使用wrapper
引用来访问共享对象,并更新其属性。 函数完成后,obj.a
将看到更新后的值42
。来源
In JavaScript, the type of the value solely controls whether that value will be assigned by value-copy or by reference-copy.
Primitive values are always assigned/passed by value-copy:
null
undefined
ES6
Compound values are always assigned/passed by reference-copy
For example
In the above snippet, because
2
is a scalar primitive,a
holds one initial copy of that value, andb
is assigned another copy of the value. When changingb
, you are in no way changing the value ina
.But both
c
andd
are separate references to the same shared value[1,2,3]
, which is a compound value. It's important to note that neitherc
nord
more "owns" the[1,2,3]
value -- both are just equal peer references to the value. So, when using either reference to modify (.push(4)
) the actual sharedarray
value itself, it's affecting just the one shared value, and both references will reference the newly modified value[1,2,3,4]
.When we make the assignment
b = [4,5,6]
, we are doing absolutely nothing to affect wherea
is still referencing ([1,2,3]
). To do that,b
would have to be a pointer toa
rather than a reference to thearray
-- but no such capability exists in JS!When we pass in the argument
a
, it assigns a copy of thea
reference tox
.x
anda
are separate references pointing at the same[1,2,3]
value. Now, inside the function, we can use that reference to mutate the value itself (push(4)
). But when we make the assignmentx = [4,5,6]
, this is in no way affecting where the initial referencea
is pointing -- still points at the (now modified)[1,2,3,4]
value.To effectively pass a compound value (like an
array
) by value-copy, you need to manually make a copy of it, so that the reference passed doesn't still point to the original. For example:Compound value (object, array, etc) that can be passed by reference-copy
Here,
obj
acts as a wrapper for the scalar primitive propertya
. When passed tofoo(..)
, a copy of theobj
reference is passed in and set to thewrapper
parameter. We now can use thewrapper
reference to access the shared object, and update its property. After the function finishes,obj.a
will see the updated value42
.Source
嗯,它是关于“性能”和“速度”,以及编程语言中简单的“内存管理”一词。
在 javascript 中,我们可以将值放在两层中:type1-
objects
和 type2-所有其他类型的值,例如string &
布尔值
& 等等,如果您将内存想象为下面的方块,其中每个方块中只能保存一个 type2 值:
每个 type2 值(绿色)都是一个单个正方形,而type1-value(蓝色)是一组:
要点是,如果您想指示 type2 值,则地址很简单,但如果您想这样做对于 type1-value 来说也是如此,这根本不容易! :
以及更复杂的故事:
所以这里参考可以拯救我们:
这里的绿色箭头是一个典型变量,而紫色箭头是一个对象变量,所以因为绿色箭头(典型变量)只有一个任务(这表明一个典型的值)我们不需要将它的值与它分开,所以我们将绿色箭头与它的值一起移动,无论它在哪里以及在所有赋值、函数等中......
但是我们不能用它做同样的事情紫色箭头,我们可能想将“约翰”单元移动到这里或许多其他东西...,因此紫色箭头将粘在其位置上,而分配给它的典型箭头将会移动...
一个非常令人困惑的情况是您无法意识到引用的变量如何变化,让我们看一个非常好的示例:
well, it's about 'performance' and 'speed' and in the simple word 'memory management' in a programming language.
in javascript we can put values in two layer: type1-
objects
and type2-all other types of value such asstring
&boolean
& etcif you imagine memory as below squares which in every one of them just one type2-value can be saved:
every type2-value (green) is a single square while a type1-value (blue) is a group of them:
the point is that if you want to indicate a type2-value, the address is plain but if you want to do the same thing for type1-value that's not easy at all! :
and in a more complicated story:
so here references can rescue us:
while the green arrow here is a typical variable, the purple one is an object variable, so because the green arrow(typical variable) has just one task (and that is indicating a typical value) we don't need to separate it's value from it so we move the green arrow with the value of that wherever it goes and in all assignments, functions and so on ...
but we cant do the same thing with the purple arrow, we may want to move 'john' cell here or many other things..., so the purple arrow will stick to its place and just typical arrows that were assigned to it will move ...
a very confusing situation is where you can't realize how your referenced variable changes, let's take a look at a very good example:
这是对按值传递和按引用传递 (JavaScript) 的更多解释。 在这个概念中,他们讨论的是通过引用传递变量和通过引用传递变量。
按值传递(原始类型)
按引用传递(对象)
c
,并且它指向一些内存,例如 (0x012)。d
指向相同的位置(0x012)。特殊情况,通过引用(对象)传递
This is little more explanation for pass by value and pass by reference (JavaScript). In this concept, they are talking about passing the variable by reference and passing the variable by reference.
Pass by value (primitive type)
Pass by reference (objects)
c
, and it points to some memory, say (0x012).d
points to the same location (0x012).Special case, pass by reference (objects)
语义!! 设置具体的定义必然会使一些答案和注释不兼容,因为即使使用相同的单词和短语,它们也不是在描述相同的事物,但克服混乱至关重要(特别是对于新程序员)。
首先,似乎并不是每个人都掌握了多个抽象层次。 学习过第四代或第五代语言的新程序员可能很难理解汇编语言或 C 程序员所熟悉的概念,而不是由指针到指针到指针来划分。 引用传递并不仅仅意味着使用函数参数变量更改引用对象的能力。
变量:符号的组合概念,引用内存中特定位置的值。 这个术语通常含义太深,无法单独用于讨论细节。
符号:用于引用变量的文本字符串(即变量的名称)。
值:存储在内存中并使用变量符号引用的特定位。
内存位置:存储变量值的位置。 (位置本身由与存储在该位置的值分开的数字表示。)
函数参数:在函数定义中声明的变量,用于引用传递给函数的变量。
函数参数:函数外部的变量,由调用者传递给函数。
对象变量:基本底层值不是“对象”本身,而是指向内存中存储对象实际数据的另一个位置的指针(内存位置值)的变量。 在大多数更高级的语言中,“指针”方面通过各种上下文中的自动取消引用而有效地隐藏。
原始变量:其值是实际值的变量。 即使这个概念可能会因各种语言的自动装箱和类似对象的上下文而变得复杂,但总体思路是变量的值是变量符号表示的实际值,而不是指向另一个内存位置的指针。
函数参数和形参不是一回事。 此外,变量的值不是变量的对象(正如很多人已经指出的那样,但显然被忽略了)。 这些区别对于正确理解至关重要。
按值传递或按共享调用(对于对象):函数参数的值被复制到由该函数引用的另一个内存位置。函数的参数符号(无论它是在堆栈还是堆上)。 换句话说,函数参数接收到传递的参数值的副本...并且(关键)调用函数永远不会更新/更改/更改参数的值。 请记住,对象变量的值不是对象本身,而是指向对象的指针,因此按值传递对象变量会将指针复制到函数参数变量。 函数参数的值指向内存中完全相同的对象。 对象数据本身可以通过函数参数直接更改,但函数参数的值永远不会更新,因此它将在整个函数调用过程中甚至在函数调用之后继续指向相同对象(即使其对象的数据被更改或者函数参数被完全分配给不同的对象)。 仅仅因为引用的对象可以通过函数参数变量进行更新,就得出函数参数是通过引用传递的结论是不正确的。
调用/传递引用:函数参数的值可以/将由相应的函数参数直接更新。 如果有帮助,函数参数将成为参数的有效“别名”——它们实际上引用同一内存位置的相同值。 如果函数参数是对象变量,则更改对象数据的能力与按值传递的情况没有什么不同,因为函数参数仍然指向与参数相同的对象。 但在对象变量的情况下,如果函数参数设置为完全不同的对象,那么参数也将同样指向不同的对象——这在按值传递的情况下不会发生。
JavaScript 不通过引用传递。 如果你仔细阅读,你会发现所有相反的观点都误解了按值传递的含义,他们错误地认为通过函数参数更新对象数据的能力与“按值传递”同义。
对象克隆/复制:创建一个新对象并复制原始对象的数据。 这可以是深复制或浅复制,但要点是创建了一个新对象。 创建对象的副本是与按值传递不同的概念。 一些语言区分类对象和结构(等等),并且对于传递不同类型的变量可能具有不同的行为。 但是 JavaScript 在传递对象变量时不会自动执行类似的操作。 但缺乏自动对象克隆并不能转化为按引用传递。
Semantics!! Setting concrete definitions will necessarily make some answers and comments incompatible since they are not describing the same thing even when using the same words and phrases, but it is critical to get past the confusion (especially for new programmers).
First of all, there are multiple levels of abstraction that not everyone seems to grasp. Newer programmers who have learned on 4th or 5th generation languages may have difficulty wrapping their mind around concepts familiar to assembly or C programmers not phased by pointers to pointers to pointers. Pass-by-reference does not simply mean the ability to change a referenced object using a function parameter variable.
Variable: Combined concept of a symbol which references a value at a particular location in memory. This term is usually too loaded to be used alone in discussing details.
Symbol: Text string used to refer to variable (i.e. variable's name).
Value: Particular bits stored in memory and referenced using variable's symbol.
Memory location: Where a variable's value is stored. (The location itself is represented by a number separate from the value stored at the location.)
Function parameter: Variable declared in a function definition, used for referencing variables passed to the function.
Function argument: Variable outside the function which is passed to the function by the caller.
Object variable: Variable whose basic underlying value is not the "object" itself, rather its value is a pointer (memory location value) to another location in memory where the object's actual data is stored. In most higher-generation languages, the "pointer" aspect is effectively hidden by automatic de-referencing in various contexts.
Primitive variable: Variable whose value IS the actual value. Even this concept can be complicated by auto-boxing and object-like contexts of various languages, but the general ideas is that the variable's value IS the actual value represented by the variable's symbol rather than a pointer to another memory location.
Function arguments and parameters are not the same thing. Also, a variable's value is not the variable's object (as already pointed out by various people, but apparently ignored). These distinctions are critical to proper understanding.
Pass-by-value or Call-by-sharing (for objects): The function argument's value is COPIED to another memory location which is referenced by the function's parameter symbol (regardless of whether it's on the stack or heap). In other words, the function parameter received a copy of the passed argument's value... AND (critical) the argument's value IS NEVER UPDATED / ALTERED / CHANGED by the calling function. Remember, an object variable's value is NOT the object itself, rather it is the pointer to the object, so passing an object variable by value copies the pointer to the function parameter variable. The function parameter's value points to the exact same object in memory. The object data itself can be altered directly via the function parameter, BUT the function argument's value IS NEVER UPDATED, so it will continue to point to the same object throughout and even after the function call (even if its object's data was altered or if the function parameter is assigned a different object altogether). It is incorrect to conclude that the function argument was passed by reference just because the referenced object is updatable via the function parameter variable.
Call / Pass-by-reference: The function argument's value can/will be updated directly by the corresponding function parameter. If it helps, the function parameter becomes an effective "alias" for the argument--they effectively refer to the same value at the same memory location. If a function argument is an object variable, the ability to change the object's data is no different than the pass-by-value case since the function parameter will still point to the same object as the argument. But in the object variable case, if the function parameter is set to a completely different object, then the argument will likewise also point to the different object--this does not happen in the pass-by-value case.
JavaScript does not pass by reference. If you read closely, you will realize that all contrary opinions misunderstand what is meant by pass-by-value and they falsely conclude that the ability to update an object's data via the function parameter is synonymous to "pass-by-value".
Object clone/copy: A new object is created and the original object's data is copied. This can be a deep copy or shallow copy, but the point is that a new object is created. Creating a copy of an object is a separate concept from pass-by-value. Some languages distinguish between class object and structs (or the like), and may have different behavior for passing variables of the different types. But JavaScript does not do anything like this automatically when passing object variables. But the absence of automatic object cloning does not translate to pass-by-reference.
在 JavaScript 中,将对象分配给变量时,分配给变量的值是对该对象的引用:
In JavaScript, when assigning an object to a variable, the value assigned to the variable is a reference to the object:
观察:如果观察者没有任何方法来检查引擎的底层内存,则无法确定是否复制了不可变值或传递了引用。
JavaScript 或多或少对底层内存模型不太不可知。 不存在参考²这样的东西。 JavaScript 有值。 两个变量可以保存相同的值(或更准确地说:两个环境记录可以绑定相同的值)。 唯一可以改变的值类型是通过抽象 [[Get]] 和 [[Set]] 操作的对象。
如果您忘记了计算机和内存,这就是描述 JavaScript 行为所需的全部内容,它可以让您理解规范。
现在您可能会问自己两个变量如何在计算机上保持相同的值。 然后,您可能会查看 JavaScript 引擎的源代码,并且很可能会找到使用该引擎编写的语言的程序员称为参考的内容。
所以事实上你可以说 JavaScript 是“按值传递”,而值可以共享,你可以说 JavaScript 是“按引用传递”,这对于低级语言的程序员来说可能是一个有用的逻辑抽象,或者您可以将此行为称为“通过共享调用”。
由于 JavaScript 中不存在引用这样的东西,因此所有这些都没有错误,也没有正确的观点。 因此,我认为寻找答案并不是特别有用。
² 规范中的术语参考并非传统意义上的参考。 它是对象和属性名称的容器,并且是中间值(例如,
ab
计算结果为Reference { value = a, name = "b" })。 术语参考有时也会出现在规范中不相关的部分中。
Observation: If there isn't any way for an observer to examine the underlying memory of the engine, there is no way to determine whether an immutable value gets copied or a reference gets passed.
JavaScript is more or less agnostic to the underlying memory model. There is no such thing as a reference². JavaScript has values. Two variables can hold the same value (or more accurate: two environment records can bind the same value). The only type of values that can be mutated are objects through their abstract [[Get]] and [[Set]] operations.
If you forget about computers and memory, this is all you need to describe JavaScript's behaviour, and it allows you to understand the specification.
Now you might ask yourself how two variables can hold the same value on a computer. You might then look into the source code of a JavaScript engine and you'll most likely find something which a programmer of the language the engine was written in would call a reference.
So in fact you can say that JavaScript is "pass by value", whereas the value can be shared, and you can say that JavaScript is "pass by reference", which might be a useful logical abstraction for programmers from low level languages, or you might call the behaviour "call by sharing".
As there is no such thing as a reference in JavaScript, all of these are neither wrong nor on point. Therefore I don't think the answer is particularly useful to search for.
² The term Reference in the specification is not a reference in the traditional sense. It is a container for an object and the name of a property, and it is an intermediate value (e.g.,
a.b
evaluates toReference { value = a, name = "b" }
). The term reference also sometimes appears in the specification in unrelated sections.一切都是按值传递的。
基本类型按值传递(即实际变量值的新副本传递给函数)。
复杂类型(对象)作为“指向对象的指针”传递。 因此,您传递的实际内容是一个按值传递的指针(它是一个地址,一个与其他值一样的数值)。 显然,如果您尝试在函数内部修改对象的属性,则即使在该函数外部,该修改也会反映出来。 这是因为您正在通过指向该属性的唯一副本的指针访问该属性。
“按值传递指针”和“按引用传递对象”是同一回事。
Everything is passed by value.
Basic types are passed by value (i.e. a new copy of the actual variable value is passed to the function).
Complex types (objects) are passed as "pointer to the object". So the actual stuff you are passing is a pointer which is passed by value (it's an address, a numerical value like any other). Obviously if you try to modify a property of the object inside the function, the modification will be reflected even outside of such function. That's because you are accessing the property via the pointer which points to the unique copy of the property.
"passing a pointer by value" and "passing an object by reference" is the same thing.
MDN 文档解释得很清楚,但又不太冗长:
来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
The MDN docs explain it clearly, without being too verbose:
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
JavaScript 中这很有趣。 考虑这个例子:
这会产生输出:
obj1
根本不是引用,则更改obj1.item
将不会对外部的obj1
产生任何影响。功能。num
将是100
,而obj2.item
将读取“changed”
。 相反,num
保持10
且obj2.item
保持“不变
”。相反,情况是传入的项目是按值传递的。 但是按值传递的项本身就是一个引用。
从技术上讲,这称为共享呼叫。
实际上,这意味着如果您更改参数本身(如
num
和obj2
),不会影响输入到参数中的项目。 但是,如果您更改参数的内部,它将传播回来(与obj1
一样)。It's interesting in JavaScript. Consider this example:
This produces the output:
obj1
was not a reference at all, then changingobj1.item
would have no effect on theobj1
outside of the function.num
would be100
, andobj2.item
would read"changed"
. Instead,num
stays10
andobj2.item
remains"unchanged
".Instead, the situation is that the item passed in is passed by value. But the item that is passed by value is itself a reference.
Technically, this is called call-by-sharing.
In practical terms, this means that if you change the parameter itself (as with
num
andobj2
), that won't affect the item that was fed into the parameter. But if you change the internals of the parameter, that will propagate back up (as withobj1
).它总是按值传递,但对于对象来说,变量的值是一个引用。 因此,当您传递一个对象并更改其成员时,这些更改将在函数外部保留。 这使得它看起来像按引用传递。 但是,如果您实际更改对象变量的值,您将看到更改不会持续存在,证明它确实是按值传递。
例子:
输出:
It's always pass by value, but for objects the value of the variable is a reference. Because of this, when you pass an object and change its members, those changes persist outside of the function. This makes it look like pass by reference. But if you actually change the value of the object variable you will see that the change does not persist, proving it's really pass by value.
Example:
Output:
变量并不“保存”对象; 它有一个参考。 您可以将该引用分配给另一个变量,现在两者都引用同一个对象。 它总是按值传递(即使该值是引用......)。
无法更改作为参数传递的变量所保存的值,如果 JavaScript 支持按引用传递,则这是可能的。
The variable doesn't "hold" the object; it holds a reference. You can assign that reference to another variable, and now both reference the same object. It's always pass by value (even when that value is a reference...).
There's no way to alter the value held by a variable passed as a parameter, which would be possible if JavaScript supported passing by reference.
我的两分钱......这就是我的理解方式。 (如果我错了,请随时纠正我)
是时候扔掉您所知道的有关按值/引用传递的所有内容了。
因为在 JavaScript 中,是按值传递还是按引用传递还是其他方式传递并不重要。
重要的是传递给函数的参数的突变与分配。
好吧,让我尽力解释一下我的意思。 假设您有一些对象。
我们所做的是“赋值”...我们将 2 个单独的空对象分配给变量“object1”和“object2”。
现在,假设我们更喜欢 object1...因此,我们“分配”一个新变量。
接下来,无论出于何种原因,我们决定更喜欢对象 2。 因此,我们做了一些重新分配。
object1 或 object2 没有发生任何事情。 我们根本没有更改任何数据。 我们所做的就是重新分配我们最喜欢的对象。 重要的是要知道 object2 和 favoriteObject 都分配给同一个对象。 我们可以通过这些变量中的任何一个来更改该对象。
好的,现在让我们看看像字符串这样的基元。
再次,我们选择一个最喜欢的。
我们的 favoriteString 和 string1 变量都分配给“Hello world”。 现在,如果我们想改变我们的 favoriteString 怎么办? 会发生什么???
呃哦……发生了什么事。 我们无法通过更改 favoriteString 来更改 string1...为什么? 因为我们没有更改我们的字符串对象。 我们所做的就是将 favoriteString 变量“重新分配”到一个新字符串。 这实际上将它与 string1 断开了。 在前面的示例中,当我们重命名对象时,我们没有分配任何内容。 (好吧,不是变量本身,...但是,我们确实将 name 属性分配给了一个新字符串。)相反,我们改变了保持两个变量和底层对象。 (即使我们想要修改或变异字符串对象本身,我们也做不到,因为字符串在JavaScript中实际上是不可变的。)
现在,我们来讨论函数和传递参数...当您调用函数并传递参数时,您本质上所做的是对新变量的“赋值”,其工作原理与使用等号 (=) 赋值完全相同。
以这些例子为例。
现在,同样的事情,但是有一个函数,
好的,现在让我们举几个使用对象的例子......首先,没有函数。
现在,同样的事情,但是通过函数调用
OK,如果您通读整篇文章,也许您现在可以更好地理解函数调用在 JavaScript 中的工作原理。 通过引用传递还是通过值传递并不重要……重要的是赋值与变异。
每次将变量传递给函数时,您都会“分配”给参数变量的名称,就像使用等号 (=) 一样。
永远记住,等号 (=) 表示赋值。
永远记住,在 JavaScript 中将参数传递给函数也意味着赋值。
它们是相同的,并且这两个变量以完全相同的方式连接(也就是说它们不是,除非您将它们分配给同一个对象)。
“修改变量”影响不同变量的唯一时间是底层对象发生变异时(在这种情况下,您没有修改变量,而是修改了对象本身。
区分对象和基元是没有意义的,因为它的工作方式与您没有函数而只是使用等号分配给新变量的方式完全相同。
唯一的问题是传递给函数的变量名称与名称相同。当发生这种情况时,您必须将函数内部的参数视为函数私有的全新变量(因为它确实是)
My two cents... This is the way I understand it. (Feel free to correct me if I'm wrong)
It's time to throw out everything you know about pass by value / reference.
Because in JavaScript, it doesn't matter whether it's passed by value or by reference or whatever.
What matters is mutation vs assignment of the parameters passed into a function.
OK, let me do my best to explain what I mean. Let's say you have a few objects.
What we have done is "assignment"... We've assigned 2 separate empty objects to the variables "object1" and "object2".
Now, let's say that we like object1 better... So, we "assign" a new variable.
Next, for whatever reason, we decide that we like object 2 better. So, we do a little re-assignment.
Nothing happened to object1 or to object2. We haven't changed any data at all. All we did was re-assign what our favorite object is. It is important to know that object2 and favoriteObject are both assigned to the same object. We can change that object via either of those variables.
OK, now let's look at primitives like strings for example
Again, we pick a favorite.
Both our favoriteString and string1 variables are assigned to 'Hello world'. Now, what if we want to change our favoriteString??? What will happen???
Uh oh.... What has happened. We couldn't change string1 by changing favoriteString... Why?? Because we didn't change our string object. All we did was "RE ASSIGN" the favoriteString variable to a new string. This essentially disconnected it from string1. In the previous example, when we renamed our object, we didn't assign anything. (Well, not to the variable itself, ... we did, however, assign the name property to a new string.) Instead, we mutated the object which keeps the connections between the 2 variables and the underlying objects. (Even if we had wanted to modify or mutate the string object itself, we couldn't have, because strings are actually immutable in JavaScript.)
Now, on to functions and passing parameters.... When you call a function, and pass a parameter, what you are essentially doing is an "assignment" to a new variable, and it works exactly the same as if you assigned using the equal (=) sign.
Take these examples.
Now, the same thing, but with a function
OK, now let’s give a few examples using objects instead... first, without the function.
Now, the same thing, but with a function call
OK, if you read through this entire post, perhaps you now have a better understanding of how function calls work in JavaScript. It doesn't matter whether something is passed by reference or by value... What matters is assignment vs mutation.
Every time you pass a variable to a function, you are "Assigning" to whatever the name of the parameter variable is, just like if you used the equal (=) sign.
Always remember that the equals sign (=) means assignment.
Always remember that passing a parameter to a function in JavaScript also means assignment.
They are the same and the 2 variables are connected in exactly the same way (which is to say they aren't, unless you count that they are assigned to the same object).
The only time that "modifying a variable" affects a different variable is when the underlying object is mutated (in which case you haven't modified the variable, but the object itself.
There is no point in making a distinction between objects and primitives, because it works the same exact way as if you didn't have a function and just used the equal sign to assign to a new variable.
The only gotcha is when the name of the variable you pass into the function is the same as the name of the function parameter. When this happens, you have to treat the parameter inside the function as if it was a whole new variable private to the function (because it is)
这些短语/概念最初是在 JS 创建之前很久就定义的,它们并没有准确地描述 javascript 的语义。 我认为尝试将它们应用到 JS 会导致更多的混乱。
因此,不要沉迷于“通过引用/值传递”。
请考虑以下事项:
因此,如果我必须给它一个名称,我会说“pass-by-pointer”——我们不处理 JS 中的指针,但底层引擎会处理。
最后一些评论:
These phrases/concepts were originally defined long before JS was created and they don't accurately describe the semantics for javascript. I think trying to apply them to JS causes more confusion than not.
So don't get hung up on "pass by reference/value".
Consider the following:
So if I had to give it a name I'd say "pass-by-pointer" -- we don't deal with pointers in JS but the underlying engine does.
Some final comments: