1.原型
2.每个函数对象都有原型对象
3.属性搜索原则
4.顶级函数对象Object唯一原型
5.原型链
6.原型链达到假继承,函数对象原型链
7.替换原型对象
8.__proto__
的兼容性
1.原型
在构造函数中直接定义方法,在使用该函数创建对象时就会重复开辟方法所占据的空间,因为他们直接隶属于对象,但是对象不是单例的,以A构造函数为例,A构造函数中有一个ptint(s)方法。
function A(){
this.print = function (s){
console.log(s);
}
}
直接在控制台写js代码,根据A构造函数创建a1、a2两个对象,两个对象显然不是一个对象,而在A构造函数肚子中的print(s)方法当然也不会是一个。
若创建对象a1在堆内存中的0xaaaaaaaa,对象a2创建在堆内存的0xaaaaaabb,那么各自隶属于两个对象的print方法的内存地址也不会相同,不是一个对象,就不会是一个东西,创建100个对象就会有100个功能一样的print。一样的功能还要各自占用内存空间,造成浪费。
避免相同功能的东西对内存资源造成浪费的方法很多,例如:
function print(s){
console.log(s);
}
function A(){
this.print = print;
}
这时两个对象的print方法是一个了。这种将函数提升到全局的方式虽然对浪费资源做到了一定的处理,但是也有变量名重复的风险等等问题。
JS提供了prototype可以很好解决这个问题,还有其它优势。
2.每个函数对象都有原型对象
prototype原型对象是每个函数对象都有的而且是单例的,一个函数一个原型对象,即使使用这个构造函数创建1000个对象,对应的原型对象也只有1个。
function A(){}
//对象访问构造函数的原型对象 对象.__proto__
//函数对象访问其原型对象 函数名.prototype
函数对象的prototype与其实例对象的proto是相等的,并且创建的不同对象的实例的proto也是相等的,足以证明函数对象的原型对象是单例的,一个函数对象一个原型对象。
使用单例的原型对象管理功能块方法。
A.prototype.haha = function(){console.log("haha");};
为A函数对象的原型对象添加一个属性haha,它的值是一个方法。之后根据构造函数A创建了a1、a2对象,可以成功调用haha()方法,比较两个对象的haha属性是相等的说明是同一个方法。
a1对象调用haha()方法的时候并没有a1.__proto__.haha();
这样先去调用proto原型对象用其调用haha(),这就涉及了属性搜索原则。
3.属性搜索原则
对象在调用一个方法时先查找自身,自身若没有则默认去原型对象查找,无,则原型对象查找原型对象的原型对象。在这一点上,prototype真有点继承的意思了,自己没有找prototype。
查找:对象.__proto__.__proto__.__proto__ ...
查找至null还未找到:属性 -->> undefined 方法-->>Uncaught TypeError: xx.x is not a function(…)。
function haha(){
console.log("A haha");
}
function A(){
this.haha = haha;
}
A.prototype.haha = function(){
console.log("A prototype haha");
}

undefined 上一步执行的返回值
a.haha(); 调用haha();
16 A haha 可以看出调用的是对象自身的haha()
undefined 调用haha()的返回值
a.__proto__.haha(); 调用原形的haha()
24 A prototype haha
undefined ```
4.顶级函数对象Object唯一原型
JS是否是面向对象语言我不清楚,但是JS是肯定可以做一些面向对象语言才可以做的事,JS与纯面向对象语言也有很多相似之处,顶级对象Object就是一个铁的事实。
纯面向对象语言Java的顶级对象是java.lang.Object,所有对象最终都继承Object,就也拥有了Object的所有方法,例如操作线程的wait()、notify()等,而JS的顶级对象也叫Object,并且顶级对象Object构造函数对象也有一个单例的原型对象(有函数对象就一定有原型对象),并且这个原型对象也有一些方法,这些方法会被所有对象继承。
Object对象:

在原型对象中有不容忽视的属性-->>constructor,这个属性存的是自己这个原型对象属于哪一个构造函数的
函数对象prototype与其实例对象的```__proto__```指向同一个原型对象。

5.原型链
已经知道顶级对象是Object,任何一个对象一层一层往上扒都会找到真实的自己(Object)。
想要层层扒皮就需要prototype原型对象了,一个原型对象就像一个链环,环环相扣,这是一个有头环和尾环的链。
DOM对象body层层扒皮:

```var body = document.querySelector("body");
undefined
body.__proto__
HTMLBodyElement {Symbol(Symbol.toStringTag): "HTMLBodyElement"}
body.__proto__.__proto__
HTMLElement {Symbol(Symbol.toStringTag): "HTMLElement"}
body.__proto__.__proto__.__proto__
Element {Symbol(Symbol.toStringTag): "Element", Symbol(Symbol.unscopables): Object}
body.__proto__.__proto__.__proto__.__proto__
Node {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5…}
body.__proto__.__proto__.__proto__.__proto__.__proto__
EventTarget {Symbol(Symbol.toStringTag): "EventTarget"}
body.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
Object {}
body.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
null```
head与body在HTMLElement对象这层便是同一个原型了。

6.原型链达到假继承,函数对象原型链
6.1实例对象原型链达到假继承
通过``` 子构造函数.prototype.__proto__ = 父构造函数.protoype;```达到继承效果。
function A(){}
A.prototype.printa = function(){
console.log("printa A");
};
function B(){}
B.prototype.__proto__ = A.prototype;
B.prototype.printb = function(){
console.log("printb B");
};
function C(){}
C.prototype.__proto__ = B.prototype;
创建C对象调用A的printa(),B的printb();,看起来就像继承了,其实就是继承了。

6.2.函数对象原型链
```Object.__proto__```指向Function原型对象。
```Function.prototype与Function.__proto__指向Function原型对象```。

印证上图:

7.替换原型对象
构造函数对象.prototype = {};替换原型对象。
```function A(){}``` 的原型对象中constructor与```__proto__```属性默认添加的,并且是灰色的,

用一个没有构造函数的对象替换原型```A.prototype = {};```之后,constructor属性在这个自定义对象中不存在了,默认还会有```__proto__```

手动加上constructor```A.prototype = {constructor:A};```属性之后虽然不是灰色的,但是并不影响使用。

8.```__proto__```的兼容性
非标准属性 ``` __proto__``` 很老的浏览器可能不支持(ie8及以下),其最早由火狐使用。