一種JavaScript類繼承和super方法呼叫的實現
在設計實現一種Java、Objective-C與JavaScript混合程式設計的程式設計風格JSAppSugar時,需要 JavaScript 語言支援類導向的程式設計風格,然而JavaScript本身是原型導向(Prototype-based)的,因此在JavaScript中也有很多種實現類繼承的方式。這裡介紹一下JSAppSugar中實現類繼承和super方法呼叫的實現方式。
檢視原始碼:https://github.com/JSAppSugar/JSA4JS/blob/master/src/JSAppSugar.js
該JS檔案設計用於JSA4Cocoa和JSA4Java中,以實現JavaScript與Java、Objective-C混合程式設計,也可單獨使用在普通JavaScript程式中實現類繼承和父類方法呼叫。
類繼承程式設計風格
JSAppSugar程式設計風格定義一個基礎類、子類,以及在子類中呼叫父類方法。
$class("my.sample.Person",{ name : "Unknown", $init : function(name){ if(name) this.name = name; }, eat : function(food){ return this.name + " is eating "+food; } }); $class("my.sample.Programmer",{ $extends : "my.sample.Person", title : "Programmer", $init : function(title,name){ $super(name); if(title){ this.title = title; } } eat : function(food){ return this.title + " " + $super.eat(food); } });
其中 $init 表示為構造器方法
呼叫:
var bob = new my.sample.Person("Bob");
var bobDoing = bob.eat("Salad"); //函式將返回字串 Bob is eating Salad
var bill = new my.sample.Programmer("CTO","Bill");
var billDoing = bill.eat("Sausage"); //函式將返回字串 CTO Bill is eating Sausage
類繼承的實現
JSAppSugar實現類繼承的方式採用了原型鏈方式:
initializing = true;//閉包屬性,用於在建立原型鏈父類物件時避免呼叫父類的構造器方法 JSAClass.prototype = new SuperClass();//構造原型鏈 initializing = false; JSAClass.prototype.constructor = JSAClass;//不是構造原型鏈繼承的必須,設定目的是為了通過類物件查詢到類原型
構造器方法的實現(這裡只列出了關鍵程式碼):
JSAClass = function(){
if(!initializing && this.$init){//initializing為判斷是否在構造原型鏈,如果是則忽略執行構造器方法
this.$init.apply(this, arguments);
}
}
通過$super呼叫父類方法
呼叫父類方法的原理就是找到父類原型類物件上的同名方法,然後使用function.apply方法呼叫這個方法,同時將this設定為當前呼叫物件。
前面DEMO中的$super物件和方法實際是不存在的,$class類定義方法會在類定義時將原始碼轉換為 this.$super("funcName")的形式。$super方法將返回當前呼叫鏈上父類的方法。$super()則會轉換為this.$super("$init") 也就是父類的$init構造器方法。
$super方法定義:
JSAClass.prototype.$super = function(){
var func = this.$SuperClass[name];//閉包方法對$SuperClass進行動態賦值
var $this = this;
return function(){
return func.apply($this,arguments);
};
}
JSAppSugar使用查詢父類原型類物件的方式為閉包引用:
var SuperClassProto = SuperClass.prototype;//SuperClass是定義中 $extends對應的類的構造器方法
if(typeof define[key] == "function" && /\$super/.test(define[key])){//函式定義使用了$super時
JSAClass.prototype[key] =(
function(defineFunction){//定義一個閉包方法
if(engine.f_redefine) defineFunction = engine.f_redefine(defineFunction);//將$super關鍵字替換為this.$super('functionName')
return function(){
var t = this.$SuperClass;
this.$SuperClass = SuperClassProto;//通過閉包引用,將執行當前方法的父類原型賦值到當前物件,以便$super方法快速獲取
var result = defineFunction.apply(this,arguments);
this.$SuperClass = t;
return result;
}
}
)(define[key]);
}else{
JSAClass.prototype[key] = define[key];
}
通過閉包引用,實現快速查詢當前方法所屬類的父類方法。