从认祖宗开始,理解原型链

148 阅读3分钟

prototype和__proto__

先简单理解两个东西的作用,我们可以直接作为结论来用:

  • prototype(显式原型):指向函数的原型对象,只有函数才会拥有的属性
  • __proto__(隐式原型):指向其构造函数的prototype,函数和对象都有的属性,因为函数也是对象的一种。

Object和Function

开局先认俩祖宗:

  • Object(对象的祖宗)
  • Function(函数的祖宗)

即作为对象,其源头最终都会指向Object;函数的源头则一定是Function构造而来的。

ObjectFunctionMap这种,其本质都是一个函数(构造函数)对象,例如typeof Object 是 function)

对于Object,由于它是对象的祖宗,所以Object.prototype原型对象上已经没有构造函数了,即没有 __proto__了:

Object.prototype.__proto__ === null  //true

但同时Object作为函数对象也需要有东西构造出来,即其Object.__proto__需要指向其构造函数,而函数的祖宗是Function,所以有:

Object.__proto__ === Function.prototype //true

再说Function,他的原型对象继承自对象的祖宗也就是Object,所以有:

Function.prototype.__proto__ === Object.prototype //true

又因为它本身也是函数对象,其构造函数,作为函数的祖宗,它选择自己造自己:

Function.__proto__ === Function.prototype //true

一般对象和函数对象

我们随便写一个对象:const obj = {}那么此时,作为一个对象其只有__propto__属性,并且我们知道对象的祖宗是Object,所以有:

obj.__proto__ === Object.prototype //true

但有的时候,我们的对象并不是直接继承自Object的,例如:const map = new Map(),那么这时:

map.__proto__ === Map.prototype //true

这个结论我们之后会继续论证。

我们再随便写一个函数对象: const fn = function() {}那么此时,他的__proto__构造函数会指向函数的祖宗,所以有:

fn.__proto__ === Function.prototype //true

同时作为一个函数,他有了prototype属性,指向他自己的原型对象,这个原型对象,作为对象,其__proto__会指向对象的祖宗:

fn.prototype.__proto__ === Object.prototype //true

Map, Number, Error等内置构造函数

作为一个函数,其__proto__指向其构造函数的prototype,也就是函数的祖宗Function

Map.__proto__ === Function.prototype //true

同时其原型对象Map.prototype__proto__属性指向对象的祖宗Object

Map.prototype.__proto__ === Object.prototype //true

与普通函数不同的是,构造函数可以构造出新的对象,并让对象继承自他们,生成原型链。(所谓原型链,拿Map举例,就是让继承自Map 的对象拥有Map原型对象和它继承的原型对象上的属性和方法)

例如,我们可以const map = new Map()生成一个map对象,此时这个对象作为对象,其祖宗还是Object,但是是间接继承自Object,直接继承自Map,所以有:

map.__proto__ === Map.prototype //true

通过对不同情况的极简论述,相信各位对prototype__proto__已经有了一些理解;当然,文中还有很多细节没有补充,例如bind方法会让返回的函数失去prototype属性等,希望大家可以在实践中多多探索😋