原型与继承
更新: 10/16/2025 字数: 0 字 时长: 0 分钟
原型(Prototype)
什么是原型
JavaScript 中每个对象都有一个内部属性 [[Prototype]],指向另一个对象,这就是原型。
javascript
// 创建对象
const person = {
name: 'Jensen',
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
// 访问原型
Object.getPrototypeOf(person); // Object.prototype
person.__proto__; // Object.prototype(不推荐使用)原型链
javascript
const animal = {
eat() {
console.log('eating');
}
};
const dog = Object.create(animal);
dog.bark = function() {
console.log('woof');
};
const myDog = Object.create(dog);
myDog.name = 'Buddy';
// 原型链查找
myDog.eat(); // 'eating' - 从 animal 找到
myDog.bark(); // 'woof' - 从 dog 找到
myDog.name; // 'Buddy' - 自身属性
// 原型链:myDog -> dog -> animal -> Object.prototype -> null
console.log(Object.getPrototypeOf(myDog) === dog); // true
console.log(Object.getPrototypeOf(dog) === animal); // true
console.log(Object.getPrototypeOf(animal) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null原型图解
myDog {
name: 'Buddy'
[[Prototype]]: dog
}
↓
dog {
bark: function()
[[Prototype]]: animal
}
↓
animal {
eat: function()
[[Prototype]]: Object.prototype
}
↓
Object.prototype {
toString: function()
valueOf: function()
hasOwnProperty: function()
...
[[Prototype]]: null
}构造函数
基本概念
javascript
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
// 创建实例
const person1 = new Person('Alice', 25);
const person2 = new Person('Bob', 30);
// 共享原型方法
person1.greet === person2.greet; // true
// 原型链
person1.__proto__ === Person.prototype; // true
Person.prototype.__proto__ === Object.prototype; // truenew 关键字的工作原理
javascript
// new 做了什么?
function myNew(Constructor, ...args) {
// 1. 创建新对象,原型指向构造函数的 prototype
const obj = Object.create(Constructor.prototype);
// 2. 执行构造函数,绑定 this
const result = Constructor.apply(obj, args);
// 3. 如果构造函数返回对象,则返回该对象,否则返回新对象
return result instanceof Object ? result : obj;
}
// 使用
const person = myNew(Person, 'Jensen', 25);constructor 属性
javascript
function Person(name) {
this.name = name;
}
// prototype.constructor 指回构造函数
Person.prototype.constructor === Person; // true
const person = new Person('Alice');
person.constructor === Person; // true
// 重写 prototype 时要注意
Person.prototype = {
greet() {
console.log('Hi');
}
};
// constructor 丢失
Person.prototype.constructor === Person; // false
// 修复
Person.prototype.constructor = Person;原型方法
Object.create()
javascript
// 创建对象,指定原型
const animal = {
type: 'Animal',
eat() {
console.log('eating');
}
};
const dog = Object.create(animal);
dog.bark = function() {
console.log('woof');
};
dog.eat(); // 'eating' - 继承自 animal
dog.type; // 'Animal' - 继承自 animal
// 创建纯净对象(没有原型)
const pureObject = Object.create(null);
pureObject.toString; // undefinedObject.getPrototypeOf() / Object.setPrototypeOf()
javascript
const animal = { type: 'Animal' };
const dog = Object.create(animal);
// 获取原型
Object.getPrototypeOf(dog) === animal; // true
// 设置原型(不推荐,性能差)
const cat = {};
Object.setPrototypeOf(cat, animal);
Object.getPrototypeOf(cat) === animal; // truehasOwnProperty()
javascript
const animal = { type: 'Animal' };
const dog = Object.create(animal);
dog.name = 'Buddy';
// 检查自身属性(不包括原型链)
dog.hasOwnProperty('name'); // true
dog.hasOwnProperty('type'); // false
// 检查属性是否存在(包括原型链)
'name' in dog; // true
'type' in dog; // trueisPrototypeOf()
javascript
const animal = { type: 'Animal' };
const dog = Object.create(animal);
// 检查原型链
animal.isPrototypeOf(dog); // true
Object.prototype.isPrototypeOf(dog); // true继承模式
1. 原型链继承
javascript
function Animal(name) {
this.name = name;
this.colors = ['black', 'white'];
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name) {
this.name = name;
}
// 原型链继承
Dog.prototype = new Animal();
const dog1 = new Dog('Buddy');
const dog2 = new Dog('Max');
// 问题:共享引用类型属性
dog1.colors.push('brown');
console.log(dog2.colors); // ['black', 'white', 'brown']2. 构造函数继承
javascript
function Animal(name) {
this.name = name;
this.colors = ['black', 'white'];
}
Animal.prototype.eat = function() {
console.log('eating');
};
function Dog(name, breed) {
// 调用父构造函数
Animal.call(this, name);
this.breed = breed;
}
const dog1 = new Dog('Buddy', 'Golden Retriever');
const dog2 = new Dog('Max', 'Husky');
// 优点:解决了引用类型共享问题
dog1.colors.push('brown');
console.log(dog2.colors); // ['black', 'white']
// 缺点:无法继承原型方法
dog1.eat(); // TypeError: dog1.eat is not a function3. 组合继承
javascript
function Animal(name) {
this.name = name;
this.colors = ['black', 'white'];
}
Animal.prototype.eat = function() {
console.log('eating');
};
function Dog(name, breed) {
// 继承属性
Animal.call(this, name);
this.breed = breed;
}
// 继承方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('woof');
};
const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 'eating'
dog.bark(); // 'woof'
// 缺点:调用了两次父构造函数4. 寄生组合继承(最佳)
javascript
function Animal(name) {
this.name = name;
this.colors = ['black', 'white'];
}
Animal.prototype.eat = function() {
console.log('eating');
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// 使用 Object.create 避免调用两次构造函数
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('woof');
};
const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 'eating'
dog.bark(); // 'woof'
// 封装继承函数
function inherit(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
}
inherit(Dog, Animal);ES6 Class
基本语法
javascript
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} is eating`);
}
// 静态方法
static create(name) {
return new Animal(name);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
bark() {
console.log('woof');
}
// 重写方法
eat() {
super.eat(); // 调用父类方法
console.log('Dog is eating');
}
}
const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 'Buddy is eating' 和 'Dog is eating'
dog.bark(); // 'woof'
// 静态方法继承
Dog.create === Animal.create; // false
Dog.create('Max') instanceof Animal; // trueClass 的本质
javascript
// Class 只是语法糖
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log('Hi');
}
}
// 等价于
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log('Hi');
};
// 但有一些区别:
// 1. Class 必须用 new 调用
// 2. Class 内部自动启用严格模式
// 3. Class 的方法不可枚举
// 4. Class 有暂时性死区私有属性和方法
javascript
class Person {
// 私有属性
#age = 0;
constructor(name, age) {
this.name = name;
this.#age = age;
}
// 私有方法
#calculateBirthYear() {
return new Date().getFullYear() - this.#age;
}
// 公共方法
getBirthYear() {
return this.#calculateBirthYear();
}
}
const person = new Person('Alice', 25);
console.log(person.name); // 'Alice'
console.log(person.#age); // SyntaxError: 私有属性
console.log(person.getBirthYear()); // 可以访问Getter 和 Setter
javascript
class Circle {
constructor(radius) {
this._radius = radius;
}
get radius() {
return this._radius;
}
set radius(value) {
if (value < 0) {
throw new Error('半径不能为负数');
}
this._radius = value;
}
get area() {
return Math.PI * this._radius ** 2;
}
}
const circle = new Circle(5);
console.log(circle.radius); // 5
console.log(circle.area); // 78.54
circle.radius = 10;
console.log(circle.area); // 314.16静态属性和方法
javascript
class MathUtils {
// 静态属性
static PI = 3.14159;
// 静态方法
static square(x) {
return x * x;
}
// 静态私有方法
static #helper() {
return 'helper';
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.square(5)); // 25
const util = new MathUtils();
console.log(util.PI); // undefined(静态属性不在实例上)原型污染
什么是原型污染
javascript
// 原型污染示例
function merge(target, source) {
for (let key in source) {
target[key] = source[key];
}
return target;
}
const obj = {};
const malicious = JSON.parse('{"__proto__": {"polluted": true}}');
merge(obj, malicious);
// 所有对象都被污染了
const newObj = {};
console.log(newObj.polluted); // true防御原型污染
javascript
// 方法 1:检查键名
function safeMerge(target, source) {
for (let key in source) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
target[key] = source[key];
}
return target;
}
// 方法 2:使用 hasOwnProperty
function safeMerge2(target, source) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
return target;
}
// 方法 3:使用 Object.create(null)
const safeObj = Object.create(null);
safeObj.__proto__ = { polluted: true };
console.log(safeObj.polluted); // undefined
// 方法 4:冻结原型
Object.freeze(Object.prototype);实用技巧
1. 检查对象类型
javascript
// instanceof
function isArray(obj) {
return obj instanceof Array;
}
// Object.prototype.toString
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
}
getType([]); // 'Array'
getType({}); // 'Object'
getType(null); // 'Null'
getType(undefined); // 'Undefined'2. 克隆对象
javascript
// 浅克隆
function shallowClone(obj) {
return Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
}
// 深克隆(简单版)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
const clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}3. 混入(Mixin)
javascript
// 混入多个对象的属性
function mixin(target, ...sources) {
return Object.assign(target, ...sources);
}
// 使用
class Person {
constructor(name) {
this.name = name;
}
}
const canEat = {
eat() {
console.log('eating');
}
};
const canWalk = {
walk() {
console.log('walking');
}
};
mixin(Person.prototype, canEat, canWalk);
const person = new Person('Alice');
person.eat(); // 'eating'
person.walk(); // 'walking'最佳实践
1. 优先使用 Class
javascript
// ✅ 推荐:使用 Class
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log('eating');
}
}
// ❌ 避免:使用构造函数(除非有特殊需求)
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log('eating');
};2. 不要修改内置原型
javascript
// ❌ 非常不好
Array.prototype.myMethod = function() {
// ...
};
// ✅ 使用工具函数
function arrayMyMethod(arr) {
// ...
}3. 使用 Object.create(null) 创建纯净对象
javascript
// ✅ 用作 Map 时使用
const map = Object.create(null);
map['key'] = 'value';
// 没有原型,更安全
// 普通对象仍然使用字面量
const obj = {};总结
原型和继承的核心要点:
原型链
- 每个对象都有原型
- 形成原型链
- 属性查找机制
构造函数
- 创建对象
- prototype 属性
- new 关键字
继承方式
- 原型链继承
- 构造函数继承
- 组合继承
- 寄生组合继承
ES6 Class
- 语法糖
- extends 关键字
- super 关键字
- 私有属性
最佳实践
- 优先使用 Class
- 避免原型污染
- 不修改内置原型