原型链
原型链是一个固有的概念,因此应该先理解一些基础的概念,这些基础的概念可能是没有逻辑的,但是也要记住。在此基础之上在深入,会事半功倍。
1. 基础
首先写一个函数,接着我们new 这个函数。 会得到一个实例对象。
function Person(name) {
}
var person1 = new Person();
这个实例对象究竟是什么呢?
我们可以看见这个对象是空的,有一个[[Prototype]]属性,其实在大多数浏览器中我们都可以通过访问 __ proto __ 属性去访问这个[[Prototype]],因此本文的__ proto __ 其实就是[[Prototype]]。里面有constructor和又一个__ proto __属性,接着我们会逐一介绍。
2. prototype
在说__ proto __和constructor之前,prototype也是一个绕不开的内容。
必须要注意的一个点: prototype是一个属性,这个属性只有函数才会有。
举一个例子:
function Person(name) {
}
Person.prototype.name = 'kevin';
const person1 = new Person();
const person2 = new Person();
console.log(person1.name); // kevin
console.log(person2.name); // kevin
函数的prototype指向的是一个对象,这个对象就是该函数通过new创建出来的实例对象的原型。 请务必记住:这是一个概念。 不需要去如何理解,深究为什么,记住即可。
原型究竟是什么呢? 我的理解就是:每一个js的对象都会有一个所谓的原型,这个原型也是一个对象,而每个js的对象都会从原型中"继承"原型的属性。 用通俗的话就是,原型就是一个对象,这个对象的属性,都会继承下来。
3. __ proto __
就如前文的一张图中可以看出,new出来的新对象是含有 __ proto __ 属性的,可以说,任何js对象都有__ proto__ 属性,这个属性指向的就是它的原型。
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
4. constructor
每个原型都有一个函数可以指向构造函数
function Person() {
}
var person = new Person();
console.log(Person === Person.prototype.constructor); // true
5. 实例与原型
在读取实例上的某个属性,如果实例上并没有定义,就会去朝着实例的原型去找是否有这个属性。如果还找不到就去找原型的原型,一直找到原型链的顶层。
function Person() {
}
const person1 = new Person();
Person.prototype.name = 'Jiang'
person1.name = 'William'
const person2 = new Person();
console.log(person1.name); // William
console.log(person2.name); // Jiang 实例上并没有定义,就去找实例的原型上是否有该属性
6. 原型的原型
原型的原型究竟是一个什么呢?之前已经说过了,其实原型的本质上就是一个对象,例如Person.prototype 其实就是个对象,而对象的构造函数就是Object。之前也说了任何对象都有__ proto __这个值,因此Person.prototype的原型就是Object.prototype。
function Person() {
}
console.log(Person.prototype.__proto__ === Object.prototype) // true
虽然第一次看上去非常的别扭,但是这个实际上是可以根据之前的概念推理出来的。
Object是一个构造函数,构造出了Person.prototype这个对象。那么Person.prototype这个对象的原型指向构造函数的原型不就顺理成章了吗。
7. Object.prototype的原型
Object.prototype的原型是null。 意思就是Object.prototype不应该有原型。
因此下面这张表也就没有任何的理解障碍了。