Skip to content

Latest commit

 

History

History
155 lines (138 loc) · 5.49 KB

README.md

File metadata and controls

155 lines (138 loc) · 5.49 KB

XClass 一个小巧精致的javascript Class系统

特点

  • 实现继承机制,使用简单
  • 简单的Implements实现多继承
  • 较完美的实现super机制
    • 使用Super调用父类构造函数
    • 如果没有显式调用Super 则默认以无参数形式调用父类构造函数
    • 使用Super访问父类的方法(特别是被子类覆盖掉的)
  • 简单的AOP机制,允许对类方法进行代理
  • 简单的mixin机制,允许对现有类进行植入式扩展
  • 核心类代码只有50多行 简洁明了

说(che)明(dan)

其实在ES6已经支持class的时代再推出Class系统,多少有些多此一举。
但是对于类的情结,在我当年从Java转行js的时候就已经深深埋下,以前有过几次实现,都不是十分完美和顺手。

其实在js基于原型链的继承成体系下,类系统的实现手段都是殊途同归的~
说白了,类系统就是语法糖,关键就看谁家的糖更甜,用起来更顺手。
ES6的class很甜 语法形式也近乎接近Java/C#之流了 然后还是有很多不爽的地方
比如构造函数里 必须手动调用super() 而且一些做一些植入性扩展也不是十分方便
所以又重新实现了一个~~

主要特点还是对于super的实现,也就是在子类构造函数里没有显式调用super时会默认调用无参super()
但是不好的一点是 这个默认调用会在子类构造函数执行完毕之后调用
不过无伤大雅,如果有次序性的要求的话 您还是手动调用super吧

其次另外加入几个糖

一个是多继承 implements 这个就比较简单了 纯属prototype拷贝
一个是mixin 基本上是prototype拷贝,不同的地方是会在构造函数里植入逻辑。
这颗糖是我写MVC框架的一个需求,就是框架里的一些核心的类暴露给用户,让用户用mixin的方式植入逻辑,进行扩展。
另外一个是AOP,这个其实就是对类方法进行动态代理(当然 ES6的proxy更牛逼 但是不敢用 proxy是没办法to ES5的)

用法

1、创建类

Class(classDefinition,noInitParent=false);
@classDefinition 传入的类定义
@noInitParent 可选 是否默认调用父类的构造函数 在有【只需要继承父类的prototype而不需要初始化父类】的需求时 设置为true 默认false

//创建一个类
var A = Class({
    //构造函数
    constructor : function(a) {
        console.log('class A construct with params:', [].join.call(arguments, ','));
        this.prop_A = a;
        this.prop = 'A';
    },
    //成员函数
    funcA       : function() {
        console.log('call funcA:', this.prop_A);
    },
    func        : function() {
        console.log('call func at[class A] with the private prop:', this.prop)
    }
});
var a=new A();//实例化类
a.func();//对实例操作
2、继承

在类定义里指定Extends字段 指定其父类
也可以指定Implements 实现多“继承” Implements的值可以是一个类也可以是一个类的数组
注意Implements只是简单的把指定的类的prototype拷贝到该类的的prototype 不存在继承性 无法用super访问到

//继承
var B = Class({
    //继承A
    Extends     : A,
    Implements  : [Impl1,Impl2],
    constructor : function(b) {
        //调用父类的构造函数
        this.Super(2);
        console.log('class B construct with params:', [].join.call(arguments, ','));
        this.prop_B = b;
        this.prop = 'B';
    },
    funcB       : function() {
        console.log('call funcB:', this.prop_B);
        console.log('prop_A:', this.prop_A);
    },
    funcX       : function() {
        this.Super.funcA();
    },
    func        : function() {
        console.log('call func at[class B] with the private prop:', this.prop)
        //调用父类的func方法
        this.Super.func();
    }
});
var b=new B();
b.func();
3、AOP

使用 AClass.addAspect(object)对类方法添加切面
object是以键值对的方式提供
value是一个函数
key的规则如下
切面位置@方法名
[before|after|around]@[methodName|*]
切面位置:

before是在目标方法执行前执行
after是在目标方法执行后执行
around是在目标方法执行前后各执行一次
methodName 目标方法名 如果是*就是给该类的所有方法添加切面

切面函数的参数和目标方法的参数一致
而this是一个特殊对象(advice)
{
host:'类实例 相当于目标方法的this',
pos:'切面位置 就是上述的那几个',
ret:'目标方法的返回值 这个只有after切面以及around切面的第二次执行会得到这个值'
} after切面函数和around切面函数如果返回一个非undefined的值 则作为目标方法的实际返回值

//AOP
B.addAspect({
    'after@funcB':function(){
        console.log('advice:',this);
        return 123;
    }
});
b.funcB();
/*结果
call funcB: haha
prop_A: 2
advice: { host: { prop_B: 'haha', prop: 'B' },
  methodName: 'funcB',
  pos: 'after',
  ret: undefined }
*/
4、Mixin

使用 AClass.mixin(object) 扩展该类
作为参数的object 有一个特殊方法 init
这个方法会在该类实例化的时候调用 (就是把这个init函数植入到该类构造函数后面执行)
其余的方法会被塞到类的prototype里

B.mixin({
    init:function(){
        console.log('init mixin',this);
    },
    newFunc:function(){
        this.Super.funcA();
    }
});