在bluebird wiki article about JavaScript optimization killers中,作者提到将arguments
关键字传递给任何函数(apply
除外)都会导致父函数无法优化。我想创建一个sweet.js宏,它允许我编写标准的惯用JavaScript,但会处理优化杀手。
理想情况下,我想要一个可以采用以下功能的宏:
function foo() {
var args = [].slice.call(arguments);
return args;
}
并输出如下内容:
function foo() {
var args = [];
for(var i, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
return args;
}
然而,我无法正确获取sweet.js宏语法。 This is what I have so far:
let arguments = macro {
rule infix {
[].slice.call |
} => {
[];
for(var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
}
}
function toArray() {
var args = [].slice.call arguments
return args;
}
其中输出以下内容:
function toArray() {
var args$2 = [];
for (var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
return args$2;
}
我尝试使我的宏在arguments
关键字周围加上括号,并且还包含var
声明,但没有任何成功。我试过这样的事情:
let arguments = macro {
rule infix {
var $var = [].slice.call ( | )
} => {
var $var = [];
for(var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
}
}
这会产生以下错误:
SyntaxError: [syntaxCase] Infix macros require a `|` separator
414:
^
答案 0 :(得分:2)
是的,所以有几种方法可以做到这一点。提出论点
在parens内部不起作用,因为中缀宏无法在外部匹配
封闭分隔符,以便在arguments
宏被调用时
在它之前或之后看到零令牌(错误应该更清楚)。
你的另一个解决方案正在遇到卫生问题
arguments
宏需要访问args
标识符,但需要中缀
当它在a中时,不允许宏在等号之前匹配
var
语句因此无法与args
标识符实际匹配。
所以有几个解决方案。最简单的就是做一些类似的事情 蓝鸟维基建议:
macro argify {
rule { ( $arg ) } => {
var $arg;
for (var i, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
}
}
function foo() {
argify(args)
return args;
}
你也可以走不卫生的路线(不是真的推荐但是
arguments
已经有点不卫生所以......):
let function = macro {
case {$mname $name ( $parens ...) { $body ... } } => {
letstx $args = [makeIdent("args", #{$mname})];
return #{
function $name ( $parens ...) {
var $args = [];
for (var i, len = arguments.length; i < len; i++) {
$args.push(arguments[i]);
}
$body ...
}
}
}
}
function foo() {
return args;
}
修改强>
我刚想到了另一种解决方案,可以让您通过覆盖var
来保留当前语法:
let var = macro {
rule { $args = [].slice.call(arguments) } => {
var $args = [];
for(var i, len = arguments.length; i < len; i++) {
$args.push(arguments[i]);
}
}
rule { $rest ... } => { var $rest ... }
}
function foo() {
var args = [].slice.call(arguments);
}
答案 1 :(得分:2)
这不是完全相同的结果,因为它有一个函数包装器(尽管它是用apply
调用的),但它不需要你覆盖var
并且可以在任何表达立场。
macro copy_args {
rule {} => {
function() {
var len = arguments.length;
var args = Array(len);
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
return args;
}.apply(this, arguments)
}
}
let slice = macro {
rule infix { []. | .call(arguments) } => { copy_args }
rule infix { []. | .apply(arguments) } => { copy_args }
rule infix { Array.prototype. | .call(arguments) } => { copy_args }
rule infix { Array.prototype. | .apply(arguments) } => { copy_args }
rule { } => { slice }
}
function go() {
var args = Array.prototype.slice.call(arguments);
return args;
}
扩展为
function go() {
var args = function () {
var len = arguments.length;
var args$2 = Array(len);
for (var i = 0; i < len; i++) {
args$2[i] = arguments[i];
}
return args$2;
}.apply(this, arguments);
return args;
}
不知道是否会破坏优化......