JavaScript物件導向那些東西-繼承

Brolly發表於2018-07-03

繼承

父類裡有些屬性方法 子類想把父類中的這些屬性方法 繼承過來給子類自己的例項也用用

( 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
複製程式碼

相關文章