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

虽然最后这并不重要,但我想知道通过约定来表达论点的正确方式是什么。是否有JavaScript规范的摘录,该规范定义了与此相关的语义?


当前回答

语义!!设置具体的定义必然会使一些答案和注释不兼容,因为即使使用相同的单词和短语,它们也不会描述相同的内容,但克服这种困惑至关重要(尤其是对于新程序员)。

首先,并非每个人都能理解抽象的多个层次。学习过第四代或第五代语言的较新程序员可能很难围绕汇编语言或C语言程序员熟悉的概念进行思考,而不是通过指向指针的指针来实现。通过引用传递不仅仅意味着能够使用函数参数变量更改被引用对象。

变量:引用内存中特定位置值的符号的组合概念。在讨论细节时,这个术语通常太过沉重,不能单独使用。

符号:用于引用变量(即变量名称)的文本字符串。

值:存储在内存中并使用变量符号引用的特定位。

内存位置:存储变量值的位置。(位置本身由与存储在该位置的值不同的数字表示。)

函数参数:在函数定义中声明的变量,用于引用传递给函数的变量。

函数参数:调用方传递给函数的函数外部的变量。

对象变量:其基本基本值不是“对象”本身,而是指向内存中存储对象实际数据的另一个位置的指针(内存位置值)。在大多数更高一代语言中,“指针”方面通过在各种上下文中自动取消引用而被有效隐藏。

原始变量:其值为实际值的变量。即使这个概念也会因为各种语言的自动装箱和类似对象的上下文而变得复杂,但一般的想法是变量的值是由变量符号表示的实际值,而不是指向另一个内存位置的指针。

函数参数和参数不是一回事。此外,变量的值不是变量的对象(正如许多人已经指出的,但显然被忽略了)。这些区别对于正确理解至关重要。

通过值传递或通过共享调用(对于对象):函数参数的值被COPIED到另一个由函数参数符号引用的内存位置(无论它是在堆栈上还是在堆上)。换句话说,函数参数收到了传递参数值的副本。。。AND(关键)参数的值从不被调用函数更新/更改/更改。记住,对象变量的值不是对象本身,而是指向对象的指针,因此逐个传递对象变量会将指针复制到函数参数变量。函数参数的值指向内存中完全相同的对象。对象数据本身可以通过函数参数直接更改,但函数参数的值从未更新,因此在整个函数调用过程中,甚至在函数调用之后,它都将继续指向同一对象(即使其对象的数据已更改,或者如果函数参数被完全分配了不同的对象)。仅仅因为被引用的对象可通过函数参数变量更新,就断定函数参数是通过引用传递的,这是不正确的。

通过引用调用/传递:函数参数的值可以/将由相应的函数参数直接更新。如果有帮助,函数参数将成为参数的有效“别名”——它们有效地引用同一内存位置的相同值。如果函数参数是一个对象变量,则更改对象数据的能力与传递值情况没有区别,因为函数参数仍然指向与参数相同的对象。但是在对象变量的情况下,如果函数参数设置为完全不同的对象,那么参数同样也会指向不同的对象——这在传递值的情况下不会发生。

JavaScript不通过引用传递。如果仔细阅读,你会发现所有相反的意见都误解了传递值的含义,他们错误地得出结论:通过函数参数更新对象数据的能力与“传递值”同义。

对象克隆/复制:创建新对象并复制原始对象的数据。这可以是深层副本或浅层副本,但重点是创建了一个新对象。创建对象副本是与传递值不同的概念。有些语言区分类对象和结构(或类似的),并且可能有不同的行为来传递不同类型的变量。但是JavaScript在传递对象变量时不会自动执行类似的操作。但是,缺少自动对象克隆并不意味着通过引用传递。

其他回答

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

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

(摘自上述文章。)

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

调用函数时,您将传递内容(引用或值),而不是变量本身。var var1=13;var var2={prop:2};//13和var2的内容(引用)正在此处传递foo(var1,var2);在函数内部,参数变量inVar1和inVar2接收传递的内容。函数foo(inVar1,inVar2){//更改inVar1和inVar2的内容不会影响外部变量inVar1=20;inVar2={prop:7};}由于inVar2收到了{prop:2}的引用,因此可以更改对象属性的值。函数foo(inVar1,inVar2){inVar2.prop=7;}

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

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

例子:

函数changeObject(x){x={member:“bar”};console.log(“in changeObject:”+x.member);}函数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);/*更改没有持续*/console.log(“changeMember之前:”+x.member);changeMember(x);console.log(“after changeMember:”+x.member);/*更改持续存在*/

输出:

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

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

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

是时候抛弃你所知道的关于传递值/引用的一切了。

因为在JavaScript中,它是通过值传递还是通过引用传递都不重要。重要的是变异与传递给函数的参数赋值。

好吧,让我尽力解释一下我的意思。假设您有几个对象。

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

我们所做的是“任务”。。。我们为变量“object1”和“object2”分配了两个单独的空对象。

现在,假设我们更喜欢对象1。。。因此,我们“分配”一个新变量。

var favoriteObject = object1;

接下来,无论出于什么原因,我们决定更喜欢对象2。所以,我们做了一点重新分配。

favoriteObject = object2;

对象1或对象2未发生任何情况。我们根本没有改变任何数据。我们所做的只是重新分配我们最喜欢的对象。重要的是要知道object2和favoriteObject都分配给了同一个对象。我们可以通过这些变量中的任何一个来改变这个对象。

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

好了,现在让我们看一下像字符串这样的原语

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

再次,我们选择一个最喜欢的。

var favoriteString = string1;

我们的favoriteString和string1变量都分配给“Helloworld”。现在,如果我们想更改我们的收藏夹字符串???会发生什么???

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

噢。。。。发生了什么。我们无法通过更改收藏夹字符串来更改字符串1。。。为什么?因为我们没有更改字符串对象。我们所做的只是将favoriteString变量“RE ASSIGN”为一个新字符串。这实际上将其与字符串1断开。在前面的示例中,当我们重命名对象时,我们没有分配任何内容。(好吧,不是变量本身,……但是,我们确实将name属性分配给了一个新字符串。)相反,我们对保持2个变量和基础对象之间连接的对象进行了变异。(即使我们想修改或变异字符串对象本身,我们也不能这样做,因为字符串在JavaScript中实际上是不可变的。)

现在,转到函数和传递参数。。。。当你调用一个函数并传递一个参数时,你所做的基本上是对一个新变量的“赋值”,它的工作方式与你使用等号(=)赋值的方式完全相同。

举这些例子。

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

现在,同样的事情,但有一个函数

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

好的,现在让我们举几个使用对象的例子。。。首先,没有函数。

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

现在,同样的事情,但有一个函数调用

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

好的,如果你通读了这篇文章,也许你现在对JavaScript中函数调用的工作方式有了更好的理解。不管什么是通过引用还是通过值传递。。。重要的是赋值与变异。

每次将变量传递给函数时,都会“赋值”参数变量的名称,就像使用等号(=)一样。

始终记住等号(=)表示赋值。始终记住,在JavaScript中向函数传递参数也意味着赋值。它们是相同的,并且这两个变量以完全相同的方式连接(也就是说它们不是,除非你认为它们被分配给了同一个对象)。

“修改变量”影响不同变量的唯一时间是基础对象发生变化时(在这种情况下,您没有修改变量,而是修改对象本身)。

区分对象和基元是没有意义的,因为它的工作方式与没有函数的情况完全相同,只是使用等号分配给一个新变量。

唯一的错误是当传递到函数中的变量的名称与函数参数的名称相同时。当发生这种情况时,您必须将函数内的参数视为该函数专用的一个全新变量(因为它是)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'