我试图在构造函数中创建一个属性,除了通过原型函数之外,它是不可变的。我正在尝试关闭此MDN文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties。但似乎没有办法让财产完全不变。考虑一个简单的例子:
function Test(){
Object.defineProperties(this,{
elems : { value : [] }
})
}
Test.prototype.addElem = function(newElem){
if (this.elems.indexOf(newElem) == -1){
this.elems.push(newElem);
}
};
在大多数情况下都可以正常工作(不可分配):
>a = new Test()
Object { , 1 more… }
>a.elems
Array [ ]
>a.elems = 10
10
>a.elems
Array [ ]
不幸的是,它仍然是可变的。考虑:
>a.elems.push(10)
1
>a.elems
Array [ 10 ]
我确信它们是其他函数(数组或对象方法?),它们会改变不可写的&的值。不可设置的财产。推是我遇到的那个。有没有办法实现这个目标?我知道一个可能的解决方案是:
function Test(){
var elems = [];
this.addElem = function(newElem){
if (elems.indexOf(newElem) == -1){
elems.push(newElem);
}
}
}
但是我已经读到这是内存效率低下的问题,特别是当有很多"类"时。另外,我正在研究的可能有很多这样的方法,所以我更担心内存的考虑。
有什么想法吗?我对JS原型的所有复杂性并不是很了解。
答案 0 :(得分:1)
在JavaScript中,默认情况下对象是可扩展的,但如果您能够利用ES5,则应该能够使用Object.seal()
或Object.freeze()
方法来获取不可变属性。
Object.freeze()
的MDN文档有一个示例,说明如何递归冻结(“deepFreeze”)对象的所有属性,从而有效地使其完全不可变。
这是一个概念验证,它将问题中的代码与文档中的代码结合起来:
function Test() {
Object.defineProperties(this, {
elems : { value : [] }
})
}
Test.prototype.addElem = function(newElem) {
if (this.elems.indexOf(newElem) == -1) {
this.elems.push(newElem);
}
};
function deepFreeze(obj) {
// Retrieve the property names defined on obj
var propNames = Object.getOwnPropertyNames(obj);
// Freeze properties before freezing self
propNames.forEach(function(name) {
var prop = obj[name];
// Freeze prop if it is an object
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// Freeze self (no-op if already frozen)
return Object.freeze(obj);
}
a = new Test();
a.elems.push(1);
console.log(a.elems); // [1]
deepFreeze(a);
a.elems.push(2);
console.log(a.elems); // Error
在FireBug中,对象“深度冻结”后的a.elems.push()
返回TypeError
异常,表示该属性不可写;
TypeError:无法在数组末尾定义数组索引属性 具有不可写的长度
Safari检查器还会返回TypeError
例外:
TypeError:尝试分配给readonly属性。
答案 1 :(得分:0)
你可以在一个闭包的帮助下完成这个。这就是您在JavaScript中实现隐私的方式。
简而言之,您在函数内部创建一个变量,并让该函数返回一个包含setter / getters的对象。
在我的例子中,foo函数包含一个_foo变量,该变量只能由函数foo返回的对象中的方法设置。您正在使用函数foo的范围有效地创建一个API。
var foo = function(config){
if (!config) {
config = {};
}
//enclosed variable
var _foo = {
bar: []
};
if (config.bar) {//All item to be initialized with data
_foo.bar = config.bar;
}
var fooAPI = {
addBarItem: function(val){
_foo.bar.push(val);
return _foo.bar.length - 1;//return idenx of item added
},
removeBarItem: function(index) {
return _foo.bar.slice(index, 1);//return the removed item
},
getBarItem: function(index) {
return _foo.bar[index];//return the removed item
},
emptyBarItems: function() {
return _foo.bar.slice(0, _foo.bar.length);//return all removed
},
getBarItems: function(){
//clone bar do not return reference to it in order to keep private
var newBar = [];
_foo.bar.forEach(function(item){
newBar.push(item);
});
return newBar;
}
};
return fooAPI;
};
var myFoo = new foo({bar: ['alpha', 'beta', 'gamma']});
console.log(myFoo.getBarItems());