js接口检查工具,为js定制一份约束契约。
iface是一个工具,可以作为单元测试的一部分,也可以作为开发文档的一部分,辅助开发,方便后期维护,信息传递。
面向接口而非实现编程,这意味着当两个类实现了同样的接口就可以相互替换,类变成了可替换的零部件。
我们只关心一个类的接口,而不关心他的实现,程序员可以自行发挥,只要最终提供了接口。
同时我们可以跳出类与接口的概念,如果一个模块export(module.exports或export default)了某些方法和属性,那么只要另外的模块实现了同样的方法和属性,两个模块就可以相互替换,模块变成了可替换的零部件。
同样的,我们只关心模块开放出来的属性和方法,而不关心他的实现,程序员可以自行发挥,只要最终export。
如果你的项目使用传统Javascript类,或者ES6的class,可以使用iface
如果你只是经常使用某个对象,里面有一些属性和方法,但只能通过备注、文档、记忆等记录,长期苦于维护,也可以使用iface,我们将提供自动生成接口文档功能。
如果你使用TypeScript,可以使用TypeScript的interface,当然你仍然可以使用Iface
npm install iface
or
<script src='../dist/iface.js'></script>
<script>
let {Iface} = iface
</script>
let stackInterface = Iface({
methods: ['pop', 'push', 'isEmpty', 'isFull'],
props: ['length', 'top'],
name: 'stackInterface'
})
注意:接口不能使用new关键字,接口只能实现,不能被实例化
上面是最简单的接口定义,下面定义了一个包含描述信息的接口,定义后可以使用render自动生成markdown
let iface = Iface({
methods: [
{
name: 'pop',
description: '获取栈顶元素',
return: '栈顶元素',
example: `
let stack = new Stack()
stack.pop()
`},
{
name: 'push',
description: '入栈',
params: ['val: 入栈元素'],
return: 'boolean',
example: `
let stack = new Stack()
stack.push('data')
`
}
],
props: [{
name: 'length',
description: '返回栈长度'
}],
name: 'stackInterface'
})
console.log(Iface.render(Iface.doc)) // 当定义多个接口时,Iface.doc会有所有接口的信息
let exStackInterface = Iface.extends(stackInterface, {
methods: ['clear', 'join'],
name: 'exStackInterface'
})
let jsonInterface = Iface({
methods: ['static stringify', 'static parse', 'toJson'],
name: 'jsonInterface'
})
let iface = Iface({
methods: ['class extends', 'class ensure'],
props: ['class all'],
name: 'iface'
})
Iface.ensure(检查对象, 接口),检查类是否实现了某个接口,检查通过返回true
Iface.ensure(new Stack(), stackInterface)
Iface.all
let stackInterface = Iface({
methods: ['pop', 'push', 'isEmpty', 'isFull'],
props: ['length', 'top'],
name: 'stackInterface'
})
Iface.isIface(stackInterface)
if (stackInterface.constructor === Iface) return 'stackInterface is a interface'
iface只关心类开放了哪些属性和方法,不关心这些属性和方法怎么实现,如下,定义了一个类Stack,iface不关心Stack没有length和max属性,只关心后续“期待”怎么被使用,所以可以检查length和max属性,而不是get length和set max方法
export class Stack {
constructor (max) {
this._data = []
this._length = 0
this._max = max
}
pop () {
}
push (val) {
}
isEmpty () {
}
isFull () {
}
get length () {
return this._length
}
set length (val) {
return
}
get max () {
return this._max
}
set max (val) {
return this._max
}
}
Javasript与其他语言相比,没有天然的public、private、protected,我们在使用私有变量通常有以下几个方式
- 约定使用下划线开头为私有变量/属性
- 立即执行函数
- ES6可以使用Symbol
但显然这几种私有形式都存在问题
- 使用下划线开头作为私有变量,这只是一种约定,实际变量仍然可以在类外访问、修改
- 使用立即执行函数,虽然确实能达到私有的效果,但不具有可读性,与private表示的方式大相径庭
- Symbol,我们只是利用了symbol的特性,实际并不具有可读性,symbol可以作为私有变量也可以作为唯一key的生成方式,如果你在代码里看到这个,只能通过语境上下文判断Symbol的用途,另外这个属性也并非真正私有,外部仍然可以访问到
介于以上几点,Iface也没有使用public、private、protected关键字,我们推荐只关心最后这个类开放出来的接口,而不关心私有变量
如果你用过其他面向对象语言,应该已经看出这里接口的不同之处:没有实际实现,只是做了一次定义(定义后会将接口信息写入all),需要ensure手动检查。
Iface虽然有构造函数,但不能用new,因为接口与抽象类都不能被实例化,不过抽象类可以达到代码的复用,基于这点,我们可以自己定义一个抽象类做接口工作.