ECMAScript_prototype原型


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

ECMAScript_prototype原型_第1张图片
Paste_Image.png

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


    function print(s){
        console.log(s);
    }

    function A(){
        this.print = print;
    }

ECMAScript_prototype原型_第2张图片
Paste_Image.png

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






2.每个函数对象都有原型对象

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


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

ECMAScript_prototype原型_第3张图片
Paste_Image.png

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

ECMAScript_prototype原型_第4张图片
Paste_Image.png

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");
    }

![console_newA.gif](http://upload-images.jianshu.io/upload_images/1759749-cac10c3f04c11cfd.gif?imageMogr2/auto-orient/strip

ECMAScript_prototype原型_第5张图片
Object_proto_.gif

)

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对象: ![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)

5.原型链

  已经知道顶级对象是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"); 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对象这层便是同一个原型了。 ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1759749-830289c33c60cd06.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

6.原型链达到假继承,函数对象原型链

  
  6.1实例对象原型链达到假继承
     通过``` 子构造函数.prototype.__proto__ = 父构造函数.protoype;```达到继承效果。   


  创建C对象调用A的printa(),B的printb();,看起来就像继承了,其实就是继承了。

![test_extends.gif](http://upload-images.jianshu.io/upload_images/1759749-d390413d2502c1f8.gif?imageMogr2/auto-orient/strip)


  
  6.2.函数对象原型链
  ```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)

7.替换原型对象

  构造函数对象.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)

8.```__proto__```的兼容性

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




你可能感兴趣的:(ECMAScript_prototype原型)