javascript - shallow - object clone




如何正確克隆JavaScript對象? (20)

在一行代碼中克隆Javascript對象的優雅方法

Object.assign方法是ECMAScript 2015(ES6)標準的一部分,可以完全滿足您的需求。

var clone = Object.assign({}, obj);

Object.assign()方法用於將所有可枚舉的自有屬性的值從一個或多個源對象複製到目標對象。

閱讀更多...

polyfill支持舊版瀏覽器:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

我有一個對象, x 。 我想將它複製為對象y ,這樣對y更改不會修改x 。 我意識到復制從內置JavaScript對象派生的對象將導致額外的,不需要的屬性。 這不是問題,因為我正在復制我自己的一個文字構造的對象。

如何正確克隆JavaScript對象?


A.Levy的答案幾乎完成,這是我的小貢獻: 有一種方法如何處理遞歸引用 ,請參閱此行

if(this[attr]==this) copy[attr] = copy;

如果對像是 XML DOM元素,我們必須使用cloneNode

if(this.cloneNode) return this.cloneNode(true);

受A.Levy詳盡的研究和Calvin的原型設計方法的啟發,我提供了這個解決方案:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

另見Andy Burke在答案中的註釋。


一個特別不優雅的解決方案是使用JSON編碼來製作沒有成員方法的對象的深層副本。 方法是對您的目標對象進行JSON編碼,然後通過對其進行解碼,您將獲得所需的副本。 您可以根據需要進行多次解碼,然後根據需要進行複制。

當然,函數不屬於JSON,因此這僅適用於沒有成員方法的對象。

這種方法非常適合我的用例,因為我將JSON blob存儲在鍵值存儲中,當它們作為JavaScript API中的對象公開時,每個對象實際上都包含對象原始狀態的副本,所以我們可以在調用者突變暴露的對像後計算增量。

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

互聯網上的大多數解決方案存在幾個問題。 所以我決定進行跟進,其中包括為什麼接受的答案不應被接受。

出發情況

我想要 Javascript Object與其所有子項及其子項進行深層複製 ,等等。 但由於我不是一個普通的開發人員,我的Object具有普通 propertiescircular structures甚至nested objects

所以讓我們先創建一個circular structure和一個nested object

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

讓我們把所有東西放在一個名為a

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

接下來,我們要將a複製到名為b的變量中並對其進行變異。

var b = a;

b.x = 'b';
b.nested.y = 'b';

你知道在這裡發生了什麼,因為如果不是你甚至不會落在這個偉大的問題上。

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

現在讓我們找到一個解決方案。

JSON

我嘗試的第一次嘗試是使用JSON

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

不要浪費太多時間,你會得到TypeError: Converting circular structure to JSON

遞歸副本(接受的“答案”)

讓我們來看看接受的答案。

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

看起來不錯,嘿? 它是對象的遞歸副本,並處理其他類型,如Date ,但這不是必需的。

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

遞歸和circular structures不能很好地協同工作... RangeError: Maximum call stack size exceeded

原生解決方案

在和我的同事爭吵之後,我的老闆問我們發生了什麼,他在谷歌搜索後找到了一個簡單的解決方案 。 它叫做Object.create

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

這個解決方案前一段時間被添加到Javascript中,甚至可以處理circular structure

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

...而且你看,它不適用於里面的嵌套結構。

用於原生溶液的polyfill

就像IE 8一樣,舊版瀏覽器中有一個Object.create的polyfill。它類似於Mozilla推薦的東西,當然,它並不完美,導致與原生解決方案相同的問題。

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

我把F放在範圍之外,這樣我們就可以看一下instanceof告訴我們的內容。

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

與本機解決方案相同的問題,但輸出稍差。

更好(但不完美)的解決方案

在挖掘時,我發現了一個類似的問題( 在Javascript中,當執行深層複製時,如何避免一個循環,由於屬性是“這個”? )到這個,但有一個更好的解決方案。

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

讓我們來看看輸出......

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

需求是匹配的,但仍然存在一些較小的問題,包括將nested instancecirc更改為Object

共享葉子的樹的結構將不會被複製,它們將成為兩個獨立的葉子:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

結論

使用遞歸和緩存的最後一個解決方案可能不是最好的,但它是對象的真正深層副本。 它處理簡單propertiescircular structuresnested object ,但在克隆時會弄亂它們的實例。

jsfiddle


使用jQuery,您可以使用extend進行淺層複製

var copiedObject = jQuery.extend({}, originalObject)

對copiedObject的後續更改不會影響originalObject,反之亦然。

或者進行深層複製

var copiedObject = jQuery.extend(true, {}, originalObject)

在ECMAScript 2018中

let objClone = { ...obj };

請注意,嵌套對象仍會被複製為引用。



如果你對淺拷貝沒問題,那麼underscore.js庫就有了一個clone方法。

y = _.clone(x);

或者你可以擴展它

copiedObject = _.extend({},originalObject);


對於那些使用AngularJS的人來說,還有直接的方法來克隆或擴展這個庫中的對象。

var destination = angular.copy(source);

要么

angular.copy(source, destination);

更多angular.copy documentation ...


您可以使用一行代碼克隆對象並從前一個引用中刪除任何引用。 簡單地說:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

對於當前不支持Object.create的瀏覽器/引擎,您可以使用此polyfill:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

您可以簡單地使用spread屬性來複製沒有引用的對象。 但要小心(請參閱註釋),'copy'只是在最低的對象/數組級別。 嵌套屬性仍然是引用!

完整克隆:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

克隆二級引用:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript本身實際上不支持深度克隆。 使用實用程序功能。 例如Ramda:

http://ramdajs.com/docs/#clone


每個MDN

  • 如果你想要淺拷貝,請使用Object.assign({}, a)
  • 對於“深度”複製,請使用JSON.parse(JSON.stringify(a))

不需要外部庫,但您需要首先檢查瀏覽器兼容性


為JavaScript中的任何對象執行此操作並不簡單或直接。 您將遇到錯誤地從對象原型中拾取屬性的問題,該屬性應保留在原型中而不會復製到新實例。 例如,如果要向Object.prototype添加clone方法,正如某些答案所描述的那樣,則需要顯式跳過該屬性。 但是,如果有其他額外的方法添加到Object.prototype或其他中間原型,你不知道怎麼辦? 在這種情況下,您將復制不應該使用的屬性,因此需要使用hasOwnProperty方法檢測無法預料的非本地屬性。

除了不可枚舉的屬性,當您嘗試複製具有隱藏屬性的對象時,您將遇到更嚴峻的問題。 例如, prototype是函數的隱藏屬性。 此外,對象的原型使用屬性__proto__引用,該屬性也是隱藏的,並且不會被遍歷源對象屬性的for / in循環複製。 我認為__proto__可能特定於Firefox的JavaScript解釋器,並且在其他瀏覽器中可能會有所不同,但您可以了解情況。 並非一切都是可以計算的。 如果您知道其名稱,則可以復制隱藏屬性,但我不知道有任何方法可以自動發現它。

尋求優雅解決方案的另一個障礙是正確設置原型繼承的問題。 如果你的源對象的原型是Object ,那麼簡單地用{}創建一個新的通用對象就行了,但是如果源的原型是Object一些後代,那麼你將會缺少你使用它跳過的原型中的其他成員。 hasOwnProperty過濾器,或原型中的過濾器,但首先不能枚舉。 一種解決方案可能是調用源對象的constructor屬性來獲取初始復制對象,然後復制屬性,但是您仍然不會獲得不可枚舉的屬性。 例如, Date對象將其數據存儲為隱藏成員:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

d1的日期字符串將比d2的日期字符串晚5秒。 使一個Date與另一個相同的方法是調用setTime方法,但這特定於Date類。 我不認為這個問題有一個防彈的一般解決方案,但我會很高興出錯!

當我不得不實現一般的深度複製時,我最終通過假設我只需要復制一個普通的ObjectArrayDateStringNumberBoolean來妥協。 最後3種類型是不可變的,所以我可以執行淺拷貝而不用擔心它會改變。 我進一步假設ObjectArray包含的任何元素也將是該列表中的6種簡單類型之一。 這可以通過以下代碼完成:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

只要對象和數組中的數據形成樹結構,上述函數就可以適用於我提到的6種簡單類型。 也就是說,對像中的相同數據的引用不超過一個。 例如:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a  due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

它將無法處理任何JavaScript對象,但它可能足以用於許多目的,只要您不認為它只適用於您拋出的任何內容。


JanTotoň的答案非常接近,由於兼容性問題,可能是最好在瀏覽器中使用,但它可能會導致一些奇怪的枚舉問題。例如,執行:

for ( var i in someArray ) { ... }

迭代遍歷數組的元素後,將clone()方法賦給i。這是一個避免枚舉並適用於node.js的改編:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

這避免了使clone()方法可枚舉,因為defineProperty()默認為可枚舉為false。


對舊問題的新答案!如果您有幸使用帶有Spread語法的 ECMAScript 2016(ES6),那很容易。

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

這為對象的淺拷貝提供了一種干淨的方法。製作深層複製,意味著在每個遞歸嵌套對像中設置每個值的新副本,需要上面較重的解決方案。

JavaScript不斷發展。


我寫了自己的實現。不確定它是否算作更好的解決方案:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

以下是實施:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

由於mindeavor聲明要克隆的對mindeavor “文字構造的”對象,因此解決方案可能是簡單地多次生成對象而不是克隆對象的實例:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

這是A. Levy的代碼的改編,也用於處理函數和多個/循環引用的克隆 - 這意味著如果克隆的樹中的兩個屬性是同一對象的引用,則克隆的對象樹將具有這些屬性指向引用對象的同一個克隆。這也解決了循環依賴的情況,如果不處理,會導致無限循環。算法的複雜性是O(n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

一些快速測試

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};




clone