JavaScript:如何按值传递对象?

vol*_*ron 85 javascript

  • 将对象作为参数传递时,JavaScript会通过引用传递它们,并且很难创建对象的本地副本.

    var o = {};
    (function(x){
        var obj = x;
        obj.foo = 'foo';
        obj.bar = 'bar';
    })(o)
    
    Run Code Online (Sandbox Code Playgroud)

    o会有.foo.bar.

  • 通过克隆来解决这个问题是可能的.简单的例子:

    var o = {};
    
    function Clone(x) {
       for(p in x)
       this[p] = (typeof(x[p]) == 'object')? new Clone(x[p]) : x[p];
    }
    
    (function(x){
        var obj = new Clone(x);
        obj.foo = 'foo';
        obj.bar = 'bar';
    })(o)
    
    Run Code Online (Sandbox Code Playgroud)

    o不会有.foo.bar.


  1. 除了创建本地副本/克隆之外,还有更好的方法按值传递对象吗?

use*_*716 57

并不是的.

根据您的实际需要,一种可能性是设置o为新对象的原型.

var o = {};
(function(x){
    var obj = Object.create( x );
    obj.foo = 'foo';
    obj.bar = 'bar';
})(o);

alert( o.foo ); // undefined
Run Code Online (Sandbox Code Playgroud)

因此,您添加的任何属性obj都不会被添加到o.添加到obj具有与属性相同的属性名称的任何属性都o将影响该o属性.

当然,如果它们没有被遮蔽,那么添加到的任何属性o都将可用obj,并且o原型链中的所有对象都将看到相同的更新o.

此外,如果obj有一个引用另一个对象的属性(如Array),则需要确保在向对象添加成员之前隐藏该对象,否则,这些成员将被添加到obj,并将在所有对象之间共享拥有obj原型链.

var o = {
    baz: []
};
(function(x){
    var obj = Object.create( x );

    obj.baz.push( 'new value' );

})(o);

alert( o.baz[0] );  // 'new_value'
Run Code Online (Sandbox Code Playgroud)

在这里,你可以看到,因为你没影的阵列,在bazo一个baz物业obj,该o.baz数组被修改.

所以相反,你需要先遮蔽它:

var o = {
    baz: []
};
(function(x){
    var obj = Object.create( x );

    obj.baz = [];
    obj.baz.push( 'new value' );

})(o);

alert( o.baz[0] );  // undefined
Run Code Online (Sandbox Code Playgroud)

  • +1,我也会在我的回答中建议`Object.create`,但我不想延伸到解释副本的浅薄;-) (3认同)

Pau*_*ese 35

这是克隆函数,它将执行对象的深层复制:

function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);

    return temp;
}
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样使用:

(function(x){
    var obj = clone(x);
    obj.foo = 'foo';
    obj.bar = 'bar';
})(o)
Run Code Online (Sandbox Code Playgroud)

  • 适合我.谢谢! (3认同)
  • 这是一个深刻的答案. (2认同)

svi*_*mre 35

看看这个答案/sf/answers/374085211/.

简而言之,JSON.parse(JSON.stringify(obj))如果您的对象可以序列化为json,则可以快速复制对象.

  • 我倾向于避免这种情况,因为对象不能总是被序列化,并且因为在像 IE7/8 这样的中年浏览器中,不包含 `JSON` 并且需要定义 - 也可能使用类似 ` 的名称更具描述性克隆`。然而,我怀疑我的观点在未来可能会改变,因为 JSON 更多地集成到非基于浏览器的软件中,如数据库和 IDE (2认同)

Bra*_*don 20

使用 Object.assign()

例:

var a = {some: object};
var b = new Object;
Object.assign(b, a);
// b now equals a, but not by association.
Run Code Online (Sandbox Code Playgroud)

一个更清洁的例子做同样的事情:

var a = {some: object};
var b = Object.assign({}, a);
// Once again, b now equals a.
Run Code Online (Sandbox Code Playgroud)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

  • 更好:`var b = Object.assign({},a);` (4认同)
  • @Brandon,在您提供的链接中,它警告您进行深度克隆:“对于深度克隆,我们需要使用其他替代方法,因为Object.assign()复制属性值。如果源值是对对象的引用,它只会复制该参考值。” (2认同)
  • 这是可行的,但请记住,它不会执行深度复制,任何包含在 `a` 中的对象都将被复制为引用。 (2认同)

And*_*y E 15

你对JavaScript中的对象如何工作有点困惑.对象的引用变量的值.没有反序列化的价值.创建对象时,其结构存储在内存中,并且为其分配的变量保存对该结构的引用.

即使你所要求的是某种简单的本地语言结构,它在技术上仍然是克隆.

JavaScript实际上只是按值传递...它只是传递的值可能是对某事物的引用.

  • 在JS中,当人们通过引用而不是通过值来表示它们意味着值就像指针而不是重复.这是非常完善的术语. (10认同)
  • 该值是对内存中位置的引用.最终,你仍然在内存中处理相同的项目,而不是使用副本.如何使这种区分模糊不清以应对新移民?这就是许多技术书籍中的解释,包括O'Reilly的权威指南,现在已经是第五版(OT:和非常弱的更新版). (6认同)
  • 对于大多数(如果不是全部?)面向对象的语言,情况也是如此...... (4认同)
  • @Erik:在我的拙见中,我认为最好更明确地定义它,以免混淆新手语言.仅仅因为某些东西已经完善并不意味着它是恰当的,甚至在技术上是正确的(因为它不是).人们说"通过引用",因为他们更容易说出来,而不是解释它是如何工作的. (3认同)
  • 嘿安迪!我对此并不感到困惑,我希望 JS 会默认进行克隆。自我克隆的效率很低,所以如果引擎做到了,我相信它会更好。不过,我怀疑需求/需求是否超过了收益。 (2认同)

小智 13

用这个

x = Object.create(x1);
Run Code Online (Sandbox Code Playgroud)

x并且x1将有两个不同的对象,改变x也不会改变x1

  • 如果对象的值不是通过引用存储的,即它们是原始数据类型,如数字或布尔值,它不仅不会改变。例如,考虑下面的代码 a = {x:[12], y:[]}; b = Object.create(a); bxpush(12); 控制台日志(轴); (2认同)

Jar*_*Par 8

Javascript总是按值传递.在这种情况下,它将引用的副本传递给o匿名函数.代码正在使用引用的副本,但它正在改变单个对象.没有办法让javascript通过除value之外的任何东西.

在这种情况下,您想要的是传递底层对象的副本.克隆对象是唯一的办法.您的克隆方法需要一些更新

function ShallowCopy(o) {
  var copy = Object.create(o);
  for (prop in o) {
    if (o.hasOwnProperty(prop)) {
      copy[prop] = o[prop];
    }
  }
  return copy;
}
Run Code Online (Sandbox Code Playgroud)


Bre*_*ber 7

作为jQuery用户的考虑因素,还有一种方法可以使用框架以简单的方式完成此操作.换句话说,jQuery让我们的生活变得更轻松.

var oShallowCopy = jQuery.extend({}, o);
var oDeepCopy    = jQuery.extend(true, {}, o); 
Run Code Online (Sandbox Code Playgroud)

参考:


Abu*_*mad 6

实际上,Javascript总是按值传递.但是因为对象引用是,所以对象的行为就像它们通过引用传递一样.

因此,为了遍历这个,使用JSON 对对象进行字符串化并将其解析回来.请参阅以下代码示例:

var person = { Name: 'John', Age: '21', Gender: 'Male' };

var holder = JSON.stringify(person);
// value of holder is "{"Name":"John","Age":"21","Gender":"Male"}"
// note that holder is a new string object

var person_copy = JSON.parse(holder);
// value of person_copy is { Name: 'John', Age: '21', Gender: 'Male' };
// person and person_copy now have the same properties and data
// but are referencing two different objects
Run Code Online (Sandbox Code Playgroud)

  • 是的,这是简单转换/重新转换的选项.通常,使用字符串效率非常低; 但是,我还没有在一段时间内评估JSON解析的性能.请记住,虽然这适用于简单对象,但包含函数作为值的对象将丢失内容. (2认同)

Gee*_*ati 5

ES6
使用像obj2 = { ...obj1 } Will 一样的扩展运算符会有相同的值但不同的引用
ES5
使用 Object.assignobj2 = Object.assign({}, obj1)

  • 这里是 javascript 新手。展开运算符创建新值而不是按引用复制?那么为什么这被否决了呢? (2认同)
  • 乍一看,它对我有用。但我发现它只是到第一层的变量。例如,当 `let a = {value: "old"}` `let b = [...a]` `b.value = "new"` 时,a 将是 `{value: "old "}`, b 将是 `{value: "new"}`。但是当 `let a = [{value: "old"}]` `let b = [...a]` `b[0].value = "new"` 时它不起作用,并且 a 将是`[{value: "new"}]`,b 将是 `[{value: "new"}]`。 (2认同)
  • 是的,它进行浅复制。如果您有嵌套对象,则需要进行深复制。为了简单起见,您可以使用 lodash 的克隆和 deepClone 功能 (2认同)