JavaScript 是基於原型的語言,在es6 之前並沒有包含內置類的實現。但是這並不意味着類在JavaScript中毫無必要 。實際上類是一種很有用的工具,像其他編程語言中一樣,類在JavaScript中同樣可以起到重要的作用 。
下面我們手動封裝一個類庫,實現一個簡單的類的創建。
預期實現的功能:
1. 可以對新創建出來的類進行初始化
2. 可以擴展類的靜態屬性與方法
3. 可以擴展類的實例的屬性與方法
4. 在創建類時可以繼承其他類的屬性與方法。
代碼實現:
varClass=function (parent) {
// 初始化類庫
var _class =function () {
this.init.apply(this,arguments); //將原型init方法上的屬性同步到當前類
}
_class.prototype.init=function () { }
// 動態擴展
// 定義別名
_class.fn=_class.prototype;
_class.fn.parent=_class;
// 給類添加屬性和方法
_class.extend=function (obj) {
var extended = obj.extended;
for(var i in obj)
{
_class[i] = obj[i]
}
if(extended) extended(_class); //回調,用於類自身擴展成功後的提示
}
// 給類的實例添加屬性和方法
_class.include=function (obj) {
var included = obj.included;
for(var i in obj)
{
_class.fn[i]=obj[i];
}
if(included) included(_class);// 回調,用於類實例擴展後的提示
}
// 給類添加繼承
if(parent)
{
var subclass=function () { };
subclass.prototype =parent.prototype;
_class.prototype = new subclass;
}
return _class;
}
// 使用“類”庫
var Cat = new Class();
Cat.prototype.init=function(name,color) { // 對類進行初始化,修改爲帶有參數的類
this.name = name;
this.color = color;
this.like = function () {
return "fish";
}
}
var cat = new Cat("mini","yellow");
console.log( cat.name + " is"+ cat.color + " it like "+ cat.like());
var Animal = new Class();
Animal.prototype.init=function () {
this.feed = "nurse";// 動物的餵養方式是哺乳
}
Animal.extend({ // 擴展類的靜態屬性和方法
extended:function (_class){ //設置回調函數,用於擴展成功後的顯示
console.log(_class+"is extended");
},
ClassName:"Animal"
})
Animal.include({ // 擴展類實例的屬性和方法
walk:"crawl",
birth:function () {
console.log("胎生哺乳");
}})
console.log(Animal.ClassName);
var Dog = new Class(Animal);
var dog = new Dog();
console.log(dog.feed);
console.log(dog.walk);
在開始分析上邊的代碼之前先和大家討論一下原型的概念,關於原型有幾個重要的點是我們需要了解的:
1. 我們將原型也稱爲“模板對象”,它上邊的屬性用來初始化一個新對象 。
2. 任何對象都可以作爲另一個對象的原型對象,以此來實現屬性共享 。
3. 我們也可以將原型理解爲某種形式的繼承。
代碼詳解:
首先因爲我們的最終目的是想通過 Class函數 ,返回給我們一個構造函數 用於表示我們新創建的類 。所以只需要在Class 函數定義另一個構造函數(_class ),最終將其返回出來就好了。
在函數_class 的原型上定義一個init方法,並運用apply方法使_class繼承自身prototype.init 方法。這樣,當我們在新類的prototype.init 方法上進行初始化設置時,就會將內容同步到當前的類中。
關於屬性和方法的擴展 。當我們要擴展的是類的靜態屬性和方法,只需要將傳入的對象上的屬性都複製到當前對象就可以了 。而如果我們需要對當前類的實例也進行屬性和方法的擴展,就需要將傳入的對象上的屬性都複製到當前類的原型上 。 當然在這裏我們加了一個回調函數的功能,用於擴展成功後的顯示 (實際上如果你不能很好地理解,去除這一部分同樣是可以的)。
給“類”庫添加繼承,我們傳入的參數是一個可選的父類。注意如果將parent直接傳入Class構造函數則所有子類都必然共享一個原型,這不是我們想要的結果。當然也不可以直接將parent的prototype與_class的prototype進行綁定,因爲一旦這樣做了,任何一方發生改變,另一個也將隨之發生改變,這同樣不是我們想要的結果。這裏還有非常重要的一點需要我們記住:只有實例的屬性纔會被繼承 。在這裏我們通過創建匿名函數的小技巧,避免了在繼承parent類的時候創建parent類的實例 。