prototype和__proto__
先简单理解两个东西的作用,我们可以直接作为结论来用:
prototype(显式原型):指向函数的原型对象,只有函数才会拥有的属性__proto__(隐式原型):指向其构造函数的prototype,函数和对象都有的属性,因为函数也是对象的一种。
Object和Function
开局先认俩祖宗:
- Object(对象的祖宗)
- Function(函数的祖宗)
即作为对象,其源头最终都会指向Object;函数的源头则一定是Function构造而来的。
(Object,Function,Map这种,其本质都是一个函数(构造函数)对象,例如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属性等,希望大家可以在实践中多多探索😋