1. 程式人生 > >[JavaScript] Uncaught TypeError: Method get Set.prototype.size called on incompatible receiver

[JavaScript] Uncaught TypeError: Method get Set.prototype.size called on incompatible receiver

fun 例子 .proto alt .get ret rabl singleton png

在對Set進行方法擴展的時候,無法覆蓋size屬性

情景:定義一個SingletonSet,繼承自Set,size只能為1,並且不能add和remove

//首先是extend函數

var extend = (function () {
    //檢查是否存在bug
    for (var p in {
            toString: null
        }) {
        //如果進來了,那說明沒有bug
        return function extend(o) {
            for (var i = 1; i < arguments.length; i++) {
                var source = arguments[i];
                for (var prop in source) {
                    // var desc = Object.getOwnPropertyDescriptor(source, prop);
                    // Object.defineProperty(o, prop, desc);
                     o[prop] = source[prop];  //書上的例子,
                }
            }
            return o;
        }
    }

    //如果存在bug的話
    return function patched_extend(o) {
        for (var i = 1; i < arguments.length; i++) {
            var source = arguments[i];

            //復制可以枚舉的屬性
            for (var prop in source) {
                // var desc = Object.getOwnPropertyDescriptor(source, prop);
                // Object.defineProperty(o, prop, desc);
                 o[prop] = source[prop];
            }

            //檢查特殊屬性並進行復制
            for (var j = 0; j < protoprops.length; j++) {
                prop = protoprops[j];
                if (source.hasOwnProperty(prop)) {
                    // var desc = Object.getOwnPropertyDescriptor(source, prop);
                    // Object.defineProperty(o, prop, desc);
                    o[prop] = source[prop];
                }
            }
        }
        return o;
    }
    var protoprops = ["toString", "valueOf", "constructor",
        "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
        "toLocalString"
    ];
}());


//調用
function SingletonSet(member) {
    this.member = member;
   this.size =2;
}

SingletonSet.prototype = inherit(Set.prototype);

extend(SingletonSet.prototype, {
    constructor: SingletonSet,
    add: function () {
        throw "read-only set"
    },
    remove: function () {
        throw "read-only set"
    },
     get size(){
        return 1;
    },
    foreach:function(f,context){
        f.call(context,this.member);
    },
    contains:function(x){
        return x===this.member;
    }

});


var newSet = new SingletonSet(1);

console.log(newSet.size); //打印

打印出來發現newSet的size屬性報錯如下:
技術分享圖片

之後通過排查,在extend函數中 該位置進行打印測試:


  for (var prop in source) {
                // var desc = Object.getOwnPropertyDescriptor(source, prop);
                // Object.defineProperty(o, prop, desc);
                 o[prop] = source[prop];
                 console.log(prop);
                 console.log(o[prop]);
            }

打印如下:

技術分享圖片

也就是說,是在執行o[prop] = source[prop];時,當prop===‘size‘時,因拋出錯誤,並未將自定義的方法賦值給目標對象。

所以,我的解決辦法,也就是註釋掉的那兩句,通過Object.defineProperty來進行方法的復制,從而避免使用o[‘size‘]而拋出錯誤。
修改後的extend函數如下

var extend = (function () {
    //檢查是否存在bug
    for (var p in {
            toString: null
        }) {
        //如果進來了,那說明沒有bug
        return function extend(o) {
            for (var i = 1; i < arguments.length; i++) {
                var source = arguments[i];
                for (var prop in source) {
                     var desc = Object.getOwnPropertyDescriptor(source, prop);
                     Object.defineProperty(o, prop, desc);
                    // o[prop] = source[prop];  //書上的例子,
                }
            }
            return o;
        }
    }

    //如果存在bug的話
    return function patched_extend(o) {
        for (var i = 1; i < arguments.length; i++) {
            var source = arguments[i];
            //復制可以枚舉的屬性
            for (var prop in source) {
                var desc = Object.getOwnPropertyDescriptor(source, prop);
                 Object.defineProperty(o, prop, desc);
                 //o[prop] = source[prop];
            }


            //檢查特殊屬性並進行復制
            for (var j = 0; j < protoprops.length; j++) {
                prop = protoprops[j];
                if (source.hasOwnProperty(prop)) {
                    var desc = Object.getOwnPropertyDescriptor(source, prop);
                    Object.defineProperty(o, prop, desc);
                    //o[prop] = source[prop];
                }
            }
        }
        return o;
    }

    var protoprops = ["toString", "valueOf", "constructor",
        "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
        "toLocalString"
    ];
}());


此時


var newSet = new SingletonSet(1);

console.log(newSet.size); //打印

//結果 為 1,符合預期

[JavaScript] Uncaught TypeError: Method get Set.prototype.size called on incompatible receiver