面试某厂时和面试官聊到了
vue中可以在适当的时候用Object.freeze冻结对象、v-once等实现性能优化,然后面试官问我有什么办法用其他JS实现Object.freeze冻结对象的效果吗?我回答应该可以用Object.defineProperty或者Proxy实现,但是这个效果我没有实现过,所以现在来尝试一下我之前说的实现方式是否可行,以及再看看有没有其他实现方案
概念
数据冻结的概念就是该数据不可更改不可删除,那么js里面有哪些方法可以冻结数据呢?
常用方法
使用const冻结
- 测试
function constantDeleteTest() {
const str = '你有本事就把我删了呀!'
// str = '你真是个大聪明' // 输出 'Uncaught TypeError: Assignment to constant variable.'
let deletedFlag = delete str; // 输出false
console.log(str); // 输出 '你有本事就把我删了呀'
const obj = {
name: 'zhangsan',
age: 24
}
obj.age += 1
console.log(obj); // 输出 { name: 'zhangsan', age: 25 }
}
- 缺点:只适合一些基本数据类型,引用数据类型值还是可以变更
使用Object.defineProperty + Object.preventExtensions冻结
- 实现方式
/**
实现原理:
利用writable属性设置为不可更改
configurable设置为不可删除
利用Object.preventExtensions禁止对象扩展
实现方式:
循环对象或者数组,给每个属性设置上Object.defineProperty,
使其不能更改,遇到引用数据类型就设置Object.preventExtensions禁止其扩展
语法:
Object.defineProperty('对象', '属性', '配置项')
配置项:
configurable 对象属性描述是否可被更改、是否可被删除
enumerable 对象属性是否可被枚举
value 属性对应的值
writable 是否可修改
get 获取该属性值时调用
set 修改该属性值时被调用
**/
const ObjectFreeze = (data) => {
const getFlag = (data) => {
const typeMap = ['[object Object]', '[object Array]'];
const flag = typeMap.includes(Object.prototype.toString.call(data));
if (flag) {
Object.preventExtensions(data);
};
return flag;
}
const freeze = (obj) => {
for (const key in obj) {
if (getFlag(obj[key])) {
freeze(obj[key])
} else {
Object.defineProperty(obj, key, {
configurable: false,
writable: false
})
}
}
}
if (getFlag(data)) {
freeze(data)
}
}
// test
let obj = {
name: 'test',
age: 38,
dog: {
age: 11,
name: 'dog'
}
}
ObjectFreeze(obj);
obj.name = '123';
obj.dog.name = '123';
delete obj.age;
delete obj.dog.age;
obj.height = 123;
console.log(obj); // 输出{ name: 'test', age: 38, dog: { age: 11, name: 'dog' } }
- 缺点:粗糙的用递归实现了,有待优化
Proxy 实现
- 实现方式
const ObjectFreeze = (data) => {
if(['[object Object]', '[object Array]'].includes(Object.prototype.toString.call(data))) {
return new Proxy(data, {
get(data, prop, value) {
console.log('获取')
return data[prop];
},
set(data, prop, value) {
// data[prop] = value
// return true;
throw new Error('不允许新增属性或者修改属性值');
return false;
},
deleteProperty(data, prop) {
throw new Error('不允许删除');
// return true;
return false;
},
})
}
}
const obj = ObjectFreeze({name: '二狗子', age: 18 })
console.log(obj);
- 缺点:返回的是proxy代理数据
知识扩展
Object.preventExtensions使对象或者数组无法扩展,只作用于一层,如果需要作用到深层次需要自己写方法递归实现Object.freeze使对象或者数组冻结,只作用于一层,如果需要作用到深层次需要自己写方法递归实现
结尾
第一次写文章,写的有点略菜,如果掘友们有发现文章中什么错误的地方,欢迎指正!如果还有其他实现方案,也欢迎掘友们在评论区讨论~