在 CoffeeScript 中定义自己的 DSL 非常容易。如果你想创建一个类型检查框架,你可以例如创建一个这样的类
class A
@def foo:
params: [isNum, isBool,isNotNull]
body: (x, y, z) -> console.log "foo: #{x}, #{y}, #{z}"
@def 应该创建一个名为“foo”的方法,并通过调用“params”数组中给出的函数来根据它们的位置检查它的参数。
让我们先写一些测试
a = new A()
a.foo 3, true, "foo"
a.foo "string", true, "foo"
a.foo 3, "string", "foo"
a.foo 3, false, null
然后我们需要一些辅助方法来进行实际的参数检查
isNum = (p)-> console.log "p isnt a number its => #{p}" if typeof p != "number"
isBool = (p)-> console.log "p isnt a bool its => #{p}" if typeof p != "boolean"
isNotNull = (p)-> console.log "p is null" if p == null or p == undefined
可能他们应该做一些更有用的事情(比如抛出异常)。对于我们的示例,它们应该足够了。
现在我们的 A 类调用了一个尚未定义的类方法。我们将为此创建一个基类并从中继承我们的类 A
class ContractBase
@def: (fndef)->
#get the name of the "function definition" object
#should be the only key
name = Object.keys(fndef)[0]
#get the real function body
fn = fndef[name]["body"]
#get the params
params = fndef[name]["params"]
# create a closure and assign it to the prototype
@::[name] = ->
#check the parameters first
for value, index in arguments
#get the check at the index of the argument
check = params[index]
#and run it if available
check(value) if check
#call the real function body
fn arguments...
#and finally change A to extend from ContractBase
class A extends ContractBase
...
显然里面有几个疣
- 参数数组和参数数组可以有不同的长度(还没有检查)
- 辅助函数应该抛出异常
- 辅助函数应该像 isNotNull(isNum) 一样可组合
- 您正在规避定义方法的“正常”方式,因此您生成的 javascript 代码将更难阅读和调试 - 也许不是
这是一次完整的运行代码
class ContractBase
@def: (fndef)->
name = Object.keys(fndef)[0]
fn = fndef[name]["body"]
params = fndef[name]["params"]
@::[name] = ->
for value, index in arguments
check = params[index]
check(value) if check
fn arguments...
isNum = (p)-> console.log "p isnt a number its => #{p}" if typeof p != "number"
isBool = (p)-> console.log "p isnt a bool its => #{p}" if typeof p != "boolean"
isNotNull = (p)-> console.log "p is null" if p == null or p == undefined
class A extends ContractBase
@def foo:
params: [isNum, isBool,isNotNull]
body: (x, y, z) -> console.log "foo: #{x}, #{y}, #{z}"
a = new A()
a.foo 3, true, "foo"
a.foo "string", true, "foo"
a.foo 3, "string", "foo"
a.foo 3, false, null
它大约是相应 Javascript 代码长度的 1/3,并且由于它更好地传达意图(imo),因此它的可读性肯定更高