实现Object.freeze
手写实现Object.freeze方法,冻结对象使其不可修改
问题
Object.freeze() 是 JavaScript 中用于冻结对象的方法,冻结后的对象不能添加新属性、删除现有属性、修改属性值,也不能修改属性的可枚举性、可配置性、可写性。本题要求手写实现一个类似的功能。
需要注意的是,Object.freeze() 是浅冻结,只冻结对象的第一层属性,嵌套对象不会被冻结。
解答
/**
* 实现 Object.freeze 方法
* @param {Object} obj - 需要冻结的对象
* @returns {Object} - 返回冻结后的对象
*/
function myFreeze(obj) {
// 如果不是对象或为 null,直接返回
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 获取对象的所有自有属性名(包括不可枚举属性)
const props = Object.getOwnPropertyNames(obj);
// 遍历所有属性,将其设置为不可写、不可配置
props.forEach(prop => {
const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
// 如果是数据属性,设置为不可写
if (descriptor && descriptor.configurable) {
Object.defineProperty(obj, prop, {
writable: false, // 不可写
configurable: false // 不可配置(不可删除)
});
}
});
// 阻止对象扩展(不能添加新属性)
Object.preventExtensions(obj);
return obj;
}
/**
* 深度冻结对象(递归冻结所有嵌套对象)
* @param {Object} obj - 需要深度冻结的对象
* @returns {Object} - 返回深度冻结后的对象
*/
function deepFreeze(obj) {
// 先冻结当前对象
myFreeze(obj);
// 递归冻结所有属性值
Object.getOwnPropertyNames(obj).forEach(prop => {
const value = obj[prop];
// 如果属性值是对象且不为 null,递归冻结
if (value !== null && typeof value === 'object') {
deepFreeze(value);
}
});
return obj;
}
使用示例
// 示例1:基本使用
const obj1 = {
name: 'Alice',
age: 25
};
myFreeze(obj1);
obj1.name = 'Bob'; // 修改无效
obj1.gender = 'female'; // 添加无效
delete obj1.age; // 删除无效
console.log(obj1); // { name: 'Alice', age: 25 }
// 示例2:浅冻结的局限性
const obj2 = {
user: {
name: 'Tom',
age: 30
}
};
myFreeze(obj2);
obj2.user = {}; // 修改无效
obj2.user.name = 'Jerry'; // 修改成功(嵌套对象未被冻结)
console.log(obj2.user.name); // 'Jerry'
// 示例3:深度冻结
const obj3 = {
user: {
name: 'Tom',
address: {
city: 'Beijing'
}
}
};
deepFreeze(obj3);
obj3.user.name = 'Jerry'; // 修改无效
obj3.user.address.city = 'Shanghai'; // 修改无效
console.log(obj3.user.name); // 'Tom'
console.log(obj3.user.address.city); // 'Beijing'
// 示例4:检测对象是否被冻结
const obj4 = { x: 1 };
myFreeze(obj4);
console.log(Object.isFrozen(obj4)); // true
// 示例5:冻结数组
const arr = [1, 2, 3];
myFreeze(arr);
arr.push(4); // 添加无效(严格模式下会报错)
arr[0] = 10; // 修改无效
console.log(arr); // [1, 2, 3]
关键点
-
三个操作:
- 使用
Object.defineProperty将所有属性设置为不可写(writable: false) - 将所有属性设置为不可配置(
configurable: false),防止删除和重新定义 - 使用
Object.preventExtensions阻止添加新属性
- 使用
-
获取所有属性:使用
Object.getOwnPropertyNames()获取所有自有属性(包括不可枚举属性),而不是Object.keys() -
浅冻结 vs 深冻结:原生
Object.freeze()只是浅冻结,嵌套对象不会被冻结。如需深度冻结,需要递归处理所有嵌套对象 -
类型检查:在冻结前需要检查参数是否为对象,非对象类型直接返回
-
返回值:
Object.freeze()返回被冻结的对象本身,而不是创建新对象 -
严格模式:在严格模式下,尝试修改冻结对象会抛出
TypeError错误;非严格模式下会静默失败 -
性能考虑:冻结对象会影响性能,特别是深度冻结大型对象时,应谨慎使用
目录