Static methods
keys()
返回一个由给定对象自身的可枚举的字符串键属性名组成的数组。
freeze()
Object.freeze() 用于冻结一个对象,冻结后该对象将不可修改——不能添加新属性、不能删除已有属性、不能修改已有属性的值(包括不能修改属性的可枚举性、可配置性、可写性),也不能修改原型。
Object.freeze(obj);- 参数:要冻结的对象
- 返回值:冻结后的对象本身(不是副本)
const obj = {
name: 'Ocean',
age: 30,
};
Object.freeze(obj);
obj.age = 40; // 静默失败(严格模式下会报错)
obj.city = 'Beijing'; // 添加新属性也无效
delete obj.name; // 删除属性也无效
console.log(obj); // { name: "Ocean", age: 30 }严格模式下修改会抛出 TypeError:
'use strict';
const obj = Object.freeze({ name: 'Ocean' });
obj.name = 'Test'; // TypeError: Cannot assign to read only property 'name'判断对象是否被冻结:
Object.isFrozen(obj); // true / false注意
Object.freeze() 只是浅冻结,如果对象的属性值是另一个对象(嵌套对象),那个嵌套对象不会被冻结:
const obj = {
name: 'Ocean',
address: {
city: 'Beijing',
},
};
Object.freeze(obj);
obj.address.city = 'Shanghai'; // ✅ 这个是可以修改的!
console.log(obj.address.city); // "Shanghai"如果需要深度冻结,需要递归实现:
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach((key) => {
const value = obj[key];
if (value && typeof value === 'object') {
deepFreeze(value);
}
});
return Object.freeze(obj);
}在定义常量这个场景中,与 const 的区别。
- 是完全不同层面的问题;
- const 锁定"变量绑定",不锁定"值内容",保证的是变量不能被重新赋值,但如果这个变量指向一个对象,对象内部的属性依然可以随意修改。
const obj = { name: 'Ocean' };
obj = { name: 'Test' }; // ❌ TypeError,const 不能重新赋值
obj.name = 'Test'; // ✅ 完全没问题!对象内容被改了
console.log(obj); // { name: "Test" }- const 约束的是变量与内存地址之间的绑定关系,而不是这块内存里存的内容;
- Object.freeze 锁定的是"值本身",是对象这块内存本身的可变性,跟变量用 let 还是 const 声明完全无关。
let obj = Object.freeze({ name: 'Ocean' });
obj.name = 'Test'; // ❌ 静默失败(严格模式报错)
obj = { name: 'New' }; // ✅ 可以,因为是 let,可以重新赋值
// 但这是一个全新的、未冻结的对象| 变量重新赋值 | 对象属性修改 | |
|---|---|---|
let obj = {...} | ✅ 可以 | ✅ 可以 |
const obj = {...} | ❌ 不可以 | ✅ 可以 |
let obj = Object.freeze({...}) | ✅ 可以 | ❌ 不可以 |
const obj = Object.freeze({...}) | ❌ 不可以 | ❌ 不可以(真正的"双重锁定") |
"值不可变"的典型场景是:防止对象被传来传去的过程中被意外篡改。
function processUser(user) {
user.name = '被篡改了'; // 如果 user 只是普通对象,这里能悄悄改掉外部的数据
}
const user = { name: 'Ocean' };
processUser(user);
console.log(user.name); // "被篡改了" —— 即使 user 是 const 声明的const 防不住这种情况,因为函数内部拿到的是同一个对象引用,只是改了属性,没有重新赋值这个变量。而如果用 Object.freeze(user),processUser 里的修改会静默失败(严格模式下报错),外部数据就安全了。
- 配置对象 / 常量表:防止被业务代码不小心改掉
- 函数参数保护:防止被调用的函数意外修改传入对象
- 状态管理(如 Redux):强制通过创建新对象来"修改"状态,而不是直接改旧状态,方便做变化检测和时间旅行调试
const 管的是"这个名字指向谁",Object.freeze 管的是"这个东西本身能不能变"。
getOwnPropertyNames
返回一个数组,包含对象自身所有属性的键名——包括不可枚举的属性,但不包括 Symbol 类型的属性。
Object.getOwnPropertyNames(obj);- 参数:目标对象
- 返回值:字符串数组,包含所有自身属性名(不含继承的)
const obj = {
name: 'Ocean',
age: 25,
};
console.log(Object.getOwnPropertyNames(obj));
// ["name", "age"]看起来跟 Object.keys() 差不多?关键区别在于不可枚举属性。
和 Object.keys() 的核心区别
const obj = {};
Object.defineProperty(obj, 'visible', {
value: 1,
enumerable: true,
});
Object.defineProperty(obj, 'hidden', {
value: 2,
enumerable: false, // 设为不可枚举
});
console.log(Object.keys(obj));
// ["visible"] —— 只拿到可枚举的
console.log(Object.getOwnPropertyNames(obj));
// ["visible", "hidden"] —— 不可枚举的也能拿到数组的 length 属性就是一个典型的不可枚举属性:
const arr = [1, 2, 3];
console.log(Object.keys(arr));
// ["0", "1", "2"]
console.log(Object.getOwnPropertyNames(arr));| 方法 | 自身属性 | 继承属性 | 可枚举 | 不可枚举 | Symbol |
|---|---|---|---|---|---|
Object.keys() | ✅ | ❌ | ✅ | ❌ | ❌ |
Object.getOwnPropertyNames() | ✅ | ❌ | ✅ | ✅ | ❌ |
Object.getOwnPropertySymbols() | ✅ | ❌ | - | - | ✅ |
Reflect.ownKeys() | ✅ | ❌ | ✅ | ✅ | ✅ |
for...in | ✅ | ✅ | ✅ | ❌ | ❌ |
想拿到对象所有键(包括 Symbol)?
Reflect.ownKeys(obj);实际应用场景
1. 实现对象的完整深拷贝或调试工具,需要拿到所有属性(包括那些故意设为不可枚举、不想被 JSON.stringify 或 for...in 遍历到的属性)。
2. 检查一个对象到底有哪些"隐藏"属性,比如调试类实例、内置对象等。