JavaScript 对象

77 阅读2分钟

一、对象的本质与创建方式

1.1 对象的基本概念

JavaScript对象是属性的无序集合,每个属性都是一个键值对:

  • 键(Key):字符串或Symbol类型
  • 值(Value):任意JavaScript值(原始值、对象或函数)

1.2 创建对象的7种方式

1.2.1 对象字面量

const person = {
  name: 'Alice',
  age: 28,
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

1.2.2 构造函数

function Person(name, age) {
  this.name = name;
  this.age = age;
}
const bob = new Person('Bob', 30);

1.2.3 Object.create()

const proto = { greet() { console.log('Hello') } };
const obj = Object.create(proto);
obj.name = 'Alice';

1.2.4 类语法(ES6)

class Person {
  constructor(name) {
    this.name = name;
  }
}
const eve = new Person('Eve');

1.2.5 工厂函数

function createUser(name) {
  return {
    name,
    createdAt: new Date()
  };
}
const user = createUser('Charlie');

1.2.6 对象解构重组

const source = { a: 1, b: 2 };
const newObj = { ...source, c: 3 };

1.2.7 动态创建

const dynamicKey = 'id';
const obj = {
  [dynamicKey + '_value']: 123,
  [Symbol('secret')]: 'hidden'
};

二、对象属性详解

2.1 属性类型分类

属性类型示例特点
数据属性{ a: 1 }包含值的普通属性
访问器属性{ get a() { return 1 } }通过getter/setter定义
内部属性[[Prototype]]引擎使用的不可直接访问的属性

2.2 属性描述符

const obj = {};
Object.defineProperty(obj, 'readOnlyProp', {
  value: 42,
  writable: false,
  enumerable: true,
  configurable: false
});

// 等效的属性描述符默认值
{
  value: undefined,
  writable: true,     // 可修改
  enumerable: true,   // 可枚举
  configurable: true  // 可删除和修改特性
}

2.3 属性检测与遍历

2.3.1 检测属性存在性

const obj = { a: 1 };

// 正确方式
'a' in obj;            // true
Object.hasOwn(obj, 'a');// true (ES2022)

// 有缺陷的方式
obj.a !== undefined;    // 无法区分undefined值
obj.hasOwnProperty('a');// 可能被覆盖

2.3.2 属性遍历方法对比

方法获取内容是否遍历原型链是否包含Symbol是否包含不可枚举
for...in可枚举的字符串键
Object.keys()自身可枚举的字符串键
Object.getOwnPropertyNames()自身所有字符串键
Object.getOwnPropertySymbols()自身所有Symbol键
Reflect.ownKeys()自身所有键

三、对象原型与继承

3.1 原型链机制

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  console.log(`${this.name} makes a noise`);
};

function Dog(name) {
  Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

const d = new Dog('Rex');
d.speak(); // Rex makes a noise

3.2 现代继承方式(ES6类)

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 d = new Dog('Rex');
d.speak(); // Rex barks

3.3 原型相关方法

// 获取原型
Object.getPrototypeOf(obj);
Reflect.getPrototypeOf(obj);

// 设置原型
Object.setPrototypeOf(obj, proto); // 性能差
const newObj = Object.create(proto); // 推荐方式

四、对象的高级特性

4.1 不可变对象

4.1.1 浅层不可变

const frozen = Object.freeze({ a: 1 });
frozen.a = 2; // 静默失败(严格模式报错)

const sealed = Object.seal({ a: 1 });
sealed.a = 2; // 允许修改值
sealed.b = 3; // 禁止添加属性

const nonExtensible = Object.preventExtensions({ a: 1 });
nonExtensible.a = 2; // 允许
nonExtensible.b = 3; // 禁止

4.1.2 深层不可变

function deepFreeze(obj) {
  Object.freeze(obj);
  Object.getOwnPropertyNames(obj).forEach(prop => {
    if (obj[prop] !== null && 
        typeof obj[prop] === 'object' && 
        !Object.isFrozen(obj[prop])) {
      deepFreeze(obj[prop]);
    }
  });
  return obj;
}

4.2 对象代理(Proxy)

const handler = {
  get(target, prop) {
    return prop in target ? target[prop] : 37;
  },
  set(target, prop, value) {
    if (prop === 'age' && !Number.isInteger(value)) {
      throw new TypeError('Age must be an integer');
    }
    target[prop] = value;
    return true;
  }
};

const p = new Proxy({}, handler);
p.age = 12;
console.log(p.age);  // 12
console.log(p.name); // 37

4.3 反射API(Reflect)

const obj = { a: 1 };

// 代替Object方法
Reflect.set(obj, 'b', 2);
Reflect.has(obj, 'a'); // true

// 与Proxy配合
const proxy = new Proxy(obj, {
  get(target, prop) {
    console.log(`Getting ${prop}`);
    return Reflect.get(target, prop);
  }
});

五、对象操作的最佳实践

5.1 安全的对象扩展

// 不安全的扩展(可能覆盖已有方法)
Object.prototype.customMethod = function() {};

// 安全的方式
if (!Object.prototype.customMethod) {
  Object.defineProperty(Object.prototype, 'customMethod', {
    value: function() { /*...*/ },
    enumerable: false // 不污染for...in
  });
}

5.2 深拷贝实现

function deepClone(obj, cache = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (cache.has(obj)) return cache.get(obj);
  
  const clone = Array.isArray(obj) ? [] : {};
  cache.set(obj, clone);
  
  Object.getOwnPropertyNames(obj).concat(
    Object.getOwnPropertySymbols(obj)
  ).forEach(prop => {
    clone[prop] = deepClone(obj[prop], cache);
  });
  
  return clone;
}

5.3 属性枚举顺序

ES6规范定义了对象属性的枚举顺序:

  1. 所有数字键按升序排列
  2. 所有字符串键按添加顺序排列
  3. 所有Symbol键按添加顺序排列
const obj = {
  '2': 'two',
  '1': 'one',
  b: 'b',
  a: 'a',
  [Symbol('s2')]: 'sym2',
  [Symbol('s1')]: 'sym1'
};

Reflect.ownKeys(obj); // ['1', '2', 'b', 'a', Symbol(s2), Symbol(s1)]

六、特殊对象类型

6.1 内置对象

类型用途示例
Array有序数据集合[1, 2, 3]
Date日期时间处理new Date()
RegExp正则表达式/\d+/g
Map/Set键值对/值集合new Map([[k, v]])
WeakMap/WeakSet弱引用集合new WeakMap()
ArrayBuffer二进制数据new ArrayBuffer(8)
Promise异步操作new Promise(...)

6.2 宿主对象

由JavaScript运行环境提供的对象:

  • 浏览器:window, document, XMLHttpRequest
  • Node.js:process, require, module

七、对象性能优化

7.1 对象结构优化

// 不佳实践:频繁改变对象结构
function process(obj) {
  obj.newProp = computeValue(); // 隐藏类改变
  // ...
}

// 优化:初始化完整结构
function createObject() {
  return {
    prop1: null,
    prop2: null, // 预先分配
    method() { /*...*/ }
  };
}

7.2 对象池技术

class ObjectPool {
  constructor(createFn) {
    this.createFn = createFn;
    this.pool = [];
  }
  
  acquire() {
    return this.pool.length ? this.pool.pop() : this.createFn();
  }
  
  release(obj) {
    // 重置对象状态
    this.pool.push(obj); 
  }
}

// 使用
const pool = new ObjectPool(() => ({ x: 0, y: 0 }));
const obj = pool.acquire();
// 使用后...
pool.release(obj);

八、现代JavaScript对象特性

8.1 对象解构(ES6)

// 基本解构
const { name, age } = user;

// 重命名
const { name: userName } = user;

// 默认值
const { permissions = 'read' } = user;

// 嵌套解构
const { address: { city } } = user;

// 函数参数解构
function greet({ name, age }) {
  return `Hello ${name}, you're ${age}`;
}

8.2 对象扩展运算符(ES2018)

// 浅拷贝
const copy = { ...original };

// 合并对象
const merged = { ...obj1, ...obj2 };

// 覆盖属性
const updated = { ...user, name: 'New Name' };

// 条件添加属性
const config = {
  baseUrl: '/api',
  ...(devMode && { debug: true })
};

8.3 可选链(ES2020)

// 安全访问嵌套属性
const street = user?.address?.street;

// 配合空值合并
const name = user?.name ?? 'Anonymous';

// 方法调用
user.sayHello?.();

// 数组访问
arr?.[0];

九、对象与类型系统

9.1 TypeScript对象类型

// 接口定义
interface User {
  id: number;
  name: string;
  email?: string; // 可选属性
  readonly createdAt: Date; // 只读属性
  [key: string]: any; // 索引签名
}

// 类型别名
type Point = {
  x: number;
  y: number;
  distance(): number;
};

// 泛型对象
type Response<T> = {
  data: T;
  status: number;
};

9.2 运行时类型检查

// 类型保护
function isUser(obj) {
  return obj && typeof obj === 'object' && 
         'id' in obj && 'name' in obj;
}

// 结构化类型检查
function validateShape(obj, shape) {
  return Object.keys(shape).every(key => 
    typeof obj[key] === shape[key]
  );
}