JavaScript之原型链

251 阅读3分钟

原型链作为JavaScript的基础知识,重要性显而易见。最近在看Vue源码时看到了Vue的构造函数为什么是function实现而非class,解释如下:

我们往后看这里有很多 xxxMixin 的函数调用,并把 Vue 当参数传入,它们的功能都是给 Vue 的 prototype 上扩展一些方法(这里具体的细节会在之后的文章介绍,这里不展开),Vue 按功能把这些扩展分散到多个模块中去实现,而不是在一个模块里实现所有,这种方式是用 Class 难以实现的。这么做的好处是非常方便代码的维护和管理。

在开始本文的分析前,先抛出一些总结的知识点(常规版)

  1. JavaScript 中万物皆为对象,函数也是对象的一种;

  2. 对象有__proto__属性,函数有prototype属性;

  3. 对象由函数生成;

  4. 函数创建对象时,对象__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函数的prototypefunction类型,这个比较特殊

虽然是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();