Клонировать объект без ссылки javascript

У меня большой объект с большим количеством данных. И я хочу клонировать это в другую переменную. Когда я устанавливаю некоторый параметр экземпляра B, тот же результат в исходном объекте:

var obj = {a: 25, b: 50, c: 75};
var A = obj;
var B = obj;

A.a = 30;
B.a = 40;

alert(obj.a + " " + A.a + " " + B.a); // 40 40 40

Мой выход должен быть 25 30 40. Любые идеи?

ИЗМЕНИТЬ

Спасибо всем. Я изменяю код dystroy, и это мой результат:

Object.prototype.clone = Array.prototype.clone = function()
{
    if (Object.prototype.toString.call(this) === '[object Array]')
    {
        var clone = [];
        for (var i=0; i<this.length; i++)
            clone[i] = this[i].clone();

        return clone;
    } 
    else if (typeof(this)=="object")
    {
        var clone = {};
        for (var prop in this)
            if (this.hasOwnProperty(prop))
                clone[prop] = this[prop].clone();

        return clone;
    }
    else
        return this;
}

var obj = {a: 25, b: 50, c: 75};
var A = obj.clone();
var B = obj.clone();
A.a = 30;
B.a = 40;
alert(obj.a + " " + A.a + " " + B.a);

var arr = [25, 50, 75];
var C = arr.clone();
var D = arr.clone();
C[0] = 30;
D[0] = 40;
alert(arr[0] + " " + C[0] + " " + D[0]);

Ответ 1

Если вы используете оператор = для назначения значения var с объектом с правой стороны, javascript не копирует, а ссылается на объект.

Вы можете использовать метод lodash clone

var obj = {a: 25, b: 50, c: 75};
var A = _.clone(obj);

Или метод lodash cloneDeep, если ваш объект имеет несколько уровней объектов

var obj = {a: 25, b: {a: 1, b: 2}, c: 75};
var A = _.cloneDeep(obj);

Или метод lodash merge, если вы хотите расширить исходный объект

var obj = {a: 25, b: {a: 1, b: 2}, c: 75};
var A = _.merge({}, obj, {newkey: "newvalue"});

Или вы можете использовать метод jQuerys extend:

var obj = {a: 25, b: 50, c: 75};
var A = $.extend(true,{},obj);

Здесь jQuery 1.11 расширяет исходный код метода:

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;

        // skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

Ответ 2

Пока это не клонирование, один простой способ получить результат - использовать оригинальный объект в качестве прототипа нового.

Вы можете сделать это, используя Object.create:

var obj = {a: 25, b: 50, c: 75};
var A = Object.create(obj);
var B = Object.create(obj);

A.a = 30;
B.a = 40;

alert(obj.a + " " + A.a + " " + B.a); // 25 30 40

Это создает новый объект в A и B, который наследуется от obj. Это означает, что вы можете добавлять свойства, не влияя на оригинал.

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

if (!Object.create)
    Object.create = function(proto) {
        function F(){}
        F.prototype = proto;
        return new F;
    }

Он не подражает всем функциям Object.create, но он будет соответствовать вашим потребностям.

Ответ 3

Вы можете определить функцию клонирования.

Я использую этот:

function goclone(source) {
    if (Object.prototype.toString.call(source) === '[object Array]') {
        var clone = [];
        for (var i=0; i<source.length; i++) {
            clone[i] = goclone(source[i]);
        }
        return clone;
    } else if (typeof(source)=="object") {
        var clone = {};
        for (var prop in source) {
            if (source.hasOwnProperty(prop)) {
                clone[prop] = goclone(source[prop]);
            }
        }
        return clone;
    } else {
        return source;
    }
}

var B = goclone(A);

Он не копирует прототип, функции и т.д. Но вы должны адаптировать его (и, возможно, упростить) для своей собственной потребности.

Ответ 4

A и B ссылаются на один и тот же объект, поэтому A.a и B.a ссылаются на одно и то же свойство одного и того же объекта.

Изменить

Здесь функция "копировать", которая может выполнять задание, может выполнять как мелкие, так и глубокие клоны. Обратите внимание на оговорки. Он копирует все перечислимые свойства объекта (не наследуемые свойства), в том числе с значениями ложности (я не понимаю, почему другие подходы игнорируют их), он также не копирует несуществующие свойства разреженных массивов.

Нет общей функции копирования или клонирования, потому что существует много разных идей о том, что должна делать копия или клон в каждом случае. Большинство из них исключают объекты хоста или ничего, кроме объектов или массивов. Это также копирует примитивы. Что должно случиться с функциями?

Итак, взгляните на следующее, это немного другой подход к другим.

/* Only works for native objects, host objects are not
** included. Copies Objects, Arrays, Functions and primitives.
** Any other type of object (Number, String, etc.) will likely give 
** unexpected results, e.g. copy(new Number(5)) ==> 0 since the value
** is stored in a non-enumerable property.
**
** Expects that objects have a properly set *constructor* property.
*/
function copy(source, deep) {
   var o, prop, type;

  if (typeof source != 'object' || source === null) {
    // What do to with functions, throw an error?
    o = source;
    return o;
  }

  o = new source.constructor();

  for (prop in source) {

    if (source.hasOwnProperty(prop)) {
      type = typeof source[prop];

      if (deep && type == 'object' && source[prop] !== null) {
        o[prop] = copy(source[prop]);

      } else {
        o[prop] = source[prop];
      }
    }
  }
  return o;
}