JavaScript 是否按引用传递?

IT技术 javascript reference pass-by-reference pass-by-value
2021-01-24 19:54:07

JavaScript 是通过引用传递还是通过值传递?

这是来自JavaScript的示例:The Good Partsmy对矩形函数参数很困惑它实际上是undefined,并在函数内部重新定义。没有原始参考。如果我从函数参数中删除它,则内部区域函数将无法访问它。

是闭包吗?但是没有返回任何函数。

var shape = function (config) {
    var that = {};
    that.name = config.name || "";
    that.area = function () {
        return 0;
    };
    return that;
};

var rectangle = function (config, my) {
    my = my || {};
    my.l = config.length || 1;
    my.w = config.width || 1;
    var that = shape(config);
    that.area = function () {
        return my.l * my.w;
    };
    return that;
};

myShape = shape({
    name: "Unhnown"
});

myRec = rectangle({
    name: "Rectangle",
    length: 4,
    width: 6
});

console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());
6个回答

原语通过值传递,对象通过“引用的副本”传递。

具体来说,当您传递一个对象(或数组)时,您(不可见地)传递了对该对象的引用,并且可以修改该对象内容,但是如果您尝试覆盖该引用,则不会影响该对象的副本调用者持有的引用 - 即引用本身是按值传递的:

function replace(ref) {
    ref = {};           // this code does _not_ affect the object passed
}

function update(ref) {
    ref.key = 'newvalue';  // this code _does_ affect the _contents_ of the object
}

var a = { key: 'value' };
replace(a);  // a still has its original value - it's unmodfied
update(a);   // the _contents_ of 'a' are changed
这个概念很简单,应该很容易理解。我们很困惑,因为 C++ 及其后代使用了令人困惑和不合逻辑的术语。因此,我们陷入了“参考副本”的困境。但对 Alnitak 表示敬意。
2021-03-10 19:54:07
虽然不受欢迎,但对象的行为被命名为“共享调用”:en.wikipedia.org/wiki/Call_by_sharing#Call_by_sharing
2021-03-13 19:54:07
@IoanAlexandruCucu 我个人认为“参考副本”更直观;-)
2021-03-14 19:54:07
如果您打算使用短语“引用的副本”,那么您不妨将原语称为“值的副本”。这就是为什么两者实际上只是“按值传递”的原因。两者都传递值的副本,无论该值是数字、布尔值、字符串还是引用。
2021-03-16 19:54:07
@Inverse 我回滚了您的编辑,因为它完全改变了它的语义对如此高票的答案做出如此实质性的改变也是完全不合适的!
2021-03-19 19:54:07

可以这样想:

每当你在 ECMAscript 中创建一个对象时,这个对象就会在一个神秘的ECMAscript 通用位置形成,在那里任何人都无法获得。你得到的只是这个神秘地方的那个对象引用

var obj = { };

Evenobj只是对对象(位于那个特别美妙的地方)的引用,因此,您只能传递这个引用实际上,任何访问obj的代码都会修改很远很远对象

@albanx 我意识到这令人沮丧,但每个专业领域都有专门的术语。“引用”的含义类似于 C 或 C++ 中的“指针”的含义(C++ 既有指针又有引用)。然而,在 JavaScript 或 Java 之类的语言中,作为特定对象的“值”只能是对对象引用所以它不是一种类型,实际上,它是对实际值的表征。
2021-03-27 19:54:07
@albanx 我会把这个意见传达给 2012 年的我 :)
2021-03-27 19:54:07
引用本身传递的value,就像在JavaScript一切。
2021-03-29 19:54:07
@Pointy 引用的值是什么?引用是一种类型吗?我觉得这个文字游戏没用。
2021-03-29 19:54:07
@jAndy 在这次交流中,我有点病态地意识到,在我离开这个世界很久之后,人们还会对我的回答和评论发表评论,这让我感到震惊。
2021-04-06 19:54:07

我的两分钱.... JavaScript 是按引用还是按值传递参数无关紧要。真正重要的是赋值与变异。

在这个链接中写了更长、更详细的解释

当你传递任何东西(无论是对象还是基元)时,JavaScript 所做的就是在函数内部分配一个新变量……就像使用等号 ( =) 一样。

该参数在函数内的行为方式与仅使用等号分配新变量时的行为方式完全相同……以这些简单的示例为例。

var myString = 'Test string 1';

// Assignment - A link to the same place as myString
var sameString = myString;

// If I change sameString, it will not modify myString,
// it just re-assigns it to a whole new string
sameString = 'New string';

console.log(myString); // Logs 'Test string 1';
console.log(sameString); // Logs 'New string';

如果我要myString作为参数传递给函数,它的行为就像我只是将它分配给一个新变量一样。现在,让我们做同样的事情,但是用一个函数而不是一个简单的赋值

function myFunc(sameString) {

  // Reassignment... Again, it will not modify myString
  sameString = 'New string';
}

var myString = 'Test string 1';

// This behaves the same as if we said sameString = myString
myFunc(myString);

console.log(myString); // Again, logs 'Test string 1';

当您将对象传递给函数时,您可以修改对象的唯一原因是因为您没有重新分配……相反,对象可以更改或变异……同样,它的工作方式相同。

var myObject = { name: 'Joe'; }

// Assignment - We simply link to the same object
var sameObject = myObject;

// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'

sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'

// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'

如果我要myObject作为参数传递给函数,它的行为就像我只是将它分配给一个新变量一样。同样,具有完全相同行为但具有功能的相同事物。

function myFunc(sameObject) {
  // We mutate the object, so the myObject gets the change too... just like before.
  sameObject.name = 'Jill';

  // But, if we re-assign it, the link is lost
  sameObject = {
    name: 'Howard'
  };
}

var myObject = {
  name: 'Joe'
};

// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'

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

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

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

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

很好的解释雷!
2021-03-21 19:54:07
“通过副本传递”和“通过引用传递”就这么简单,传达了所有相关的含义。我得到的是“它自己的东西”还是“东西”就是你所关心的。
2021-03-24 19:54:07
您的作业(没有 &),类比似乎只是对传递值的解释,不是吗?那为什么不这么说呢?为什么在谈论按值传递时说按值传递无关紧要
2021-03-31 19:54:07

函数参数通过值或共享传递,但在 JavaScript 中永远不会通过引用!

value调用

原始类型按值传递:

var num = 123, str = "foo";

function f(num, str) {
  num += 1;
  str += "bar";
  console.log("inside of f:", num, str);
}

f(num, str);
console.log("outside of f:", num, str);

函数作用域内的重新分配在周围作用域中不可见。

这也适用于Strings,它是一种复合数据类型,但不可变:

var str = "foo";

function f(str) {
  str[0] = "b"; // doesn't work, because strings are immutable
  console.log("inside of f:", str);
}

f(str);
console.log("outside of f:", str);

呼叫共享

对象,即所有不是基元的类型,都是共享传递的。保存对象引用的变量实际上仅保存该引用的副本。如果 JavaScript 将采用按引用调用的评估策略,则该变量将保存原始引用。这是 by-sharing 和 by-reference 之间的关键区别。

这种区别的实际后果是什么?

var o = {x: "foo"}, p = {y: 123};

function f(o, p) {
  o.x = "bar"; // Mutation
  p = {x: 456}; // Reassignment
  console.log("o inside of f:", o);
  console.log("p inside of f:", p);
}

f(o, p);

console.log("o outside of f:", o);
console.log("p outside of f:", p);

变异意味着修改现有Object. 变量绑定到的引用副本和引用此对象的引用副本保持不变。因此,突变在调用者的范围内是可见的。

重新分配意味着替换绑定到变量的引用副本。由于它只是一个副本,因此持有相同引用副本的其他变量不受影响。因此,重新分配在调用者的范围内是不可见的,就像它们在调用引用评估策略中一样。

有关ECMAScript 中评估策略的更多信息

与 C 一样,归根结底,一切都是按值传递的。与 C 不同,您实际上无法备份和传递变量的位置,因为它没有指针,只有引用。

它所拥有的引用都是对象,而不是变量。有几种方法可以实现相同的结果,但它们必须手动完成,而不仅仅是在调用或声明站点添加关键字。

在幕后,我敢打赌对象是指针。一个对象参数是一个新创建的指针,它指向与传入的指针相同的地址。
2021-03-14 19:54:07
这实际上是这里答案中最正确的。如果您深入研究 V8 或竞争引擎,这就是函数调用的实际实现方式。
2021-03-23 19:54:07