js 繼承小結

BSOXDTDTPJHVCKFD發表於2018-04-09

js的是利用原型鏈實現物件導向的類行為,和oo語言中的類有這巨大的不同。

下面講述幾種繼承的方式以及一些擴充套件。

我對js的原型繼承的一些理解:

  • js中只有函式存在prototype這個屬性,代表原型,object中只有__proto__屬性
  • js中函式也是object的一種,所以函式同時存在prototype和__proto__屬性,但是這是兩個完全不同的屬性
  • js中所有的object都是繼承於底層的Object.prototype
  • js中所有函式的建構函式是Function
  • Function.prototype是函式(這就是解決原型鏈迴圈的關鍵),正常函式的prototype屬性都是一個object
  • js中例項的__proto__總是指向它建構函式的prototype(這個也是原型鏈的重點)
// 父類
function Animal() {
  this.name = 'animal';
  this.type = 'animal';
}
Animal.prototype.say = function () {
  console.log(this.name);
}
複製程式碼

原型鏈繼承, 通過子類的prototype是父類的一個例項,從而實現一條原型鏈,如下:(Sub代表子類,sub代表子類的一個例項,Super代表父類,super代表父類的例項)

sub.__proto__ = Sub.prototype = super, super.__proto__ = Super.prototype
複製程式碼

缺點:父類的資料物件處於子類的prototype中,prototype是所有子類例項所共有的,所以對於引用型別的資料存在一改全改的問題

function Dog() {
  this.name = 'dog'
}
Dog.prototype = new Animal();

var dog = new Dog();
dog.say();
複製程式碼

dog的詳細結構

js 繼承小結

為了解決上面的原型鏈缺點,利用建構函式的優點(組合繼承)

function Cat(data) {
  Animal.call(this, data); // 將父類的資料型別在子類複製一份
  this.name = 'cat';
}
Cat.prototype = new Animal();

var cat = new Cat(); // 建構函式再次儲存了一份父類的資料型別
cat.say();
複製程式碼

cat詳細結構,可以發現有個缺點就是父類的資料型別被儲存了2遍

js 繼承小結

ES5中有個api:

Object.create(object1, object2): 返回一個object,object的prototype = object1,object2代表prototype中新增或者覆蓋的屬性
eg:
var cat = Object.create(new Animal(), {
    name: {
        value: 'cat'
    }
})
複製程式碼

上述cat詳細結構

js 繼承小結
參考紅寶書(js高階程式設計):這種方式稱為原型式繼承

function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
複製程式碼

結合原型式繼承和建構函式,可以有如下的繼承方式

function Bird(data) {
  Animal.call(this, data);
  this.name = 'bird';
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;

var bird = new Bird();
bird.say();
複製程式碼

bird的詳細結構:解決了原型鏈繼承和組合繼承的問題

js 繼承小結

建構函式有個特點,就是return的是一個物件的時候new的時候生成的就是這個物件,否則將按照正常的原型鏈生成物件。eg:

function Test() {
    var arr = [1, 2, 3];
    this.arr = arr;
    return arr;
}
Test.prototype.say = function() {
    console.log(this.arr);
}
var test = new Test();
test.say // undefined
複製程式碼

這就是傳說中的寄生繼承,寄生繼承返回的物件跟原型鏈基本保持一致,唯一的問題就是丟失了子類的建構函式吧,以及第一層prototype

function Horse() {
  var o = Object.create(new Animal());
  o.name = 'horse';
  return o;
}
var horse = new Horse();
horse.say();
複製程式碼

horse詳細結構:

js 繼承小結

擴充套件題目: 如何繼承陣列?

// 實現陣列繼承
var push = [].push;
function myArray() {
  var array = Array.apply(this, arguments);
  array.__proto__ = myArray.prototype;
  return array;
}
myArray.prototype = Object.create(Array.prototype)
myArray.prototype.constructor = myArray;
myArray.prototype.push = function(value) {
  console.log('push:', value);
  push.call(this, value);
}
var list = new myArray(1, 2, 3);
list.push(4);
複製程式碼

因為陣列的獨特性所以利用了寄生繼承以及object的__proto__屬性,以及原型式繼承

相關文章