2

bluebird wiki 关于 JavaScript 优化杀手的文章中,作者提到将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 宏语法。 这是我到目前为止所拥有的

例子.sjs

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: 
                                ^
4

2 回答 2

2

是的,所以有几种方法可以做到这一点。将参数放在括号内不起作用,因为中缀宏在封闭分隔符之外无法匹配,因此当arguments宏被调用时,它会在它之前或之后看到零标记(错误应该更清楚)。

您的另一个解决方案遇到了卫生问题,因为 arguments宏需要访问args标识符,但是当它在语句中时,中缀宏不允许在等号之前匹配, var因此它实际上无法匹配args标识符。

所以有几个解决方案。最简单的方法是像 bluebird wiki 建议的那样做:

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);
}
于 2014-10-25T19:18:52.377 回答
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;
}

不知道这是否会扼杀优化...

于 2014-10-25T21:38:03.693 回答