Object的原型拷貝-create、assign、getPrototypeOf 方法的結合


一、實現原型拷貝

    1.1、代碼

        tips:為了體現原型鏈,寫了繼承實現的代碼,這部分可跳過~
    
         <script>
            /* 創建包含原型鏈的實驗對象obj1-- start */
            function inheritPrototype(subType, superType) {
                var prototype = Object(superType.prototype);
                prototype.constructor = subType;
                subType.prototype = prototype;
            }

            function SuperType(name) {
                this.name = name;
                this.colors = ['red', 'yellow', 'black'];
            }

            SuperType.prototype.sayName = function() {
                alert(this.name);
            }

            function SubType(name, age) {
                SuperType.call(this, name);
                this.age = age;
            }

            inheritPrototype(SubType, SuperType);

            SubType.prototype.sagAge = function() {
                alert(this.age);
            };

            var obj1 = new SubType('puppy', 12);
             /* 完成實驗對象obj1的創建  --end */
            
            /* 原型拷貝-- start */
            var obj2 = Object.getPrototypeOf(obj1);
            var obj3 = Object.assign(Object.create(obj2), obj1);
            /* 原型拷貝-- end */

            console.log(obj1);
            console.log(obj2);
            console.log(obj3);
        </script>
    以上代碼用於驗證原型鏈的拷貝,代碼分析如下。

    1.2、代碼分析

        step1:設置 擁有原型鏈的實例obj1,其屬性如下:
        
        其中,自身屬性:age、colors、name;原型鏈屬性:sayAge、sayName;可忽略部分為對象的默認原型屬性,本實驗不考察。
 
        step2:使用 Object.getPrototypeOf() 取得實例對象obj1的 原型屬性對象 obj2,屬性如下:
        
 
        step3:使用 Object.create() 將原型屬性對象obj2設置成 新對象的原型屬性
 
        step4:使用 Object.assign() 實現 自身屬性的拷貝,將其疊加到擁有原型屬性的新對象上,形成obj3。
        
        如上,通過step2-step4的過程,實現了對obj1的原型鏈拷貝,形成新的對象obj3。

    1.3、拷貝代碼整合(淺拷貝、深拷貝、原型拷貝)

            function clone(origin) {
                return Object.assign({}, origin);
            }
 
            function moreClone(origin) {
                let oriProto = Object.getPrototypeOf(origin);
                return Object.assign(Object.create(oriProto), origin);
            }
    函數clone為簡單復制,得到原對象自身可枚舉屬性的拷貝;
    函數moreClone可拷貝原始對象的繼承鏈及自身可枚舉的屬性。   
    tips:這里所用的拷貝方法為淺拷貝。深拷貝、淺拷貝簡單說就是看源、目標的所有屬性是否相互影響(自身屬性、或者原型屬性是否指向同一個地址),如果影響就是淺拷貝,如果不影響則為深拷貝。如果origin內有復雜類型的數據,會使得屬性指向相同地址,從而使得源、目標對象之間相互影響。    

    

    如上圖,對obj1的colors數據插入一個值,數組的length增1;同時,發現obj3的length也增加了1。淺拷貝得證。

    jquery的$.extend實現的深度拷貝可通過for...in實現,也就是其深拷貝是對自身的與繼承的可枚舉屬性進行~其丟掉了原型鏈。所以,原型拷貝與深拷貝之間有所區別,可根據需要進行選擇。在使用原生JS的環境下,可通過for...in來實現深拷貝。

    將obj1深拷貝給obj4,如下:

            let obj4 = {};
            $.extend(true, obj4, obj1);
            console.log(obj4);    

    chrome控制台輸出如下:

        

    該結構與通過for...in輸出的obj1的結構相同,其丟失了obj1當中的繼承的原型屬性。(此處的__proto__為對象基礎類型的原型屬性,而非繼承自自類的原型屬性)。

    

    對obj1復雜類型colors插入值,obj4不受影響。深拷貝,得證。

    所以,通過for...in可實現深拷貝、Object.create()結合Object.getPrototypeOf()、Object.assign()實現原型拷貝。

    

二、Object方法深入了解

    以上用到了對象的三種方法,來實現原型鏈的拷貝。依照第一節中的實驗代碼,加深對這些方法的理解:

    2.1、Object.getPrototypeOf(obj1)

        概念:返回指定對象的prototype(原型);
        如1.2 step2中的圖可知,其返回的原型為obj1的原型。

    2.2、Object.create(proto, [propertiesObject])

            概念:是一種新的對象創建的方法,其有兩個參數:
                第一個proto,為要創建的對象的原型;
                第二個propertiesObject,為對象的屬性描述符,與Object.definePropertyOf()同。
            如圖:
            
            上圖通過 Object.create() 分別創建 obj1 與 obj2 對應的新對象,從圖中可得出以下結論:
              第一個參數:作為新對象的原型對象”__proto__”;
                  第二個參數:為屬性描述符對象。(通過Object.defineProperties(obj, props)的第二個參數了解)
      所以,Object.create(proto, [propertiesObject]) 可創建一個帶有原型屬性的新對象。

    2.3、Object.assign(target, …sources)

        概念:將一個或多個源對象自身的所有 可枚舉屬性 復制 到目標對象。
        如圖:
            
        將obj1復制給一個空對象,可以看到obj1的原型屬性未被復制。由於原型屬性並非對象自身的屬性,未被復制。
    

三、附加Object.defineProperty(obj, prop, descriptor)

    概念:直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。
    參數:obj,被操作對象;
                prop:要定義或修改的屬性的名稱;
                descriptor: 將被定義或修改的屬性的描述符
    屬性的描述符相關描述如下:
        
 
    看實例:
            var obj = {};
            Object.defineProperty(obj, ‘test',  {
                configurable: false,
                enumerable: false,
                get() {
                    return this._value;
                },
                set(value) {
                    this._value = value + ' yeap';
                    return true;
                }
             });
            console.log(obj.property1) //undefined
            obj.test = 2
            console.log(obj.property1) // 2 yeap    
    可以看到,給obj.test賦值時,會先通過屬性描述符進行處理;通過set方法,將value處理以后,賦值給_value;當取值時,返回_value的值。
    如上,通過設置 set() 和 get() ,可對屬性值的存取進行處理。該屬性操作方法已廣泛用於數據雙向綁定的一些MVVM框架中,其中VUE就使用了該方法。其通過Object.defineProperty方法,實現setter和getter,形成依賴追蹤,從而在數據被訪問或修改時通知變化。
 
    tips:Object.defineProperties(obj, props)是Object.defineProperty(obj, prop, descriptor)的擴展,可一次設置多個屬性的描述符。
    示例:
            Object.defineProperties(obj, {
                test1: {
                    configurable: false,
                    enumerable: false,
                    get() {
                       return this._value;
                    },
                    set(value) {
                        this._value = value + ' yeap';
                        return true;
                    }
                },
                test2: {
                    value: ‘cutcut’,
                    writable: true
                }
             });
    
    
    
    
    
        


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM