提问人:Xotic750 提问时间:7/17/2013 最后编辑:Xotic750 更新时间:7/17/2013 访问量:1161
使对象对开发人员不可扩展,但可使用 ES5 在内部扩展
Make an object non-extensible to developer but extensible internally with ES5
问:
我想使对象对开发人员/用户不可扩展,但仍然能够通过自己的方法向自身添加属性。我已经尝试了很多事情并做了相当多的阅读,但我似乎找不到任何解决方案,也许没有?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)
});
}());
更新:我希望添加的属性(索引)可枚举(就像数组一样的对象),以便您可以遍历它们。
进一步研究:因此,我采取了不暴露后备对象的想法:一个,(尽可能多地),并得到了以下结果。仍然不完全是我想要实现的目标,并且对那些标记的.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
}());
On jsfiddle
And more: Here is the extent of my research using an as a backing store, phew, jumping through hoops to get something as reasonable as the and some things better, some worse.Object
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
}());
剩下唯一要做的就是将原型移动到它们各自的构造函数中,以便方法使用私有变量 for 作为后备对象,我相信这是@bfavaretto建议的,但随后我们失去了定义的品质:由于在 上创建方法,内存增加和构造时间增加data
prototypes
new
答: 暂无答案
评论
Object.preventExtensions
numerable
String
Array
String