原型链作为JavaScript的基础知识,重要性显而易见。最近在看Vue源码时看到了Vue的构造函数为什么是
function实现而非class,解释如下:我们往后看这里有很多 xxxMixin 的函数调用,并把 Vue 当参数传入,它们的功能都是给 Vue 的 prototype 上扩展一些方法(这里具体的细节会在之后的文章介绍,这里不展开),Vue 按功能把这些扩展分散到多个模块中去实现,而不是在一个模块里实现所有,这种方式是用 Class 难以实现的。这么做的好处是非常方便代码的维护和管理。
在开始本文的分析前,先抛出一些总结的知识点(常规版)
-
JavaScript 中万物皆为对象,函数也是对象的一种;
-
对象有
__proto__属性,函数有prototype属性; -
对象由函数生成;
-
函数创建对象时,对象的
__proto__属性指向该函数的prototype属性;
初始化一段代码,用于下文的分析
class Animal() {}
var cat = new Animal()
var dog = new Animal()
cat.name = "miaomiao"
console.log(cat.name) // miaomiao
dog.name = "wangwang"
console.log(dog.name) // wangwang
梳理一些概念
下面开始逐步分解
普通对象
对象有__proto__属性,该属性指向创建该对象的函数原型。
cat.__proto__ === Animal.prototype
dog.__proto__ === Animal.prototype
cat.__proto__ === dog.__proto__
函数对象
因为函数也是对象的一种,最终会由Function函数创建,所以函数既有__proto__属性也有prototype属性。
__proto__属性根据上面的规则会指向创建该对象的函数原型
prototype在函数创建时自动生成,prototype有两个属性__proto__和constructor;
constructor属性指向了该函数自身。
一般函数对象
一般函数可以理解为function关键字声明的函数,__proto__指向了Function.prototype,默认的prototype是一个类型为"object"的对象,由Object创建。所以prototype属性的__proto__属性指向Object.prototype;
使用ES6 extends 关键字创建的函数
class Animal() {}
class Monkey extends Animal {}
子类的__proto__属性表示构造函数的继承,指向父类, 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性;
Function函数
typeof Function.prototype; // "function"
Function函数的prototype是function类型,这个比较特殊
虽然是function类型,但是依然有这两个属性:__proto__和constructor;
在控制台可以看到:
Function.__proto__ === Function.prototype // true
Function.prototype.__proto__ === Object.prototype // true
由此可见,Function函数由自身生成。Function.prototype__proto__却指向了Object.prototype
是不是快要明朗了,深不可测的原型链渐渐浮出了水面,下面看Object函数;
Object函数
本着函数由Function创建的原则,大胆的猜测一下Object函数由Function函数生成;
Object.__proto__ === Function.prototype // true
果然没错。那问题来了, Object.prototype的上游是什么
console.log(Object.prototype.__proto__) // null
重点终于找到了,拨开云雾见青天,原来Object.prototype就是JS原型链的终点!
再看一下实例和原型之间有什么直接的关系
function Animal() {}
const cat = new Animal()
cat.name = "miaomiao"
Animal.prototype.name = "animal"
console.log(cat.name) // miaomiao
delete cat.name
console.log(cat.name) // animal
当读取实例的属性时,如果找不到,就会查找该对象关联的原型中的属性,如果查不到,就去找原型的原型,逐级往上寻找。
总结
知道了上面的一些规则,原型链基本也就差不多了,附上demo代码以及原型链关系图
class Animal() {}
const cat = new Animal()
const dog = new Animal()
class Monkey extends Animal {}
const monkey = new Monkey();