第一次接触 JavaScript 时,可能会被它独特的继承机制所困惑。与传统的基于类的语言不同,JavaScript 采用基于原型的继承模型。
这个看似复杂的概念实际上是 JavaScript 最强大和灵活的特性之一,它为动态编程和代码复用提供了无限可能。
一、原型基础概念:理解 JavaScript 的继承之源
在深入原型链之前,我们需要理解几个核心概念和它们之间的关系。
1. 什么是原型(Prototype)?
在 JavaScript 中,每个对象都有一个隐藏的内部属性 [[Prototype]],它指向另一个对象,这个被指向的对象就是原型。
// 创建一个简单对象
const person = {
name: "Alice",
greet() {
console.log(`Hello, I'm ${this.name}`);
},
};
// 创建另一个对象,以 person 为原型
const student = Object.create(person);
student.name = "Bob";
student.study = function () {
console.log(`${this.name} is studying`);
};
student.greet(); // "Hello, I'm Bob" - 从原型继承的方法
student.study(); // "Bob is studying" - 自己的方法
三个重要属性的区别
[[Prototype]]:对象的内部原型引用(不可直接访问)__proto__:访问[[Prototype]]的非标准方式(已废弃)prototype:函数的原型对象属性(用于构造函数)
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
const alice = new Person("Alice");
console.log(alice.__proto__ === Person.prototype); // true
console.log(Object.getPrototypeOf(alice) === Person.prototype); // true(标准方式)
重要提示:始终使用
Object.getPrototypeOf()和Object.setPrototypeOf()来操作原型,避免使用已废弃的__proto__。
2. 原型链的工作机制
原型链是一个链式查找过程。当访问对象属性时,JavaScript 引擎会按照以下顺序查找:
const grandParent = {
species: "human",
speak() {
console.log("Speaking...");
},
};
const parent = Object.create(grandParent);
parent.canWalk = true;
const child = Object.create(parent);
child.name = "Tom";
child.age = 5;
// 属性查找过程
console.log(child.name); // "Tom" - 在 child 对象上找到
console.log(child.canWalk); // true - 在 parent 原型上找到
console.log(child.species); // "human" - 在 grandParent 原型上找到
console.log(child.fly); // undefined - 整条链上都没找到
伪代码描述属性查找过程:
function getProperty(obj, prop) {
let current = obj;
while (current !== null) {
if (current.hasOwnProperty(prop)) {
return current[prop];
}
current = Object.getPrototypeOf(current);
}
return undefined;
}
二、构造函数与原型:对象创建的经典模式
构造函数是 JavaScript 中创建对象的传统方式,理解它与原型的关系是掌握继承的关键。
1. 构造函数的原型机制
每个函数在创建时都会自动获得一个 prototype 属性:
function Animal(name) {
this.name = name;
this.energy = 100;
}
// 在原型上定义共享方法
Animal.prototype.eat = function (food) {
console.log(`${this.name} is eating ${food}`);
this.energy += 10;
};
Animal.prototype.sleep = function () {
console.log(`${this.name} is sleeping`);
this.energy += 20;
};
Animal.prototype.play = function () {
console.log(`${this.name} is playing`);
this.energy -= 10;
};
// 创建实例
const cat = new Animal("Whiskers");
const dog = new Animal("Buddy");
cat.eat("fish");
dog.play();
// 所有实例共享原型方法
console.log(cat.eat === dog.eat); // true - 同一个函数引用
new Animal('Whiskers') 的执行过程:
// 1. 创建一个空对象
const obj = {};
// 2. 设置新对象的原型
Object.setPrototypeOf(newObj, Animal.prototype);
// 等价于: newObj.__proto__ = Animal.prototype;
// 3. 执行构造函数, this 指向新对象
Animal.call(newObj, "Whiskers");
// 4. 返回新对象(除非构造函数显式返回其他对象)
return newObj;
2. 原型方法 vs 实例方法
方法定义位置会影响性能和内存使用:
function Person(name) {
this.name = name;
// 不推荐: 每个实例都创建新函数
this.greetBad = function () {
console.log(`Hello, I'm ${this.name}`);
};
}
// 推荐: 所有实例共享同一个函数
Person.prototype.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
const person1 = new Person("Alice");
const person2 = new Person("Bob");
console.log(person1.greetBad === person2.greetBad); // false - 不同函数
console.log(person1.greet === person2.greet); // true - 同一函数
最佳实践:
function Vehicle(brand, model) {
// 实例属性
this.brand = brand;
this.model = model;
this.speed = 0;
}
// 原型方法 - 所有实例共享
Vehicle.prototype.accelerate = function (increment = 10) {
this.speed += increment;
console.log(`${this.brand} ${this.model} accelerating to ${this.speed} km/h`);
};
Vehicle.prototype.brake = function (decrement = 10) {
this.speed = Math.max(0, this.speed - decrement);
console.log(`${this.brand} ${this.model} slowing down to ${this.speed} km/h`);
};
// 静态方法 - 属于构造函数本身
Vehicle.compare = function (vehicle1, vehicle2) {
return vehicle1.speed - vehicle2.speed;
};
三、原型继承的实现方式
JavaScript 提供了多种实现继承的方式,从传统的原型链到现代的 class 语法。
1. 经典原型继承
// 父类
function Animal(name) {
this.name = name;
this.alive = true;
}
Animal.prototype.breathe = function () {
console.log(`${this.name} is breathing`);
};
Animal.prototype.move = function () {
console.log(`${this.name} is moving`);
};
// 子类构造函数
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 () {
console.log(`${this.name} is barking: Woof!`);
};
Dog.prototype.wagTail = function () {
console.log(`${this.name} is wagging tail`);
};
// 方法重写
Dog.prototype.move = function () {
console.log(`${this.name} is running on four legs`);
};
const buddy = new Dog("Buddy", "Golden Retriever");
buddy.breathe(); // 继承的方法
buddy.bark(); // 自己的方法
buddy.move(); // 重写的方法
继承链检查:
console.log(buddy instanceof Dog); // true
console.log(buddy instanceof Animal); // true
console.log(buddy instanceof Object); // true
console.log(Object.getPrototypeOf(buddy) === Dog.prototype); // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype) === null); // true
2. 工厂函数模式
不使用构造函数的继承方式:
// 父对象工厂
function createAnimal(name) {
const animal = Object.create(animalMethods);
animal.name = name;
animal.alive = true;
return animal;
}
const animalMethods = {
breathe() {
console.log(`${this.name} is breathing`);
},
move() {
console.log(`${this.name} is moving`);
},
};
// 子对象工厂
function createDog(name, breed) {
const dog = createAnimal(name);
Object.setPrototypeOf(dog, dogMethods);
dog.breed = breed;
return dog;
}
const dogMethods = Object.create(animalMethods);
dogMethods.bark = function () {
console.log(`${this.name} is barking: Woof!`);
};
dogMethods.move = function () {
console.log(`${this.name} is running on four legs`);
};
const rex = createDog("Rex", "German Shepherd");
rex.breathe(); // 继承的方法
rex.bark(); // 子类方法
3. 组合继承的问题与寄生组合继承
传统组合继承的问题:
function Parent(name) {
this.name = name;
this.colors = ["red", "blue"];
}
Parent.prototype.getName = function () {
return;
this.name;
};
function Child(name, age) {
Parent.call(this, name); // 第一次调用 Parent 构造函数
this.age = age;
}
// 第二次调用 Parent 构造函数
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// 问题:Parent 构造函数被调用两次,Child.prototype 上存在多余属性
寄生组合继承:
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.colors = ["red", "blue"];
}
Parent.prototype.getName = function () {
return;
this.name;
};
function Child(name, age) {
Parent.call(this, name); // 只调用一次
this.age = age;
}
inheritPrototype(Child, Parent);
Child.prototype.getAge = function () {
return this.age;
};
const child = new Child("Tom", 10);
console.log(child.getName()); // "Tom"
console.log(child.getAge()); // 10
常见继承模式优缺点
- 原型链继承:简单;但无法向父构造函数传参,父级引用类型属性会被所有子实例共享
- 借用构造函数继承:可以传参;实例之间不会共享父级引用类型属性;但父类方法无法复用
- 组合继承:结合了两者优势,但父构造函数会被调用两次
- 寄生式继承:基于
Object.create()快速构建对象,可灵活添加能力;但每次创建实例都要新建方法 - 寄生组合式继承:只调用一次父构造函数 + 复用原型,是传统继承的最佳实践
- ES6 class 继承:语法更直观,本质仍基于原型链
四、ES6+ 现代继承语法
ES6 引入的 class 语法为 JavaScript 提供了更清晰的继承语法。
1. Class 基础语法
class Animal {
// 类字段 (ES2022)
#energy = 100; // 私有字段
constructor(name, species) {
this.name = name;
this.species = species;
}
// 实例方法
eat(food) {
this.#energy += 10;
console.log(`${this.name} is eating ${food}. Energy: ${this.#energy}`);
}
sleep() {
this.#energy += 20;
console.log(`${this.name} is sleeping. Energy: ${this.#energy}`);
}
// Getter 和 Setter
get energy() {
return this.#energy;
}
set energy(value) {
if (value < 0) {
throw new Error("Energy cannot be negative");
}
this.#energy = value;
}
// 静态方法
static compare(animal1, animal2) {
return;
animal1.energy - animal2.energy;
}
// 静态字段
static kingdom = "Animalia";
}
const lion = new Animal("Leo", "Lion");
lion.eat("meat");
console.log(lion.energy); // 110
2. Class 继承
class Mammal extends Animal {
constructor(name, species, furColor) {
super(name, species); // 调用父类构造函数
this.furColor = furColor;
this.warmBlooded = true;
}
// 方法重写
eat(food) {
super.eat(food); // 调用父类方法
this.groom();
}
// 新方法
groom() {
console.log(`${this.name} is grooming its ${this.furColor} fur`);
}
giveBirth() {
console.log(`${this.name} is giving birth to live young`);
}
}
class Dog extends Mammal {
constructor(name, breed, furColor) {
super(name, "Canis lupus", furColor);
this.breed = breed;
this.loyalty = 100;
}
bark(intensity = "normal") {
const sounds = {
quiet: "woof",
normal: "Woof!",
loud: "WOOF WOOF!",
};
console.log(`${this.name} barks: ${sounds[intensity]}`);
}
// 异步方法示例
async fetchToy() {
console.log(`${this.name} is looking for a toy...`);
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(`${this.name} found a ball!`);
return "ball";
}
// 重写 toString
toString() {
return `${this.name} - ${this.breed} dog with ${this.furColor} fur`;
}
}
const buddy = new Dog("Buddy", "Golden Retriever", "golden");
buddy.eat("kibble"); // 调用重写的方法
buddy.bark("loud"); // 调用自己的方法
console.log(buddy.toString());
3. Mixin 模式 - 多重继承的实现
JavaScript 不支持多重继承,但可以通过 Mixin 实现类似功能:
const Flyable = {
fly() {
console.log(`${this.name} is flying at altitude ${this.altitude}m`);
},
land() {
this.altitude = 0;
console.log(`${this.name} has landed`);
},
};
const Swimmable = {
swim() {
console.log(`${this.name} is swimming at depth ${this.depth}m`);
},
dive(targetDepth) {
this.depth = targetDepth;
console.log(`${this.name} dived to ${targetDepth}m`);
},
};
// Mixin 辅助函数
function mixin(target, ...sources) {
sources.forEach((source) => {
Object.getOwnPropertyNames(source).forEach((name) => {
if (name !== "constructor") {
target.prototype[name] = source[name];
}
});
});
return target;
}
// 应用 Mixin
class Bird extends Animal {
constructor(name, species, wingspan) {
super(name, species);
this.wingspan = wingspan;
this.altitude = 0;
}
}
class Duck extends Bird {
constructor(name, wingspan) {
super(name, "Duck", wingspan);
this.depth = 0;
}
}
mixin(Duck, Flyable, Swimmable);
const mallard = new Duck("Donald", 0.8);
mallard.fly(); // 来自 Flyable
mallard.dive(2); // 来自 Swimmable
mallard.swim(); // 来自 Swimmable
mallard.land(); // 来自 Flyable
五、原型链的高级应用
掌握了基础知识后,我们可以探索一些高级应用场景。
1. 动态原型修改
JavaScript 允许在运行时修改原型,这是其动态特性的体现:
function Person(name) {
this.name = name;
}
const alice = new Person("Alice");
const bob = new Person("Bob");
// 动态添加方法到原型
Person.prototype.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
// 所有实例立即获得新方法
alice.greet(); // "Hello, I'm Alice"
bob.greet(); // "Hello, I'm Bob"
// 动态添加属性
Person.prototype.species = "Homo sapiens";
console.log(alice.species); // "Homo sapiens"
// 方法重写和原始方法保存
const originalGreet = Person.prototype.greet;
Person.prototype.greet = function () {
originalGreet.call(this);
console.log("Nice to meet you!");
};
alice.greet();
// "Hello, I'm Alice"
// "Nice to meet you!"
注意事项:
- 修改内置对象原型要谨慎
- 可能影响第三方库
- 性能开销需要考虑
// 不推荐: 修改内置原型
Array.prototype.last = function () {
return this[this.length - 1];
};
// 推荐: 使用工具函数
function getLastItem(array) {
return array[array.length - 1];
}
const last = (arr) => arr[arr.length - 1];
2. 原型代理和拦截
使用 Proxy 可以拦截原型上的操作:
class SmartObject {
constructor() {
this.data = new Map();
return new Proxy(this, {
get(target, prop) {
console.log(`Accessing property: ${prop}`);
if (target.data.has(prop)) {
return;
target.data.get(prop);
}
return target[prop];
},
set(target, prop, value) {
console.log(`Setting property: ${prop} = ${value}`);
if (typeof prop === "string" && prop.startsWith("_")) {
console.warn(`Warning: Setting private property ${prop}`);
}
target.data.set(prop, value);
return true;
},
has(target, prop) {
return;
target.data.has(prop) || prop in target;
},
});
}
}
unsafeAssign({}, JSON.parse('{ "__proto__": { "hacked": true } }'));
console.log({}.hacked); // true —— 原型被污染
// ❌ 危险示例
function unsafeAssign(target, source) {
for (const key in source) {
target[key] = source[key];
}
toJSON() {
return Object.fromEntries(
this.data
);
}
}
const obj = new SmartObject();
obj.name
= 'John'; // Setting property: name = John
obj._secret = 'hidden'; // Warning: Setting private property _secret
console.log(
obj.name
); // Accessing property: name
3. 原型污染防护
原型污染是一个安全问题,需要采取防护措施:
function safeAssign(target, source) {
const dangerousKeys = ["__proto__", "constructor", "prototype"];
for (const key in source) {
if (dangerousKeys.includes(key)) {
console.warn(`Skipping dangerous key: ${key}`);
continue;
}
if (typeof key === "string" && key.includes("prototype")) {
console.warn(`Skipping suspicious key: ${key}`);
continue;
}
target[key] = source[key];
}
return target;
}
// 创建安全的对象
function createSecureObject() {
const obj = Object.create(null); // 没有原型链
return new Proxy(obj, {
set(target, prop, value) {
if (prop === "__proto__") {
throw new Error("Prototype pollution attempt blocked");
}
target[prop] = value;
return true;
},
});
}
const secureObj = createSecureObject();
secureObj.data = "safe";
// secureObj.__proto__ = {}; // Error: Prototype pollution attempt blocked
六、性能考虑与最佳实践
在实际开发中,原型链的使用需要考虑性能和可维护性。
1. 原型查找的性能影响
// 性能测试: 不同深度的原型链
function createChain(depth) {
let current = {};
const root = current;
for (let i = 0; i < depth; i++) {
const next = {};
Object.setPrototypeOf(current, next);
current = next;
}
// 在最顶层添加属性
current.deepProperty = "found";
return root;
}
// 测试不同深度的查找性能
function testLookupPerformance() {
const depths = [1, 5, 10, 20, 50];
const iterations = 1000000;
depths.forEach((depth) => {
const obj = createChain(depth);
console.time(`Depth ${depth}`);
for (let i = 0; i < iterations; i++) {
obj.deepProperty; // 查找属性
}
console.timeEnd(`Depth ${depth}`);
});
}
testLookupPerformance();
性能优化建议:
class OptimizedClass {
constructor() {
// 预分配常用属性, 避免动态添加
this.x = 0;
this.y = 0;
this.visible = true;
this.cached = null;
}
expensiveOperation() {
// 使用缓存避免重复计算
if (this.cached !== null) {
return this.cached;
}
// 模拟耗时操作
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.random();
}
this.cached = result;
return result;
}
// 直接在实例上定义热点方法
hotMethod() {
return this.x * this.y;
}
}
// 对于需要大量实例的场景, 考虑对象池
class ObjectPool {
constructor(createFn, resetFn, initialSize = 10) {
this.createFn = createFn;
this.resetFn = resetFn;
this.pool = [];
// 预创建对象
for (let i = 0; i < initialSize; i++) {
this.pool.push(this.createFn());
}
}
acquire() {
return this.pool.pop() || this.createFn();
}
release(obj) {
this.resetFn(obj);
this.pool.push(obj);
}
}
2. 内存泄漏预防
原型链使用不当可能导致内存泄漏:
class EventEmitter {
constructor() {
this.listeners = new Map();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
// 返回清理函数
return () => this.off(event, callback);
}
off(event, callback) {
if (!this.listeners.has(event)) return;
const callbacks = this.listeners.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
if (callbacks.length === 0) {
this.listeners.delete(event);
}
}
emit(event, ...args) {
if (!this.listeners.has(event)) return;
this.listeners.get(event).forEach((callback) => {
try {
callback(...args);
} catch (error) {
console.error("Event callback error:", error);
}
});
}
// 重要: 提供清理方法
destroy() {
this.listeners.clear();
}
}
const emitter = new EventEmitter();
// 自动清理的监听器
const cleanup = emitter.on("data", (data) => {
console.log("Received data:", data);
});
window.addEventListener("beforeunload", cleanup);
七、调试和工具
掌握调试原型链的工具和技巧对开发很有帮助。
1. 原型链检查工具
// 通用原型链分析函数
function analyzePrototypeChain(obj, maxDepth = 10) {
const chain = [];
let current = obj;
let depth = 0;
while (current && depth < maxDepth) {
const info = {
depth,
object: current,
constructor: current.constructor?.name || "Unknown",
ownProperties: Object.getOwnPropertyNames(current),
methods: [],
};
Object.getOwnPropertyNames(current).forEach((name) => {
if (typeof current[name] === "function") {
info.methods.push(name);
}
});
chain.push(info);
current = Object.getPrototypeOf(current);
depth++;
}
return chain;
}
class A {}
class B extends A {}
class C extends B {}
const instance = new C();
const analysis = analyzePrototypeChain(instance);
console.table(
analysis.map((item) => ({
depth: item.depth,
constructor: item.constructor,
properties: item.ownProperties.length,
methods: item.methods.length,
})),
);
2. 实时监控原型修改
// 监控原型修改的工具
function watchPrototype(constructor, callback) {
const original = constructor.prototype;
const handler = {
set(target, prop, value) {
callback({
action: "set",
property: prop,
value,
target,
});
target[prop] = value;
return true;
},
deleteProperty(target, prop) {
callback({
action: "delete",
property: prop,
target,
});
delete target[prop];
return true;
},
};
constructor.prototype = new Proxy(original, handler);
return () => {
constructor.prototype = original; // 恢复原始原型
};
}
class TestClass {}
const stopWatching = watchPrototype(TestClass, (change) => {
console.log("Prototype changed:", change);
});
TestClass.prototype.newMethod = function () {
return "added";
}; // 触发监控
delete TestClass.prototype.newMethod; // 触发监控
stopWatching(); // 停止监控
八、现代 JavaScript 的替代方案
随着 JavaScript 的发展,出现了一些原型链的替代或补充方案。
1. 组合优于继承
// 传统继承方式
class FlyingBird extends Bird {
fly() {
/* ... */
}
}
class SwimmingBird extends Bird {
swim() {
/* ... */
}
}
// 问题: 如果需要会飞又会游泳的鸟怎么办?
// 组合方式
const flyBehavior = {
fly() {
console.log(`${this.name} is flying`);
},
};
const swimBehavior = {
swim() {
console.log(`${this.name} is swimming`);
},
};
const walkBehavior = {
walk() {
console.log(`${this.name} is walking`);
},
};
class Bird {
constructor(name, behaviors = []) {
this.name = name;
// 动态混入行为
behaviors.forEach((behavior) => {
Object.assign(this, behavior);
});
}
}
const eagle = new Bird("Eagle", [flyBehavior]);
const duck = new Bird("Duck", [flyBehavior, swimBehavior, walkBehavior]);
const penguin = new Bird("Penguin", [swimBehavior, walkBehavior]);
eagle.fly(); // ✅
duck.swim(); // ✅
duck.fly(); // ✅
penguin.walk(); // ✅
//
penguin.fly(); // undefined - 没有此能力
2. WeakMap 私有属性
使用 WeakMap 实现真正的私有属性:
const privateData = new WeakMap();
class SecureClass {
constructor(secret) {
privateData.set(this, {
secret,
internalCounter: 0,
});
}
getSecret() {
return privateData.get(this).secret;
}
increment() {
const data = privateData.get(this);
data.internalCounter++;
return data.internalCounter;
}
}
const instance = new SecureClass("top-secret");
console.log(instance.getSecret()); // "top-secret"
console.log(instance.increment()); // 1
console.log(instance.secret); // undefined
console.log(Object.keys(instance)); // []
3. 函数式编程方法
// 函数式的 "继承" 方式
const createAnimal = (name) => ({
name,
speak: () => console.log(`${name} makes a sound`),
});
const createDog = (name, breed) => ({
...createAnimal(name),
breed,
speak: () => console.log(`${name} barks`),
wagTail: () => console.log(`${name} wags tail`),
});
const createCat = (name, color) => ({
...createAnimal(name),
color,
speak: () => console.log(`${name} meows`),
purr: () => console.log(`${name} purrs`),
});
const dog = createDog("Buddy", "Golden Retriever");
const cat = createCat("Whiskers", "tabby");
dog.speak(); // "Buddy barks"
cat.purr(); // "Whiskers purrs"
// 优势: 简单、可预测、易测试
// 劣势: 每个实例都有方法的副本(内存占用)
九、总结与最佳实践
JavaScript 的原型链是一个强大而灵活的特性,理解它的工作原理对成为优秀的 JavaScript 开发者至关重要。
1. 核心要点回顾
- 原型链是属性查找机制:访问对象属性时,会沿着原型链向上查找
- 构造函数的
prototype属性:决定了实例对象的原型 Object.getPrototypeOf():标准的获取对象原型的方式- 继承的多种实现:从构造函数到 ES6 class,各有优劣
- 性能考虑:过深的原型链会影响属性访问性能
2. 最佳实践总结
// 推荐的做法
// 1. 使用 ES6+ class 语法
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
// 2. 优先组合而非深层继承
class Dog {
constructor(name, behaviors) {
this.name = name;
this.behaviors = behaviors;
}
performBehavior(behaviorName) {
if (this.behaviors[behaviorName]) {
this.behaviors[behaviorName].call(this);
}
}
}
// 3. 使用 Object.create() 创建原型关系
const dogPrototype = Object.create(Animal.prototype);
// 4. 避免修改内置原型
// Array.prototype.customMethod = ... // 不推荐
// 5. 使用私有字段(ES2022)
class ModernClass {
#privateField = "secret";
getPrivateField() {
return this.#privateField;
}
}
3. 常见陷阱避免
// 常见错误
function Constructor() {}
Constructor.prototype = {
method: function () {},
// 忘记设置 constructor 属性
};
// 正确做法
function Constructor() {}
Constructor.prototype = {
constructor: Constructor,
method: function () {},
};
// 原型污染风险
function unsafeExtend(target, source) {
for (let key in source) {
target[key] = source[key]; // 危险!
}
}
// 安全的扩展
function safeExtend(target, source) {
const safeKeys = Object.keys(source).filter(
(key) => !["__proto__", "constructor", "prototype"].includes(key),
);
safeKeys.forEach((key) => {
target[key] = source[key];
});
}
掌握原型链不仅帮助我们理解 JavaScript 的对象系统,更重要的是让我们能够写出更高效、更安全、更易维护的代码。
在现代 JavaScript 开发中,虽然有了 class 语法等新特性,但原型链仍然是底层的核心机制(面试高频考点),值得每一个开发者深入掌握!
Table of Contents
- 一、原型基础概念:理解 JavaScript 的继承之源
- 1. 什么是原型(Prototype)?
- 2. 原型链的工作机制
- 二、构造函数与原型:对象创建的经典模式
- 1. 构造函数的原型机制
- 2. 原型方法 vs 实例方法
- 三、原型继承的实现方式
- 1. 经典原型继承
- 2. 工厂函数模式
- 3. 组合继承的问题与寄生组合继承
- 常见继承模式优缺点
- 四、ES6+ 现代继承语法
- 1. Class 基础语法
- 2. Class 继承
- 3. Mixin 模式 - 多重继承的实现
- 五、原型链的高级应用
- 1. 动态原型修改
- 2. 原型代理和拦截
- 3. 原型污染防护
- 六、性能考虑与最佳实践
- 1. 原型查找的性能影响
- 2. 内存泄漏预防
- 七、调试和工具
- 1. 原型链检查工具
- 2. 实时监控原型修改
- 八、现代 JavaScript 的替代方案
- 1. 组合优于继承
- 2. WeakMap 私有属性
- 3. 函数式编程方法
- 九、总结与最佳实践
- 1. 核心要点回顾
- 2. 最佳实践总结
- 3. 常见陷阱避免