1. 程式人生 > >一種JavaScript類繼承和super方法呼叫的實現

一種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檔案設計用於JSA4CocoaJSA4Java中,以實現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];
}

通過閉包引用,實現快速查詢當前方法所屬類的父類方法。