Skip to content
On this page

JavaScript 对象与原型

JavaScript是一种基于原型的面向对象语言,对象是其核心概念之一。理解对象和原型机制对于掌握JavaScript至关重要。

对象创建

JavaScript提供了多种创建对象的方式:

对象字面量

最常用的创建对象方式,简洁明了。

javascript
const person = {
  name: 'John',
  age: 30,
  greet() {
    return `Hello, I'm ${this.name}`;
  },
  // ES6简写方法
  introduce: function() {
    return `Hi, I'm ${this.name} and I'm ${this.age} years old.`;
  }
};

构造函数

使用函数作为模板创建对象。

javascript
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function() {
    return `Hello, I'm ${this.name}`;
  };
}

const john = new Person('John', 30);

Object构造函数

javascript
const person = new Object();
person.name = 'John';
person.age = 30;
person.greet = function() {
  return `Hello, I'm ${this.name}`;
};

Object.create()

直接指定原型创建对象。

javascript
const personPrototype = {
  greet() {
    return `Hello, I'm ${this.name}`;
  },
  introduce() {
    return `Hi, I'm ${this.name} and I'm ${this.age} years old.`;
  }
};

const john = Object.create(personPrototype);
john.name = 'John';
john.age = 30;

// 或者指定属性描述符
const jane = Object.create(personPrototype, {
  name: { value: 'Jane', writable: true, enumerable: true },
  age: { value: 25, writable: true, enumerable: true }
});

ES6 类(语法糖)

ES6引入的类语法,本质上仍是基于原型的。

javascript
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
  
  static species() {
    return 'Homo sapiens';
  }
}

const john = new Person('John', 30);

原型链

原型链是JavaScript实现继承的核心机制。

原型概念

  • 每个JavaScript对象都有一个指向其原型对象的内部链接
  • 原型对象也有自己的原型,直到到达Object.prototype
  • 查找属性时,JavaScript会沿着原型链向上查找
javascript
function Animal(name) {
  this.name = name;
}

// 在原型上添加方法
Animal.prototype.speak = function() {
  console.log(`${this.name} makes a noise.`);
};

const dog = new Animal('Rex');
dog.speak(); // 'Rex makes a noise.'

// 原型链关系
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true

原型链继承

构造函数继承

javascript
function Animal(name) {
  this.name = name;
}

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

function Dog(name, breed) {
  Animal.call(this, name); // 调用父构造函数
  this.breed = breed;
}

// 设置原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// 添加Dog特有的方法
Dog.prototype.bark = function() {
  console.log(`${this.name} barks.`);
};

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // 'Buddy makes a noise.' (继承自Animal)
myDog.bark();  // 'Buddy barks.' (Dog自己的方法)

// 检查实例关系
console.log(myDog instanceof Dog);    // true
console.log(myDog instanceof Animal); // true

ES6 类继承

javascript
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} barks.`);
  }
  
  // 重写父类方法
  speak() {
    super.speak(); // 调用父类方法
    this.bark();
  }
}

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // 'Buddy makes a noise.' \n 'Buddy barks.'

对象属性操作

属性访问

javascript
const obj = { name: 'John', age: 30 };

// 点表示法
console.log(obj.name); // 'John'

// 括号表示法(可计算属性名)
console.log(obj['name']); // 'John'
const propName = 'age';
console.log(obj[propName]); // 30

属性描述符

每个属性都有描述符,定义其特性:

javascript
const obj = {};
Object.defineProperty(obj, 'name', {
  value: 'John',
  writable: true,      // 是否可修改
  enumerable: true,    // 是否可枚举(for...in循环)
  configurable: true   // 是否可配置(删除、修改描述符)
});

// 获取属性描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
console.log(descriptor);

属性类型

数据属性

包含一个值的属性,有四个特性:

  • [[Value]]:属性的值
  • [[Writable]]:是否可修改
  • [[Enumerable]]:是否可枚举
  • [[Configurable]]:是否可配置

访问器属性

不包含值,而是包含getter和setter函数:

javascript
const person = {
  firstName: 'John',
  lastName: 'Doe',
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  set fullName(name) {
    const parts = name.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
};

console.log(person.fullName); // 'John Doe'
person.fullName = 'Jane Smith';
console.log(person.firstName); // 'Jane'
console.log(person.lastName);  // 'Smith'

对象方法

属性检测

javascript
const obj = { name: 'John', age: 30 };

// 检测自有属性
console.log(obj.hasOwnProperty('name')); // true

// 检测属性是否存在(包括原型链)
console.log('name' in obj); // true
console.log('toString' in obj); // true (继承自Object.prototype)

// 检测是否为自有属性(而非继承属性)
console.log(obj.propertyIsEnumerable('name')); // true

对象遍历

javascript
const obj = { a: 1, b: 2, c: 3 };

// for...in 循环(遍历所有可枚举属性,包括继承的)
for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(key, obj[key]);
  }
}

// Object.keys() - 获取所有自有可枚举属性名
console.log(Object.keys(obj)); // ['a', 'b', 'c']

// Object.values() - 获取所有自有可枚举属性值
console.log(Object.values(obj)); // [1, 2, 3]

// Object.entries() - 获取所有自有可枚举属性的键值对数组
console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]

// Object.getOwnPropertyNames() - 获取所有自有属性名(包括不可枚举的)
console.log(Object.getOwnPropertyNames(obj)); // ['a', 'b', 'c']

对象复制

javascript
const original = { a: 1, b: { c: 2 } };

// 浅拷贝
const shallow1 = Object.assign({}, original);
const shallow2 = { ...original }; // ES6扩展运算符

// 深拷贝(简单实现)
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof Array) return obj.map(item => deepClone(item));
  if (typeof obj === 'object') {
    const cloned = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        cloned[key] = deepClone(obj[key]);
      }
    }
    return cloned;
  }
}

// 使用JSON方法进行深拷贝(有局限性)
const deep = JSON.parse(JSON.stringify(original));
// 注意:此方法不能处理函数、undefined、Symbol、循环引用等

解构赋值

ES6引入的解构赋值语法,可以从数组或对象中提取值:

对象解构

javascript
const person = { name: 'John', age: 30, city: 'New York' };

// 基本解构
const { name, age } = person;
console.log(name, age); // 'John', 30

// 重命名
const { name: personName, age: personAge } = person;
console.log(personName, personAge); // 'John', 30

// 默认值
const { name, age, country = 'USA' } = person;
console.log(country); // 'USA'

// 嵌套解构
const user = {
  name: 'John',
  address: {
    city: 'New York',
    zip: '10001'
  }
};
const { address: { city, zip } } = user;
console.log(city, zip); // 'New York', '10001'

// 函数参数解构
function displayUser({ name, age, city = 'Unknown' }) {
  console.log(`Name: ${name}, Age: ${age}, City: ${city}`);
}
displayUser(person); // 'Name: John, Age: 30, City: New York'

数组解构

javascript
const numbers = [1, 2, 3, 4, 5];

// 基本解构
const [first, second, third] = numbers;
console.log(first, second, third); // 1, 2, 3

// 跳过元素
const [a, , c] = numbers; // 跳过第二个元素
console.log(a, c); // 1, 3

// 剩余元素
const [x, y, ...rest] = numbers;
console.log(x, y, rest); // 1, 2, [3, 4, 5]

// 默认值
const [p, q, r = 10] = [1, 2];
console.log(r); // 10

对象增强特性(ES6+)

计算属性名

javascript
const nameKey = 'name';
const ageKey = 'age';

const obj = {
  [nameKey]: 'John',
  [ageKey]: 30,
  [nameKey + 'Full']: 'John Doe' // 'nameFull': 'John Doe'
};

方法简写

javascript
const obj = {
  // 简写方法
  greet() {
    return `Hello!`;
  },
  
  // 与getter/setter结合
  get currentYear() {
    return new Date().getFullYear();
  },
  
  set currentYear(year) {
    console.log(`Setting year to ${year}`);
  }
};

Object.assign() 和扩展运算符

javascript
// Object.assign() - 合并对象
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = Object.assign({}, obj1, obj2);

// 扩展运算符 - 更简洁的合并方式
const spreadMerged = { ...obj1, ...obj2 };

特殊对象类型

Map

键值对集合,键可以是任意类型:

javascript
const map = new Map();
map.set('name', 'John');
map.set(1, 'number one');
map.set({}, 'empty object');

console.log(map.get('name')); // 'John'
console.log(map.size); // 3

Set

唯一值的集合:

javascript
const set = new Set([1, 2, 3, 3, 2, 1]); // [1, 2, 3]
set.add(4);
console.log(set.has(4)); // true
console.log(set.size); // 4

WeakMap 和 WeakSet

弱引用集合,用于防止内存泄漏:

javascript
const weakMap = new WeakMap();
const objKey = {};
weakMap.set(objKey, 'value');
// 当objKey不再被其他地方引用时,它可以从内存中被清理

const weakSet = new WeakSet();
const obj = {};
weakSet.add(obj);