尝试在不可扩展对象上定义属性时,WeakMap polyfill抛出错误

时间:2016-02-02 06:28:42

标签: javascript mutation-observers

尝试在不可扩展对象上定义属性时,WeakMap polyfill会抛出错误。 那些是在一堆节点,javascript代码和库的中间,所以我实际上无法指出导致问题的原因。还有许多其他库也有自己的polyfill。调试哪个库导致错误很困难。并且,错误仅在IE10上发生 为了摆脱它,我在定义一个属性之前添加了一个检查器(如上面文件中的第26行):

if (entry && entry[0] === key) {
    entry[1] = value;
}
else if (Object.isExtensible(key)) {
    defineProperty(key, this.name, {
        value: [ key, value ],
        writable: true
    }); 
}

我的问题是,在上面的代码中修复它是否安全/正确?如果没有,我该如何解决我的问题?

2 个答案:

答案 0 :(得分:1)

最好的我可以告诉weakMap polyfill仅用于将可扩展对象作为键。它只是不能使用不可扩展的对象。

您的修改已经使它不会抛出异常,但是不可扩展的项目也不会在weakMap中。所以,你的修复并不是一个真正的修复。必须重写该特定的polyfill以处理不可扩展的键。这不是一个简单的解决方案,因为它需要一个概念上不同的方法。

还有许多其他填料采用不同的方法。我还没有研究过哪方面可能会更好。我怀疑实际存在与弱者之间存在一些难题。 (例如,允许垃圾收集)vs.可以处理不可扩展的对象。根本问题是,如果你要变弱,那么你就不能存储对象本身的引用。因此,您需要存储对该对象的某些字符串表示的引用。好吧,JS对象没有内置的保证唯一字符串表示。因此,通常的解决方法是使用某种计数器来硬币并将其作为属性存储在对象上,然后将该字符串表示存储在地图中。但是,如果对象不可扩展,那么你也无法做到。因此,您已经卡住了在地图中存储实际的对象引用,但它并没有真正的“弱”"更多。你可以看到你是如何陷入困境的。

我认为这是一个polyfill不能完全符合真实情况的情况。在这方面,不同的填料将具有不同的权衡。你选择了一个真正弱的,但要求对象是可扩展的,这样就可以添加一个属性。

答案 1 :(得分:0)

在我的问题中,我想要一个对其他功能或插件没有太大影响的解决方案,所以这是我对此问题的修复。该修复程序基于上面@jfriend00的答案以及互联网上其他实现的参考。

var defineProperty = Object.defineProperty;
var counter = Date.now() % 1e9;
var FrozenStore = function() {
    this.a = [];
};
var findFrozen = function(store, key){
    return store.a.forEach(function(it){
        if (it[0] === key) {
            return it;
        }
    });
};
var findIndexFrozen = function(store, key){
    return store.a.forEach(function(it, id){
        if (it[0] === key) {
            return id;
        }
    });
};
FrozenStore.prototype = {
    get: function(key){
        var entry = findFrozen(this, key);
        if (entry) return entry[1];
    },
    has: function(key){
        return !!findFrozen(this, key);
    },
    set: function(key, value){
        var entry = findFrozen(this, key);
        if (entry) entry[1] = value;
        else this.a.push([key, value]);
    },
    "delete": function(key){
        var index = findIndexFrozen(this, key);
        if (~index) this.a.splice(index, 1);
        return !!~index;
    }
};
var WeakMap = function() {
    this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
};
var frozenStore = function(that){
    return that._l || (that._l = new FrozenStore);
};
WeakMap.prototype = {
    set: function(key, value) {
        var entry = key[this.name];
        if (entry && entry[0] === key) {
            entry[1] = value;
        } else {
            if (!Object.isExtensible(key)) {
                frozenStore(this).set(key, value);
            } else {
                defineProperty(key, this.name, {
                    value: [ key, value ],
                    writable: true
                });
            }
        }
        return this;
    },
    get: function(key) {
        var entry;
        if ((entry = key[this.name]) && entry[0] === key) {
            return entry[1];
        } else if (!Object.isExtensible(key)) {
            frozenStore(this).get(key);
        } else {
            return undefined;
        }
    },
    "delete": function(key) {
        var entry = key[this.name];
        if (!entry || entry[0] !== key) return false;
        if (!Object.isExtensible(key)) frozenStore(this)['delete'](key);
        entry[0] = entry[1] = undefined;
        return true;
    },
    has: function(key) {
        var entry = key[this.name];
        if (!entry) return false;
        if(!Object.isExtensible(key)) return frozenStore(this).has(key);
        return entry[0] === key;
    }
};
window.WeakMap = WeakMap;

这引入了FrozenStore,它将管理添加到WeakMap的所有不可扩展的密钥。我不确定它是否打破了WeakMap的概念,但它确实让我摆脱了这个问题。