kotlin的htmlx构建器如何正确地工作?

这是解释htmlx构建器某些部分的片段(来自文档):

protected fun  initTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } 

最重要的是children.add(tag) ,所以我们可以声明:

 html { head {} body {} } 

因为head和body是html的成员函数。 但是DIV标签呢? 我可以在任何地方宣布div,而且我可以这样写:

 someEnclosingTag { (1..3).forEach { div {+"MyCustomDivFromEverywhere"} }} 

如何包装lambda知道“孩子”lambda(并分别添加到整个html的“孩子”标签),可以在任何地方宣布?

请纠正我,如果我在某个地方错了。

UPDATE

基于回答,我结束了以下肮脏的虚拟代码,显示函数范围(封闭的一些方面)和隐式的接收器遗漏(希望它可以以某种方式帮助某人):

 fun main(args: Array) { Child().childFun { /*childFun lambda receiver implements parent1Fun lambda receiver, so the receiver can be omitted*/ parent1Fun { /*call from Child.() -> Unit receiver*/ someIntrestingFun() } /*same as with parent1Fun*/ parent2Fun { /*call from Child.() -> Unit receiver*/ someIntrestingFun() } } } fun Child.childFun(lambda: Child.() -> Unit): Child = genericFun(Child(), lambda) fun ParentInt1.parent1Fun(lambda: ParentInt1.() -> Unit): ParentInt1 = genericFun(Child(), lambda) fun ParentInt2.parent2Fun(lambda: ParentInt2.() -> Unit): ParentInt2 = genericFun(Child(), lambda) fun  genericFun(instance:T, lambda:T.() -> Unit): T { instance.lambda() return instance } interface ParentInt1 interface ParentInt2 class Child : ParentInt1, ParentInt2 { fun someIntrestingFun() { println(this) } } 

您可以在语言参考中find关于构建这种DSL的技术的更多信息,请参阅: types安全构建器 ,该页面提供了一个示例HTML构建器(尽管kotlinx.html更复杂)。

如何封装lambda知道可以随处宣告的“孩子”lambda?

这就是函数解析的工作原理:当你嵌套lambdas时,不管是否使用接收者,在内部函数中你可以调用外部接收者(*)的成员/扩展函数,这里有一个非常合成的例子:

 with(arrayListOf()) { with(hashMapOf()) { // You can call both functions of `ArrayList` and `HashMap`: add("foo") put(1, "bar") // Even in the nested lambdas with no receiver: baz.forEach { put(it, "it = $it") } } } 

(*):在高级DSL中,范围可以用@DslMarker来限制,以避免从外部范围意外地调用接收器上的函数。