为了账号安全,请及时绑定邮箱和手机立即绑定

ECMAScript_prototype原型

标签:
JavaScript

</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。一样的功能还要各自占用内存空间,造成浪费。

webp

Paste_Image.png

避免相同功能的东西对内存资源造成浪费的方法很多,例如:

    function print(s){        console.log(s);
    }    function A(){        this.print = print;
    }

webp

Paste_Image.png

这时两个对象的print方法是一个了。这种将函数提升到全局的方式虽然对浪费资源做到了一定的处理,但是也有变量名重复的风险等等问题。
  JS提供了prototype可以很好解决这个问题,还有其它优势。

</br>
</br>
</br>

<h3 id="2">2.每个函数对象都有原型对象</h3>

prototype原型对象是每个函数对象都有的而且是单例的,一个函数一个原型对象,即使使用这个构造函数创建1000个对象,对应的原型对象也只有1个。

    function A(){}    //对象访问构造函数的原型对象  对象.__proto__ 
    //函数对象访问其原型对象 函数名.prototype

webp

Paste_Image.png

函数对象的prototype与其实例对象的proto是相等的,并且创建的不同对象的实例的proto也是相等的,足以证明函数对象的原型对象是单例的,一个函数对象一个原型对象。
  使用单例的原型对象管理功能块方法。

webp

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

webp

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


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消