jsklass-import

jsklass, framework for ES6 oop converted to Es5 in javascript, inspired from https://github.com/liangwengao/jsklass-import

Usage no npm install needed!

<script type="module">
  import jsklassImport from 'https://cdn.skypack.dev/jsklass-import';
</script>

README

JsKlass简介

JsKlass是基于Javascript实现的OOP代码结构的JS库;具有类、继承、协议、类常量、类静态、final类型修饰、property特性、内置安全校验等功能特征

website: www.jsklass.com

使用前言

JsKlass不依懒任何第三方库,是一个独立基于Javascript的库,兼容IE8+以上的所有主流浏览器;在使用它前仅仅只需要有Javascript基础并且对OOP有所了解

Browser版本: https://github.com/liangwengao/jsklass-browser
Import版本: https://github.com/liangwengao/jsklass-import
Require版本: https://github.com/liangwengao/jsklass-require

安装方式

Bower模块

bower install liangwengao/jsklass-browser --save

Import模块

npm install liangwengao/jsklass-import --save dev

或者

npm install jsklass-import --save dev

Require模块

npm install liangwengao/jsklass-require --save dev

或者

npm install jsklass-require --save dev

JsKlass特性:

支持无入侵式定义类
支持类继承/多继承
支持类构造
支持类实例化多个独立的类对象
支持类对象encoder/decoder
支持类、类对象、协议的安全校验
支持属性setter/getter
支持属性final类型修饰
支持协议的遵循约束/多重实现/多重继承
支持类常量定义
支持类静态数据共享
支持类原型与其他Function类型进行绑定
兼容浏览器环境(major),兼容node环境

JsKlass 全局API:

JK.DefClass: 定义类
JK.DefProtocol: 定义协议
JK.ProtocolType: 指定协议对应的类型,用于约束类定义属性的类型
JK.Global: 用于保存全局模块,只有浏览器版本才拥有

一、JsKlass的语法

1.定义类语法

const 类名 = DefClass('类名', function (sl) {
// ...
})

2.类继承语法

const 类名 = DefClass('类名', function (sl) {
  // ...
}, 被继承的类)

3.定义协议

const 协议名 = DefProtocol('协议名', {
  // 对self指向约束
  'self': {
    // 例如:在类遵循此协议时,对username属性进行“字符串”类型的约束
    username: ProtocolType.string()  
  },
  // 对const指向约束
  'const': {
     
  },
  // 对static指向约束
  'static': {
     
  }
})

4.类遵循协议语法

const 类名 = DefClass('类名', function (sl) {
  // ...
}, null, 被遵循的协议)

二、使用JsKlass

< Script > 标签式使用JsKlass

在浏览器版本中使用 'JsKlass' 或 'JK' 两者是等价的

<script type='text/javascript' src='/bower_components/jsklass/lib/jsklass.js'></script>
<script type='text/javascript'>
  
  /**
   * @description Demo类
   * @class Demo
   */
const Demo = JK.DefClass('Demo', function (sl) {
    // todo...
  })
  
</script>  

或使用自动执行匿名函数包装简化

<script type='text/javascript' src='/bower_components/jsklass/lib/jsklass.js'></script>
<script type='text/javascript'>
  (function(DefClass, DefProtocol, ProtocolType, Global){
    
    /**
     * @description Demo类
     * @class Demo
     */
    const Demo = DefClass('Demo', function (sl) {
      // todo...
    })
    
  })(JK.DefClass, 
  JK.DefProtocol, 
  JK.ProtocolType,
  JK.Global)  
</script>

Require方式使用JsKlass

const JK = require('jsklass-require')

/**
* @description Demo类
* @class Demo
*/
const Demo = JK.DefClass('Demo', function (sl) {
// todo...
})

Import方式使用JsKlass

import * as JK from 'jsklass-import'

/**
* @description Demo类
* @class Demo
*/
const Demo = JK.DefClass('Demo', function (sl) {
  // todo...
})
  

或者

import {
    DefClass,
    DefProtocol,
    ProtocolType
} from 'jsklass-import'

/**
* @description Demo类
* @class Demo
*/
const Demo = DefClass('Demo', function (sl) {
  // todo...
})

三、类的指向

DefClass(..., function (sl, co, st, su, po) { ... })

Name Pointer Describe
sl self或this 实例指向
co const 常量指向
st static 静态指向
st super 父类指向
po prototype 原型指向

四、类实例

在定义一个类时,如果在类中的 'sl' 指向声名了 '_construct' 构造函数,在类实例化对象时则会自动调用一次

/**
* @description Demo类
* @class Demo
*/
const Demo = DefClass('Demo', function (sl) {
  /**
   * @description 构造函数
   * @function _construct
   */
  sl._construct = function (name) {
    console.log('Demo._construct():' + name)
  }
  
  /**
   * @description show方法
   * @function Demo#show
   */
  sl.show = function () {
    console.log('Demo.show()')
  }
  
  /**
   * @description hide方法
   * @function Demo#hide
   */
  sl.hide = function () {
    console.log('Demo.hide()')
  }
})

let demo = new Demo('awen')
demo.show()
demo.hide()

/**运行结果:
> Demo._construct():awen
> Demo.show()
> Demo.hide()
*/

五、类继承

一个类在定义时可以去继承另一个类的属性或方法,支持多态继承(即一次只可继承一个类),不支持多重继承

/**
 * @description DemoA类
 * @class DemoA
 */
const DemoA = DefClass('DemoA', function (sl) {
  /**
   * @description show方法
   * @function DemoA#show
   */
  sl.show = function () {
    console.log('DemoA.show()')
  }
})


/**
 * @description DemoB类
 * @class DemoB
 */
const DemoB = DefClass('DemoB', function (sl) {
  sl._construct = function () {
    console.log('DemoB._construct()')
  }

  /**
   * @description hide方法
   * @function DemoB#hide
   */
  sl.hide = function () {
    console.log('DemoB.hide()')
  }
}, DemoA)

let demo = new DemoB
demo.show()
demo.hide()

/**运行结果:
 > DemoB._construct()
 > DemoA.show()
 > DemoB.hide()
 */

六、类覆写

当一个类去继承另外一个类时,父类的方法或属性可以在子类中使用相同的名称去声明覆写

/**
 * @description DemoA类
 * @class DemoA
 */
const DemoA = DefClass('DemoA', function (sl) {
  /**
   * @description show方法
   * @function DemoA#show
   */
  sl.show = function () {
    console.log('DemoA.show()')
  }

  /**
   * @description hide方法
   * @function DemoA#hide
   */
  sl.hide = function () {
    console.log('DemoA.hide()')
  }
})


/**
 * @description DemoB类
 * @class DemoB
 */
const DemoB = DefClass('DemoB', function (sl) {
  sl._construct = function () {
    console.log('DemoB._construct()')
  }

  /**
   * @description hide方法
   * @function DemoB#hide
   */
  sl.hide = function () {
    console.log('DemoB.hide()')
  }

  /**
   * @description display方法
   * @function DemoB#display
   */
  sl.display = function () {
    console.log('DemoB.display()')
  }
}, DemoA)

let demo = new DemoB()
demo.show()
demo.hide()
demo.display()

/**运行结果:
 > DemoB._construct()
 > DemoA.show()
 > DemoB.hide()
 > DemoB.display()
 */

七、super父类

子类继承父类时被覆盖的方法或属性,子类可通过 'su' 指向去访问

/**
 * @description DemoA类
 * @class DemoA
 */
const DemoA = DefClass('DemoA', function (sl) {
  sl._construct = function () {
    console.log('DemoA._construct()')
  }

  /**
   * @description show方法
   * @function DemoA#show
   */
  sl.show = function () {
    console.log('DemoA.show()')
  }

  /**
   * @description hide方法
   * @function DemoA#hide
   */
  sl.hide = function () {
    console.log('DemoA.hide()')
  }
})


/**
 * @description DemoB类
 * @class DemoB
 */
const DemoB = DefClass('DemoB', function (sl, co, st, su) {
  sl._construct = function () {
    console.log('DemoB._construct()')
    su._construct()
  }

  /**
   * @description hide方法
   * @function DemoB#hide
   */
  sl.hide = function () {
    console.log('DemoB.hide()')
    su.hide()
  }

}, DemoA)

let demo = new DemoB()
demo.show()
demo.hide()

/**运行结果:
 > DemoB._construct()
 > DemoA._construct()
 > DemoA.show()
 > DemoB.hide()
 > DemoA.hide()
 */

八、Final受保护类型修饰符 '

在继承时,如果父类声明的方法或属性前缀使用了 ' 进行修饰,则该方法或属性就会变成final受保护类型,子类 或 类实例都将不可被覆写

注:' 修饰符只对 'sl' 或 'po' 指向有效

/**
 * @description DemoA类
 * @class DemoA
 */
const DemoA = DefClass('DemoA', function (sl) {
  /**
   * @description show方法
   * @function DemoA#show
   */
  sl.$show = function () {
    console.log('DemoA.show()')
  }

  /**
   * @description hide方法
   * @function DemoA#hide
   */
  sl.hide = function () {
    console.log('DemoA.hide()')
  }
})


/**
 * @description DemoB类
 * @class DemoB
 */
const DemoB = DefClass('DemoB', function (sl) {
  sl._construct = function () {
    console.log('DemoB._construct()')
  }

  /******由于父类的show使用了 '

 修饰符,此处show会报ERROR******/
  /**
   * @description show方法
   * @function DemoB#show
   */
  //sl.show = function () {
  //  console.log('DemoB.show()')
  //}

  /**
   * @description hide方法
   * @function DemoB#hide
   */
  sl.hide = function () {
    console.log('DemoB.hide()')
  }

}, DemoA)

let demo = new DemoB()

/******由于父类的show使用了 '

 修饰符,此处覆写show不生效******/
demo.show = function () {
  console.log('demo.rewire.show()')
}

demo.show()
demo.hide()

/**运行结果:
 > DemoB._construct()
 > DemoA.show()
 > DemoB.hide()
 */

九、类协议约束

协议是类的约束,是指定一个类的基本功能实现

在声明一个类时如果遵循了类协议,那么该类必须实现协议中的方法或属性,并且约束对应的类型;如不实现协议中的方法或属性,则会抛出一个声明级别的Error

const DemoPTL = DefProtocol('DemoPTL', {
  self: {
    // 约束属性是字符串类型
    name: ProtocolType.string(),
    // 约束属性是数字类型
    age: ProtocolType.number(),
    // 约束方法不带参数
    eat: ProtocolType.function(),
    // 约束方法不带参数,并且可实现的
    eat1: ProtocolType.function('@optional'),
    // 约束方法带参数,并且可实现的
    eat2: ProtocolType.function('@optional', 'name')
  },
  const: {
    // ...
  },
  static: {
    // ...
  }
})

十、约束类型列表

ProtocolType Describe
ProtocolType.function() 方法类型
ProtocolType.string() 字符串类型
ProtocolType.number() 数字类型
ProtocolType.array() Array类型
ProtocolType.objects() Object类型
ProtocolType.class() DefClass定义的Class类型
ProtocolType.own() 所有类型

十一、约束类型中的单一参数

在指定协议类型时,每个类型都会默认隐藏一个 '@required' 参数:ProtocolType.function('@required') 代表必须要实现该方法或属性

注:如 ProtocolType.function('@required','name', ...) 此时在类定义时需要省略带 '@' 符号的约束类型的参数:sl.xxx = function(name, ...)

ProtocolTypeParameter Describe
@optional 可实现的
@required 必须实现的

十二、协议与协议继承

协议支持多重继承,支持多层继承

//DemoA协议
const DemoAPTL = DefProtocol('DemoAPTL', {
  self: {
    name: ProtocolType.string()
  }
})

//DemoB协议,继承了DemoA协议
const DemoBPTL = DefProtocol('DemoBPTL', {
  self: {
    age: ProtocolType.number(),
    eat: ProtocolType.function()
  }
}, DemoAPTL)

十三、类遵循协议

一个类在遵循协议时,即支持单一实现,也支持多重实现,在多重实现时需要传入协议数组

//DemoA协议
const DemoAPTL = DefProtocol('DemoAPTL', {
  self: {
    show: ProtocolType.function()
  }
})

//DemoB协议
const DemoBPTL = DefProtocol('DemoBPTL', {
  self: {
    hide: ProtocolType.function()
  }
})

/**
 * @description DemoImpl类,使用协议数组多重实现
 * @class DemoImpl
 */
const DemoImpl = DefClass('DemoImpl', function (sl) {
  sl._construct = function () {
    console.log('DemoImpl._construct()')
  }

  /**
   * @description show方法
   * @function DemoImpl#show
   */
  sl.show = function () {
    console.log('DemoImpl.show()')
  }

  /**
   * @description hide方法
   * @function DemoImpl#hide
   */
  sl.hide = function () {
    console.log('DemoImpl.hide()')
  }
}, null, [DemoAPTL, DemoBPTL])

let demoImpl = new DemoImpl
demoImpl.show()
demoImpl.hide()

/**运行结果:
 > DemoImpl._construct()
 > DemoImpl.show()
 > DemoImpl.hide()
 */

十四、property特性 <需要结合类协议使用>

property特性是把prototype定义的属性都编译成 getter/setter 的方法,调用时会调用_getter方法,赋值时会调用_setter方法


/**
 * @description Demo类
 * @class Demo
 */
const Demo = DefClass('Demo', function (sl, co, st, su, po) {
  /**
   * @description 实例username属性
   * @function Demo#username
   */
  po.username = 'jsklass'
  
  /**
   * @description 实例username属性getter
   * @function Demo#username
   */
  po._getUsername = function(val) {
    console.log('call getUsername()...')
    return val + ':get'
  }
  
  /**
   * @description 实例username属性setter
   * @function Demo#username
   */
  sl._setUsername = function(val) {
    console.log('call setUsername()...')
    return val + '-set'
  }
  
})

let demo = new Demo
console.log(demo.username)
demo.username = 'jsklass-browser'
console.log(demo.username)

/**运行结果:
 > call getUsername()...
 > jsklass
 > call setUsername()...
 > call getUsername()...
 > jsklass-browser
 */

十五、类常量

常量是值不可变的量,它不属于类的任何一个实例,而是属于类本身;常量被定义后,无论在什么地方都不可改变它的值

/**
 * @description Demo类
 * @class Demo
 */
const Demo = DefClass('Demo', function (sl, co) {
  co.VERSION = '1.0.1';
  /**
   * @description 常量show方法
   * @function Demo.show
   */
  co.show = function () {
    console.log('Demo.show()->const')
  }
  
  /**
   * @description 实例show方法
   * @function Demo#show
   */
  sl.show = function () {
    console.log('Demo.show()->self')
  }
})

Demo.show()
let demo = new Demo
demo.show()

/**运行结果:
 > Demo.show()->const
 > Demo.show()->self
 */

十六、类静态

静态与普通的成员属性不同,静态属性属于类本身而不属于类的任何实例;静态属性可以理解是存储在类当中的全局变量,可以在任何地方通过 '自身类' 或 '实例' 来进行修改或访问

/**
 * @description Demo类
 * @class Demo
 */
const Demo = DefClass('Demo', function (sl, co, st) {
  st.name = 'jsklass';
  /**
   * @description 静态show方法
   * @function Demo.show
   */
  st.show = function () {
    console.log('Demo.show()->static')
  }
  
  /**
   * @description 实例show方法
   * @function Demo#show
   */
   sl.show = function () {
     console.log('Demo.show()->self')
   }
})

Demo.show()
let demo = new Demo
demo.show()

/**运行结果:
 > Demo.show()->static
 > Demo.show()->self
 */

十七、类常量与类静态默认访问优先级问题

类常量的默认优先级高于类静态,如果常量与静态中都定义同名的变量或属性,使用 '.' 点方式访问会优先访问常量再访问静态 ,这时你可以使用类预置的 getStatic(name) 来获取访问

十八、Prototype原型

在javascript中每个函数就是一个对象(Function),函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的prototype表示该函数的原型,也表示一个类的成员的集合

在JsKlass中,prototype就是一个实例化对象的原型对象,当前实例化的对象中存在与原型对象中的一致的属性或方法时,会优先读取实例化对象中的属性或方法

/**
 * @description Demo类
 * @class Demo
 */
const Demo = DefClass('Demo', function (sl, co, st, su, po) {
  po.username = 'jsklass';
  /**
   * @description 实例show方法
   * @function Demo#show
   */
  po.show = function () {
    console.log('Demo.show()->prototype')
  }
  
  /**
   * @description 实例show方法
   * @function Demo#show
   */
  sl.show = function () {
    console.log('Demo.show()->self')
  }
})

let demo = new Demo()
console.log(demo.username)
demo.show()

/**运行结果:
 > jsklass
 > Demo.show()->self
 */

十九、encoder与decoder

使用JsKlass定义的类,而类实例化的对象都具有encoder编码与decoder解码操作

const Demo = JK.DefClass('Demo', function (sl, co, st, su) {
  let name = null
  let age = null

  sl._construct = function () {
    name = "jsklass"
    age = 18
  }

  /**
   * @description show方法
   * @function Demo#_decoder
   */
  sl._decoder = function (decoder) {
    name = decoder.name
    age = decoder.age
    su._decoder(decoder.super)
  }
  
  /**
   * @description show方法
   * @function Demo#_encoder
   */
  sl._encoder = function () {
    return {"name": name, "age": age, "super": su._encoder()}
  }

  /**
   * @description get方法
   * @function Demo#get
   */
  sl.get = function () {
    return "name = "+name+"; age = "+age
  }
})

let demo = new Demo
console.log(demo.get())
console.log(demo._encoder())

let coderStr = '{ "name": "jsklass-reset", "age": 20, "super": {} }'
let demo2 = Demo.renew(coderStr)
console.log(demo2.get())

/**运行结果:
 > name = jsklass; age = 18
 > { name: "jsklass", age: 18, super: {} }
 > name = jsklass-reset; age = 20
 */

二十、JsKlass类原型与其他Function类型进行绑定

JsKlass提供一个类原型能与其他Function类型进行绑定的功能

/**
 * @description 例如:Demo类原型需要绑定Error类
 * @class Demo
 */
const Demo = DefClass('Demo', function (sl) {
  
}, null, null, Error)

let demo = new Demo()
console.log(demo.stack)

/**运行结果:
 > Class@http://localhost/jsklass-dev/dist/klass.js:1907:67
   DefClass@http://localhost/jsklass-dev/dist/klass.js:3203:11
   @http://localhost/jsklass-dev/dist/klass.html:333:15
   ....
 */

二十一、类剖析

当定义一个类后,类自身会固定内置有一些属性与方法,以下说明:

/**
 * @description Demo类
 * @class Demo
 */
const Demo = DefClass('Demo', function (sl, co, st) {
  co.VERSION = '1.0.1'

  st.username = 'awen'
  
  po.user = 'jsklass'
  
  /**
   * @description show方法
   * @function Demo#show
   */
  sl.show = function () {
    console.log('Demo.show()')
  }
})

console.log(Demo)

/*
|-Demo()                      //Demo类
  |-id                        //类的唯一标识
  |-getConst                  //获取常量
  |-getStatic                 //获取静态属性
  |-setStatic                 //设置静态属性
  |-extendOf                  //校验是否继承某个类
  |-instanceOf                //校验某个实例对象是否是该类实例
  |-protocolOf                //校验是否遵循某个协议
  |-protocol_name             //所有协议的名称集合
  |-new                       //预置 xxx.new() ,与 new xxx 等价
  |-renew                     //预置 xxx.renew(),在decoder还原一个类实例时使用
  |-name                      //类名称
  |-super_name                //所有父类名称
  |-toString                  //把该类转成code
  |->>>>>const_pro<<<<<       //常量区
    |-VERSION: '1.0.1'
    |-...
      |->>>>>static_pro<<<<<  //静态区
        |-username: 'awen'    
        |-...
  |-...
*/

二十二、类实例剖析

当一个类被实例化对象后,对象自身也会固定内置有一些属性与方法,以下说明:

const Demo = JK.DefClass('Demo', function (sl, co, st, su, po) {
  co.VERSION = '1.0.1'
  
  st.username = 'awen'
  
  po.user = 'jsklass'
  
  /**
   * @description show方法
   * @function Demo#show
   */
  sl.show = function () {
    console.log('Demo.show()')
  }
})

console.log(new Demo)

/*
|-{}                              //实例化的对象
  |-show: function()              //sl指向中的show方法
  |-...
​  |->>>>>prototype<<<<<           //类中prototype原型对象
    |-user: 'jsklkass'            //po实例原型指向的属性与方法
    |-...
    |->>>>>prototype<<<<<
      |-id                        //实例唯一标识
      |-classOf                   //检验该实例是否属于某个类
      |-class_name                //类名
      |-getConst                  //获取常量
      |-getStatic                 //获取静态属性
      |-setStatic                 //设置静态属性
      |-protocolOf                //校验是否遵循某个协议
      |-toString                  //把该实例转成code
*/

二十三、浏览器版本的Global

JsKlass在浏览器版本中提供了一个额外的Global,它的作用是使得闭包里的类定义或实例对象变成全局,在运行环境中任何一个地方都可访问使用

demo-a.js 模块

(function (DefClass, DefProtocol, ProtocolType, Global) {

  Global.DemoA = DefClass('DemoA', function (sl, co, st, su, po) {
    sl.show = function () {
      console.log('DemoA.show()')
    }
  })

})(
  JK.DefClass,
  JK.DefProtocol,
  JK.ProtocolType,
  JK.Global
)

demo-b.js 模块

(function (DefClass, DefProtocol, ProtocolType, Global) {

  // 继承demo-a.js模块的DemoA类
  const DemoB = DefClass('DemoB', function (sl, co, st, su, po) {
    sl.show = function () {
      console.log('DemoB.show()')
    }
  }, Global.DemoA)

  Global.demo = new DemoB;
})(
  JK.DefClass,
  JK.DefProtocol,
  JK.ProtocolType,
  JK.Global
)

demo.js

(function (DefClass, DefProtocol, ProtocolType, Global) {
  console.log(new Global.DemoA)
  console.log(Global.demo)
})(
  JK.DefClass,
  JK.DefProtocol,
  JK.ProtocolType,
  JK.Global
)

二十四、JsKlass类内置API

Api Parameter Describe
getConst (name) 获取常量,不传name获取所有
getStatic (name) 获取静态,不传name获取所有
setStatic (name, value) 设置静态属性
extendOf (Class) 校验是否继承某个类
instanceOf (Instance) 校验某个实例对象是否是该类实例
protocolOf (Protocol) 校验是否遵循某个协议
new (...) 与 new xxx 等价
renew (...) 在decoder还原一个类实例时使用

二十五、JsKlass类的实例内置API

Api Parameter Describe
getConst (name) 获取常量,不传name获取所有
getStatic (name) 获取静态,不传name获取所有
setStatic (name, value) 设置静态属性
classOf (Class) 校验当前实例对象是否是属性某个类实例
protocolOf (Protocol) 校验当前实例的类是否遵循某个协议

二十六、JsKlass协议内置API

Api Parameter Describe
extendOf (Class) 校验是否继承某个协议
instanceOf (Instance) 校验某个实例对象是否是遵循该协议

二十七、JsKlass关键词

JsKlass自身保留一些关键词,而在声名属性或方法时不可以使用关键词,具体如下:

CLASS(const/static)常量与静态关键词:

'class_name',
'protocol_name',
'super_name',
'prototype',
'constructor',
'id',
'setStatic',
'getStatic',
'getConst',
'instanceOf',
'extendOf',
'classOf',
'protocolOf',
'apply',
'bind',
'call',
'caller',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'valueOf',
'__proto__',

CLASS(self、prototype)关键词:

'class_name',
'protocol_name',
'super_name',
'prototype',
'constructor',
'id',
'setStatic',
'getStatic',
'getConst',
'instanceOf',
'extendOf',
'classOf',
'protocolOf',
'apply',
'bind',
'call',
'caller',
'toSource',
'toString',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'valueOf',
'__proto__',

官方文档: www.jsklass.com/document

LICENSE

MIT