每个属性都有对应的属性描述符,控制属性的行为:
const obj = { name: "John" };
// 获取属性描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, "name");
console.log(descriptor);
// {
// value: 'John',
// writable: true, // 可修改
// enumerable: true, // 可枚举
// configurable: true // 可配置
// }
const obj = {
a: 1,
b: 2,
};
// 定义不可枚举属性
Object.defineProperty(obj, "c", {
value: 3,
enumerable: false,
});
// 1. for...in (包含继承的可枚举属性)
console.log("for...in:");
for (let key in obj) {
console.log(key); // a, b (没有c)
}
// 2. Object.keys() (自有可枚举属性)
console.log("Object.keys:", Object.keys(obj)); // ['a', 'b']
// 3. Object.values() (自有可枚举属性的值)
console.log("Object.values:", Object.values(obj)); // [1, 2]
// 4. Object.entries() (自有可枚举属性的键值对)
console.log("Object.entries:", Object.entries(obj)); // [['a', 1], ['b', 2]]
const obj = {
a: 1,
b: 2,
};
Object.defineProperties(obj, {
c: { value: 3, enumerable: false },
d: { value: 4, enumerable: false },
});
// 1. Object.getOwnPropertyNames() (所有自有属性,无论是否可枚举)
console.log("getOwnPropertyNames:", Object.getOwnPropertyNames(obj)); // ['a', 'b', 'c', 'd']
// 2. Object.getOwnPropertySymbols() (所有自有Symbol属性)
const sym = Symbol("private");
obj[sym] = "secret";
console.log("getOwnPropertySymbols:", Object.getOwnPropertySymbols(obj)); // [Symbol(private)]
// 3. Reflect.ownKeys() (所有自有键,包括Symbol)
console.log("Reflect.ownKeys:", Reflect.ownKeys(obj)); // ['a', 'b', 'c', 'd', Symbol(private)]
function Person(name) {
this.name = name;
}
// 在原型上定义方法
Person.prototype.sayHello = function () {
return `Hello, I'm ${this.name}`;
};
const john = new Person("John");
// 定义自有不可枚举属性
Object.defineProperty(john, "id", {
value: 123,
enumerable: false,
});
console.log("for...in (包含继承):");
for (let key in john) {
console.log(key); // name, sayHello (没有id)
}
console.log("Object.keys (自有可枚举):", Object.keys(john)); // ['name']
console.log("getOwnPropertyNames (自有所有):", Object.getOwnPropertyNames(john)); // ['name', 'id']
const obj = {};
// 定义完整属性描述符
Object.defineProperty(obj, "fullName", {
// 数据描述符
value: "John Doe",
writable: true, // 可修改
enumerable: true, // 可枚举
configurable: true, // 可配置(可删除、可修改属性描述符)
// 访问器描述符(不能与数据描述符同时存在)
// get: function() { return this._fullName; },
// set: function(value) { this._fullName = value; }
});
// 批量定义属性
Object.defineProperties(obj, {
age: {
value: 30,
writable: false, // 不可修改(类似const)
enumerable: true,
configurable: true,
},
salary: {
value: 50000,
enumerable: false, // 不可枚举(私有属性效果)
configurable: false, // 不可配置(锁死属性)
},
});
obj.age = 40; // 静默失败(严格模式下会报错)
console.log(obj.age); // 30
delete obj.salary; // false(无法删除)
console.log(obj.salary); // 50000
const user = {
firstName: "John",
lastName: "Doe",
// 定义访问器属性
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(value) {
const parts = value.split(" ");
this.firstName = parts[0];
this.lastName = parts[1] || "";
},
// 计算属性
get age() {
return new Date().getFullYear() - this.birthYear;
},
};
user.birthYear = 1990;
console.log(user.fullName); // John Doe
console.log(user.age); // 计算出的年龄
user.fullName = "Jane Smith";
console.log(user.firstName); // Jane
console.log(user.lastName); // Smith
class Animal {
constructor(name) {
this.name = name;
}
// 类方法是不可枚举的
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
// 重写的方法也是不可枚举的
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog("Rex");
// 检查属性描述符
console.log(Object.getOwnPropertyDescriptor(dog, "name"));
// { value: 'Rex', writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(dog), "speak"));
// { value: [Function: speak], writable: true, enumerable: false, configurable: true }
function createPrivateObject() {
const privateData = new WeakMap();
class PrivateObject {
constructor() {
// 存储私有数据
privateData.set(this, {
secret: Math.random(),
count: 0,
});
// 定义不可枚举的访问器
Object.defineProperty(this, "_internal", {
enumerable: false,
configurable: false,
get() {
return privateData.get(this);
},
});
}
// 公有方法
increment() {
const data = privateData.get(this);
data.count++;
return data.count;
}
getSecret() {
const data = privateData.get(this);
return data.secret;
}
}
return PrivateObject;
}
const PrivateObject = createPrivateObject();
const obj = new PrivateObject();
console.log(Object.keys(obj)); // []
console.log(obj.increment()); // 1
console.log(obj.getSecret()); // 随机数
function createConfig(options = {}) {
const config = {
...options,
// 默认配置
apiUrl: "https://api.example.com",
timeout: 30000,
retryCount: 3,
};
// 冻结所有属性
Object.keys(config).forEach(key => {
Object.defineProperty(config, key, {
value: config[key],
writable: false,
enumerable: true,
configurable: false,
});
});
// 防止添加新属性
Object.preventExtensions(config);
return config;
}
const config = createConfig({ timeout: 5000 });
config.timeout = 10000; // 静默失败
config.newProp = "test"; // 静默失败
delete config.apiUrl; // false
console.log(Object.keys(config)); // ['apiUrl', 'timeout', 'retryCount']
class User {
constructor(name, email, password) {
this.name = name;
this.email = email;
// 密码设为不可枚举,不会被 JSON.stringify 包含
Object.defineProperty(this, "password", {
value: password,
enumerable: false,
writable: false,
configurable: false,
});
// 内部状态设为不可枚举
Object.defineProperty(this, "_metadata", {
value: {
created: new Date(),
version: 1,
},
enumerable: false,
});
}
toJSON() {
// 自定义序列化
return {
name: this.name,
email: this.email,
// 不包含 password 和 _metadata
metadata: this._metadata, // 需要时手动添加
};
}
}
const user = new User("John", "john@example.com", "secret123");
console.log("JSON.stringify:", JSON.stringify(user));
// {"name":"John","email":"john@example.com","metadata":{"created":"2024-01-01T00:00:00.000Z","version":1}}
console.log("Object.keys:", Object.keys(user)); // ['name', 'email']
function createValidatedObject(schema) {
const obj = {};
for (const [key, validator] of Object.entries(schema)) {
let value;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: false,
get() {
return value;
},
set(newValue) {
// 验证
if (!validator(newValue)) {
throw new Error(`Invalid value for ${key}: ${newValue}`);
}
// 通知变化
if (obj._listeners && obj._listeners[key]) {
obj._listeners[key].forEach(callback => {
callback(newValue, value);
});
}
value = newValue;
},
});
// 添加监听方法
obj.onChange = function (prop, callback) {
if (!this._listeners) {
Object.defineProperty(this, "_listeners", {
value: {},
enumerable: false,
});
}
if (!this._listeners[prop]) {
this._listeners[prop] = [];
}
this._listeners[prop].push(callback);
};
}
return obj;
}
// 使用示例
const schema = {
age: value => Number.isInteger(value) && value >= 0,
email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
name: value => typeof value === "string" && value.length > 0,
};
const user = createValidatedObject(schema);
user.onChange("age", (newAge, oldAge) => {
console.log(`Age changed from ${oldAge} to ${newAge}`);
});
user.age = 25; // OK
user.age = -5; // Error: Invalid value for age: -5
const obj = {};
// 创建大量属性
for (let i = 0; i < 10000; i++) {
obj[`prop${i}`] = i;
// 每10个属性设一个不可枚举
if (i % 10 === 0) {
Object.defineProperty(obj, `hidden${i}`, {
value: `hidden${i}`,
enumerable: false,
});
}
}
// 性能测试
console.time("for...in");
for (let key in obj) {
}
console.timeEnd("for...in");
console.time("Object.keys");
Object.keys(obj);
console.timeEnd("Object.keys");
console.time("getOwnPropertyNames");
Object.getOwnPropertyNames(obj);
console.timeEnd("getOwnPropertyNames");
// 缓存可枚举属性列表
function getEnumerableKeys(obj) {
if (!obj.__enumerableKeys) {
Object.defineProperty(obj, "__enumerableKeys", {
value: Object.keys(obj),
enumerable: false,
writable: true,
configurable: true,
});
}
return obj.__enumerableKeys;
}
// 当对象改变时更新缓存
function defineEnumerableProperty(obj, key, value) {
Object.defineProperty(obj, key, {
value,
enumerable: true,
writable: true,
configurable: true,
});
// 更新缓存
if (obj.__enumerableKeys) {
if (!obj.__enumerableKeys.includes(key)) {
obj.__enumerableKeys.push(key);
}
}
}
const arr = [1, 2, 3];
// 数组索引是可枚举的
console.log(Object.keys(arr)); // ['0', '1', '2']
// 添加不可枚举属性
Object.defineProperty(arr, "custom", {
value: "hidden",
enumerable: false,
});
// for...in 会包含索引
for (let key in arr) {
console.log(key); // 0, 1, 2 (没有 custom)
}
// 但 length 是不可枚举的
console.log(Object.getOwnPropertyDescriptor(arr, "length"));
// { value: 3, writable: true, enumerable: false, configurable: false }
class MyClass {
instanceProp = "instance"; // 实例属性,可枚举
constructor() {
this.constructorProp = "constructor"; // 也是可枚举的
}
// 原型方法,不可枚举
method() {}
// 静态属性,可枚举
static staticProp = "static";
}
const instance = new MyClass();
console.log(Object.keys(instance));
// ['instanceProp', 'constructorProp']
console.log(Object.getOwnPropertyNames(instance));
// ['instanceProp', 'constructorProp']
const obj = {
regular: "regular",
[Symbol("sym1")]: "symbol1",
[Symbol("sym2")]: "symbol2",
};
// 定义不可枚举的Symbol
const sym3 = Symbol("sym3");
Object.defineProperty(obj, sym3, {
value: "symbol3",
enumerable: false,
});
console.log("Object.keys:", Object.keys(obj)); // ['regular']
console.log("for...in:"); // 只有 'regular'
console.log("getOwnPropertySymbols:", Object.getOwnPropertySymbols(obj)); // [Symbol(sym1), Symbol(sym2), Symbol(sym3)]
console.log("Reflect.ownKeys:", Reflect.ownKeys(obj)); // ['regular', Symbol(sym1), Symbol(sym2), Symbol(sym3)]