原型链(Prototype Chain)概述
在 JavaScript 中,原型链是用于实现对象继承的机制。每个对象都有一个内部属性 [[Prototype]]
,它指向该对象的原型(在大多数现代 JavaScript 引擎中,通常通过 __proto__
来访问)。原型链是由多个对象通过它们的原型链接在一起的链式结构,最终链的尽头指向 null
。
关键概念
-
__proto__
和prototype
的区别__proto__
是指向当前对象的原型对象的内部属性。prototype
是构造函数对象的属性,指向新实例的原型对象。
-
原型链的结构
- 每个对象都有一个原型对象,原型对象本身也是一个对象,这个原型对象可能会有它自己的原型,这样就形成了一个链条,直到原型链的末端(通常是
Object.prototype
)。 - 通过原型链,JavaScript 对象可以继承其他对象的属性和方法。
- 每个对象都有一个原型对象,原型对象本身也是一个对象,这个原型对象可能会有它自己的原型,这样就形成了一个链条,直到原型链的末端(通常是
详细解析
-
对象的
[[Prototype]]
属性(原型)每个 JavaScript 对象都有一个隐式的内部属性
[[Prototype]]
,它指向该对象的原型对象。你可以通过__proto__
来访问该属性。比如:jsconst obj = {}; console.log(obj.__proto__); // 输出 Object.prototype
上述代码中的
obj
对象的原型是Object.prototype
,它是所有普通对象的根原型。 -
prototype
属性
prototype
是函数对象的一个特殊属性,它指向函数的原型对象。当你通过一个构造函数创建实例时,实例对象的[[Prototype]]
会指向该构造函数的prototype
属性。例如:jsfunction Person(name) { this.name = name; } const john = new Person('John'); console.log(john.__proto__ === Person.prototype); // 输出 true
在这个例子中,
john
对象的[[Prototype]]
指向Person.prototype
,即Person
构造函数的prototype
属性。 -
如何通过原型链访问属性和方法
当你访问一个对象的属性时,JavaScript 会首先检查该对象自身是否有这个属性。如果没有,它会沿着原型链向上查找,直到找到该属性,或者到达原型链的末端(即
null
)。例如:
jsconst person = { name: 'Alice', greet() { console.log('Hello, ' + this.name); } }; const employee = Object.create(person); employee.job = 'Developer'; employee.greet(); // 输出 "Hello, Alice" console.log(employee.job); // 输出 "Developer"
在这个例子中,
employee
对象没有自己的greet
方法,但它通过原型链继承了person
对象的greet
方法。 -
原型链的查找机制
JavaScript 对象的查找机制如下:
- 当访问一个对象的属性时,JavaScript 会首先检查该对象本身是否包含该属性。
- 如果对象本身没有该属性,JavaScript 会检查对象的原型(即
__proto__
)。 - 如果原型对象也没有该属性,查找会继续沿着原型链向上,直到达到
null
,也就是Object.prototype
,此时如果还没有找到该属性,就会返回undefined
。
例如:
jsconst animal = { species: 'Animal', speak() { console.log('Animal makes a sound'); } }; const dog = Object.create(animal); dog.breed = 'Golden Retriever'; console.log(dog.species); // 输出 'Animal' 通过原型链继承 dog.speak(); // 输出 'Animal makes a sound' 通过原型链继承 console.log(dog.breed); // 输出 'Golden Retriever' dog 对象自身的属性
在上面的例子中,
dog
对象有一个breed
属性,但它没有species
和speak
属性。因此,它会查找animal
对象的原型,找到了species
和speak
。
原型链的终点
原型链的终点通常是 Object.prototype
,这是所有对象的根原型。Object.prototype
的 [[Prototype]]
是 null
,它是原型链的最后一个对象。
js
const obj = {};
console.log(obj.__proto__); // 输出 Object.prototype
console.log(obj.__proto__.__proto__); // 输出 null
在 JavaScript 中,__proto__
和 prototype
是两个密切相关但作用不同的概念。它们都是用来描述对象之间继承关系的,但它们分别处于不同的层次和使用场景。理解它们的区别有助于更好地掌握 JavaScript 中的原型链和继承机制。
prototype
和 __proto__
的区别
1. prototype
是构造函数的属性
- 作用 :
prototype
是每个构造函数(函数对象)上的一个属性,它指向该构造函数的原型对象。这个原型对象上通常会定义一些方法或属性,所有通过该构造函数创建的实例都会继承这些方法或属性。 - 位置 :
prototype
只存在于构造函数上,而不是实例对象上。
例子:
js
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log("Hello, " + this.name);
};
const p1 = new Person("Alice");
p1.sayHello(); // "Hello, Alice"
- 在上面的代码中,
Person.prototype
是一个包含sayHello
方法的对象,而p1
这个实例通过其原型链访问到了sayHello
方法。
2. __proto__
是实例对象的属性
- 作用 :
__proto__
是每个实例对象(由构造函数创建的对象)上的一个属性,它指向该对象的构造函数的prototype
。也就是说,__proto__
是对象的原型链的一部分,指向对象原型的"父"对象(即构造函数的prototype
)。 - 位置 :
__proto__
存在于对象实例上,用于表示实例与其构造函数的原型对象之间的关系。
例子:
js
function Person(name) {
this.name = name;
}
const p1 = new Person("Alice");
console.log(p1.__proto__ === Person.prototype); // true
- 在这个例子中,
p1.__proto__
指向Person.prototype
,即p1
实例继承自Person.prototype
。
关键区别总结
-
prototype
是构造函数的属性:- 每个构造函数(如
Person
)都会有一个prototype
属性,这个属性指向构造函数的原型对象。该原型对象上可以定义方法和属性,所有通过该构造函数创建的实例会继承这些方法和属性。 - 例如,
Person.prototype.sayHello
就是Person
的所有实例共享的方法。
- 每个构造函数(如
-
__proto__
是实例对象的属性:- 每个通过构造函数创建的实例对象(如
p1
)都会有一个__proto__
属性,这个属性指向实例的构造函数的prototype
。也就是说,p1.__proto__
指向Person.prototype
。 __proto__
表示对象的原型链关系,它是实例对象访问其构造函数原型对象的桥梁。
- 每个通过构造函数创建的实例对象(如
更深一步:原型链
-
当你访问一个对象的属性或方法时,JavaScript 会先检查对象本身是否有这个属性。如果没有,它会沿着对象的
__proto__
链查找,直到找到该属性或到达Object.prototype
为止。 -
对象实例通过
__proto__
链接到构造函数的prototype
,而构造函数的prototype
又指向Object.prototype
,从而形成了一个原型链。
js
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log("Hello, " + this.name);
};
const p1 = new Person("Alice");
console.log(p1.__proto__ === Person.prototype); // true
console.log(p1.__proto__.__proto__ === Object.prototype); // true
- 在这个例子中:
p1.__proto__
指向Person.prototype
。Person.prototype.__proto__
指向Object.prototype
。- 如果在
p1
上找不到sayHello
方法,JavaScript 会继续沿着原型链查找,直到Object.prototype
。
prototype
与 __proto__
之间的联系
prototype
是构造函数上用来设置实例对象原型的属性。__proto__
是实例对象用来访问构造函数原型的属性。
prototype
和 __proto__
共同组成了原型链(prototype chain)。实例对象通过 __proto__
链接到构造函数的 prototype
,而构造函数的 prototype
上定义的方法和属性被实例继承。
小结
总结
- 原型链 是 JavaScript 对象继承和属性查找的机制,允许一个对象通过其原型继承另一个对象的属性和方法。
__proto__
是对象的原型对象,指向该对象的父对象。prototype
是构造函数的属性,指向通过该构造函数创建的实例对象的原型。- 查找一个对象的属性时,JavaScript 会沿着原型链向上查找,直到找到该属性或者原型链的终点
null
。 prototype
是 构造函数的属性,定义了所有由该构造函数创建的实例共享的属性和方法。__proto__
是 实例对象的属性 ,指向构造函数的prototype
,通过它可以访问和查找实例对象的原型链上的属性和方法。
原型链是 JavaScript 中非常重要的特性,它使得继承、方法共享、属性访问等成为可能。