lofn 现在已经上线,rednaxelafx 还给我做采访讨论lofn。和 CoffeeScript 不同,lofn 并没有把 lofn 源码转换成“完全”等价的 JavaScript 源码——虽然 lofn 的对象系统就是 JavaScript 的,但 lofn 的变量系统不是。
让我不能完全转换的原因是,lofn 的变量系统对未定义变量的读写规定不同于 JavaScript:lofn 中,读取未定义变量永远得到 undefined,写入未定义变量则会增加一个局部变量。
最早我试图通过原型继承构建对象系统,代码是这样:
var Nai = function() {}; Nai.prototype = {toString: undefined} // Make all properties into undefined globalEnv = new Nai(); // Entering a function // ...... var env = createDerivate(parentEnv); return rawFunc(env, thisp, args); // Getting a variable env[varname] // Setting a variable env[varname] = value
rawFunc由lofn“虚拟机”生成的“裸”函数,相关的工作——包括参数放置(lofn有命名参数)等等都在之前完成,之后才调用这个裸函数。三个参数一目了然——作用域、this指针和参数表。
但是这样有个问题——试图写外部变量将会把那个外部变量“移动”到局部。代码如下:
var x = 1 var f = {x = 2} f() cout << x //x is still 1
如果有这个限制,很多有用处的程序——如一些计数器——将会失效。
简单应用原型继承难以实现,所以现在lofn中的变量系统采用的是双表——也就是说,env表存储的并不是变量的值,而是变量的“存储点”位置;“存储点”位置上的值才是真正的变量值。即:
env[varname][varname] // value of variable
这时,进入函数时要做的事情就多多了——
// vm.js // ... var env = createDerivate(parentEnv); // Wash the local variables to envRec; RC.wash(env); // Invoke RC.func(env, thisp, args);
RC是语法解析阶段生成的描述符,每个RC表示在代码中的函数“文字”。RC.wash的过程则是这样:
// parse.js ScopedScript.prototype.wash = function(e, g) { var r = g || new Nai(); e[''] = r; for (var i = 0; i < this.variables.length; i++) { e[this.variables[i]] = r; } if (this.paremeters) { for (i = 0; i < this.paremeters.length; i++) { e[this.paremeters.names[i]] = r; } } }
当然,这其中省略了很多细节,实际上vm.js中RC.func(裸函数)接受五个参数,其中有一个是所谓的“嵌套函数-Env派生缓存”,还有用于自省的“callee”等等。(lofn中callee是关键字。)
在这种状况下,读写变量就比较烦人了。lofn中读一个变量的代码是这样:
((__temp = env[varname]) ? __temp[varname] : undefined)
写一个变量则是:
((__temp = env[varname]) ? (__temp[varname] = value) : (env[varname] = lvs, lvs[varname] = value))
这个lvs是什么家伙?实际上,lvs是“局部变量”的存储地,等于env[''],这是用于优化的:在读写局部变量的时候,所有的,用上复杂分支判断的代码都会简化为对lvs的读写。
lofn的变量系统其实还有很多值得优化的地方,比如,对代码块函数(指没有定义局部变量的函数)、小函数(没有用到外部变量的函数)的专门优化。因为lofn没有eval,进行这些优化难度应该不大。
One Trackback
[...] 原文地址:http://typeof.net/2010/07/variable-system-in-lofn/ [...]