• 浅谈JS继承


        今天呢,我们来谈谈继承,它也是JS语言中的一大重点,一般什么时候我们会用继承呢,比如有两个拖拽的面板,两个功能基本一致,只是第二个面板多了一些不同的东西,这个时候,我们就会希望,要是第二个直接能继承第一个面板相同的功能就好了。所以这个时候继承就登场啦。。。

      继承:在原有对象的基础上,略作修改,得到一个新的对象,并且不影响原有对象的功能

       在具体讲继承前,首先来了解一个东西,这个东西叫做原型链,是继承的基础。我们先来看一段代码:

    function Aaa(){}
    Aaa.prototype.num = 3;
    var a1 = new Aaa();
    alert(a1.num); //3   a1是怎么找到的原型下面的num呢?

    这是一个很简单的代码,但是我们有没有想过一个问题,既然我们把变量放到了原型下面,那么a1是怎么找到这个变量的呢?实际上就是通过原型链啦。

    原型链是指:实例对象与原型之间的连接,__proto__(它是一种隐式连接 ,我们看不到,但确实存在,a1能跟着这个链找到对应的原型下面的东西)。

    看下面的这个图:首先a1先看看自己的下面有没有num,发现没有之后,就会随着原型链,找找找,找到了原型,发现下面有,就弹出了这个num.打开firebug可以看到num是通过原型链中原型下被找到的。

    那如果是下面这种情况呢:

    function Aaa(){
       this.num =10;
    }
    Aaa.prototype.num = 3;
    var a1 = new Aaa();
    alert(a1.num); //10

    我们之前讲过原型的优先级是比较低的,其实也是原型链的原因,对象会首先在自己的地盘找,找不到才会从原型链上找,而且最外层的原型链是object,所以上面的意思其实下面就是这样的:

       1 拷贝继承

    首先来看个栗子:父类有属性name,age, 方法show,子类有属性name,age,sex,怎么能让子类直接继承父类的属性和方法呢?

    对于属性的继承:其实我们可以在子类里面直接调用父类的函数。

    方法的继承:因为方法在原型下面,原型本身也是一个对象,我们可以直接把父类的原型对象赋给子类的原型对象。那么子类就会有父类的方法了。

    function Create(name,age){ 
        this.name = name;
        this.age = age;
    }
    Create.prototype.show = function(){ 
        alert(this.name);     
    }
    function Create2(name,age,sex){ 
        Create(name,age); //这样调用this指的是window
        this.sex = sex;
    }
    Create2.prototype = Create.prototype;  //这样对象赋值会存在引用关系

    但是这个方法存在两个问题:

    1  因为在子类中,我们希望的是this指的是当前的对象,但直接这样调用就指的是window,所以我们需要用call()方法修改一下this的指向。

    2  对象复制会存在引用问题,也就是说现在两个对象指向的是同一个地址,其中一个原型的一些东西的更改会影响另一个,这肯定不是我们所希望的。所以我们通过拷贝赋值,来避免引用。

    function Create(name,age){ 
        this.name = name;
        this.age = age;
    }
    Create.prototype.show = function(){ 
        alert(this.name);     
    }
    function Create2(name,age,sex){ 
        Create.call(this,name,age);
        this.sex = sex;
    }
    extend(Create2.prototype ,Create.prototype ); //函数之间赋值不会存在引用 
    Create2.prototype.showJob = function(){}; //不会影响父类
    var p2 = new Create2('hua',11,'men');
    p2.show();   // hua
    function extend(obj1,obj2){   
          for(var attr in obj2){   //循环遍历对象下面的属性和方法,进行赋值,需要知道的是函数之间的复制是不存在引用的,所以方法之间就不存在引用了
               obj1[attr] = obj2[attr];
         }
    }

    这种继承方式叫做拷贝继承。(jquery也是采用拷贝继承extend)

    2 类式继承(利用构造函数继承,一句话即可完成继承)

    来看下面的代码:

    function Aaa(){   //父类
        this.name = [1,2,3];
    }    
    Aaa.prototype.showName = function(){
        alert( this.name );
    };
    function Bbb(){ }//子类
    Bbb.prototype = new Aaa(); 
    //这句话覆盖了Bbb所有的原型,所以构造函数指向改变(这里不懂的话,参建之前的博客,JS面向对象之创建对象中讲到的constructor)
    //这句话就可以让子类找到父类的方法和属性
    var b1 = new Bbb();
    b1.showName();//弹出[1,2,3]
    alert(b1.constructor);//Aaa 可见构造函数指向变了 b1.name.push(4);// 改变b1.name var b2 = new Bbb(); alert(b2.name) // [1,2,3,4] 由此可见属性存在引用

    我们通过看一个图看理解是怎么继承的:

    Bbb.prototype = new Aaa(); 这句话,使得图上的a1和Bbb的原型指向了同一个地方,所以当指向b1.showName();这句的时候,它现在自己下寻找有没有这个方法,没有就通过原型链去原型下找,发现也没有,又通过原型链去父级那边找,最终就找到了。

    不过通过注释可以看到,其实这种方法其实是存在一些问题的:

                1 构造函数指向问题      2 属性存在引用 

    解决:1 修正构造函数指向  2 方法和属性(call)分开继承
    代码如下:1 属性继承和拷贝继承一样,通过构造函数调用继承  2 方法继承,通过中间人来做到之继承方法(看注释应该能懂,也可以自己试着画原型链图理解)
    function Aaa(){ //父类
    this.name = [1,2,3];
    }    
    Aaa.prototype.showName = function(){
    alert( this.name );
    };
    function Bbb(){   //子类
        Aaa.call(this);    //属性继承
    }
    var F = function(){}; //声明空的构造函数,中间媒介
    F.prototype = Aaa.prototype;//把Aaa的原型赋给F,这个时候F会拥有Aaa原型下所有的方法
    Bbb.prototype = new F();  //这个同上,Bbb会用于F的所有方法和属性,因为上一句复制F只是拥有了Aaa的方法,所以这里实质上Bbb拥有的也只是Aaa的方法
    Bbb.prototype.constructor = Bbb; //修正指向问题
    var b1 = new Bbb();
    b1.name // [1,2,3] 
    b1.constructor;//Bbb 指向正常
    b1.name.push(4);
    var b2 = new Bbb(); 
    b2.name // [1,2,3]   属性互相不影响

    3 原型继承(借助原型来实现对象继承对象)

    var a = {
        name : '小明'
    };
    
    var b = cloneObj(a); 
    
    b.name = '小强'; 
    alert( b.name );//小强 会优先在自己的下面找name 
    alert( a.name );//小明 
    function cloneObj(obj){
          var F = function(){}; //声明一个构造函数
          F.prototype = obj; //F原型下拥有obj的方法和属性
          return new F(); // 返回声明通过F声明的对象,通过上一句知道,这个对象具有obj的方法和属性 
     }

    以上就是三种继承方法,其实也有别的,后续可以继续补充,对于继承也许用的不是很多,但对理解JS语言还是很重要的。一起加油把。

    
    
    
  • 相关阅读:
    git如何忽略文件或者文件夹
    jsonp跨域请求发布出去
    PHP
    curl保存图片
    curl 请求
    输入框纯数字
    PHP允许AJAX跨域请求的两种方法
    IOS开发笔记 IOS如何访问通讯录
    Android 蓝牙开发(整理大全)
    新的移动服务示例
  • 原文地址:https://www.cnblogs.com/moqing/p/5625031.html
Copyright © 2020-2023  润新知