3

我想使new对象对开发人员/用户不可扩展,但仍然能够通过自己的方法向自身添加属性。我已经尝试了很多事情并进行了大量阅读,但我似乎无法找到任何解决方案,也许没有?

这是我正在尝试/尝试做的一个例子。

/*jslint maxerr: 50, indent: 4, browser: true, white: true, devel: true */

(function () {
    "use strict";

    function isValid(property) {
        if (typeof property === "number") {
            property = property.toString();
        }

        return typeof property === "string" && (/^\d{1,10}$/).test(property) && property >= 0 && property <= 4294967294;
    }

    function Foo() {}

    Object.defineProperties(Foo.prototype, {
        put: {
            value: function (number, value) {
                if (isValid(number)) {
                    Object.defineProperty(this, number, {
                        configurable: true,
                        enumerable: true,
                        value: value
                    });
                }
            }
        },

        clear: {
            value: function () {
                var property;

                for (property in this) {
                    if (this.hasOwnProperty(property) && isValid(property)) {
                        delete this[property];
                    }
                }
            }
        }
    });

    function newFoo(object, name) {
        return Object.defineProperty(object, name, {
            configurable: true,
            value: new Foo()
        });
    }

    var bar = {};

    newFoo(bar, "fee");

    /* All of the following prevent the condition below, but there seems
     * no way to undo them once done
     */
    //Object.preventExtensions(bar.fee)
    //Object.seal(bar.fee);
    //Object.freeze(bar.fee)

    bar.fee.clear();
    bar.fee.put(0, true);
    bar.fee.put(10, true);
    bar.fee.put(100, true);
    bar.fee.put(1000, true);
    //bar.fee[1000] = true; // prevent this, OK
    bar.fee[10000] = true; // prevent this, not OK

    console.log({
        0: bar,
        1: Object.keys(bar.fee)
    });
}());

jsfiddle

更新:我希望添加的属性(索引)是可枚举的(就像数组一样的对象),这样你就可以遍历它们。

进一步研究:所以​​我采取了不暴露支持对象的想法:一个Array,(我能想到的),并得到以下结果。仍然不完全是我想要实现的目标,并且对那些标记为bad.

/*jslint maxerr: 50, indent: 4, browser: true, white: true, devel: true */

(function (undef) {
    "use strict";

    var noop = function () {},
        bar,
        fum,
        neArray;

    function isValid(property) {
        if (typeof property === "number") {
            property = property.toString();
        }

        return typeof property === "string" && (/^\d{1,10}$/).test(property) && property >= 0 && property <= 4294967294;
    }

    function Foo() {
        Object.defineProperty(this, "data", {
            value: Object.preventExtensions([]) // tried seal and freeze
        });
    }

    Object.defineProperties(Foo.prototype, {
        length: {
            get: function () {
                return this.data.length;
            },

            set: noop
        },

        put: {
            value: function (number, value) {
                this.data[number] = value;
            }
        },

        item: {
            value: function (number) {
                return isValid(number) ? this.data[number] : undef;
            }
        },

        keys: {
            get: function () {
                return Object.keys(this.data);
            },

            set: noop
        },

        clear: {
            value: function () {
                this.data.length = 0;
            }
        }
    });

    ["forEach", "some", "every", "map", "filter", "reduce", "slice", "splice", "push", "pop", "shift", "unshift", "indexOf", "lastIndexOf", "valueOf", "toString", "hasOwnProperty"].forEach(function (element) {
        Object.defineProperty(Foo.prototype, element, {
            value: function () {
                return this.data[element].apply(this.data, arguments);
            }
        });
    });

    function newFoo() {
        return Object.preventExtensions(Object.defineProperty({}, "fee", {
            value: new Foo()
        }).fee);
    }

    bar = newFoo();
    fum = newFoo();

    bar.clear();
    bar.put(0, true);
    bar.put(10, true);
    //bar.put(10000, true); // bad
    bar.valueOf()[100] = false; // not so great
    bar.data[1000] = false; // not so great
    //bar.put("xxx", false); // prevent this, OK
    //bar.data["xxx"] = false; // prevent this, OK
    //bar[1000] = false; // prevent this, OK
    //bar[10000] = false; // prevent this, OK

    console.log({
        0: bar,
        1: Object.keys(bar.data), // not so great
        2: bar.keys,
        3: fum,
        4: bar.hasOwnProperty(0),
        5: bar.valueOf(),
        6: bar.toString(),
        7: bar instanceof Foo,
        8: bar.item("forEach") // prevent this, OK
    });

    bar.forEach(function (element, index, object) {
        console.log("loop", element, index, object);
    });

    neArray = Object.preventExtensions([]);

    //neArray[10000] = true; // bad
}());

jsfiddle

还有更多:这是我使用 anObject作为后备商店的研究范围,唷,跳过箍以获得与 the 一样合理的Array东西,有些东西更好,有些更糟。

/*jslint maxerr: 50, indent: 4, browser: true, white: true, devel: true */

(function (undef) {
    "use strict";

    var noop = function () {},
    bar,
    fum,
    neObject;

    function isValid(property) {
        if (typeof property === "number") {
            property = property.toString();
        }

        return typeof property === "string" && (/^\d{1,10}$/).test(property) && property >= 0 && property <= 4294967294;
    }

    function Foo() {
        var data = {
            length: 0
        };

        Object.defineProperty(data, "length", {
            enumerable: false
        });

        Object.defineProperty(this, "data", { // can't prevent extension on object
            value: data
        });
    }

    Object.defineProperties(Foo.prototype, {
        valueOf: {
            value: function () {
                return [].slice.call(this.data);  //OK, disable for large numbers
            }
        },

        toString: {
            value: function () {
                return this.valueOf().toString();  //OK, disable for large numbers
            }
        },

        length: {
            get: function () {
                return this.data.length;
            },

            set: noop
        },

        put: {
            value: function (number, value) {
                if (isValid(number)) {
                    this.data[number] = value;
                    Object.defineProperty(this.data, "length", {
                        writable: true
                    });

                    var newLength = number + 1;
                    if (newLength > this.data.length) {
                        this.data.length = number + 1;
                    }

                    Object.defineProperty(this.data, "length", {
                        writable: false
                    });
                }
            }
        },

        item: {
            value: function (number) {
                return isValid(number) ? this.data[number] : undef;
            }
        },

        keys: {
            get: function () {
                var length = this.data.length;

                return Object.keys(this.data).filter(function (property) {
                    return isValid(property) && property <= length;
                }).map(function (property) {
                    return +property;
                }); // not so good, hack to filter bad
            },

            set: noop
        },

        clear: {
            value: function () {
                var property;

                for (property in this.data) {
                    if (this.data.hasOwnProperty(property) && this.data.propertyIsEnumerable(property)) {
                        delete this.data[property];
                    }
                }

                Object.defineProperty(this.data, "length", {
                    writable: true
                });

                this.data.length = 0;
                Object.defineProperty(this.data, "length", {
                    writable: false
                });
            }
        }
    });

    ["forEach", "some", "every", "map", "filter", "reduce", "slice", "splice", "push", "pop", "shift", "unshift", "indexOf", "lastIndexOf", "hasOwnProperty"].forEach(function (element) {
        Object.defineProperty(Foo.prototype, element, {
            value: function () {
                return [][element].apply(this.data, arguments);
            }
        });
    });

    function newFoo() {
        return Object.preventExtensions(Object.defineProperty({}, "fee", {
            value: new Foo()
        }).fee);
    }

    bar = newFoo();
    fum = newFoo();

    bar.clear();
    bar.put(0, true);
    bar.put(10, true);
    //bar.put(4294967294, true); // OK, disabled because of processing
    bar.put(4294967295, true);
    //bar.valueOf()[100] = false; // prevent this, OK
    bar.data[1000] = false; // bad
    //bar.put("xxx", false); // prevent this, OK
    bar.data.xxx = false; // not so good
    Object.defineProperty(bar.data, "yyy", {
        value: false
    });

    //bar[1000] = false; // prevent this, OK
    //bar[10000] = false; // prevent this, OK
    //bar.clear(); // OKish, won't clear something set as innumerable through bad

    console.log({
        0: bar,
        1: Object.keys(bar.data), // not so good // disable for large numbers
        2: bar.keys, // OKish with hack
        3: fum,
        4: bar.hasOwnProperty(0),
        5: bar.valueOf(),
        6: bar.toString(),
        7: bar instanceof Foo,
        8: bar.item("forEach") // prevent this, OK
    });

    bar.forEach(function (element, index, object) {
        console.log("loop", element, index, object);
    });

    neObject = Object.preventExtensions({});

    //neObject[10000] = true; // bad
}());

jsfiddle

剩下要做的就是将原型移动到它们各自的构造函数中,以便这些方法使用私有变量data作为支持对象,这是我相信@bfavaretto 所建议的,但随后我们失去了定义的质量prototypes:增加内存由于创建方法而增加了施工时间new

4

0 回答 0