大家可以先仔细分析下该图,然后让我们进入主题

prototype

首先来介绍下prototype属性。这是一个显式原型属性,只有函数才拥有该属性。基本上所有函数都有这个属性,但是也有一个例外

letfun=Function.prototype.bind()

如果你以上述方法创建一个函数,那么可以发现这个函数是不具有prototype属性的。

prototype 如何产生的

当我们声明一个函数时,这个属性就被自动创建了。

functionFoo(){}

并且这个属性的值是一个对象(也就是原型),只有一个属性constructor

constructor对应着构造函数,也就是Foo

constructor

constructor是一个公有且不可枚举的属性。一旦我们改变了函数的prototype,那么新对象就没有这个属性了(当然可以通过原型链取到constructor)。

那么你肯定也有一个疑问,这个属性到底有什么用呢?其实这个属性可以说是一个历史遗留问题,在大部分情况下是没用的,在我的理解里,我认为他有两个作用:

让实例对象知道是什么函数构造了它

如果想给某些类库中的构造函数增加一些自定义的方法,就可以通过xx.constructor.method来扩展

_proto_

这是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。其实这个属性指向了 [[prototype]],但是 [[prototype]] 是内部属性,我们并不能访问到,所以使用_proto_来访问。

因为在 JS 中是没有类的概念的,为了实现类似继承的方式,通过_proto_将对象和原型联系起来组成原型链,得以让对象可以访问到不属于自己的属性。

实例对象的_proto_如何产生的

从上图可知,当我们使用new操作符时,生成的实例对象拥有了_proto_属性。

functionFoo(){}//这个函数是Function的实例对象//function就是一个语法糖//内部调用了newFunction(...)

所以可以说,在new的过程中,新对象被添加了_proto_并且链接到构造函数的原型上。

new 的过程

新生成了一个对象

链接到原型

绑定 this

返回新对象

在调用new的过程中会发生以上四件事情,我们也可以试着来自己实现一个new

functioncreate(){//创建一个空的对象letobj=newObject()//获得构造函数letCon=[].shift.call(arguments)//链接到原型obj.__proto__=Con.prototype//绑定this,执行构造函数letresult=Con.apply(obj,arguments)//确保new出来的是个对象returntypeofresult==='object'?result:obj}

对于实例对象来说,都是通过new产生的,无论是function Foo()还是let a = { b : 1 }

对于创建一个对象来说,更推荐使用字面量的方式创建对象。因为你使用new Object()的方式创建对象需要通过作用域链一层层找到Object,但是你使用字面量的方式就没这个问题。

functionFoo(){}//function就是个语法糖//内部等同于newFunction()leta={b:1}//这个字面量内部也是使用了newObject()Function.proto=== Function.prototype

对于对象来说,xx.__proto__.contrcutor是该对象的构造函数,但是在图中我们可以发现Function.__proto__ === Function.prototype,难道这代表着Function自己产生了自己?

答案肯定是否认的,要说明这个问题我们先从Object说起。

从图中我们可以发现,所有对象都可以通过原型链最终找到Object.prototype,虽然Object.prototype也是一个对象,但是这个对象却不是Object创造的,而是引擎自己创建了Object.prototype。所以可以这样说,所有实例都是对象,但是对象不一定都是实例。

接下来我们来看Function.prototype这个特殊的对象,如果你在浏览器将这个对象打印出来,会发现这个对象其实是一个函数。

我们知道函数都是通过new Function()生成的,难道Function.prototype也是通过new Function()产生的吗?答案也是否定的,这个函数也是引擎自己创建的。首先引擎创建了Object.prototype,然后创建了Function.prototype,并且通过__proto__将两者联系了起来。这里也很好的解释了上面的一个问题,为什么let fun = Function.prototype.bind()没有prototype属性。因为Function.prototype是引擎创建出来的对象,引擎认为不需要给这个对象添加prototype属性。

所以我们又可以得出一个结论,不是所有函数都是new Function()产生的。

有了Function.prototype以后才有了function Function(),然后其他的构造函数都是function Function()生成的。

现在可以来解释Function.__proto__ === Function.prototype这个问题了。因为先有的Function.prototype以后才有的function Function(),所以也就不存在鸡生蛋蛋生鸡的悖论问题了。对于为什么Function.__proto__会等于Function.prototype,个人的理解是:其他所有的构造函数都可以通过原型链找到Function.prototype,并且function Function()本质也是一个函数,为了不产生混乱就将function Function()__proto__联系到了Function.prototype上。

总结

Object是所有对象的爸爸,所有对象都可以通过__proto__找到它

Function是所有函数的爸爸,所有函数都可以通过__proto__找到它

Function.prototypeObject.prototype是两个特殊的对象,他们由引擎来创建

除了以上两个特殊对象,其他对象都是通过构造器new出来的

函数的prototype是一个对象,也就是原型

对象的__proto__指向原型,__proto__将对象和原型连接起来组成了原型链