<menu id="ycqsw"></menu><nav id="ycqsw"><code id="ycqsw"></code></nav>
<dd id="ycqsw"><menu id="ycqsw"></menu></dd>
  • <nav id="ycqsw"></nav>
    <menu id="ycqsw"><strong id="ycqsw"></strong></menu>
    <xmp id="ycqsw"><nav id="ycqsw"></nav>
  • js實現深拷貝的方法(js深拷貝和淺拷貝的區別)


    在實際開發當中,我們經常會遇到要對對象進行深拷貝的情況。而且深拷貝這個問題在面試過程中也經常會遇到,下面就對本人在學習過程中的收獲,做以簡單的總結。

    什么是淺拷貝,什么是深拷貝?

    什么是淺拷貝

    關于淺拷貝的概念,我在網上看到一種說法,直接上代碼。

    var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};
    var person1 = person;       //他們認為這是淺拷貝

    但是我個人認為,上面這個根本不涉及拷貝,只是一個簡單的引用賦值。以我的理解,淺拷貝應該是不考慮對象的引用類型的屬性,只對當前對象的所有成員進行拷貝,代碼如下:

    function copy(obj){
        var objCopy = {};
        for(var key in obj){
            objCopy[key] = obj[key];
        }
        return objCopy;
    }
    
    var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};
    var personCopy = copy(person);

    上面這段代碼中,person對象擁有兩個基本類型的屬性name和age,一個引用類型的屬性car,當使用如上方法進行拷貝的時候,name和age屬性會被正常的拷貝,但是car屬性,只會進行引用的拷貝,這樣會導致拷貝出來的對象personCopy和person會共用一個car對象。這樣就是所謂的淺拷貝。

    什么是深拷貝

    深拷貝的就是在拷貝的時候,需要將當前要拷貝的對象內的所有引用類型的屬性進行完整的拷貝,也就是說拷貝出來的對象和原對象之間沒有任何數據是共享的,所有的東西都是自己獨占的一份。

    如何實現深拷貝

    實現深拷貝需要考慮的問題

    實現深拷貝需要考慮如下幾個因素:

    • 傳入的對象是使用對象字面量{}創建的對象還是由構造函數生成的對象
    • 如果對象是由構造函數創建出來的,那么是否要拷貝原型鏈上的屬性
    • 如果要拷貝原型鏈上的屬性,那么如果原型鏈上存在多個同名的屬性,保留哪個
    • 處理循環引用的問題

    第三方庫實現深拷貝

    jQuery的$.extend()

    我們可以通過$.extend()方法來完成深復制。值得慶幸的是,我們在jQuery中可以通過添加一個參數來實現遞歸extend。調用$.extend(true, {}, …)就可以實現深復制,參考下面的例子:

    var x = {
        a: 1,
        b: { f: { g: 1 } },
        c: [ 1, 2, 3 ]
    };
    
    var y = $.extend({}, x),          //shallow copy
        z = $.extend(true, {}, x);    //deep copy
    
    y.b.f === x.b.f       // true
    z.b.f === x.b.f       // false

    但是jQuery的這個$.extend()方法,有弊端,什么弊端呢?我們看下面的例子:

    var objA = {};
    var objB = {};
    
    objA.b = objB;
    objB.a = objA;
    
    $.extend(true,{},a);
    
    //這個時候就出現異常了
    //Uncaught RangeError: Maximum call stack size exceeded(…)

    也就是說,jQuery中的$.extend()并沒有處理循環引用的問題。

    使用JSON對象實現深拷貝

    使用JSON全局對象的parse和stringify方法來實現深復制也算是一個簡單討巧的方法。

    function jsonClone(obj) {
        return JSON.parse(JSON.stringify(obj));
    }
    var clone = jsonClone({ a:1 });

    然而使用這種方法會有一些隱藏的坑,它能正確處理的對象只有 Number, String, Boolean, Array, 扁平對象,即那些能夠被 json 直接表示的數據結構。

    自己造輪子

    下面我們給出一個簡單的解決方案,當然這個方案是參考別人的方式來實現的。希望對大家有用。

    var clone = (function() {
        //這個方法用來獲取對象的類型 返回值為字符串類型 "Object RegExp Date Array..."
        var classof = function(o) {
            if (o === null) {
                return "null";
            }
            if (o === undefined) {
                return "undefined";
            }
            // 這里的Object.prototype.toString很可能用的就是Object.prototype.constructor.name
            // 這里使用Object.prototype.toString來生成類型字符串
            var className = Object.prototype.toString.call(o).slice(8, -1);
            return className;
        };
    
        //這里這個變量我們用來存儲已經保存過的屬性,目的在于處理循環引用的問題
        var references = null;
    
        //遇到不同類型的對象的處理方式
        var handlers = {
            //正則表達式的處理
            'RegExp': function(reg) {
                var flags = '';
                flags += reg.global ? 'g' : '';
                flags += reg.multiline ? 'm' : '';
                flags += reg.ignoreCase ? 'i' : '';
                return new RegExp(reg.source, flags);
            },
            //時間對象處理
            'Date': function(date) {
                return new Date(+date);
            },
            //數組處理 第二個參數為是否做淺拷貝
            'Array': function(arr, shallow) {
                var newArr = [],
                i;
                for (i = 0; i < arr.length; i++) {
                    if (shallow) {
                        newArr[i] = arr[i];
                    } else {
                        //這里我們通過reference數組來處理循環引用問題
                        if (references.indexOf(arr[i]) !== -1) {
                            continue;
                        }
                        var handler = handlers[classof(arr[i])];
                        if (handler) {
                            references.push(arr[i]);
                            newArr[i] = handler(arr[i], false);
                        } else {
                            newArr[i] = arr[i];
                        }
                    }
                }
                return newArr;
            },
            //正常對象的處理 第二個參數為是否做淺拷貝
            'Object': function(obj, shallow) {
                var newObj = {}, prop, handler;
                for (prop in obj) {
                    //關于原型中屬性的處理太過復雜,我們這里暫時不做處理
                    //所以只對對象本身的屬性做拷貝
                    if (obj.hasOwnProperty(prop)) {
                        if (shallow) {
                            newObj[prop] = obj[prop];
                        } else {
                            //這里還是處理循環引用的問題
                            if (references.indexOf(obj[prop]) !== -1) {
                                continue;
                            }
    
                            handler = handlers[classof(obj[prop])];
                            //如果沒有對應的處理方式,那么就直接復制
                            if (handler) {
                                references.push(obj[prop]);
                                newObj[prop] = handler(obj[prop], false);
                            } else {
                                newObj[prop] = obj[prop];
                            }
                        }
                    }
                }
                return newObj;
            }
        };
    
        return function(obj, shallow) {
            //首先重置我們用來處理循環引用的這個變量
            references = [];
            //我們默認處理為淺拷貝
            shallow = shallow === undefined ? true : false;
            var handler = handlers[classof(obj)];
            return handler ? handler(obj, shallow) : obj;
        };
    }());
    
    (function() {
        //下面是一些測試代碼
        var date = new Date();
        var reg = /hello word/gi;
        var obj = {
            prop: 'this ia a string',
            arr: [1, 2, 3],
            o: {
                wow: 'aha'
            }
        };
        var refer1 = {
            arr: [1, 2, 3]
        };
        var refer2 = {
            refer: refer1
        };
        refer1.refer = refer2;
    
        var cloneDate = clone(date, false);
        var cloneReg = clone(reg, false);
        var cloneObj = clone(obj, false);
        alert((date !== cloneDate) && (date.valueOf() === cloneDate.valueOf()));
        alert((cloneReg !== reg) && (reg.toString() === cloneReg.toString()));
        alert((obj !== cloneObj) && (obj.arr !== cloneObj.arr) && (obj.o !== cloneObj.o) && (JSON.stringify(obj) === JSON.stringify(cloneObj)));
    
        clone(refer2, false);
        alert("I'm not dead yet!");
        // Output:
        // true
        // true
        // true
        // I'm not dead yet!
    }());

    版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。

    發表評論

    登錄后才能評論
    国产精品区一区二区免费