在設計實現一種Java、Objective-C與JavaScript混合編程的編程風格JSAppSugar時,須要 JavaScript 語言支持類導向的編程風格,然而JavaScript自己是原型導向(Prototype-based)的,所以在JavaScript中也有不少種實現類繼承的方式。這裏介紹一下JSAppSugar中實現類繼承和super方法調用的實現方式。javascript
查看源碼:https://github.com/JSAppSugar/JSA4JS/blob/master/src/JSAppSugar.jsjava
該JS文件設計用於JSA4Cocoa和JSA4Java中,以實現JavaScript與Java、Objective-C混合編程,也可單獨使用在普通JavaScript程序中實現類繼承和父類方法調用。git
JSAppSugar編程風格定義一個基礎類、子類,以及在子類中調用父類方法。github
$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實現類繼承的方式採用了原型鏈方式:app
initializing = true;//閉包屬性,用於在建立原型鏈父類對象時避免調用父類的構造器方法 JSAClass.prototype = new SuperClass();//構造原型鏈 initializing = false; JSAClass.prototype.constructor = JSAClass;//不是構造原型鏈繼承的必須,設置目的是爲了經過類對象查找到類原型
構造器方法的實現(這裏只列出了關鍵代碼):函數
JSAClass = function(){ if(!initializing && this.$init){//initializing爲判斷是否在構造原型鏈,若是是則忽略執行構造器方法 this.$init.apply(this, arguments); } }
調用父類方法的原理就是找到父類原型類對象上的同名方法,而後使用function.apply方法調用這個方法,同時將this設置爲當前調用對象。this
前面DEMO中的$super對象和方法實際是不存在的,$class類定義方法會在類定義時將原代碼轉換爲 this.$super("funcName")的形式。$super方法將返回當前調用鏈上父類的方法。$super()則會轉換爲this.$super("$init") 也就是父類的$init構造器方法。prototype
$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]; }
經過閉包引用,實現快速查找當前方法所屬類的父類方法。