ECMAScript_prototype原型
</br>
<a href="#1">1.原型</a>
<a href="#2">2.每个函数对象都有原型对象</a>
<a href="#3">3.属性搜索原则</a>
<a href="#4">4.顶级函数对象Object唯一原型</a>
<a href="#5">5.原型链</a>
<a href="#6">6.原型链达到假继承,函数对象原型链</a>
<a href="#7">7.替换原型对象</a>
<a href="#8">8.__proto__
的兼容性</a>
</br>
</br>
<h3 id="1">1.原型</h3>
在构造函数中直接定义方法,在使用该函数创建对象时就会重复开辟方法所占据的空间,因为他们直接隶属于对象,但是对象不是单例的,以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。一样的功能还要各自占用内存空间,造成浪费。
Paste_Image.png
避免相同功能的东西对内存资源造成浪费的方法很多,例如:
function print(s){ console.log(s); } function A(){ this.print = print; }
Paste_Image.png
这时两个对象的print方法是一个了。这种将函数提升到全局的方式虽然对浪费资源做到了一定的处理,但是也有变量名重复的风险等等问题。
JS提供了prototype可以很好解决这个问题,还有其它优势。
</br>
</br>
</br>
<h3 id="2">2.每个函数对象都有原型对象</h3>
prototype原型对象是每个函数对象都有的而且是单例的,一个函数一个原型对象,即使使用这个构造函数创建1000个对象,对应的原型对象也只有1个。
function A(){} //对象访问构造函数的原型对象 对象.__proto__ //函数对象访问其原型对象 函数名.prototype
Paste_Image.png
函数对象的prototype与其实例对象的proto是相等的,并且创建的不同对象的实例的proto也是相等的,足以证明函数对象的原型对象是单例的,一个函数对象一个原型对象。
使用单例的原型对象管理功能块方法。
Paste_Image.png
A.prototype.haha = function(){console.log("haha");};
为A函数对象的原型对象添加一个属性haha,它的值是一个方法。之后根据构造函数A创建了a1、a2对象,可以成功调用haha()方法,比较两个对象的haha属性是相等的说明是同一个方法。
a1对象调用haha()方法的时候并没有a1.__proto__.haha();
这样先去调用proto原型对象用其调用haha(),这就涉及了属性搜索原则。
</br>
</br>
<h3 id="3">3.属性搜索原则</h3>
对象在调用一个方法时先查找自身,自身若没有则默认去原型对象查找,无,则原型对象查找原型对象的原型对象。在这一点上,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"); }
![console_newA.gif](http://upload-images.jianshu.io/upload_images/1759749-cac10c3f04c11cfd.gif?imageMogr2/auto-orient/strip
Object_proto_.gif
)
undefined 上一步执行的返回值 a.haha(); 调用haha(); 16 A haha 可以看出调用的是对象自身的haha()undefined 调用haha()的返回值 a.__proto__.haha(); 调用原形的haha()24 A prototype haha undefined ``` </br> </br> <h3 id="4">4.顶级函数对象Object唯一原型</h3> JS是否是面向对象语言我不清楚,但是JS是肯定可以做一些面向对象语言才可以做的事,JS与纯面向对象语言也有很多相似之处,顶级对象Object就是一个铁的事实。 纯面向对象语言Java的顶级对象是java.lang.Object,所有对象最终都继承Object,就也拥有了Object的所有方法,例如操作线程的wait()、notify()等,而JS的顶级对象也叫Object,并且顶级对象Object构造函数对象也有一个单例的原型对象(有函数对象就一定有原型对象),并且这个原型对象也有一些方法,这些方法会被所有对象继承。 Object对象: ![Object_proto_.gif](http://upload-images.jianshu.io/upload_images/1759749-399d89fc36b0eb2c.gif?imageMogr2/auto-orient/strip) 在原型对象中有不容忽视的属性-->>constructor,这个属性存的是自己这个原型对象属于哪一个构造函数的 函数对象prototype与其实例对象的```__proto__```指向同一个原型对象。 ![prototype实现的间接继承_类似于代理_r1_c1.png](http://upload-images.jianshu.io/upload_images/1759749-80e89a31728b24f8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) </br> </br> <h3 id="5">5.原型链</h3> 已经知道顶级对象是Object,任何一个对象一层一层往上扒都会找到真实的自己(Object)。 想要层层扒皮就需要prototype原型对象了,一个原型对象就像一个链环,环环相扣,这是一个有头环和尾环的链。 DOM对象body层层扒皮: ![body_proto.gif](http://upload-images.jianshu.io/upload_images/1759749-60ea92265109aa99.gif?imageMogr2/auto-orient/strip) ```var body = document.querySelector("body");undefinedbody.__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对象这层便是同一个原型了。 ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1759749-830289c33c60cd06.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) </br> </br> <h3 id="6">6.原型链达到假继承,函数对象原型链</h3> <h6 id="6_1"> 6.1实例对象原型链达到假继承</h6> 通过``` 子构造函数.prototype.__proto__ = 父构造函数.protoype;```达到继承效果。
<script>
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;
</script>
创建C对象调用A的printa(),B的printb();,看起来就像继承了,其实就是继承了。 ![test_extends.gif](http://upload-images.jianshu.io/upload_images/1759749-d390413d2502c1f8.gif?imageMogr2/auto-orient/strip) <h6 id="6_2"> 6.2.函数对象原型链</h6> ```Object.__proto__```指向Function原型对象。 ```Function.prototype与Function.__proto__指向Function原型对象```。 ![Untitled——template3.png](http://upload-images.jianshu.io/upload_images/1759749-573e7322a0155379.png) 印证上图: ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1759749-7a2f0d979e69ab4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</br></br><h3 id="7">7.替换原型对象</h3> 构造函数对象.prototype = {};替换原型对象。 ```function A(){}``` 的原型对象中constructor与```__proto__```属性默认添加的,并且是灰色的, ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1759749-43d6730e10625174.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 用一个没有构造函数的对象替换原型```A.prototype = {};```之后,constructor属性在这个自定义对象中不存在了,默认还会有```__proto__``` ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1759749-fa92d96507b7b85a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 手动加上constructor```A.prototype = {constructor:A};```属性之后虽然不是灰色的,但是并不影响使用。 ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1759749-a098bcc361e95423.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</br></br><h3 id="8">8.```__proto__```的兼容性</h3> 非标准属性 ``` __proto__``` 很老的浏览器可能不支持(ie8及以下),其最早由火狐使用。 </br></br></br></br></br>
作者:ThingLin
链接:https://www.jianshu.com/p/41ee141586b8
共同学习,写下你的评论
评论加载中...
作者其他优质文章