• 你真的懂原型么?


    基本看过高程等书的人都可以对原型继承,原型链查找侃侃而谈,代码中也可以使用原型完成一些事情,但是,我们对于原型真的彻底搞明白了么?

    原型由构造器确定

    我们的原型是一个对象,构造器函数有一个属性指向这个对象,prototype;而我们每次new出来的实例也有一个属性指向这个对象,__proto__。为什么说原型是由构造器确定的,因为在new之后,这个实例的原型就已经确定了,实例的__proto__和构造器的prototype都会指向那个原型对象。

    var Person=function(){};
    var p=new Person();
    console.log(p.__proto__===Person.prototype);  // true
    Person.prototype={
       say:function(){
            console.log("hello");
       }
    }
    var t=new Person();
    console.log(t.__proto__===Person.prototype);  // true
    t.say();   // hello
    
    console.log(p.__proto__===Person.prototype);  //  false
    p.say();  // 报错

    这段代码就很好的说明了这个问题。实例p通过构造器函数Person创建出来(new)之后,它的__proto__和Person的prototype指向同一个对象,所以console的时候相等。然后,我们改变一下构造器函数Person的原型指向,并创建了一个实例t,我们发现t的__proto__和Person的prototype的指向依旧是一致的,但是因为Person.prototype已经改变了,而p.__proto__依旧指向原来的原型对象,所以p.__proto__和现在的Person.prototype指向的对象并不是同一个,会返回false。证据就是p.say()会报错,但是t.say()可以返回正确的值。

    原型链查找,是通过实例的属性

    看下面的代码:

    var Person=function(){};
    var p=new Person();
    Person.prototype.say=function(){
       console.log("1");
    }
    p.say();  //1

    我们先创建了实例,再给原型添加方法,最后调用时,依旧可以返回正确结果,这说明p.say()是一个查找的过程,它会去原型对象里查找相应的方法,如果有返回结果,没有就会报错。

    那么,既然是通过实例调用的方法,自然也是通过实例的__proto__属性去访问到那个原型对象,如何证明?我们看下面的代码:

    var Person=function(){};
    var p=new Person();
    Person.prototype.say=function(){
       console.log(1);
    }
    p.say();  //1
    p.__proto__={
      say:function(){
         console.log(2);
      }
    }
    p.say();   //2

    当我们重写p.__proto__之后,相当于p这个实例的原型就是我们重写的这个对象。无论我们对Person.prototype添加任何方法,p这个实例都访问不到这些方法,因为原型链查找通过的是__proto__属性,而现在,p.__proto__和Person.prototype指向的其实是不同的两个对象了。

    原型是客观存在的

    当我们new了一个实例之后,原型对象就已经存在了,实例的__proto__和构造器的prototype都会指向它。而创建实例之后,我们如果修改了__proto__或者prototype,其实都是在修改那个唯一的原型对象。要是我们重写了__proto__或者prototype,原型对象依旧存在,但是通过__proto__或者prototype只能访问到重写的那个对象。所以我们一直在强调,new是一个分界线,new的过程就是把实例的__proto__指向构造器的prototype所指向的那个对象。

    看点例子:

    var Person=function(){};
    var p=new Person();
    var t=new Person();
    
    p.__proto__.say=function(){
       console.log(1);
    }
    p.say();  //1
    t.say();  //1

    p.__proto__={ say:function(){ console.log(2); } } p.say(); //2 t.say(); //1 Person.prototype={ say:function(){ console.log(3); } } var x=new Person(); p.say(); //2 t.say(); //1 x.say(); //3

    如果你对上述代码没有任何疑问,那么,你应该是真的懂了原型。

  • 相关阅读:
    Activity的几种启动模式
    android 环境搭建
    认识python中__name__、程序主入口
    32位与64位之谈
    shell中字符串基本用法
    C++ push方法与push_back方法
    8-10总结
    第九章 硬件抽象层:HAL
    第十章 嵌入式Linux的调试技术
    第八章
  • 原文地址:https://www.cnblogs.com/grey-zhou/p/6142238.html
Copyright © 2020-2023  润新知