JavaScript中如何禁止属性变更?组件属性防篡改失效问题
彻底禁止组件属性变更的解决方案
这是个非常典型的引用类型陷阱问题!你当前的实现确实阻止了直接给obj.data赋值的操作,但因为getter返回的是内部数组data的直接引用,外部拿到这个引用后,完全可以通过数组的方法(比如push、splice)修改数组内部的元素,根本不会触发setter。
问题根源
你的代码中,get方法直接返回了内部变量data:
get: function(){ return data; },
这意味着obj.data拿到的是和内部data指向同一个数组的引用,外部对obj.data进行的任何数组操作,都会直接作用在内部的原始数组上,绕过了set方法的限制。
解决方案:返回不可变的副本
要彻底禁止外部修改内部数据,核心是不要暴露内部数据的引用,而是返回一个不可变的副本。这里有几种常见的实现方式:
1. 返回数组的浅拷贝(基础场景)
如果数组里都是原始类型(字符串、数字、布尔等),可以返回数组的浅拷贝,这样外部修改拷贝后的数组不会影响内部的原始数据:
function MyClass(){ var data = []; Object.defineProperty(this, 'data', { get: function(){ return [...data]; }, // 使用扩展运算符创建浅拷贝 // 或者用 slice():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('test'); console.log(obj.data); // 输出 [],内部原始数据未被修改
2. 冻结返回的数组(严格禁止修改)
如果希望连外部拿到的数组本身也不能被修改(比如禁止push、pop等操作),可以用Object.freeze()冻结返回的拷贝数组:
function MyClass(){ var data = []; Object.defineProperty(this, 'data', { get: function(){ return Object.freeze([...data]); }, // 冻结浅拷贝后的数组 set: function(){ throw new Error('This operation is not allowed'); }, configurable: false, }); } var obj = new MyClass(); obj.data.push('test'); // 抛出错误:Cannot add property 0, object is not extensible
3. 深拷贝+深冻结(复杂数据场景)
如果你的数组里包含对象、数组等引用类型,浅拷贝和浅冻结就不够用了——外部还是能修改嵌套对象的属性。这时候需要实现深拷贝+深冻结:
// 实现一个深冻结函数 function deepFreeze(obj) { // 遍历对象/数组的所有属性 Object.keys(obj).forEach(key => { const value = obj[key]; // 如果属性值是引用类型,递归冻结 if (typeof value === 'object' && value !== null) { deepFreeze(value); } }); // 冻结当前对象/数组 return Object.freeze(obj); } function MyClass(){ var data = [{ name: 'test' }]; Object.defineProperty(this, 'data', { get: function(){ return deepFreeze([...data]); }, // 深拷贝+深冻结 set: function(){ throw new Error('This operation is not allowed'); }, configurable: false, }); } var obj = new MyClass(); obj.data[0].name = 'modified'; // 抛出错误:Cannot assign to read only property 'name' of object '#<Object>'
总结
不管哪种方案,核心思路都是切断外部和内部原始数据的引用关联,通过返回不可变的副本,彻底阻止外部对内部数据的任何修改操作。
内容的提问来源于stack exchange,提问作者Rajesh Dixit
相关产品推荐
相关产品推荐

