原型链(Prototype Chain)概述
在 JavaScript 中,原型链是用于实现对象继承的机制。每个对象都有一个内部属性 [[Prototype]],它指向该对象的原型(在大多数现代 JavaScript 引擎中,通常通过 __proto__ 来访问)。原型链是由多个对象通过它们的原型链接在一起的链式结构,最终链的尽头指向 null。
关键概念
-
__proto__和prototype的区别__proto__是指向当前对象的原型对象的内部属性。prototype是构造函数对象的属性,指向新实例的原型对象。
-
原型链的结构
- 每个对象都有一个原型对象,原型对象本身也是一个对象,这个原型对象可能会有它自己的原型,这样就形成了一个链条,直到原型链的末端(通常是
Object.prototype)。 - 通过原型链,JavaScript 对象可以继承其他对象的属性和方法。
- 每个对象都有一个原型对象,原型对象本身也是一个对象,这个原型对象可能会有它自己的原型,这样就形成了一个链条,直到原型链的末端(通常是
详细解析
-
对象的
[[Prototype]]属性(原型)
每个 JavaScript 对象都有一个隐式的内部属性[[Prototype]],它指向该对象的原型对象。你可以通过__proto__来访问该属性。比如:const obj = {}; console.log(obj.__proto__); // 输出 Object.prototype上述代码中的
obj对象的原型是Object.prototype,它是所有普通对象的根原型。 -
prototype属性
prototype是函数对象的一个特殊属性,它指向函数的原型对象。当你通过一个构造函数创建实例时,实例对象的[[Prototype]]会指向该构造函数的prototype属性。例如:function 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)。例如:
const 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。
例如:
const 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,它是原型链的最后一个对象。
const obj = {};
console.log(obj.__proto__); // 输出 Object.prototype
console.log(obj.__proto__.__proto__); // 输出 null
在 JavaScript 中,__proto__ 和 prototype 是两个密切相关但作用不同的概念。它们都是用来描述对象之间继承关系的,但它们分别处于不同的层次和使用场景。理解它们的区别有助于更好地掌握 JavaScript 中的原型链和继承机制。
prototype 和 __proto__ 的区别
1. prototype 是构造函数的属性
- 作用:
prototype是每个构造函数(函数对象)上的一个属性,它指向该构造函数的原型对象。这个原型对象上通常会定义一些方法或属性,所有通过该构造函数创建的实例都会继承这些方法或属性。 - 位置:
prototype只存在于构造函数上,而不是实例对象上。
例子:
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__存在于对象实例上,用于表示实例与其构造函数的原型对象之间的关系。
例子:
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,从而形成了一个原型链。
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 中非常重要的特性,它使得继承、方法共享、属性访问等成为可能。
