我正在创建一个组件,并试图破坏我的实现。我们的想法是不允许用户操纵暴露的属性。
实施是这样的:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data; },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
如您所见,设置属性已处理,但用户仍可使用.push
对其进行变更。这是因为我正在返回参考文献。
我已经处理过这样的案件:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data.slice(); },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
如你所见,我正在返回一个新数组来解决这个问题。不确定它将如何影响性能。
问题:是否有另一种方法可以不允许用户改变 object 类型的属性?
我尝试过使用writable: false
,但在get
使用它时会出错。
注意:我希望这个数组在课堂内是可变的,但不是来自外部。
答案 0 :(得分:2)
您的问题是您实际上阻止了修改MyClass
的尝试。但是,MyClass
的其他对象成员仍然是JavaScript对象。这样你就是这样做(每次调用get
都返回一个新的数组)是最好的方法之一,当然,这取决于你调用get
的频率或数组的长度可能有性能缺陷。
当然,如果您可以使用ES6,则可以扩展本机Array以创建ReadOnlyArray
类。实际上,您也可以在ES5中执行此操作,但是您无法使用方括号从数组中的特定索引检索值。
如果可以避免使用Internet Explorer,另一个选择是使用Proxies
(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)。
使用代理,您可以捕获调用以获取对象的属性,并决定返回或执行的操作。
在下面的示例中,我们为数组创建了一个代理。正如您在处理程序中看到的,我们定义了一个get
函数。只要访问目标对象的属性值,就会调用此函数。这包括访问索引或方法,因为调用方法基本上是检索属性(函数)的值然后调用它。
如您所见,如果属性是整数,我们将返回数组中的该位置。如果属性是'length',那么我们返回数组的长度。在任何其他情况下,我们返回一个void函数。
这样做的好处是proxyArray仍然表现得像一个数组。您可以使用方括号来获取其索引并使用属性长度。但是,如果你尝试做proxyArray.push(23)
之类的事情,那就什么都不会发生。
当然,在最终解决方案中,您可能希望根据哪个来决定做什么
正在调用方法。您可能需要map
,filter
等方法才能正常工作。
最后,这种方法的最后一个优点是你保留了对原始数组的引用,因此你仍然可以修改它,并且可以通过代理访问它的值。
var handler = {
get: function(target, property, receiver) {
var regexp = /[\d]+/;
if (regexp.exec(property)) { // indexes:
return target[property];
}
if (property === 'length') {
return target.length;
}
if (typeof (target[property]) === 'function') {
// return a function that does nothing:
return function() {};
}
}
};
// this is the original array that we keep private
var array = [1, 2, 3];
// this is the 'visible' array:
var proxyArray = new Proxy(array, handler);
console.log(proxyArray[1]);
console.log(proxyArray.length);
console.log(proxyArray.push(32)); // does nothing
console.log(proxyArray[3]); // undefined
// but if we modify the old array:
array.push(23);
console.log(array);
// the proxy is modified
console.log(proxyArray[3]); // 32
当然,问题在于proxyArray
实际上不是一个数组,因此,根据您打算如何使用它,这可能是一个问题。
答案 1 :(得分:0)
据我所知,你想要的东西在JavaScript中是不可行的。您可以期待的最好的方法是尽可能地隐藏用户的数据。最好的方法是使用WeakMap
let privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
data: []
});
}
addEntry(entry) {
privateData.get(this).data.push(entry);
}
getData() {
return privateData.get(this).data.concat();
}
}
只要你永远不会从模块中导出privateData
不export
,或在IIFE等内包装,那么你的MyClass
实例就可以访问数据但外部力量不能(除了通过你创建的方法)
var myInstance = new MyClass();
myInstance.getData(); // -> []
myInstance.getData().push(1);
myInstance.getData(); // -> []
myInstance.addEntry(100);
myInstance.getData(); // -> [100]