原型链是JavaScript实现继承和属性查找的机制,通过对象的[[Prototype]]链接形成链条,当访问属性时会沿链向上查找直至找到或到达null。原型(prototype)是函数特有的属性,指向实例共享方法的原型对象;原型链则是由__proto__连接构成的查找路径,二者共同实现对象间的方法共享与继承。利用原型链可优化内存、实现继承并提升性能,ES6的class本质仍是基于原型链的语法糖,提供更清晰的继承写法但底层机制不变。

JavaScript中的原型链,简单来说,就是一套对象间继承属性和方法的机制。当你试图访问一个对象的某个属性或方法时,如果它本身没有,JavaScript引擎就会沿着一条由
__proto__
[[Prototype]]
null
理解原型链,我觉得就像理解一棵家族树,但不是从上往下看,而是从你自身往上追溯你的祖先。每个对象都有一个“父母”对象(它的原型),如果父母没有,就去找爷爷奶奶(父母的原型),以此类推,直到找到“始祖”(
Object.prototype
要深入理解原型链,我们得从几个核心概念入手。首先是每个JavaScript对象内部都有一个
[[Prototype]]
__proto__
prototype
prototype
[[Prototype]]
我们通常创建对象的方式,其实都在默默地利用原型链。
立即学习“Java免费学习笔记(深入)”;
1. 对象字面量: 当你写
{}new Object()
Object.prototype
const myObj = {};
console.log(myObj.__proto__ === Object.prototype); // true
console.log(myObj.toString()); // "[object Object]" - 这个方法就来自Object.prototypemyObj
toString
myObj.__proto__
Object.prototype
toString
2. 构造函数: 这是JavaScript实现“类”或“蓝图”的传统方式。
function Person(name) {
this.name = name;
}
// 在Person.prototype上添加方法,所有Person实例共享
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person('Alice');
console.log(alice.name); // 'Alice'
alice.sayHello(); // 'Hello, my name is Alice'
// 关键来了:alice的原型是Person.prototype
console.log(alice.__proto__ === Person.prototype); // true
// Person.prototype的原型是Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype); // true
// Object.prototype的原型是null,链的终点
console.log(Object.prototype.__proto__ === null); // true当
alice.sayHello()
alice
sayHello
alice.__proto__
Person.prototype
sayHello
3. Object.create()
const animal = {
eats: true,
walk() {
console.log("Animal walks.");
}
};
const rabbit = Object.create(animal); // rabbit的原型是animal
rabbit.jumps = true;
rabbit.walk(); // "Animal walks."
console.log(rabbit.__proto__ === animal); // true
console.log(animal.__proto__ === Object.prototype); // truerabbit
walk
animal
walk
原型链的魔力在于它实现了属性和方法的共享,这意味着内存效率更高,因为方法只存储在一个地方(原型对象上),所有实例都通过原型链去访问它。同时,它也是JavaScript实现“继承”的基石。
这确实是初学者最容易混淆的地方,也是我当年踩过不少坑的地方。要我说,它们俩是紧密相连但又有着明确分工的两个概念。
原型(prototype
prototype
new MyFunction()
[[Prototype]]
__proto__
MyFunction.prototype
举个例子,
Array.prototype
Array
[1, 2, 3]
__proto__
Array.prototype
Array.prototype
push
pop
原型链(prototype chain
[[Prototype]]
__proto__
[[Prototype]]
[[Prototype]]
null
Object.prototype
[[Prototype]]
所以,关联在于:原型(prototype
__proto__
区别在于:
prototype
__proto__
[[Prototype]]
你可以把
prototype
原型链在实际开发中的应用远不止于理论,它确实能帮助我们写出更优雅、更高效的代码。我个人觉得,最直接和最常见的应用场景就是共享方法和属性,这直接关系到内存和性能。
1. 共享方法,减少内存开销: 这是原型链最核心的优势之一。想象一下,如果你有1000个
User
greet()
function UserBad(name) {
this.name = name;
this.greet = function() { // 每个实例都会有自己的greet方法副本
console.log(`Hi, I'm ${this.name}`);
};
}
const user1 = new UserBad('Alice');
const user2 = new UserBad('Bob');
// user1.greet !== user2.greet // 它们是不同的函数对象这样,每个
UserBad
greet
而利用原型链,我们可以将方法定义在构造函数的
prototype
function UserGood(name) {
this.name = name;
}
UserGood.prototype.greet = function() { // 所有实例共享同一个greet方法
console.log(`Hi, I'm ${this.name}`);
};
const userA = new UserGood('Charlie');
const userB = new UserGood('David');
// userA.greet === userB.greet // 它们引用的是同一个函数对象这样,
greet
UserGood.prototype
UserGood
2. 实现继承: 虽然ES6有了
class
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} eats.`);
};
function Dog(name, breed) {
Animal.call(this, name); // 继承Animal的属性
this.breed = breed;
}
// 核心:让Dog的原型继承Animal的原型
// 避免直接赋值Animal.prototype,那样会覆盖Dog.prototype,导致所有Dog实例的原型都指向Animal.prototype
// Object.create() 是一个很好的方式来创建一个新对象,并指定其原型
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复constructor指向
Dog.prototype.bark = function() {
console.log(`${this.name} barks!`);
};
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.eat(); // Buddy eats. (来自Animal.prototype)
myDog.bark(); // Buddy barks! (来自Dog.prototype)这里,
myDog
myDog
Dog.prototype
Animal.prototype
Object.prototype
null
Dog
animal
3. 扩展内置对象(需谨慎): 虽然不推荐大规模修改内置对象的原型(因为它可能导致命名冲突和难以调试的问题),但在某些特定且受控的环境下,为内置对象添加一些辅助方法确实能提高开发效率。
// 示例:给Array.prototype添加一个求和方法
// 实际项目中应避免直接修改内置原型,除非你完全控制环境
if (!Array.prototype.sum) { // 检查是否已存在,避免重复定义
Array.prototype.sum = function() {
return this.reduce((acc, current) => acc + current, 0);
};
}
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.sum()); // 15这样,所有数组实例都能直接调用
sum()
通过合理利用原型链,我们能够构建出模块化、可维护且高效的JavaScript代码,这对于大型应用尤其重要。
class
ES6引入的
class
相同点:
基于原型链的继承:
class
extends
class Dog extends Animal {}Dog.prototype
[[Prototype]]
Animal.prototype
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} eats.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类的构造函数
this.breed = breed;
}
bark() {
console.log(`${this.name} barks!`);
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.eat(); // Buddy eats.
myDog.bark(); // Buddy barks!
// 底层原理:
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(myDog.__proto__ === Dog.prototype); // true可以看到,
Dog.prototype
Animal.prototype
方法共享: 在
class
prototype
Function.prototype
constructor
class
constructor
this
不同点(主要是语法和一些细节行为):
语法更清晰: 这是最显而易见的区别。
class
prototype
Object.create()
super
super()
this
super()
super
class
Parent.call(this, ...)
Parent.prototype.method.call(this, ...)
class
class
class MyClass {}
console.log(typeof MyClass); // "function"不过,
class
new
new
this
class
new
arguments
class
arguments
class
"use strict"
静态方法和属性:
class
static
class Utils {
static add(a, b) {
return a + b;
}
}
console.log(Utils.add(1, 2)); // 3
// const util = new Utils();
// util.add(1, 2); // TypeError: util.add is not a function在传统原型链中,静态方法通常是直接作为构造函数的属性来添加的,比如
Function.staticMethod = ...
私有字段(提案中或已实现): 现代JavaScript的
class
#privateField
Symbol
总结来说,
class
class
以上就是如何理解JavaScript中的原型链?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号