說到繼承,先來個開胃菜:求方法實現 f(6).num(1),結果等於6。
想到方法了嗎?不如先來看一段代碼:
var $ = function(selector){
return new $.prototype.init(selector)
};
$.prototype = {
constructor:$,
init:function(selector){
this.length = selector
},
product(val){
return val * this.length
},
length:0
};
$.prototype.init.prototype = $.prototype; //關鍵
console.log($(6).product(1));
如果研究過jQuery源碼,應該瞭解,這就是jQuery面向對象的基本思路,關鍵點在繼承,子類繼承父類的原型屬性和方法。jQuery的巧妙在於:子類同時也是父類的一個原型方法。
1、利用 call、apply 繼承
function Food(i){
this.name = i;
}
Food.prototype.use = "eat";
function Fish(x,y,z){
Food.apply(this,[z]); //Food.call(this,z) x可省略
this.color = x;
this.weight = y
}
var fish = new Fish("golden","1kg","Salmon");
console.log(fish.name,fish.use);//fish.use:undefined 說明不能繼承父類原型的屬性和方法
缺點:不能繼承父類**原型**的屬性和方法
2、原型鏈繼承
function Food(i){
this.name = i;
}
Food.prototype.use = "eat";
var food = new Food("Cod");
function Fish(x,y){
this.color = x;
this.weight = y
}
Fish.prototype = new Food("Salmon");
console.log(Fish.prototype.constructor === Food);//true 此時,構造函數已經指向了Food
Fish.prototype.constructor = Fish; //因爲“Fish.prototype = new Food("Salmon");”將構造函數指向了Food,需要再次指回Fish
var fish = new Fish("golden","1kg");
console.log(fish.name,fish.use);
缺點
· 繼承時,要再次生成父類實例,耗費內存;
· 子類要添加屬性和方法,只能在“Fish.prototype = new Food("Salmon");”後面。
3、直接繼承prototype
function Food(i){
this.name = i
}
function Fish(x,y){
this.color = x;
this.weight = y
}
Fish.prototype = Food.prototype; //相當於完全刪除了prototype 對象原先的值
console.log(Fish.prototype.constructor === Food); //true,同樣,構造函數已經指向了Food
Fish.prototype.constructor = Fish;
console.log(Food.prototype.constructor === Fish); //true true 問題所在:父類和子類的構造函數,都指向了同一個對象:Fish
var fish = new Fish("golden","1kg");
console.log(fish.name,fish.color);//fish.name:undefined 說明不能繼承父類構造函數內的屬性和方法
缺點:
· 只能繼承父類**原型**的屬性和方法,不能繼承父類構造函數內的屬性和方法;
· 父類和子類的構造函數都指向了同一個對象。
4、 Object.create(父類原型)
Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__。
* 基於方法3的優化,解決了父類和子類的構造函數指向同一個對象的問題。
function Food(i){
this.name = i
}
Food.prototype.use = "eat";
function Fish(x,y){
this.color = x;
this.weight = y
}
Fish.prototype = Object.create(Food.prototype);
Fish.prototype.constructor = Fish;
console.log(Food.prototype.constructor === Food,Fish.prototype.constructor === Fish); //true true
var fish = new Fish("golden","1kg","Salmon");
console.log(fish.name,fish.use); //undefined "eat"
缺點:只能繼承父類原型的屬性和方法,不能繼承父類構造函數內的屬性和方法;
5、call/apply + Object.create(父類原型)
function Food(i){
this.name = i
}
Food.prototype.use = "eat";
function Fish(x,y,z){
Food.call(this,z);
this.color = x;
this.weight = y
}
Fish.prototype = Object.create(Food.prototype);
Fish.prototype.constructor = Fish;
var fish = new Fish("golden","1kg","Salmon");
console.log(fish.name,fish.use);
這樣,前面的問題,都有效得到解決,算是比較完美的一個方案。
6、ES6 class
ES6中加入了靜態方法和屬性,也能通過 extends 繼承
class Food{
static staticFn(){
return "static-fn"
};
static staticVal = "static-val";
constructor(i){
this.name = i
};
info(){
return "demo-"+this.name
}
}
class Fish extends Food{
constructor(x,y,z){
super(z);
this.color = x;
this.weight = y
}
}
const fish = new Fish("golden","1kg","Salmon");
console.log(fish.name,fish.info());
console.log(Fish.staticFn(),Fish.staticVal);
最後,提前祝各位大小朋友:六一兒童節快樂!