zoukankan      html  css  js  c++  java
  • 菜鸟快飞之JavaScript对象、原型、继承(三)

      正文之前需要声明的一点是,菜鸟系列博文全是基于ES5的,不考虑ES6甚至更高版本。

    继承

      由于我个人不是学计算机的,所以对于很多东西只是知其然,不知其所以然。就像这个继承,刚开始学JavaScript就听人说了JavaScript几大核心,但是自己平时似乎都没怎么用到,所以一直不明白为什么需要这些东西,面试还总是问这些。

      但是随着一点点学习,也有去看过jQuery源码,虽然到现在也没怎么看懂(主要也是有些懒),但慢慢还是对这些东西有了更深的了解。

    为什么需要继承 

      举个很简单的例子,我在平时学习写一些页面的时候,里面的代码很少,而且更多还是用jQuery去写的,若非是专门为了练习JavaScript,否则根本用都用不上。

      但是这只是一些很小的东西,如果你需要写一个jQuery这样的库呢,angular这样的框架呢,那么你必然会需要大量的优化你的代码,让能够写一次就解决的东西尽量不要再写第二次,否则几千行能写完的东西,你可能需要写上万行才行了。

      这个时候继承就是一个很好的解决方法,让一个子类能够拥有父类的属性和方法,同时子类也能拥有自己的属性和方法。

      所以什么是继承,就是让一个子类拥有父类的属性和方法,自己不用再去重复写一次。

    原型链

      JavaScript和其他语言不同,没有类,也就是class,所以不能用常规的方式实现继承。但作为一门面向对象的语言,JavaScript有其独特的实现方式——原型链。

      之前说过,每个函数都有一个原型对象prototype,每个实例都有一个指向原型对象的内部指针__proto__。

     

    function Fun(){}
    
    var fun1 = new Fun();

      fun1.__proto__ ----> Fun.prototype.__proto__  ----> Object.prototype.__proto__  ----> null

      让一个类型的原型对象等于另一个类型的实例:

    function Fun() {
    
    }
    
    function Sub() {
    
    }
    
    Sub.prototype = new Fun();
    
    Sub.prototype.__proto__ === Fun.prototype; // true  

    var sub1 = new Sub();

      sub1.__proto__ ----> Sub.prototype.__proto__ ----> Fun.prototype.__proto__  ----> Object.prototype.__proto__  ----> null

      这样就形成了一条原型链,通过原型链,我们便可以实现继承,让子类拥有父类的属性和方法了。

    function Fun() {
        this.name = 'Mike';
        this.age = 10;
        this.sayHello = function() {
            console.log('Hello! 我叫' + this.name + '今年' + this.age + '岁');
        };
        this.nums = [1,2,3,4];
    }
    
    function Sub() {
    
    }
    
    Sub.prototype = new Fun();
    
    var sub1 = new Sub();    
    sub1.sayHello(); // Hello! 我叫Mike今年10岁 
    sub1.name = 'Jarry';
    sub1.sayHello(); // Hello! 我叫Jarry今年10岁 
    
    var sub2 = new Sub();
    sub2.sayHello(); //Hello! 我叫Mike今年10岁 
    sub2.nums; // [1,2,3,4]
    
    sub1.nums.push(6,7);
    sub2.nums;  // [1,2,3,4,6,7]

      但是单独使用原型链来继承会存在问题,就像上面的代码一样。

      我们知道,通过原型链的方式,父类的方法和属性都是在子类的原型链上,原型链上的方法和属性是会被实例共享的,所以当一个引用类型的值被一个实例改变后,另外一个实例再次调用的时候,其值也会发生改变。而且还有个问题就是参数传递不方便。

    组合继承

      为了解决上面的问题,我们在原型链的基础上加上一些其他东西,call()或者apply().

    function Fun(name,age) {
        this.name = name;
        this.age = age;
        this.sayHello = function() {
            console.log('Hello! 我叫' + this.name + '今年' + this.age + '岁');
        };
        this.nums = [1,2,3,4];
    }
    
    function Sub(name,age) {
        Fun.apply(this,arguments);
    }
    
    Sub.prototype = new Fun();
    
    var sub1 = new Sub('小红',10);    
    sub1.sayHello();
    
    var sub2 = new Sub('小明',20);
    sub2.sayHello();
    
    sub1.nums.push(6,7);
    sub1.nums; // [1,2,3,4,6,7]
    sub2.nums; // [1,2,3,4]

      这样问题就解决了,这个模式也是现在使用最多的模式,当然还有一些其他的,比如原型式继承,寄生式继承,各有优点,不过并不是很常见罢了。

    对象的扩展

      有时对于一些不是很复杂的项目,为了简单方便,我们可能会直接对通过字面量创建的对象进行扩展,只将公共的部分抽出来,比如:

      

    // 公共部分
    var commonObj = {
      identity: 'student',
      color: ['red','blue']
    };
    
    // 对象a 
    var a = Object.create(commonObj )
    a.name = 'Mike';
    
    // 对象b
    var b = Object.create(commonObj )
    b.name = 'Tom';
    
    // 使用
    b.identity // 'student'
    b.color // ["red", "blue"]
    
    a.color.push('black')  // 3
    a.identity = 'teacher'
    
    b.identity // 'student'
    b.color // ["red", "blue", "black"]

      我们可以通过ES5的新方法Object.create(),来规范原型继承,不过通过上面的代码会发现一个问题,就是当公共的部分是直接通过字面量的方式创建的对象时,里面的引用类型的值会被不同的继承者给改变,因为Object.create()只是潜复制而已。

      所以我们将公共部分换成函数,用构造函数来创建对象,然后使用new关键字,这样就能创建不同的实例了,不同的继承者之间就不会再相互影响:

    // 公共部分
    var commonObj = function () {
      this.identity = 'student';
      this.color = ['red','blue'];
    };
    
    // 对象a 
    var a = Object.create(new commonObj() )
    a.name = 'Mike';
    
    // 对象b
    var b = Object.create(new commonObj() )
    b.name = 'Tom';
    
    // 使用
    b.identity // 'student'
    b.color // ["red", "blue"]
    
    a.color.push('black')  // 3
    a.identity = 'teacher'
    
    b.identity // 'student'
    b.color // ["red", "blue"]

      这样就好了,但是这样继承者的属性和方法只能一个一个的添加,还是有些麻烦,所以再改改,使用apply():

    // 公共部分
    var commonObj = function () {
      this.identity = 'student';
      this.color = ['red','blue'];
    };
    
    // 对象a 
    var a = {
      name: 'Mike'
    }
    commonObj.apply(a)
    
    // 对象b
    var b = {
      name: 'Tom'
    }
    commonObj.apply(b)
    // 使用
    b.identity // 'student'
    b.color // ["red", "blue"]
    
    a.color.push('black')  // 3
    a.identity = 'teacher' // 'teacher'
    
    b.identity // 'student'
    b.color // ["red", "blue"]

      这样,a对象和b对象都可以直接把属性和方法直接写在{}里了。

      

      

       

      

  • 相关阅读:
    python自动化测试(3)- 自动化框架及工具
    python自动化测试(2)-自动化基本技术原理
    软件开发过程自动化原理及技术(完整示例)
    网络验证码--你到底是爱它还是恨它?
    python的高性能web应用的开发与测试实验
    接口应用小玩具-博客园积分排名变动监控工具
    openwrt-智能路由器hack技术(2)---"网路信息监控和窃取"
    Java中的Date和时区转换
    fastjson JSONObject遍历
    【git】强制覆盖本地代码(与git远程仓库保持一致)
  • 原文地址:https://www.cnblogs.com/mcbai/p/5210962.html
Copyright © 2011-2022 走看看