JavaScript 繼承方式詳解

JavaScript 繼承方式的概念

js 中實現繼承有兩種經常使用方式:數組

原型鏈繼承(對象間的繼承)
類式繼承(構造函數間的繼承)

JavaScript不是真正的面向對象的語言,想實現繼承能夠用JS的原型prototype機制或者callapply方法app

在面向對象的語言中,可使用類來建立一個自定義對象,固然ES6中也引入了class來建立類。在這以前,咱們須要使用js的原型來建立自定義對象。函數

原型繼承與類繼承

類繼承是在子類型構造函數的內部調用父類型的構造函數this

function Super (){
    this.colors = ["blue","red"];
}

function Sub () {
    Super.call(this);
}

原型式繼承是藉助已有的對象建立新的對象,將子類的原型指向父類。prototype

function Super (id) {
    this.id = id;
}
Super.prototype.toString = function () {
    return 'Super' + this.id;
}

function Sub (id) {
    this.id = id;
}

Sub.prototype = new Super();  // 這句即原型式繼承的核心代碼

原型鏈繼承

爲了讓子類繼承父類的屬性和方法,首先須要定義一個構造函數,而後將父類的實例賦值給構造函數的原型。code

function Parent () {
    this.name = 'Parent';
}

function Child () {
    this.age = 10;
}
Chid.prototype = new Parent();  // Chid繼承Parent,造成原型鏈

var test = new Child();
console.log(test.name) // Parent
console.log(test.age)  // 10    獲得被繼承的屬性

// 繼續原型鏈繼承
function Brother () {
    this.weight = 60;
}
Brother.prototype = new Child();
var peter = new Brother();
console.log(peter.name)  //繼承了Child和Parent,輸出Parent
console.log(peter.age)  // 輸出10

全部的構造函數都繼承自Object,它是自動完成繼承的並不須要咱們手動繼承,那麼接着看它們的從屬關係對象

肯定原型和實例的關係

能夠經過兩種方式肯定原型和實例的關係,經過操做符instanceofisPrototypeof()方法繼承

console.log(peter instanceof Object); //true
console.log(test instanceof Brother)  //false,test是brother的父類
console.log(peter instanceof Child) //true
console.log(peter instanceof Parent)  //true

只要是原型鏈中出現過的原型,均可以說是改原型鏈派生的實例的原型。所以,isProtptypeof()方法也會返回trueip

在JS中,被繼承的函數成爲父類(或者 基類、超類),繼承的函數成爲子類(派生類)。使用原型繼承主要有兩個問題,一是字面量重寫原型會中斷關係,使用引用類型的原型,而且子類型沒法給父類型傳遞參數。原型鏈

僞類解決引用共享和超類型沒法傳參的問題,咱們能夠採用'類式繼承'的方式

類式繼承(借用構造函數)

function Parent (age) {
    this.colors = ["blue","red","green"];
    this.age = age;
}

function Child (age) {
    Parent.call(this,age);
}

var peter = new Child(20);
console.log(peter.age) //20
console.log(peter.colors) //blue,red,green

peter.colors.push("white");
console.log(peter.colors) //blue,red,green,white

借用構造函數雖然解決了上面兩張問題,但沒有原型,因此咱們須要原型鏈+借用構造函數的模式,這種模式成爲組合繼承

組合繼承

function Parent (age) {
    this.colors = ["blue","red","green"];
    this.age = age;
}

Parent.prototype.run = function () {
    return this.colors + ' is ' +this.age;
}
function Child (age) {
    Parent.call(this,age);  //對象冒充,給父類型傳參
}
Child.prototype = new Parent();  //原型鏈繼承

var peter = new Child(20);
console.log(peter.run()); //blue,red,green is 20

組合繼承是一種比較經常使用的方法,思路是使用原型鏈實現對原型屬性和方法的繼承,借用構造函數來實現對實例屬性的繼承。這樣,既實現了原型上定義方法的函數複用,又保證每一個實例都有本身的屬性。

call()與apply()的用法:調用一個對象的一個方法,用另外一個對象替換當前對象。

call(thisObj,Object);  // call接收一個對象
apply(thisObj,[argArray])  //apply接收一個數組

原型式繼承

這種繼承藉助原型並基於已有的對象建立新的對象,同時還不用建立自定義類型的方式成爲原型式繼承

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

var box = {
        name : 'peter',
        arr : ['blue','red','green']
    };

var b1 = obj(box);
console.log(b1.name) // peter

b1.name = 'jack';
console.log(b1.name) //jack

console.log(b1.arr) //blue,red,green
b1.arr.push('white') //blue,red,green,white

var b2 = obj(box);
console.log(b2.name) // peter
console.log(b1.arr) //blue,red,green

原型式繼承首先在obj()函數內部建立一個臨時性的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回這個臨時類型的新實例。

寄生式繼承

這種繼承方式是把原型式+工廠模式結合起來,目的是爲了封裝建立的過程。

function create(o){
        var f = obj(o);
        f.run = function () {
            return this.arr;//一樣,會共享引用
        };
        return f;
    }

組合式繼承的問題

組合式繼承是JS最經常使用的繼承模式,但組合繼承的父類型會在使用過程當中被調用兩次,一次是建立子類型的時候,另外一次是在子類型構造函數的內部

function Parent(name){
        this.name = name;
        this.arr = ['哥哥','妹妹','父母'];
    }

    Parent.prototype.run = function () {
        return this.name;
    };

    function Child(name,age){
        Parent.call(this,age);//第二次調用
        this.age = age;
    }

    Child.prototype = new Parent();//第一次調用

以上代碼是組合繼承,那麼寄生組合繼承解決了兩次調用的問題

寄生組合繼承

function obj() {
    function F() {}
    F.prototype = o;
    return new F();
}
function create(parent,test) {
    var f = obj(parent.prototype); //建立對象
    f.constructor = test; //加強對象
}
function Parent(name){
        this.name = name;
        this.arr = ['brother','sister','parents'];
    }

Parent.prototype.run = function () {
        return this.name;
    };

function Child(name,age){
        Parent.call(this,name);
        this.age =age;
    }
inheritPrototype(Parent,Child);  //經過這裏實現繼承

var test = new Child('peter',20);
test.arr.push('new');
console.log(test.arr);  //brother,sister,parents,new
console.log(test.run());  //只共享了方法

var test2 = new Child('jack',22);
console.log(test2.arr);  //引用問題解決

call和apply

call和apply能夠用來改變函數中this的指向:

// 定義一個全局函數
function f () {
    console.log(this.name);
}
// 定義一個全局變量
var name = 'peter';
var obj = {
    name: 'jack';
};

f.apply(window); //apple, 此時this 等於window  至關於window.f()
f.apply(obj);  //jack, 此時this === obj 至關於obj.f()
相關文章
相關標籤/搜索