1、對象冒充es6
其原理以下:構造函數使用 this 關鍵字給全部屬性和方法賦值(即採用類聲明的構造函數方式)。由於構造函數只是一個函數,因此可以使 Parent 構造函數
成爲 Children 的方法,而後調用它。Children 就會收到 Parent 的構造函數中定義的屬性和方法。例如,用下面的方式定義 Parent 和 Children:數組
]// 父類構造函數 var Parent = function(name){ this.name = name; this.sayHi = function(){ console.log("Hi! " + this.name + "."); } }; // 子類構造函數 var Children = function(name){ this.method = Parent; this.method(name); // 實現繼承的關鍵 delete this.method; this.getName = function(){ console.log(this.name); } }; var p = new Parent("john"); var c = new Children("joe"); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! joe. c.getName(); // 輸出: jo
原理:就是把 Parent 構造函數放到 Children 構造函數裏面執行一次。那爲何不直接執行,非要轉個彎把 Parent 賦值給 Children 的 method 屬性再執行呢?
這跟 this 的指向有關,在函數內 this 是指向 window 的。當將 Parent 賦值給 Children 的 method 時, this 就指向了 Children 類的實例。app
2、原型鏈繼承函數
衆所周知,JavaScript 是一門基於原型的語言,在 JavaScript 中 prototype 對象的任何屬性和方法都被傳遞給那個類的全部實例。原型鏈利用這種功能來實現繼承機制:this
// 父類構造函數 var Parent = function(){ this.name = "john"; this.sayHi = function(){ console.log("Hi! " + this.name + "."); } }; // 子類構造函數 var Children = function(){}; Children.prototype = new Parent(); // 實現繼承的關鍵 var p = new Parent(); var c = new Children(); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! john.
注意:調用 Parent 的構造函數,沒有給它傳遞參數。這在原型鏈中是標準作法。要確保構造函數沒有任何參數。spa
3、使用 call 或 applay 方法prototype
這個方法是與對象冒充方法最類似的方法,由於它也是經過改變了 this 的指向而實現繼承:code
// 父類構造函數 var Parent = function(name){ this.name = name; this.sayHi = function(){ console.log("Hi! " + this.name + "."); } }; // 子類構造函數 var Children = function(name){ Parent.call(this, name); // 實現繼承的關鍵 this.getName = function(){ console.log(this.name); } }; var p = new Parent("john"); var c = new Children("joe"); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! john. c.getName(); // 輸出: joe
apply 方法本人就不舉列了,它和 call 方法的區別在於它的第二個參數必須是數組。對象
4、混合方式blog
對象冒充的主要問題是必須使用構造函數方式,這不是最好的選擇。不過若是使用原型鏈,就沒法使用帶參數的構造函數了。如何選擇呢?答案很簡單,二者都用。
在 JavaScript 中建立類的最好方式是用構造函數定義屬性,用原型定義方法。這種方式一樣適用於繼承機制:
// 父類構造函數 var Parent = function(name){ this.name = name; }; Parent.prototype.sayHi = function(){ console.log("Hi! " + this.name + "."); }; // 子類構造函數 var Children = function(name, age){ Parent.call(this, name); // 實現繼承的關鍵 this.age = age; }; Children.prototype = new Parent(); // 實現繼承的關鍵 Children.prototype.getAge = function(){ console.log(this.age); }; var p = new Parent("john"); var c = new Children("joe",30); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! joe. c.getAge(); // 輸出: 30
5、使用Object.create 方法
Object.create 方法會使用指定的原型對象及其屬性去建立一個新的對象:
// 父類構造函數 var Parent = function(name){ this.name = name; }; Parent.prototype.sayHi = function(){ console.log("Hi! " + this.name + "."); }; // 子類構造函數 var Children = function(name, age){ Parent.call(this, name); // 實現繼承的關鍵 this.age = age; }; Children.prototype = Object.create(Parent.prototype); // 實現繼承的關鍵 Children.prototype.constructor = Children; // @ Children.prototype.getAge = function(){ console.log(this.age); }; var p = new Parent("john"); var c = new Children("joe",30); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! joe. c.getAge(); // 輸出: 30
@ 當執行 Children.prototype = Object.create(Parent.prototype) 這個語句後,Children 的 constructor 就被改變爲 Parent ,所以須要將 Children.prototype.constructor 重
新指定爲 Children 自身。
6、extends 關鍵字實現繼承
這個是 ES6 的語法糖,下面看下es6實現繼承的方法:
class Parent { constructor(name, age) { this.name = name; this.age = age; } } class Children extends Parent { constructor(name, age, job) { this.job = job; // 這裏會報錯 super(name, age); this.job = job; // 正確 } }
上面代碼中,子類的constructor
方法沒有調用super
以前,就使用this
關鍵字,結果報錯,而放在super
方法以後就是正確的。子類Children
的構造函數之中的super()
,表明調用父類Parent的構造函數。這是必須的,不然 JavaScript 引擎會報錯。
注意,super
雖然表明了父類Parent的構造函數,可是返回的是子類Children的實例,即super
內部的this
指的是Children,所以super()
在這裏至關於Parent.prototype.constructor.call(this)
。