zoukankan      html  css  js  c++  java
  • JavaScript原型,原型链 !

    js原型

    问题:什么是js原型?
    js每声明一个function,都有prototype原型,prototype原型是函数的一个默认属性,在函数的创建过程中由js编译器自动添加。
    也就是说:当生产一个function对象的时候,就有一个原型prototype。
    举个栗子:
    是不是还看到了一个_proto_的属性?!骚年,你的眼睛不错~待会在解释prototype和_proto_的基友关系!
    prototype的属性值是一个对象,是属性的集合,是属性的集合,是属性的集合,重要事情说三遍!
    为什么要说他的属性的集合呢?我再来举个栗子~:
     
    function Person(name,age){ 
        this.name=name; 
        this.age=age; 
    } 
        Person.prototype.sayHello=function(){ 
        alert("使用原型得到Name:"+this.name); 
    } 
    var per=new Person("alin",21); 
    per.sayHello(); //输出:使用原型得到Name:alin
    

       

    在函数Person里面自定义了属性name和age,而prototype是我们的属性集合,也就是说,我要添加sayHello这个属性到Person,则要这样写:Person.prototype.sayHello,就能添加Person的属性。

    (我们可以简单的把prototype看做是一个模板,新创建的自定义对象都是这个模板prototype的一个拷贝,其实准确来说,不应该说是一个拷贝,而是一个连接,只不过这种链接是不可见,新实例化的对象内部有一个看不见的_Proto_指针,指向原型对象)。

    使用原型来优化代码:

    普通code:
    function add(x,y){
       return x+y;
    }
    function subtract(x,y){
       return x-y;
    }
    console.log(add(1,3));
     
    

      

    第一种方式用原型优化后:
    var Calculator = function(){
     
    };
    Calculator.prototype = {
        add:function(x,y){
         return x+y;
    },
    subtract:function(x,y){
         return x-y;
    }
    };
    console.log((new Calculator()).add(1,3));
    
    第二种方式用原型优化后:
    var Calculator = function () {};
    Calculator.prototype = function(){
       add = function(x,y){
       return x+y;
    },
    subtract = function(x,y){
        return x-y;
    }
    return{
         add:add,
         subtract:subtract
    }
    }();
    
    console.log((new Calculator()).add(1,3));
    

      

    它目的:封装私有的function,通过return的形式暴露出简单的使用名称,以达到public/private的效果。
     
    ----------------------------------------------------------------------------------华丽的分割线-------------------------------------------------------------------------------------------

    js原型链

    问题:什么是原型链?
    根据《JavaScript高级程序设计》P162页可以作出回答:原型链是实现继承的主要方法。其基本思想是:利用原型让一个引用类型继承另一个应用类型的属性和方法。
    简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
     
     
    首先,我觉得有必要先解释一下prototype 和_proto_之间的关系。
    每一个基本对象都有自己的_proto_属性,而每一个函数对象都有自己的prototype原型(函数对象也属于基本对象,所以也有_proto_),每当去定义一个prototype的时候,就相当于把该实例的__proto__指向一个结构体,那么这个被指向结构体就称为该实例的原型。 我们还是来看图吧~比较清晰:
    var foo = { 
           x: 10, 
           y: 20 
    }; 
    

      

    解析:当你定义一个函数对象的时候,其内部就有这样一个链表关系。声明foo对象,自带了_proto_的属性,而这个属性指向了prototype,从而实现对象的扩展(例如继承等操作)。

    再看一个栗子:

    var a = { 
          x: 10, 
          calculate: function (z) { 
                  return this.x + this.y + z 
           } 
    }; 
    var b = { 
             y: 20, 
             __proto__: a 
    }; 
    
    var c = { 
            y: 30, 
            __proto__: a 
    }; 
    
    
    b.calculate(30); // 60 
    

      

    附上另外说明:

    1、一个没有继承操作的函数的_proto_都会指向Object.prototype,而Object.prototype都会指向null。

    2、所以,也可以很明显知道,为何null是原型链的终端。

    理解了__proto__这个属性链接指针的本质。。再来理解constructor。

    prototype默认的有一个叫做constructor的属性,指向这个函数本身。
    一般construtor就是我们平时对函数设置的实例化对象

    如上图:SuperType是一个函数,下面包括他的原型对象prototype,原型对象指向了构造函数的指针,而构造函数指回像了原型对象的内部指针,这样就形成了链式关系了。
    就是说,当一个函数对象被创建时候,Function构造器产生的函数对象会运行类似这样的一行代码:
    this.prototype = {constructor:this};
    这个prototype对象是存放继承特征的地方。因为js没有提供一个方法去确定哪个函数是打算用来做构造器,所以每个函数都会得到一个prototype对象。constructor属性没有什么用,重要的是prototype对象。


    实现原型链有一种基本模式,其代码大致如下:

    function A(){
    this.Aproperty = "111";
    }
    
    A.prototype.getA = function(){
    return this.Aproperty;
    };
    
    function B(){
    this.Bproperty = "222";
    }
    
    B.prototype = new A();//B继承A
    B.prototype.getB = function(){
    return this.Bproperty;
    };
    
    var C = new B();
    console.log(C.getA());//111
    

      

    以上定义了两个类型A和B。每个类型分别有一个属性和一个方法。它们的主要区别是B继承了A,而继承是通过创建A的实例,并将实例赋给B.prototype实现的。实现的本质是重写原型的对象,代之以一个新的类型的实例。换句话说,原来存在于A的实例中的所有属性和方法,现在也存在于B.prototype中了。在确立了继承关系之后,我们给B.prototype添加了一个方法,这样就继承A的属性和方法的基础上又添加了一个新方法。
    如图:

    另外一个很重要的链式继承模式

    function A(x){
      this.x = x;
    }
    A.prototype.a = "a";
    function B(x,y){
      this.y = y;
      A.call(this,x);
    }
    B.prototype.b1 = function(){
      alert("b1");
    }
    B.prototype = new A();
    B.prototype.b2 = function(){
      alert("b2");
    }
    B.prototype.constructor = B;
    var obj = new B(1,3);
    

      

       就是说把B的原型指向了A的1个实例对象,这个实例对象具有x属性,为undefined,还具有a属性,值为"a"。所以B原型也具有了这2个属性(或者说,B和A建立了原型链,B是A的下级)。而因为方才的类继承,B的实例对象也具有了x属性,也就是说obj对象有2个同名的x属性,此时原型属性x要让位于实例对象属性x,所以obj.x是1,而非undefined。第13行又定义了原型方法b2,所以B原型也具有了b2。虽然第9~11行设置了原型方法b1,但是你会发现第12行执行后,B原型不再具有b1方法,也就是obj.b1是undefined。因为第12行使得B原型指向改变,原来具有b1的原型对象被抛弃,自然就没有b1了。


      第12行执行完后,B原型(B.prototype)指向了A的实例对象,而A的实例对象的构造器是构造函数A,所以B.prototype.constructor就是构造对象A了(换句话说,A构造了B的原型)。

    alert(B.prototype.constructor)出来后就是"function A(x){...}" 。同样地,obj.constructor也是A构造对象,alert(obj.constructor)出来后就是"function A(x){...}" ,也就是说B.prototype.constructor===obj.constructor(true),但是B.prototype===obj.constructor.prototype(false),因为前者是B的原型,具有成员:x,a,b2,后者是A的原型,具有成员:a。如何修正这个问题呢,就在第16行,将B原型的构造器重新指向了B构造函数,那么B.prototype===obj.constructor.prototype(true),都具有成员:x,a,b2。

      如果没有第16行,那是不是obj = new B(1,3)会去调用A构造函数实例化呢?答案是否定的,你会发现obj.y=3,所以仍然是调用的B构造函数实例化的。虽然obj.constructor===A(true),但是对于new B()的行为来说,执行了上面所说的通过构造函数创建实例对象的3个步骤,第一步,创建空对象;第二步,obj.__proto__ === B.prototype,B.prototype是具有x,a,b2成员的,obj.constructor指向了B.prototype.constructor,即构造函数A;第三步,调用的构造函数B去设置和初始化成员,具有了属性x,y。虽然不加16行不影响obj的属性,但如上一段说,却影响obj.constructor和obj.constructor.prototype。所以在使用了原型继承后,要进行修正的操作。

      关于第12、16行,总言之,第12行使得B原型继承了A的原型对象的所有成员,但是也使得B的实例对象的构造器的原型指向了A原型,所以要通过第16行修正这个缺陷。

    文章说明:个人查看各种资料,原创所得,观点不一定准确,欢迎各路大牛勘误,小女子感激不尽。

  • 相关阅读:
    基于深度学习的单目图像深度估计
    OpenCV探索之路(二十三):特征检测和特征匹配方法汇总
    TensorFlow练习24: GANs-生成对抗网络 (生成明星脸)
    深度估计&平面检测小结
    论文翻译——Rapid 2D-to-3D conversion——快速2D到3D转换
    Opencv改变图像亮度和对比度以及优化
    如何将OpenCV中的Mat类绑定为OpenGL中的纹理
    Eclipse控制台中文乱码
    给java中的System.getProperty添加新的key value对
    中文简体windows CMD显示中文乱码解决方案
  • 原文地址:https://www.cnblogs.com/Yirannnnnn/p/4896542.html
Copyright © 2011-2022 走看看