内置对象

发布于 2026-09-02

Object

对象是 JavaScript 中的基本数据类型,用于存储和组织数据。

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. 检查一个对象到底有哪些"隐藏"属性,比如调试类实例、内置对象等。