JavaScript:如何按值传递对象?

时间:2011-09-27 18:37:15

标签: javascript

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

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

    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)
    

    o不会有.foo.bar


问题

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

14 个答案:

答案 0 :(得分:54)

不是。

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

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

alert( o.foo ); // undefined

因此,您添加到obj的所有媒体资源都不会添加到o。添加到obj的所有属性都具有与o中的属性相同的属性名称,这将影响o属性。

当然,添加到o的所有属性都可以从obj获得,如果它们没有被镜像,并且原型链中具有o的所有对象都会看到相同的更新到o

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

obj

在这里你可以看到,因为你没有var o = { baz: [] }; (function(x){ var obj = Object.create( x ); obj.baz.push( 'new value' ); })(o); alert( o.baz[0] ); // 'new_value' baz obaz的{​​{1}} 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

答案 1 :(得分: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;
}

现在你可以像这样使用:

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

答案 2 :(得分:33)

查看此答案https://stackoverflow.com/a/5344074/746491

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

答案 3 :(得分:16)

使用Object.assign()

示例:

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

执行相同操作的更简洁的示例:

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

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

答案 4 :(得分:15)

您对JavaScript中的对象工作方式有点困惑。对象的引用变量的值。没有反序列化的价值。创建对象时,其结构存储在内存中,并且分配给它的变量保存对该结构的引用。

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

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

答案 5 :(得分:12)

使用此

x = Object.create(x1);

xx1将是两个不同的对象,x中的更改不会更改x1

答案 6 :(得分: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;
}

答案 7 :(得分:7)

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

var oShallowCopy = jQuery.extend({}, o);
var oDeepCopy    = jQuery.extend(true, {}, o); 

参考:

答案 8 :(得分:6)

实际上,Javascript总是按值传递。但由于对象引用为,因此对象的行为类似于通过引用传递

因此,为了绕过这个, stringify 对象,然后解析,两者都使用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

答案 9 :(得分:2)

我需要按值复制对象(不是引用),我觉得这个页面很有用:

What is the most efficient way to deep clone an object in JavaScript?。特别是,使用John Resig的以下代码克隆对象:

//Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

答案 10 :(得分:2)

使用ES6语法:

let obj = Object.assign({}, o);

答案 11 :(得分:1)

当你归结为它时,它只是一个花哨的过于复杂的代理,但也许Catch-All Proxies可以做到这一点?

var o = {
    a: 'a',
    b: 'b',
    func: function() { return 'func'; }
};

var proxy = Proxy.create(handlerMaker(o), o);

(function(x){
    var obj = x;
    console.log(x.a);
    console.log(x.b);
    obj.foo = 'foo';
    obj.bar = 'bar';
})(proxy);

console.log(o.foo);

function handlerMaker(obj) {
  return {
   getOwnPropertyDescriptor: function(name) {
     var desc = Object.getOwnPropertyDescriptor(obj, name);
     // a trapping proxy's properties must always be configurable
     if (desc !== undefined) { desc.configurable = true; }
     return desc;
   },
   getPropertyDescriptor:  function(name) {
     var desc = Object.getOwnPropertyDescriptor(obj, name); // not in ES5
     // a trapping proxy's properties must always be configurable
     if (desc !== undefined) { desc.configurable = true; }
     return desc;
   },
   getOwnPropertyNames: function() {
     return Object.getOwnPropertyNames(obj);
   },
   getPropertyNames: function() {
     return Object.getPropertyNames(obj);                // not in ES5
   },
   defineProperty: function(name, desc) {

   },
   delete:       function(name) { return delete obj[name]; },   
   fix:          function() {}
  };
}

答案 12 :(得分:0)

如果您使用的是lodash或npm,请使用lodash的合并功能将所有对象的属性深层复制到一个新的空对象,如下所示:

var objectCopy = lodash.merge({}, originalObject);

https://lodash.com/docs#merge

https://www.npmjs.com/package/lodash.merge

答案 13 :(得分:0)

使用obj2 = {... obj1} 现在两个对象具有相同的值,但引用不同