为什么要有原型?

首先来看一段简短的代码

1
2
var obj = {}
obj.toString()

这段代码在浏览器中为什么不报错?

简单解释一下,obj有一个隐藏的属性,隐藏属性存储了Object.prototype对象的地址,输入obj.toString()时发现obj上没有toString的地址,于是就去隐藏属性里面找,最终找到了Object.prototype.toString.

再来看一段代码

1
2
var obj2 = {}
obj2.toString()

objobj2有什么联系?

相同点是他们都可以调用.toString()。不同点是他们的地址不同,可以拥有不同的属性。反过来说就是

**XXX.prototype存储了XXX对象的共同属性。而这就是原型。**

如果没有原型声明一个对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var obj = {
toString: window.Object.prototype.toString,
hasOwnPropertyOf: window.Object.......
}
obj.toString()
var obj2 = {
toString: window.Object.prototype.toString,
hasOwnPropertyOf: window.Object.......
}
obj2.toString()

这显然是不现实的。

原型让你无需重复声明共有属性。省代码,省内存。

每个对象都有一个隐藏属性指向原型,这个隐藏属性就叫__proto__

prototype__proto__区别是什么?

它们都保存着原型的地址,但是prototype挂在函数上,__proto__挂在每个新生成的对象上。

原型三大最重要知识

众所周知,JS有三座大山,它们分别是原型、this以及AJAX。这三个知识点是JS的门槛,理解起来需要时间。不理解是正常的,否则JS就真的毫无门槛了。怎么翻过这三座大山呢?反复学反复理解。

要翻过原型这座大山,有三个最重要的知识必须掌握。

一.原型公式

**对象.__proto__ === 其构造函数.prototyoe**

遇到这类型的题目可以直接用套公式来解决。

二.原型根公理

Object.prototype 是所有对象的(直接或间接)原型

三.函数构造公理

所有函数都是由Function构造的,即任何函数.__proto__===Function.prototype ,任意函数有Object/Array/Function

基于这三个知识和基础知识,我们可以推出整个JS世界。

JS世界的构造顺序

  1. 创建根对象#001(toString),根对象没有名字。
  2. 创建函数的原型#200(call/apply),原型__proto__#001
  3. 创建数组的原型#300(push/pop),原型__proto__#001
  4. 创建Function#301,原型__proto__#200
  5. Function.prototype存储函数的原型,令其等于#200
  6. 此时发现Function__proto__prototype都是#200
  7. Funciton创建Object函数
  8. Object.prototype存储对象的原型,令其等于#001
  9. Function创建Array函数
  10. Array.prototype存储数组对象的原型,令其等于#200
  11. 创建window函数
  12. window中的'Object''Array'属性将7和9中的函数命名

记住一点,JS创建一个对象时,是不会给这个对象名字的。

原型与构造函数

所谓构造函数就是用来构造对象的,会预先保存好对象的原型,new的时候将对象__p指向原型。

所有对象都直接或间接指向根对象,对象想要分类时,就可以在原型链上加以换,用构造函数就可以加这一环。

例子:

  1. 自己定义构造函数X,函数里给this加属性
  2. X自动创建prototype属性和对应的对象#502
  3. 在X.prototype#502上面加属性
  4. 用new X()创建对象x
  5. new会将x的原__proto__设为#502

原型与原型链终极图示