正式學習以前,咱們先溫習下原型鏈的相關知識:(圖片來源:Javascript的原型鏈圖)es6
圖片中有待補充的內容包括:每個new出來的對象或者函數有一個constructor
屬性指向構造函數,例子以下:bash
var b = new Function();
b.constructor === Function; //true
複製代碼
先對照圖片對下面的例子作出解釋app
var a = {};
console.log(a.prototype); //=> undefined (實例化出來的對象)
var b = function(){};
console.log(b.prototype); //=> {} (對應Foo.prototype)
var c = 'Hello';
console.log(c.prototype); //=> undefined (實例出來的字符串)
複製代碼
補充知識點:函數
var o1 = new Object();
typeof(o1); //=> 'object'
var o2 = {};
typeof(o2); //=> 'object'
typeof(Object); //=> 'function'
複製代碼
咱們不可貴出一個結論,typeof所的到的對象類型和對象的__proto__
類型相同,Function.prototype
除外。學習
typeof Function.prototype.__proto__ ; //=> 'object'
複製代碼
(相關閱讀:ES5和ES6中的繼承/ECMAScript 繼承機制實現)ui
將父類實例當作子類構造函數的原型this
//先來個父類,帶些屬性
function Super(){
this.flag = true;
}
//爲了提升複用性,方法綁定在父類原型屬性上
Super.prototype.getFlag = function(){
return this.flag;
}
//來個子類
function Sub(){
this.subFlag = false;
}
//實現繼承
Sub.prototype = new Super;
//給子類添加子類特有的方法,注意順序要在繼承以後
Sub.prototype.getSubFlag = function(){
return this.subFlag;
}
//構造實例
var es5 = new Sub;
複製代碼
優勢:es5
缺點:spa
例子以下:prototype
function Super(){
this.flag = true;
}//父類
function Sub(){
this.subFlag = false;
}//子類
Sub.prototype = new Super();//繼承
var obj = new Sub();//子類實例1
obj.flag = flase; //修改以後,因爲是原型上的屬性,以後建立的全部實例都會受到影響
var obj_2 = new Sub();//子類實例2
console.log(obj.flag) //false;
複製代碼
call方法,它的第一個參數用做 this 的對象。其餘參數都直接傳遞給函數自身。
function sayColor(sPrefix,sSuffix) {
alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "blue";
sayColor.call(obj, "The color is ", "a very nice color indeed.");
複製代碼
在這個例子中,函數 sayColor()
在對象外定義,即便它不屬於任何對象,也能夠引用關鍵字 this
。對象 obj
的 color
屬性等於blue
。調用call()
方法時,第一個參數是 obj
,說明應該賦予sayColor()
函數中的 this 關鍵字值是 obj。第二個和第三個參數是字符串。它們與 sayColor(
) 函數中的參數sPrefix
和 sSuffix
匹配,最後生成的消息 "The color is blue, a very nice color indeed."
將被顯示出來。
優勢:
缺點:
function A (color){
this.color = color
}
A.prototype.sayColor = function(){
console.log(this.color)
}
function B(sColor,name){
A.call(this,sColor);
this.name = name;
}
B.prototype = new A();
B.prototype.sayName = function(){
console.log(this.name)
}
var b = new B("red","nik")
複製代碼
請注意:
在執行 B.prototype = new A();
以後,B.prototype.constructor
指向A
。然而constructor
的定義是要指向原型屬性對應的構造函數的,B.prototype
是B
構造函數的原型,因此應該添加一句糾正:
B.prototype.constructor = B;
複製代碼
相關閱讀(Class的繼承)
Class能夠經過extends
關鍵字實現繼承,這比 ES5 的經過修改原型鏈實現繼承,要清晰和方便不少。
class Point {
}
class ColorPoint extends Point {
}
複製代碼
以上代碼實際等同於下面的代碼:
class ColorPoint extends Point {
}
// 等同於
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
複製代碼
上面代碼中,constructor
方法和toString
方法之中,都出現了super
關鍵字,它在這裏表示父類的構造函數,用來新建父類的this對象。
子類必須在constructor
方法中調用super
方法,不然新建實例時會報錯。這是由於子類沒有本身的this
對象,而是繼承父類的this
對象,而後對其進行加工。若是不調用super
方法,子類就得不到this
對象。
**注意:**在子類的構造函數中,只有調用super
以後,纔可使用this
關鍵字,不然會報錯。這是由於子類實例的構建,是基於對父類實例加工,只有super
方法才能返回父類實例。
super
這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。在這兩種狀況下,它的用法徹底不一樣。
第一種狀況,super
做爲函數調用時,表明父類的構造函數。ES6 要求,子類的構造函數必須執行一次super
函數。
super
雖然表明了父類A
的構造函數,可是返回的是子類B
的實例,即super
內部的this
指的是B
,所以super()
在這裏至關於A.prototype.constructor.call(this)
。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
複製代碼
上面代碼中,new.target
指向當前正在執行的函數。能夠看到,在super()
執行時,它指向的是子類B
的構造函數,而不是父類A
的構造函數。也就是說,super()
內部的this
指向的是B
。
第二種狀況,super
做爲對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
複製代碼
上面代碼中,子類B當中的super.p()
,就是將super
看成一個對象使用。這時,super
在普通方法之中,指向A.prototype
,因此super.p()
就至關於A.prototype.p()
。
因爲super
指向父類的原型對象,因此定義在父類實例上的方法或屬性,是沒法經過super
調用的。
以下:
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
複製代碼
上面代碼中,p
是父類A
實例的屬性,super.p
就引用不到它。
若是屬性定義在父類的原型對象上,super
就能夠取到。以下:
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
複製代碼
ES6 規定,經過super
調用父類的方法時,方法內部的this
指向子類。
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
複製代碼
待補充。。。