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);
  }
原文地址:https://www.cnblogs.com/cart55free99/p/4789009.html