JavaScript 是基于原型的语言,理解原型链是掌握 JavaScript 继承机制的关键。本文将带你深入理解原型链的工作原理和现代类语法。
1. 什么是原型?
每个 JavaScript 对象都有一个内部属性 [[Prototype]](可通过 __proto__ 或 Object.getPrototypeOf() 访问),指向另一个对象,这个对象就是它的原型。
const obj = { name: "皮皮" };
const proto = Object.getPrototypeOf(obj);
console.log(proto === Object.prototype); // true
2. 原型链的工作原理
当访问对象的属性时,JavaScript 会:
- 首先在对象本身查找
- 如果找不到,沿着原型链向上查找
- 直到找到该属性或到达原型链末端(null)
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
return `Hello, I'm ${this.name}`;
};
const p = new Person("Tom");
console.log(p.name); // "Tom" - 实例属性
console.log(p.sayHello()); // "Hello, I'm Tom" - 原型方法
3. 构造函数与 new
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.introduce = function() {
return `${this.name}, ${this.age} years old`;
};
const p1 = new Person("Tom", 25);
const p2 = new Person("Jerry", 30);
console.log(p1.constructor === Person); // true
console.log(p1.__proto__ === Person.prototype); // true
4. ES6 Class 语法
ES6 引入了 class 语法,让继承更加直观:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
return `${this.name}, ${this.age} years old`;
}
}
class Developer extends Person {
constructor(name, age, language) {
super(name, age);
this.language = language;
}
code() {
return `${this.name} codes in ${this.language}`;
}
}
const dev = new Developer("Tom", 25, "JavaScript");
console.log(dev.introduce()); // "Tom, 25 years old"
console.log(dev.code()); // "Tom codes in JavaScript"
5. 原型链图示
dev (实例)
↓ __proto__
Developer.prototype
↓ __proto__
Person.prototype
↓ __proto__
Object.prototype
↓ __proto__
null (原型链终点)
6. 继承的多种方式
原型链继承
function Parent() {
this.colors = ["red", "blue"];
}
function Child() {}
Child.prototype = new Parent();
const c1 = new Child();
const c2 = new Child();
c1.colors.push("green");
console.log(c2.colors); // ["red", "blue", "green"] - 引用共享!
构造函数继承
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name); // 调用父类构造函数
this.age = age;
}
组合继承(最常用)
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 继承属性
this.age = age;
}
Child.prototype = new Parent(); // 继承方法
Child.prototype.constructor = Child;
现代建议: 优先使用 ES6 class 语法,代码更清晰,底层仍然是原型继承。
7. 实用技巧
判断继承关系
const obj = new Person("Tom");
console.log(obj instanceof Person); // true
console.log(obj instanceof Object); // true
console.log(Person.prototype.isPrototypeOf(obj)); // true
检查自有属性
const obj = { name: "Tom" };
console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("toString")); // false - 来自原型
总结
原型链是 JavaScript 继承的核心机制。理解原型链不仅有助于掌握传统继承模式,也能更好地理解 ES6 class 的本质。无论是使用构造函数还是 class 语法,底层都是原型继承。