JavaScript繼承(一)——原型鏈中提出原型鏈繼承的兩個問題:一是原型的數據共享問題,二是建立子類型的實例時,不能向父類型的構造函數中傳遞參數。這兩個問題的根源仍是在於使用原型模式建立對象,能夠參考JavaScript建立對象(三)——原型模式。本篇文章來討論一下這兩個問題的解決方案——借用構造函數繼承。javascript
借用構造函數也被稱爲僞造對象或經典繼承。這種技術的基本思想至關簡單,即在子類型構造函數的內部調用父類型構造函數。在Javascript中,從本質上來說,咱們聲明的函數不過是Function
類型在特定執行環境中執行代碼的實例,所以能夠經過使用apply
方法在建立子類型的對象時,把子類型的執行環境傳遞給父類型。java
首先咱們來看下apply
的用法,以下代碼所示:數組
var nums = { n1: 10, n2: 8 } function sum(a, b) { console.log(this); console.log(arguments); } sum(2, 4);//this: window 。 arguments:[2, 4] sum.apply(nums, [2, 4]);//this: nums 。 argument:[2, 4]
直接調用sum
時,函數sum
中的this
指代的是調用sum
的執行環境的變量對象,即全局變量對象window
,使用sum.apply
的形式調用,第一個參數傳的nums
,第二個參數傳的數組,那麼sum
在執行時,this
指的就是nums
,arguments
指的就是傳入的參數數組。app
明白了apply
的用法,下面咱們來看借用構造函數繼承,如代碼所示:函數
function Human(){ this.colors = ['yellow', 'white', 'black']; } function Person(){ Human.apply(this); } var p1 = new Person(); p1.colors.push('brone'); console.log(p1.colors);//["yellow", "white", "black", "brone"] var p2 = new Person(); console.log(p2.colors);//["yellow", "white", "black"]
Human
中聲明瞭一個數組colors
,Person
中使用apply
的方式調用Human
,並把this
傳遞給Human
,以實現Person
繼承Human
,當執行new Person()
時,函數Person
中的this
指的就是新建立出的對象,接着就會傳遞到Human
,因而這個新對象上就有了colors
屬性,實現了繼承的效果,經過這種方式,每次建立對象時,子類型中都生成了父類型中定義的屬性的一個副本,沒有共享屬性,也就不存在數據共享的問題了。this
另外,對於在子類型構造函數中向超類型構造函數傳遞參數的問題來看如下示例:.net
function Human(){ this.name = arguments[0]; } function Person(name){ Human.apply(this, arguments); //從新定義子類型的屬性,防止被父類型覆蓋 this.age = 18; } var p1 = new Person('Bob'); console.log(p1.name);//Bob console.log(p1.age);//18
建立Person
的實例時傳遞了一個參數Bob
,在構造函數中經過apply
的方式又將參數傳遞給了Human
,在Human
中進行賦值操做實際上就是給新建立的實例設置name
屬性。爲了防止子類型的屬性被重寫,能夠在調用超類型的構造函數以後再添加應該在子類型中定義的特有的屬性。code
若是僅僅是借用構造函數,那麼也將沒法避免構造函數模式存在的問題——方法都在構造函數中定義,所以函數複用就無從談起了。並且,在超類型的原型中定義的方法,對子類型而言也是不可見的,由於整個過程當中並無建立超類型的對象,根據原型鏈的特徵,天然也就沒法使用超類型的原型,結果繼承鏈上的全部類型都只能使用構造函數模式建立對象。考慮到這些問題,借用構造函數繼承的技術也是不多使用的。對象
讀者也許已經想到能夠組合使用構造函數和原型鏈繼承,就像組合使用構造函數模式和原型模式建立對象同樣,那麼下一篇文章就來談談組合繼承。blog