03-对象与原型链
分类:01-JavaScript基础核心
发布于:
阅读时间:88 分钟
对象与原型链
📋 学习目标
- 深入理解JavaScript对象的本质和创建方式
- 掌握原型链的工作机制和继承原理
- 学会使用ES6类语法进行面向对象编程
- 理解JavaScript中继承和多态的实现
- 掌握对象的高级特性和设计模式
🎯 对象基础
1. 对象的创建方式
// 1. 对象字面量(最常用)
const person = {
name: 'Alice',
age: 25,
greet: function() {
return `Hello, I'm ${this.name}`;
}
};
console.log(person.name); // 'Alice'
console.log(person.greet()); // 'Hello, I'm Alice'
// 2. 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
return `Hello, I'm ${this.name}`;
};
}
const person2 = new Person('Bob', 30);
console.log(person2.name); // 'Bob'
console.log(person2.greet()); // 'Hello, I'm Bob'
// 3. Object.create()(指定原型)
const personProto = {
greet: function() {
return `Hello, I'm ${this.name}`;
}
};
const person3 = Object.create(personProto);
person3.name = 'Charlie';
person3.age = 35;
console.log(person3.greet()); // 'Hello, I'm Charlie'
// 4. 工厂函数
function createPerson(name, age) {
return {
name,
age,
greet() {
return `Hello, I'm ${this.name}`;
}
};
}
const person4 = createPerson('David', 40);
console.log(person4.greet()); // 'Hello, I'm David'
2. 对象的属性
const car = {
brand: 'Toyota',
model: 'Camry',
year: 2020,
'property with spaces': 'value'
};
// 访问属性
console.log(car.brand); // 'Toyota'
console.log(car['brand']); // 'Toyota'
console.log(car['property with spaces']); // 'value'
// 动态属性名
const propertyName = 'model';
console.log(car[propertyName]); // 'Camry'
// 添加属性
car.color = 'blue';
car['engine'] = '2.5L';
// 删除属性
delete car.year;
// 检查属性是否存在
console.log('brand' in car); // true
console.log('year' in car); // false
console.log(car.hasOwnProperty('brand')); // true
// 枚举属性
for (const key in car) {
if (car.hasOwnProperty(key)) {
console.log(`${key}: ${car[key]}`);
}
}
// Object.keys(), Object.values(), Object.entries()
console.log(Object.keys(car)); // ['brand', 'model', 'property with spaces', 'color', 'engine']
console.log(Object.values(car)); // ['Toyota', 'Camry', 'value', 'blue', '2.5L']
console.log(Object.entries(car)); // [['brand', 'Toyota'], ['model', 'Camry'], ...]
3. 属性描述符
const obj = {};
// 添加属性(默认描述符)
Object.defineProperty(obj, 'name', {
value: 'Alice',
writable: true, // 可写
enumerable: true, // 可枚举
configurable: true // 可配置
});
// 只读属性
Object.defineProperty(obj, 'readOnly', {
value: 'Cannot change',
writable: false
});
// 不可枚举属性
Object.defineProperty(obj, 'hidden', {
value: 'You cannot see me',
enumerable: false
});
// 访问器属性
const person = {
_age: 25,
get age() {
return this._age;
},
set age(newAge) {
if (newAge > 0 && newAge < 150) {
this._age = newAge;
}
}
};
console.log(person.age); // 25
person.age = 30;
console.log(person.age); // 30
person.age = -5; // 无效,不会设置
// 获取属性描述符
const descriptor = Object.getOwnPropertyDescriptor(person, 'age');
console.log(descriptor);
// {
// get: [Function: get age],
// set: [Function: set age],
// enumerable: true,
// configurable: true
// }
// 批量定义属性
Object.defineProperties(obj, {
firstName: {
value: 'John',
writable: true
},
lastName: {
value: 'Doe',
writable: true
},
fullName: {
get() {
return `${this.firstName} ${this.lastName}`;
},
enumerable: true
}
});
🔗 原型链
1. 原型基础概念
// 每个函数都有prototype属性
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
// 每个对象都有__proto__属性(指向其构造函数的原型)
const person = new Person('Alice');
console.log(person.__proto__ =<mark> Person.prototype); // true
console.log(Person.prototype.constructor </mark>= Person); // true
// 原型链查找
console.log(person.greet()); // 在Person.prototype上找到greet方法
// 添加到原型上的方法被所有实例共享
Person.prototype.sayAge = function() {
return `I am ${this.age} years old`;
};
person.age = 25;
console.log(person.sayAge()); // 'I am 25 years old'
2. 原型链继承
// 基础继承
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
return `${this.name} is eating`;
};
function Dog(name, breed) {
Animal.call(this, name); // 调用父构造函数
this.breed = breed;
}
// 设置原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
return `${this.name} is barking`;
};
const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.eat()); // 'Buddy is eating' (继承自Animal)
console.log(dog.bark()); // 'Buddy is barking' (Dog自己的方法)
// 检查继承关系
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
3. 原型链的实际应用
// 方法继承
function ArrayExtender() {
// 空构造函数
}
ArrayExtender.prototype = Object.create(Array.prototype);
ArrayExtender.prototype.constructor = ArrayExtender;
ArrayExtender.prototype.first = function() {
return this[0];
};
ArrayExtender.prototype.last = function() {
return this[this.length - 1];
};
const extendedArray = new ArrayExtender();
extendedArray.push(1, 2, 3, 4, 5);
console.log(extendedArray.first()); // 1
console.log(extendedArray.last()); // 5
console.log(extendedArray instanceof Array); // true
// 混入模式(Mixin)
const canFly = {
fly() {
return `${this.name} is flying`;
}
};
const canSwim = {
swim() {
return `${this.name} is swimming`;
}
};
function Duck(name) {
this.name = name;
}
// 混入功能
Object.assign(Duck.prototype, canFly, canSwim);
const duck = new Duck('Donald');
console.log(duck.fly()); // 'Donald is flying'
console.log(duck.swim()); // 'Donald is swimming'
🏗️ ES6类语法
1. 基础类定义
// ES6类语法(语法糖,本质还是原型链)
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
return `Hello, I'm ${this.name}`;
}
// 访问器方法
get info() {
return `${this.name} is ${this.age} years old`;
}
set info(newInfo) {
const parts = newInfo.split(' is ');
this.name = parts[0];
this.age = parseInt(parts[1]);
}
}
const person = new Person('Alice', 25);
console.log(person.greet()); // 'Hello, I'm Alice'
console.log(person.info); // 'Alice is 25 years old'
person.info = 'Bob is 30 years old';
console.log(person.name); // 'Bob'
console.log(person.age); // 30
2. 继承
// 父类
class Animal {
constructor(name) {
this.name = name;
}
eat() {
return `${this.name} is eating`;
}
// 静态方法
static getKingdom() {
return 'Animalia';
}
}
// 子类
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父构造函数
this.breed = breed;
}
bark() {
return `${this.name} is barking`;
}
// 重写父方法
eat() {
return `${this.name} (${this.breed}) is eating happily`;
}
// 调用父方法
eatLikeAnimal() {
return super.eat(); // 调用父类的eat方法
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.eat()); // 'Buddy (Golden Retriever) is eating happily'
console.log(dog.eatLikeAnimal()); // 'Buddy is eating'
console.log(dog.bark()); // 'Buddy is barking'
console.log(Animal.getKingdom()); // 'Animalia'
3. 私有字段和方法
// 私有字段(ES2022)
class BankAccount {
[#balance](/tags/balance) = 0; // 私有字段
constructor(initialBalance) {
if (initialBalance >= 0) {
this.#balance = initialBalance;
}
}
// 公共方法
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
return this.#balance;
}
throw new Error('Amount must be positive');
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
return this.#balance;
}
throw new Error('Invalid amount');
}
getBalance() {
return this.#balance;
}
// 私有方法
[#validateAmount(amount)](/tags/validateAmount(amount)) {
return amount > 0;
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// account.#balance = 10000; // SyntaxError: Private field '#balance' must be declared in an enclosing class
4. 静态成员和实例成员
class MathUtils {
// 静态属性
static PI = 3.14159;
// 静态方法
static circleArea(radius) {
return this.PI * radius * radius;
}
static max(...numbers) {
return Math.max(...numbers);
}
// 实例方法
constructor() {
this.calculations = 0;
}
calculateCircleArea(radius) {
this.calculations++;
return MathUtils.circleArea(radius);
}
getCalculationCount() {
return this.calculations;
}
}
// 使用静态成员
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.circleArea(5)); // 78.53975
console.log(MathUtils.max(1, 5, 3, 9, 2)); // 9
// 使用实例成员
const calculator = new MathUtils();
console.log(calculator.calculateCircleArea(3)); // 28.27431
console.log(calculator.getCalculationCount()); // 1
🎨 高级对象特性
1. 对象冻结和密封
// Object.seal() - 密封对象(不能添加或删除属性,但可以修改)
const sealedObj = { a: 1, b: 2 };
Object.seal(sealedObj);
sealedObj.c = 3; // 严格模式下会报错
delete sealedObj.a; // 严格模式下会报错
sealedObj.a = 10; // 可以修改
console.log(Object.isSealed(sealedObj)); // true
// Object.freeze() - 冻结对象(完全不可变)
const frozenObj = { a: 1, b: 2 };
Object.freeze(frozenObj);
frozenObj.c = 3; // 严格模式下会报错
delete frozenObj.a; // 严格模式下会报错
frozenObj.a = 10; // 严格模式下会报错
console.log(Object.isFrozen(frozenObj)); // true
// 深度冻结
function deepFreeze(obj) {
Object.freeze(obj);
Object.getOwnPropertyNames(obj).forEach(prop => {
if (obj[prop] !<mark> null && typeof obj[prop] </mark>= 'object') {
deepFreeze(obj[prop]);
}
});
return obj;
}
const nestedObj = {
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
};
deepFreeze(nestedObj);
2. 对象比较
// 浅比较
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
const obj3 = obj1;
console.log(obj1 =<mark> obj2); // false (不同的对象引用)
console.log(obj1 </mark>= obj3); // true (相同的对象引用)
// 深度比较
function deepEqual(obj1, obj2) {
if (obj1 =<mark> obj2) return true;
if (typeof obj1 !</mark> 'object' || typeof obj2 !<mark> 'object' || obj1 </mark> null || obj2 == null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
console.log(deepEqual(obj1, obj2)); // true
3. 对象合并
// Object.assign() - 浅拷贝合并
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = Object.assign({}, obj1, obj2);
console.log(merged); // { a: 1, b: 3, c: 4 }
// 展开运算符 - 更简洁的合并
const merged2 = { ...obj1, ...obj2 };
console.log(merged2); // { a: 1, b: 3, c: 4 }
// 深度合并
function deepMerge(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
deepMerge(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return deepMerge(target, ...sources);
}
function isObject(item) {
return item && typeof item === 'object' && !Array.isArray(item);
}
const deepMerged = deepMerge(
{ a: 1, b: { x: 1, y: 2 } },
{ b: { y: 3, z: 4 }, c: 5 }
);
console.log(deepMerged); // { a: 1, b: { x: 1, y: 3, z: 4 }, c: 5 }
🎯 设计模式
1. 工厂模式
// 简单工厂
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
speak() {
return `${this.name} barks`;
}
}
class Cat extends Animal {
speak() {
return `${this.name} meows`;
}
}
class AnimalFactory {
static createAnimal(type, name) {
switch (type.toLowerCase()) {
case 'dog':
return new Dog(name);
case 'cat':
return new Cat(name);
default:
return new Animal(name);
}
}
}
const dog = AnimalFactory.createAnimal('dog', 'Buddy');
const cat = AnimalFactory.createAnimal('cat', 'Whiskers');
console.log(dog.speak()); // 'Buddy barks'
console.log(cat.speak()); // 'Whiskers meows'
2. 观察者模式
class EventEmitter {
constructor() {
this.events = {};
}
// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
// 触发事件
emit(eventName, data) {
if (this.events[eventName]) {
this.events[eventName].forEach(callback => callback(data));
}
}
// 取消订阅
off(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
}
}
}
// 使用示例
const emitter = new EventEmitter();
emitter.on('userLogin', (user) => {
console.log(`User ${user.name} logged in`);
});
emitter.on('userLogin', (user) => {
console.log(`Sending welcome email to ${user.email}`);
});
emitter.emit('userLogin', { name: 'Alice', email: 'alice@example.com' });
// 'User Alice logged in'
// 'Sending welcome email to alice@example.com'
3. 单例模式
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.connection = this.createConnection();
DatabaseConnection.instance = this;
}
createConnection() {
// 模拟数据库连接
return {
connected: true,
query: (sql) => `Result of: ${sql}`
};
}
query(sql) {
return this.connection.query(sql);
}
}
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true (同一个实例)
console.log(db1.query('SELECT * FROM users')); // 'Result of: SELECT * FROM users'
⚠️ 常见陷阱
1. 原型链污染
// ❌ 危险:修改原型
Array.prototype.sum = function() {
return this.reduce((a, b) => a + b, 0);
};
console.log([1, 2, 3, 4].sum()); // 10
// 可能导致意外的行为
for (const key in []) {
console.log(key); // 会输出 'sum'
}
// ✅ 更好的方式:使用工具函数
const arrayUtils = {
sum: (arr) => arr.reduce((a, b) => a + b, 0)
};
console.log(arrayUtils.sum([1, 2, 3, 4])); // 10
2. this指向问题
const obj = {
name: 'Object',
methods: {
regularMethod: function() {
console.log(this.name); // 'Object'
},
arrowMethod: () => {
console.log(this.name); // undefined (箭头函数不绑定this)
}
}
};
obj.methods.regularMethod(); // 'Object'
obj.methods.arrowMethod(); // undefined
// 在对象方法中使用箭头函数要小心
3. 属性枚举陷阱
const obj = { a: 1, b: 2 };
// 添加不可枚举属性
Object.defineProperty(obj, 'c', {
value: 3,
enumerable: false
});
for (const key in obj) {
console.log(key); // 只输出 'a' 和 'b',不会输出 'c'
}
console.log(Object.keys(obj)); // ['a', 'b']
console.log(Object.getOwnPropertyNames(obj)); // ['a', 'b', 'c']
📝 最佳实践
1. 对象创建
// ✅ 推荐使用类语法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
// ✅ 使用工厂函数当需要复杂初始化逻辑
function createUser(config) {
const defaults = {
name: 'Anonymous',
age: 18,
role: 'user'
};
return { ...defaults, ...config };
}
2. 继承
// ✅ 使用extends进行继承
class Animal {
constructor(name) {
this.name = name;
}
eat() {
return `${this.name} is eating`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
return `${this.name} is barking`;
}
}
3. 属性访问
// ✅ 使用访问器进行数据验证
class User {
constructor(email) {
this._email = email;
}
get email() {
return this._email;
}
set email(newEmail) {
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail)) {
this._email = newEmail;
} else {
throw new Error('Invalid email format');
}
}
}
🎯 小结
- 掌握了JavaScript对象的本质和多种创建方式
- 深入理解了原型链的工作机制和继承原理
- 学会了使用ES6类语法进行面向对象编程
- 理解了JavaScript中继承和多态的实现
- 掌握了对象的高级特性和常用设计模式
- 了解了常见的陷阱和最佳实践
下一步学习:ES6新特性概览