zoukankan      html  css  js  c++  java
  • JS 继承

    JS 继承

    搬运
    https://segmentfault.com/a/1190000000766541
    http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

    为什么js没有类?

    截止ES5, JS并没有类的概念, 为何呢
    class太正式了, 增加了初学者的难度 因此没有引入class 的概念

    为什么要有prototype

    function DOG(name){
        this.name = name;
        this.species = '犬科';
        this.say = function(){ ... }
    }
      var dogA = new DOG('大毛');
      var dogB = new DOG('二毛');  
    

    生成的每一个对象都有自己的属性和方法副本!! 方法副本!!
    造成资源浪费,因此引入prototype属性
    这个属性包含一个对象(以下简称"prototype对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
    实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。

    原型继承在开发中经常用到。它有别于类继承是因为继承不在对象本身,而在对象的原型上(prototype)。每一个对象都有原型(字面量对象也有 只不过其__proto__是object),在浏览器中它体现在一个隐藏的__proto__属性上。在

    Object.create()

    Object.create()创建的对象, 拥有指定的原型和属性

    Object.create(prototype, optionalObjects)
    
    var rectangle = {
        area : function(){
            return this.width * this.height ;
        }
    } ;
    var rect = Object.create(rectangle) ;
    
    

    这里rect的原型是 rectangle

    对象的继承 使用原型链

    把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。

    	function object(o) {
    	    function F() {}
    	    F.prototype = o;
    	    return new F();
    	}
    

    比如医生继承中国人

    var Chinese = {
      nation:'中国'
    };
    
    var doctor ={
      career:'医生'
    }
    var doctor = object(Chinese);
    alert(doctor.nation); //中国
    

    对象继承 使用deepcopy

      function deepCopy(p, c) {
        var c = c || {};
        for (var i in p) {
          if (typeof p[i] === 'object') {
            c[i] = (p[i].constructor === Array) ? [] : {};
            deepCopy(p[i], c[i]);
          } else {
             c[i] = p[i];
          }
        }
        return c;
      }
       var Doctor = deepCopy(Chinese);
    

    经典的额原型继承

    var Father = function(name) {
       this.name = name;
    }
     
    Father.prototype.a = function() {
    	console.log(this.name)
    }
     
    var Child = function(name){
        this.name = name;
    }
     
    //开始继承 
    Child.prototype = new Father(); 
    var man = new Child('huhu'); 
    man.a();
    
    

    借用构造函数的继承

    原型继承不是挺好的么 为什么还要有一个构造函数的继承呢?
    最大的弊病, 就是父类构造函数中的东东 , 子类还要再写一遍, 很明显不符合逻辑

    所以借用父类构造函数
    Father.apply(this, arguments); 这样就好啦

    
      var Father = function(name, age) {
        this.name = name;
        this.age = age;
      }
      Father.prototype.a = function() {
        console.log(this.name + ' ' +this.age)
      }
      var Child = function(name, age, hehe) {
        Father.apply(this, arguments);
        this.hehe = hehe;
      }
    
      Child.prototype = new Father();
      var man = new Child('huhu',22,'hehe');
      man.a();
      console.log(man.hehe);
    

    另外需要注意的是此刻man.constructor == Father 而不是Children
    因为Child.prototype = xxx 的方式改了整个prototype的指向
    所以还要再加上一个 Child.prototype.constructor = Child;

    看起来挺完美了 然而父类的构造函数被调用了两次
    第一次是 Child.prototype = new Father(); 第二次Child的构造函数又call了父类构造函数
    所以肯定不能用 Child.prototype = new Father() 了

    临时构造函数

    使用临时的构造函数 避免了父类构造函数被调用2次的问题

      var Father = function(name, age) {
        this.name = name;
        this.age = age;
      }
      Father.prototype.a = function() {
        console.log(this.name + ' ' +this.age)
      }
      var Child = function(name, age, hehe) {
        Father.apply(this, arguments);
        this.hehe = hehe;
      }
      var Temp = function(){}
      Temp.prototype = Father.prototype;
      Child.prototype = new Temp();
      var man = new Child('huhu',22,'hehe');
      man.a();
    
    

    前面不是提到了Object.create嘛 他可以创建指定原型的对象
    也可以避免父类函数被多次调用

    
      var Father = function(name, age) {
        console.log('father constructor');
        this.name = name;
        this.age = age;
      }
      Father.prototype.a = function() {
        console.log(this.name + ' ' +this.age)
      }
      Father.prototype.obj = {b:1};
      var Child = function(name, age, hehe) {
        Father.apply(this, arguments);
        this.hehe = hehe;
      }
      Child.prototype = Object.create(Father.prototype);
      Child.constructor = Child;
      var man = new Child('huhu',22,'hehe');
    
    

    Child.prototype = Father.prototype 有什么问题

    
      var Father = function(name, age) {
        console.log('father constructor');
        this.name = name;
        this.age = age;
      }
      Father.prototype.a = function() {
        console.log(this.name + ' ' +this.age)
      }
      Father.prototype.obj = {b:1};
      var Child = function(name, age, hehe) {
        Father.apply(this, arguments);
        this.hehe = hehe;
      }
      Child.prototype = Father.prototype;
      Child.constructor = Child;
      var man = new Child('huhu',22,'hehe');
      Child.prototype.a = function(){
        console.log(this.name + ' ' +this.age + ' '+ this.hehe);
      }
      new Father('hehe', 44).a(); //hehe 44 undefined //父类的a方法被改了
      //Child.prototype = Father.prototype; 导致任何对Child原型上的改动都会影响到Father
    
    

    所以前面提到的经典原型继承 借用构造函数 Object.create() 就没有问题了吗?
    注意上面那句话 导致任何对Child原型上的改动都会影响到Father
    是 "任何" 也就是说经典原型等上述三种在某些情况下 也会影响到父类的原型

    比如

    
      var Father = function(name, age) {
        // console.log('father constructor');
        this.name = name;
        this.age = age;
      }
      Father.prototype.a = function() {
        console.log(this.name + ' ' +this.age)
      }
      Father.prototype.obj = {b:1};
      var Child = function(name, age, hehe) {
        Father.apply(this, arguments);
        this.hehe = hehe;
      }
      Child.prototype = Object.create(Father.prototype);
      Child.constructor = Child;
      var man = new Child('huhu',22,'hehe');
    
      //这样是肯定不会影响到父类原型的  不论是不是糟糕的原型直接赋值都不会
      //因为下面的做法不过是给man对象增加了一个a属性
      // man.a = function(){
      //   console.log(this.name + ' ' +this.age + ' '+ this.hehe);
      // }
      // man.a();
    
      //这种当然也不会影响
      Child.prototype.a = function(){
        console.log(this.name + ' ' +this.age + ' '+ this.hehe);
      }
      man.a();
      new Father('hehe',55).a();
    
      // 这种也不会  因为等于给man对象增加一个属性
      // 你也可以认为man的obj这个指针指向了另一个值
      // man.obj = {b:2}; console.log(new Father().obj.b);//1
    
    
      // 影响到父类的原型   因为obj指向的就是 父类原型中的obj对象  我试图修改了这个obj中的值
      // man.obj.b = 2;
      // console.log(new Father().obj.b);//2  被改了
    
      Child.prototype.obj.b = 2;
      console.log(new Father().obj.b);//2  被改了
    
    

    也就是说稍微对子类的原型做了一些稍微深层的改动 父类的原型就会被影响
    只有说子类的原型和父类原型彻底的不存在指向关系, 才有可能解决

    Copy继承

      function Animal(){}
      Animal.prototype.species = "动物";
    
    function extend2(Child, Parent) {
        var p = Parent.prototype;
        var c = Child.prototype;
        for (var i in p) {
          c[i] = p[i];
          }
        c.uber = p;
    }
    
    
    extend2(Cat, Animal);
    var cat1 = new Cat("大毛","黄色");
    alert(cat1.species); // 动物
    
    

    当然这个copy还不是很完善, 毕竟深层次的copy就不行了
    于是用一个深度clone来处理吧

      var Father = function(name, age) {
        this.name = name;
        this.age = age;
      }
      Father.prototype.a = function() {
        console.log(this.name + ' ' +this.age)
      }
      var Child = function(name, age, hehe) {
        Father.apply(this, arguments);
        this.hehe = hehe;
      }
      var Temp = function(){}
      Child.prototype = deepClone(Father.prototype);
      var f = new Father('heh',55);
      var man = new Child('huhu',22,'hehe');
      man.a = function(){
        console.log(this.name + ' ' +this.age + ' '+ this.hehe);
      }
      f.a();
      man.a();
      // man.obj.b = 2; console.log(new Father().obj.b);// 仍是1
    
    
    
    
    
    
      function deepClone(origin) {
        var isObj = function(o) {
          return Object.prototype.toString.call(o) == '[object Object]';
        }
        var isArr = function(o) {
          // return [].toString.call(o) == '[object Array]';// 数组的toString本身就被Array改写了
          return Object.prototype.toString.call(o) == '[object Array]'
        }
        function clone(origin) {
          var rs = isArr(origin) ? [] : {};
          for (key in origin) {
            if (isObj(origin[key]) || isArr(origin[key])) {
              rs[key] = clone(origin[key]);
            } else {
              if (origin.hasOwnProperty(key)) {
                rs[key] = origin[key];
              }
            }
          }
          return rs;
        }
        return clone(origin);
      }
    
  • 相关阅读:
    在普通类中调用service
    layui util 工具时间戳转换
    最大值
    药房管理
    线段树2
    线段树1
    Dijkstra
    最大值最小化
    图的M 着色问题
    取余运算
  • 原文地址:https://www.cnblogs.com/cart55free99/p/4789009.html
Copyright © 2011-2022 走看看