我想这样做,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
上 以及更多:以下是我使用Object
作为支持商店的研究范围,phew,跳过篮球以获得与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