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

Не совсем.

В зависимости от того, что вам действительно нужно, одной из возможностей может быть установка 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 имеет свойство, которое ссылается на другой объект, например 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'

Здесь вы можете видеть, что, поскольку вы не затеняли массив в baz на o с помощью свойства 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

Ответ 2

Вот функция клонирования, которая будет выполнять глубокую копию объекта:

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)

Ответ 3

Проверьте этот ответ fooobar.com/questions/97/....

Короче говоря, JSON.parse(JSON.stringify(obj)) - быстрый способ копирования ваших объектов, если ваши объекты могут быть сериализованы для json.

Ответ 4

Вы немного смущены тем, как объекты работают в JavaScript. Ссылка на объект - это значение переменной. Нет неэтериализованной ценности. Когда вы создаете объект, его структура хранится в памяти, а переменная, которой она была назначена, содержит ссылку на эту структуру.

Даже если то, что вы просите, было предоставлено в какой-то простой конструкции на родном языке, все равно будет технически клонироваться.

JavaScript действительно просто передается по значению... это просто, что переданное значение может быть ссылкой на что-то.

Ответ 6

Javascript всегда проходит по значению. В этом случае он передает копию ссылки o в анонимную функцию. Код использует копию ссылки, но она мутирует один объект. Невозможно сделать javascript-проход ничем, кроме значения.

В этом случае вы должны передать копию базового объекта. Клонирование объекта является единственным средством обращения. Ваш метод clone нуждается в обновлении, хотя

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

Ответ 7

В качестве рассмотрения пользователям jQuery существует также способ сделать это простым способом с использованием фреймворка. Еще один способ, которым jQuery облегчает нашу жизнь.

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

ссылки:

Ответ 8

Используйте этот

x = Object.create(x1);

x и x1 будут два разных объекта, изменение в x не изменится x1

Ответ 9

Мне нужно было скопировать объект по значению (без ссылки), и я нашел эту страницу полезной:

Каков наиболее эффективный способ глубокого клонирования объекта в JavaScript?. В частности, клонирование объекта с помощью следующего кода Джона Ресига:

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

Ответ 10

Собственно, 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

Ответ 11

Когда вы сворачиваетесь на него, это просто необычный слишком сложный прокси, но, возможно, 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 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 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

Если вы используете lodash или npm, используйте функцию lodash merge, чтобы полностью скопировать все свойства объекта на новый пустой объект:

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

https://lodash.com/docs#merge

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

Ответ 13

С синтаксисом ES6:

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