JavaScript面向对象那些东西-继承

413 阅读4分钟

继承

父类里有些属性方法 子类想把父类中的这些属性方法 继承过来给子类自己的实例也用用

( ps: →_→ 能不能专业点 没文化真可怕 )

一、原型链继承

   // 原型链继承:把子类的原型作为父类的实例
   // 子类把父类中的私有和公有 都继承过来作为子类的原型属性(公有的)
   function A() {
       this.a = 123;
       this.say = function () {
           console.log('say');
       }
   }
   A.prototype.mess = 'mess';
   B.prototype = new A; // 将子类 的原型对象 重构为 父类A的实例
   B.prototype.constructor = B;
   console.log(B.prototype instanceof A);
   console.log(B.prototype.__proto__ === A.prototype);
   function B() {
   }
   var b1 = new B;
   var b2 = new B;
   console.log(b1);
   console.log(b1.a);
   console.log(b1.say === b2.say); //这个原型属性 是从父类的私有属性中继承过来的
   console.log(B.prototype.__proto__ === A.prototype);
   // 多态 子类重写父类 导致父类中所有实例都会受到影响
   B.prototype.__proto__.mess = '已经被子类修改了';
   
   var a1 = new A;
   console.log(a1.mess);
   

二、借用构造函数

// 借用构造函数
    // 把父类构造函数当做普通函数执行 将里面this修改为B类中实例
    // 这样的话父类中this.xxx 就相当于给我子类实例添加私有属性
    // 只能继承父类中私有属性
    function A() {
        this.a = 123;
        this.say = function () {
            console.log('say');
        }
    }
    A.prototype.mess = 'mess';
    function B() { // 子类中this 当前实例(子类的实例)
        A.call(this); // 把父类构造函数当做普通函数执行 将里面this修改为B类中实例
    }
    var b = new B;
    console.log(b);
    console.log(b.a);
    

三、组合继承

// 组合继承:原型链+借用构造函数继承
    // 原型链:把父类私有和公有 都继承为子类的公有属性
    // 借用构造函数:只能继承父类私有属性
    // 组合继承 缺点
    // 1.子类会把父类的实例属性继承过来两组 一组作为子类实例的私有属性 一组作为子类的公有属性
    // 2.父类会被调用俩次
    function A() {
        console.log(2222);
        this.a = 123;
        this.say = function () {
            console.log('say');
        }
    }
    A.prototype.mess = 'mess';
    B.prototype = new A; // 继承父类中原型属性(继承公有) 原型链继承是执行一次
    B.prototype.constructor = B;
    function B() {
        A.call(this); // 继承父类私有的 call继承时执行一次
    }
    var b = new B;
    console.log(b);
    console.log(b.a);
    console.log(b.mess);
    

四、原型式继承

    // 原型式继承
    // Object.create();  创建一个新对象 然后 让这个新对象的__proto__指向这个传递进来的参数对象
    var obj1 = {name: 2, id: 1};
    var obj2 = Object.create(obj1); // obj2.__proto__ = obj1;
    // console.log(obj2.name);
    Object.myCreate = function (o) {
        // 创建一个临时构造函数
        function Fn() {
        }
        // 让这个临时构造函数 原型等于传递进来的对象
        Fn.prototype = o; // A.prototype
        return new Fn; // 返回这临时构造函数的实例 Fn的实例__proto__ = o
    };
    function A() {
        this.a = 123;
        this.say = function () {
            console.log('say');
        }
    }
    A.prototype.mess = 'mess';
//    B.prototype = Object.create(A.prototype); // 创建一个新对象(并不是直接返回的A类的实例)作为子类的原型对象 并且这个对象__proto__ = A.prototype;
    // 继承父类公有的属性 作为 子类的公有属性
    // 将子类的 原型对象重构为 这个Fn的实例对象 并且这个实例__proto__ = A.prototype
    B.prototype = Object.myCreate(A.prototype);
    B.prototype.constructor = B;
    
    function B() {
        
    }
    var b = new B;
    console.log(b);

五、寄生组合继承

    // 寄生组合继承 借用构造函数 + 原型式继承
    // 弥补 组合继承的缺点
    Object.myCreate = function (o) {
        function F() {
        }
        F.prototype = o;
        return new F;
    };
    function A() {
        this.a = 123;
        this.say = function () {
            console.log('say');
        }
    }
    A.prototype.mess = 'mess';
    // 通过原型式继承 将父类的 公有属性 继承为子类的公有属性
    // B.prototype = Object.create(A.prototype);
    B.prototype = Object.myCreate(A.prototype);
    B.prototype.constructor = B;
    function B() {
        A.call(this); // 将父类的私有属性继承为子类的实例的私有属性
    }
    var b = new B;
    console.log(b);


六、冒充对象继承


    // 冒充对象继承:在子类构造函数中 生成一个父类的实例 然后把父类的实例当做一个普通对象 进行遍历 将遍历出来的私有和公有 复制一份 作为 子类的私有属性
    // 把父类的公有和私有 继承过来作为子类的私有属性
    function A() {
        this.a = 123;
        this.say = function () {
            console.log('say');
        }
    }
    A.prototype.mess = 'mess';
    function B() {
        var temp = new A;
        for(var k in temp){
           this[k] = temp[k];
        }
    }
    var b = new B;
    console.log(b)
    

七、中间类继承

// 中间类 IE 屏蔽了
    var arr = [11,22,13,14];
    arr.shift();
    console.log(arr.__proto__ === Array.prototype);
    console.log(arr.shift); // 通过arr的原型链能够找到这个方法
    console.log(arr instanceof Array);
    function sum() {
        arguments.__proto__ = Array.prototype;
//      console.log(arguments instanceof Array);
        console.log(arguments.shift());
    }
    sum(1,34,5,6,5)

八、ES6继承


class Sub extends Super {
  constructor() {
    super()
  }
}

ES6继承其实是 寄生组合式继承实现,并将子类__proto__ 指向了父类本身

"use strict";

// 检验当前构造函数是否是通过new关键字调用
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

// 实现继承
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }
  // 寄生组合继承
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  // 子类__proto__指向父类
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

var Child = function (_Super) {
  _inherits(Child, _Super);

  function Child() {
    _classCallCheck(this, Child);
    
    // call继承
    return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this));
  }

  return Child;
}(Super);

ES6方法 设置原型对象 Object.setPrototypeOf

// 仅适用于Chrome和FireFox,在IE中不工作:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
  obj.__proto__ = proto;
  return obj; 
}

Object.create(proto, [propertiesObject]) 创建一个对象 并且这个对象的__proto__指向第一个参数proto

let obj1 = {id: 1}
let obj2 = Object.create(obj1)

console.log(obj2.__proto__ === obj1) // true