本文同步分布在个人 Github 上git
本文不会过多介绍基础知识,而是把重点放在原型的各个难点上。github
你们能够先仔细分析下该图,而后让咱们进入主题浏览器
prototype
首先来介绍下 prototype
属性。这是一个显式原型属性,只有函数才拥有该属性。基本上全部函数都有这个属性,可是也有一个例外markdown
let fun = Function.prototype.bind() 复制代码
若是你以上述方法建立一个函数,那么能够发现这个函数是不具备 prototype
属性的。app
当咱们声明一个函数时,这个属性就被自动建立了。函数
function Foo() {} 复制代码
而且这个属性的值是一个对象(也就是原型),只有一个属性 constructor
oop
constructor
对应着构造函数,也就是 Foo
。this
constructor
是一个公有且不可枚举的属性。一旦咱们改变了函数的 prototype
,那么新对象就没有这个属性了(固然能够经过原型链取到 constructor
)。spa
那么你确定也有一个疑问,这个属性到底有什么用呢?其实这个属性能够说是一个历史遗留问题,在大部分状况下是没用的,在个人理解里,我认为他有两个做用:prototype
xx.constructor.method
来扩展_proto_
这是每一个对象都有的隐式原型属性,指向了建立该对象的构造函数的原型。其实这个属性指向了 [[prototype]],可是 [[prototype]] 是内部属性,咱们并不能访问到,因此使用 _proto_
来访问。
由于在 JS 中是没有类的概念的,为了实现相似继承的方式,经过 _proto_
将对象和原型联系起来组成原型链,得以让对象能够访问到不属于本身的属性。
_proto_
如何产生的从上图可知,当咱们使用 new
操做符时,生成的实例对象拥有了 _proto_
属性。
function Foo() {} // 这个函数是 Function 的实例对象 // function 就是一个语法糖 // 内部调用了 new Function(...) 复制代码
因此能够说,在 new
的过程当中,新对象被添加了 _proto_
而且连接到构造函数的原型上。
在调用 new
的过程当中会发生以上四件事情,咱们也能够试着来本身实现一个 new
function create() { // 建立一个空的对象 let obj = new Object() // 得到构造函数 let Con = [].shift.call(arguments) // 连接到原型 obj.__proto__ = Con.prototype // 绑定 this,执行构造函数 let result = Con.apply(obj, arguments) // 确保 new 出来的是个对象 return typeof result === 'object' ? result : obj } 复制代码
对于实例对象来讲,都是经过 new
产生的,不管是 function Foo()
仍是 let a = { b : 1 }
。
对于建立一个对象来讲,更推荐使用字面量的方式建立对象。由于你使用 new Object()
的方式建立对象须要经过做用域链一层层找到 Object
,可是你使用字面量的方式就没这个问题。
function Foo() {} // function 就是个语法糖 // 内部等同于 new Function() let a = { b: 1 } // 这个字面量内部也是使用了 new Object() 复制代码
对于对象来讲,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.prototype
和 Object.prototype
是两个特殊的对象,他们由引擎来建立new
出来的prototype
是一个对象,也就是原型__proto__
指向原型, __proto__
将对象和原型链接起来组成了原型链