JavaScript 是一种按引用传递还是按值传递的语言?

发布于 2024-07-13 04:56:06 字数 184 浏览 11 评论 0原文

基本类型(数字、字符串等)按值传递。 尽管如此,对象仍然是未知的,因为它们既可以按值传递(在这种情况下,我们认为保存对象的变量是对该对象的引用),也可以按引用传递(当我们认为对象的变量保存对象本身)。

尽管最终并不重要,但我想知道通过约定呈现参数的正确方法是什么。 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

感性不性感 2024-07-20 04:56:09

我理解这一点的简单方法...

  • 调用函数时,您正在传递内容(引用或
    value)的参数变量,而不是变量本身。

    var var1 = 13; 
      var var2 = { 属性:2 }; 
    
      //13和var2的内容(引用)被传递到这里 
      foo(var1, var2);  
      
  • 在函数内部,参数变量inVar1inVar2接收传递的内容。

    函数 foo(inVar1, inVar2){ 
          //改变inVar1和inVar2的内容不会影响外部变量 
          inVar1 = 20; 
          inVar2 = { 道具:7 }; 
      } 
      
  • 由于inVar2收到了{ prop: 2 }的引用,因此可以更改对象属性的值。

    函数 foo(inVar1, inVar2){ 
          inVar2.prop = 7;  
      } 
      

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.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
    
  • Inside the function, parameter variables, inVar1 and inVar2, receive the contents being passed.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
    
  • Since inVar2 received the reference of { prop: 2 }, you can change the value of the object's property.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
    
放飞的风筝 2024-07-20 04:56:09

JavaScript 按值传递基本类型,按引用传递对象类型

现在,人们喜欢无休止地争论是否“通过引用传递”
是描述 Java 等的正确方法。 实际上确实如此。 重点
是这样的:

  1. 传递对象并不复制该对象。
  2. 传递给函数的对象可以由该函数修改其成员。
  3. 传递给函数的原始值不能被函数修改。 已制作副本。

在我的书中,这称为通过引用传递。

Brian Bi - 哪些编程语言是通过引用传递的?


更新

这是对此的反驳:

JavaScript 中没有可用的“通过引用传递”。

JavaScript passes primitive types by value and object types by reference

Now, people like to bicker endlessly about whether "pass by reference"
is the correct way to describe what Java et al. actually do. The point
is this:

  1. Passing an object does not copy the object.
  2. An object passed to a function can have its members modified by the function.
  3. A primitive value passed to a function cannot be modified by the function. A copy is made.

In my book that's called passing 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.

我的影子我的梦 2024-07-20 04:56:09

在 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;
}

Passing arguments to a function in JavaScript is analogous to passing
parameters by pointer value in 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;
}
情话墙 2024-07-20 04:56:09

对于编程语言律师,我已经阅读了 ECMAScript 5.1 的以下部分(比最新版本更容易阅读),并一直阅读 在 ECMAScript 邮件列表上询问

TL;DR:一切都是按值传递的,但对象的属性是引用,并且标准中令人毛骨悚然地缺乏对象的定义。

参数列表的构造

第 11.2.4 节“参数列表”对于生成仅包含 1 个参数的参数列表进行了以下说明:

生产 ArgumentList :AssignmentExpression 的计算如下:

  1. 令 ref 为赋值表达式的计算结果。
  2. 令 arg 为 GetValue(ref)。
  3. 返回一个列表,其唯一项目是 arg。

本节还列举了参数列表具有 0 或 >1 个参数的情况。

因此,一切都是通过引用传递的。

对象属性的访问

第 11.2.1 节“属性访问器”

生产式 MemberExpression : MemberExpression [ Expression ] 的计算如下:

  1. 设 baseReference 为 MemberExpression 的计算结果。
  2. 令 baseValue 为 GetValue(baseReference)。
  3. 设 propertyNameReference 为表达式求值的结果。
  4. 令 propertyNameValue 为 GetValue(propertyNameReference)。
  5. 调用 CheckObjectCoercible(baseValue)。
  6. 令 propertyNameString 为 ToString(propertyNameValue)。
  7. 如果正在评估的语法产生式包含在严格模式代码中,则令 strict 为 true,否则令
    严格为假。
  8. 返回Reference类型的值,其基值为baseValue,引用名称为
    propertyNameString,其严格模式标志为 strict。

因此,对象的属性始终可用作参考。

关于引用

第 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 production ArgumentList : AssignmentExpression is evaluated as follows:

  1. Let ref be the result of evaluating AssignmentExpression.
  2. Let arg be GetValue(ref).
  3. Return a List whose sole item is arg.

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"

The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:

  1. Let baseReference be the result of evaluating MemberExpression.
  2. Let baseValue be GetValue(baseReference).
  3. Let propertyNameReference be the result of evaluating Expression.
  4. Let propertyNameValue be GetValue(propertyNameReference).
  5. Call CheckObjectCoercible(baseValue).
  6. Let propertyNameString be ToString(propertyNameValue).
  7. If the syntactic production that is being evaluated is contained in strict mode code, let strict be true, else let
    strict be false.
  8. Return a value of type Reference whose base value is baseValue and whose referenced name is
    propertyNameString, and whose strict mode flag is strict.

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.

撕心裂肺的伤痛 2024-07-20 04:56:09

引用提问者的话:

原始类型(数字、字符串等)是按值传递的,但对象 [...$ 可以都是按值传递的(在这种情况下,我们认为保存对象的变量实际上是一个对对象的引用)和按引用传递(当我们认为对象的变量保存对象本身时)。

这不是真的。 对象作为参数传递的方式只有一种。 尽管其他答案也区分了原始值和非原始值,但这会分散注意力并且无关紧要。

无按引用传递

考虑此代码,它不关心 a 是原语还是对象:

function alter(arg) {  /* some magic happening here to `arg` */ }

function main() {
    var a, b;
    a = b = { x: 1 }; // Example value. It can be an object or primitive: irrelevant here
    console.log(a === b); // true
    alter(a);
    console.log(a === b); // false? (not possible)
}

如果函数 alter 有某种方法可以使第二个输出 false那么我们可以说传递引用,因为 arg 将是一个别名 代表a。 但这在 JavaScript 中是不可能的。 原语的概念与此无关:JavaScript 中没有引用传递。

对象

在 JavaScript 中,对象是对属性(和槽)集合的引用。 当使用对象作为参数调用函数时,不会创建新的属性集合。 函数的参数变量(局部变量)将接收相同的值(即对属性集合的引用)。

变量赋值与对象突变

调用者的数据结构可能会发生突变,也可以通过函数调用进行突变。 显然,这仅适用于可变值(根据定义),而在 JavaScript 中,这是通过将值分配给属性来实现的。 有两点需要区分:

  • 对 objext 属性的赋值会改变调用者和被调用者都引用的对象。
  • 对参数变量的赋值不会改变调用者的数据。 特别是,如果该参数变量是一个对象(引用),则该引用将被覆盖,因此函数将自身与调用者的数据分离。

Quoting the asker:

The primitive types (number, string, etc.) are passed by value, but objects [...$ can be both passed-by-value (in which case we consider that a variable holding an object is in fact a reference to the object) and passed-by-reference (when we consider that the variable to the object holds the object itself).

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:

function alter(arg) {  /* some magic happening here to `arg` */ }

function main() {
    var a, b;
    a = b = { x: 1 }; // Example value. It can be an object or primitive: irrelevant here
    console.log(a === b); // true
    alter(a);
    console.log(a === b); // false? (not possible)
}

If the function alter had some way to make that second output false, then we could speak of pass-by-reference, as then arg would have been an alias for a. 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:

  • Assignment to an objext property mutates the object that both the caller and the callee have a reference to.
  • Assignment to a parameter variable will not mutate the caller's data. In particular, if that parameter variable was an object (a reference), that reference is overwritten and so the function detaches itself from the caller's data.
迟到的我 2024-07-20 04:56:09

我发现的最简洁的解释是在 AirBNB 风格指南中:

  • Primitives< /strong>:当您访问原始类型时,您可以直接对其进行操作

    • 字符串
    • 数量
    • 布尔值
    • 未定义

例如:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complex:当您访问复杂类型时,您将处理对其值的引用

    • 对象
    • 数组
    • 功能

的引用,例如:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

即有效地基本类型按值传递,复杂类型按引用传递。

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

    • string
    • number
    • boolean
    • null
    • undefined

E.g.:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complex: When you access a complex type you work on a reference to its value

    • object
    • array
    • function

E.g.:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

I.e. effectively primitive types are passed by value, and complex types are passed by reference.

零崎曲识 2024-07-20 04:56:09

我已经多次阅读这些答案,但直到我了解 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

The semantics of call by sharing differ from call by reference in that assignments to function arguments within the function aren't visible to the caller (unlike by reference semantics)[citation needed], so e.g. if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned — it is shared.

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.

剑心龙吟 2024-07-20 04:56:09

Java 脚本变量是按值传递的。 例如:

function process(a){
    a = 20
    console.log(a)
}


var a = 10

process(a) // prints 20
console.log(a) // print 10

但是java脚本对象是通过引用传递的。 例如:

function process(obj){
    obj.a = 'aa'
    console.log(obj)
}

let obj ={
    a: 'a_val',
    b: 'b_val'
}

process(obj)
console.log(obj)

输出:

 {a: 'aa', b: 'b_val' }
 { a: 'aa', b: 'b_val' }

要将对象传递给函数作为按值传递,您可以使用扩展运算符,如下所示

function process({...obj}){
    obj.a = 'aa'
    console.log(obj)
}

let obj ={
    a: 'a_val',
    b: 'b_val'
}

process(obj)
console.log(obj)

输出:

{ a: 'aa', b: 'b_val' }
{ a: 'a_val', b: 'b_val' }

希望能够澄清。

Java script variable are pass by value. For example:

function process(a){
    a = 20
    console.log(a)
}


var a = 10

process(a) // prints 20
console.log(a) // print 10

But java script objects are pass by reference. For example:

function process(obj){
    obj.a = 'aa'
    console.log(obj)
}

let obj ={
    a: 'a_val',
    b: 'b_val'
}

process(obj)
console.log(obj)

Output:

 {a: 'aa', b: 'b_val' }
 { a: 'aa', b: 'b_val' }

To pass object to function as pass by value you can use spread operator as below

function process({...obj}){
    obj.a = 'aa'
    console.log(obj)
}

let obj ={
    a: 'a_val',
    b: 'b_val'
}

process(obj)
console.log(obj)

Output:

{ a: 'aa', b: 'b_val' }
{ a: 'a_val', b: 'b_val' }

Hope that clarifies.

挖个坑埋了你 2024-07-20 04:56:09

我找到了 扩展方法当我想将一个对象作为参数传递(可以完全修改或替换)时,Underscore.js 库非常有用。

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

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

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.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}
和我恋爱吧 2024-07-20 04:56:09

在低级语言中,如果要通过引用传递变量,则必须在创建函数时使用特定语法:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

&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:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

The &age is a reference to myAge, 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

吖咩 2024-07-20 04:56:09

如果您想要像其他语言一样的(正常)函数参数行为(传递值的副本)
然后在传递给函数之前克隆该对象:

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();    

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:

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();    
眼眸 2024-07-20 04:56:09
function passByCopy ([...array], {...object})
{
   console .log ("copied objects", array, object)
}

passByCopy ([1,2,3], {a:1, b:2, c:3})

function passByReference (array, object)
{
   console .log ("same objects", array, object)
}

passByReference ([1,2,3], {a:1, b:2, c:3})
function passByCopy ([...array], {...object})
{
   console .log ("copied objects", array, object)
}

passByCopy ([1,2,3], {a:1, b:2, c:3})

function passByReference (array, object)
{
   console .log ("same objects", array, object)
}

passByReference ([1,2,3], {a:1, b:2, c:3})
孤独难免 2024-07-20 04:56:09

我想说这是传递复制 -

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

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

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.

别闹i 2024-07-20 04:56:08

通过给出对​​外部对象的引用,将函数外部的对象传递到函数中。

当您使用该引用来操作其对象时,外部对象就会受到影响。 但是,如果在函数内部您决定将引用指向其他内容,则根本不会影响外部对象,因为您所做的只是将引用重新定向到其他内容。

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.

七月上 2024-07-20 04:56:08

可以这样想:它总是按值传递。 然而,对象的值不是对象本身,而是对该对象的引用。

这是一个示例,传递一个数字(基本类型)

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}

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)

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

Repeating this with an object yields different results:

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 }

One more example:

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}
小ぇ时光︴ 2024-07-20 04:56:08

关于按值和按引用复制、传递和比较的非常详细的说明位于本章“JavaScript:权威指南”一书。

在我们离开主题之前
操作对象和数组
参考,我们需要明确一点
命名法。

短语“路过
参考”可以有多种含义。
对于一些读者来说,这个短语指的是
一种函数调用技术
允许函数分配新值
其论点并拥有那些
修改后的值在外部可见
功能。 这不是这个词的方式
本书中使用。

这里,我们的意思是
只是对对象的引用
或数组——不是对象本身——
被传递给一个函数。 一个功能
可以使用参考修改
对象或元素的属性
数组的。 但如果函数
用一个覆盖引用
对新对象或数组的引用,
该修改不可见
在函数之外。

读者
熟悉的其他含义
这个词可能更喜欢说
对象和数组被传递
value,但传递的值是
实际上是一个参考而不是
对象本身。

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.

Before we leave the topic of
manipulating objects and arrays by
reference, we need to clear up a point
of nomenclature.

The phrase "pass by
reference" can have several meanings.
To some readers, the phrase refers to
a function invocation technique that
allows a function to assign new values
to its arguments and to have those
modified values visible outside the
function. This is not the way the term
is used in this book.

Here, we mean
simply that a reference to an object
or array -- not the object itself --
is passed to a function. A function
can use the reference to modify
properties of the object or elements
of the array. But if the function
overwrites the reference with a
reference to a new object or array,
that modification is not visible
outside of the function.

Readers
familiar with the other meaning of
this term may prefer to say that
objects and arrays are passed by
value, but the value that is passed is
actually a reference rather than the
object itself.

拔了角的鹿 2024-07-20 04:56:08

JavaScript 总是按值传递; 一切都是值类型。

对象是值,对象的成员函数本身就是值(请记住,函数是 JavaScript 中的第一类对象)。 另外,关于 JavaScript 中的一切都是对象的概念; 这是错误的。 字符串、符号、数字、布尔值、空值和未定义是基元

有时,他们可以利用从其基本原型继承的一些成员函数和属性,但这只是为了方便。 这并不意味着它们本身就是物体。 尝试以下方法供参考:

x = "test";
console.log(x.foo);
x.foo = 12;
console.log(x.foo);

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:

x = "test";
console.log(x.foo);
x.foo = 12;
console.log(x.foo);

In both console.log you will find the value to be undefined.

盛夏尉蓝 2024-07-20 04:56:08

在 JavaScript 中,值的类型控制该值是由值复制分配还是由引用复制分配。

原始值总是通过值复制来赋值/传递

  • 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 中的值。

cd 都是对同一共享值 [1,2,3] 的单独引用,该共享值是一个复合值。 值得注意的是,cd 都不“拥有”[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 引用的副本分配给 xxa 是指向相同 [1,2,3] 值的单独引用。 现在,在函数内部,我们可以使用该引用来改变值本身 (push(4))。 但是当我们进行赋值x = [4,5,6]时,这绝不会影响初始引用a指向的位置——仍然指向(现已修改)[1,2,3,4] 值。

要通过值复制有效地传递复合值(例如数组),您需要手动创建它的副本,以便传递的引用不再指向原始值。 例如:

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

来源

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
  • string
  • number
  • boolean
  • symbol in ES6

Compound values are always assigned/passed by reference-copy

  • objects
  • arrays
  • function

For example

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]

In the above snippet, because 2 is a scalar primitive, a holds one initial copy of that value, and b is assigned another copy of the value. When changing b, you are in no way changing the value in a.

But both c and d are separate references to the same shared value [1,2,3], which is a compound value. It's important to note that neither c nor d 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 shared array value itself, it's affecting just the one shared value, and both references will reference the newly modified value [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]

When we make the assignment b = [4,5,6], we are doing absolutely nothing to affect where a is still referencing ([1,2,3]). To do that, b would have to be a pointer to a rather than a reference to the array -- but no such capability exists in 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]

When we pass in the argument a, it assigns a copy of the a reference to x. x and a 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 assignment x = [4,5,6], this is in no way affecting where the initial reference a 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:

foo( a.slice() );

Compound value (object, array, etc) that can be passed by reference-copy

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

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Here, obj acts as a wrapper for the scalar primitive property a. When passed to foo(..), a copy of the obj reference is passed in and set to the wrapperparameter. We now can use the wrapper reference to access the shared object, and update its property. After the function finishes, obj.a will see the updated value 42.

Source

柠檬色的秋千 2024-07-20 04:56:08

嗯,它是关于“性能”和“速度”,以及编程语言中简单的“内存管理”一词。

在 javascript 中,我们可以将值放在两层中:type1-objectstype2-所有其他类型的值,例如 string & 布尔值 & 等等,

如果您将内存想象为下面的方块,其中每个方块中只能保存一个 type2 值:

在此处输入图像描述

每个 type2 值(绿色)都是一个单个正方形,而type1-value(蓝色)是一组

在此处输入图像描述

要点是,如果您想指示 type2 值,则地址很简单,但如果您想这样做对于 type1-value 来说也是如此,这根本不容易! :

输入图片此处的描述

以及更复杂的故事:

在此处输入图像描述

所以这里参考可以拯救我们:

输入图像描述这里

这里的绿色箭头是一个典型变量,而紫色箭头是一个对象变量,所以因为绿色箭头(典型变量)只有一个任务(这表明一个典型的值)我们不需要将它的值与它分开,所以我们将绿色箭头与它的值一起移动,无论它在哪里以及在所有赋值、函数等中......

但是我们不能用它做同样的事情紫色箭头,我们可能想将“约翰”单元移动到这里或许多其他东西...,因此紫色箭头将粘在其位置上,而分配给它的典型箭头将会移动...

一个非常令人困惑的情况是您无法意识到引用的变量如何变化,让我们看一个非常好的示例:

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'];

在此处输入图像描述
输入图像描述这里

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 as string & boolean & etc

if you imagine memory as below squares which in every one of them just one type2-value can be saved:

enter image description here

every type2-value (green) is a single square while a type1-value (blue) is a group of them:

enter image description here

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! :

enter image description here

and in a more complicated story:

enter image description here

so here references can rescue us:

enter image description here

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:

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'];

enter image description here
enter image description here

番薯 2024-07-20 04:56:08

这是对按值传递和按引用传递 (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" }
  • equal(=) 运算符设置新的内存空间或地址

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)

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
  • applies to all primitive type in JavaScript (string, number, Boolean, undefined, and null).
  • a is allocated a memory (say 0x001) and b creates a copy of the value in memory (say 0x002).
  • So changing the value of a variable doesn't affect the other, as they both reside in two different locations.

Pass by reference (objects)

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" }
  • The JavaScript engine assigns the object to the variable c, and it points to some memory, say (0x012).
  • When d=c, in this step d points to the same location (0x012).
  • Changing the value of any changes value for both the variable.
  • Functions are objects

Special case, pass by reference (objects)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • The equal(=) operator sets up new memory space or address
命比纸薄 2024-07-20 04:56:08

语义!! 设置具体的定义必然会使一些答案和注释不兼容,因为即使使用相同的单词和短语,它们也不是在描述相同的事物,但克服混乱至关重要(特别是对于新程序员)。

首先,似乎并不是每个人都掌握了多个抽象层次。 学习过第四代或第五代语言的新程序员可能很难理解汇编语言或 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.

属性 2024-07-20 04:56:08

分享我所知道的 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

sharing what I know of references in JavaScript

In JavaScript, when assigning an object to a variable, the value assigned to the variable is a reference to the object:

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

落花随流水 2024-07-20 04:56:08

观察:如果观察者没有任何方法来检查引擎的底层内存,则无法确定是否复制了不可变值或传递了引用。

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 中不存在引用这样的东西,因此所有这些都没有错误,也没有正确的观点。 因此,我认为寻找答案并不是特别有用。

² 规范中的术语参考并非传统意义上的参考。 它是对象和属性名称的容器,并且是中间值(例如,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.

 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

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 to Reference { value = a, name = "b" }). The term reference also sometimes appears in the specification in unrelated sections.

可是我不能没有你 2024-07-20 04:56:08

一切都是按值传递的。

基本类型按值传递(即实际变量值的新副本传递给函数)。

复杂类型(对象)作为“指向对象的指针”传递。 因此,您传递的实际内容是一个按值传递的指针(它是一个地址,一个与其他值一样的数值)。 显然,如果您尝试在函数内部修改对象的属性,则即使在该函数外部,该修改也会反映出来。 这是因为您正在通过指向该属性的唯一副本的指针访问该属性。

“按值传递指针”和“按引用传递对象”是同一回事。

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.

神妖 2024-07-20 04:56:08

MDN 文档解释得很清楚,但又不太冗长:

函数调用的参数是函数的参数
参数按值传递给函数。 如果功能改变
参数的值,这种变化不会在全局或在
调用函数。 然而,对象引用也是值,并且
它们很特殊:如果函数改变了引用对象的
属性,该更改在函数外部可见,(...)

来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description

The MDN docs explain it clearly, without being too verbose:

The parameters of a function call are the function's arguments.
Arguments are passed to functions by value. If the function changes
the value of an argument, this change is not reflected globally or in
the calling function. However, object references are values, too, and
they are special: if the function changes the referred object's
properties, that change is visible outside the function, (...)

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description

谈情不如逗狗 2024-07-20 04:56:07

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 保持 10obj2.item 保持“不变”。

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

实际上,这意味着如果您更改参数本身(如 numobj2),不会影响输入到参数中的项目。 但是,如果您更改参数的内部,它将传播回来(与obj1一样)。

It's interesting in JavaScript. Consider this example:

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);

This produces the output:

10
changed
unchanged
  • If obj1 was not a reference at all, then changing obj1.item would have no effect on the obj1 outside of the function.
  • If the argument was a proper reference, then everything would have changed. num would be 100, and obj2.item would read "changed". Instead, num stays 10 and obj2.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 and obj2), 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 with obj1).

怪我太投入 2024-07-20 04:56:07

它总是按值传递,但对于对象来说,变量的值是一个引用。 因此,当您传递一个对象并更改其成员时,这些更改将在函数外部保留。 这使得它看起来像按引用传递。 但是,如果您实际更改对象变量的值,您将看到更改不会持续存在,证明它确实是按值传递。

例子:

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

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:

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

Output:

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

before changeMember: foo
in changeMember: bar
after changeMember: bar
当梦初醒 2024-07-20 04:56:07

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

无法更改作为参数传递的变量所保存的值,如果 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.

画离情绘悲伤 2024-07-20 04:56:07

我的两分钱......这就是我的理解方式。 (如果我错了,请随时纠正我)

是时候扔掉您所知道的有关按值/引用传递的所有内容了。

因为在 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;

我们的 favoriteString 和 string1 变量都分配给“Hello world”。 现在,如果我们想改变我们的 favoriteString 怎么办? 会发生什么???

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

呃哦……发生了什么事。 我们无法通过更改 favoriteString 来更改 string1...为什么? 因为我们没有更改我们的字符串对象。 我们所做的就是将 favoriteString 变量“重新分配”到一个新字符串。 这实际上将它与 string1 断开了。 在前面的示例中,当我们重命名对象时,我们没有分配任何内容。 (好吧,不是变量本身,...但是,我们确实将 name 属性分配给了一个新字符串。)相反,我们改变了保持两个变量和底层对象。 (即使我们想要修改或变异字符串对象本身,我们也做不到,因为字符串在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

OK,如果您通读整篇文章,也许您现在可以更好地理解函数调用在 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'

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.

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

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.

var favoriteObject = object1;

Next, for whatever reason, we decide that we like object 2 better. So, we do a little re-assignment.

favoriteObject = object2;

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.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

OK, now let's look at primitives like strings for example

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Again, we pick a favorite.

var favoriteString = string1;

Both our favoriteString and string1 variables are assigned to 'Hello world'. Now, what if we want to change our favoriteString??? What will happen???

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

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.

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'

Now, the same thing, but with a function

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'

OK, now let’s give a few examples using objects instead... first, without the function.

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

Now, the same thing, but with a function call

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

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)

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'
沉溺在你眼里的海 2024-07-20 04:56:07

这些短语/概念最初是在 JS 创建之前很久就定义的,它们并没有准确地描述 javascript 的语义。 我认为尝试将它们应用到 JS 会导致更多的混乱。

因此,不要沉迷于“通过引用/值传递”。

请考虑以下事项:

  1. 变量是指向值的指针
  2. 重新分配变量只是将该指针指向一个新值。
  3. 重新分配变量永远不会影响指向同一对象的其他变量,因为每个变量都有自己的指针。

因此,如果我必须给它一个名称,我会说“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'

最后一些评论:

  • 短语“按值/引用传递”仅用于描述语言的行为,不一定是实际的底层实现。 由于这种抽象,丢失了正确解释所必需的关键细节,这不可避免地导致当前的情况,即在没有附加信息的情况下,单个术语无法充分描述实际行为。
  • 人们很容易认为基元是由特殊规则强制执行的,而对象则不然,但基元只是指针链的末端。
  • 作为最后一个示例,请考虑为什么清除数组的常见尝试无法按预期工作。
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

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:

  1. Variables are pointers to values.
  2. Reassigning a variable merely points that pointer at a new value.
  3. Reassigning a variable will never affect other variables that were pointing at that same object because each variable has its own pointer.

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.

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

Some final comments:

  • The phrases "pass by value/reference" are only used to describe the behavior of a language, not necessarily the actual underlying implementation. As a result of this abstraction, critical details that are essential for a decent explanation are lost, which inevitably leads to the current situation where a single term doesn't adequately describe the actual behavior without additional info.
  • It's tempting to think that primitives are enforced by special rules while objects are not, but primitives are simply the end of the pointer chain.
  • As a final example, consider why a common attempt to clear an array doesn't work as expected.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文