翻译-js表达式闭包(expression closures)的进一步亲密接触

这篇文章发布于 2011年02月18日,星期五,22:24,归类于 外文翻译。 阅读 50607 次, 今日 5 次 3 条评论

 

以下为翻译全文,有编辑

最近我在Firebug控制台上折腾一些东西的时候记住了表达式闭包(expression closures)。这是个附加的语言,在Mozilla的JavaScript 1.8中引入。

MDC抓住了表达式闭包(expression closures)的本质,并对其进行了简明的解释://zxx:我琢磨着,这个MDC应该是Mozilla Develope Center,Mozilla开发中心。

该添加只不过可以让简单的函数速写,让语言类似的一个典型的λ符号。[…]该语法允许你不要花括号({})以及”return”声明——使其更隐式。这种代码书写形式除了语法构成上讲短了外没有其他的好处。

正好,这是我需要的——迅速地尝试一些东西,缩短的函数表达式以节约打字时间。

MDC同时给了个例子,展示如何使用表达式闭包写函数:

// 正常的语法
function() { return x * x }

// 表达式闭包
function(x) x * x

正如你所见,表达式闭包骨子里就是个创建更简单函数的方式。根据MDC,表达式闭包允许我们把函数体中的花括号啊以及”return”啊什么的如擦屁股纸般扔掉。

然而,MDC提供的关于表达式闭包的信息比王之涣流传下来的诗还少,于是我就好奇表达式闭包的其他一些出色的细节。它们是不是只能用来创建函数表达式?函数声明怎么样?已命名函数表达式又如何?是否function() ...总是和function(){return ...}一样,而与 ...(函数体)无关?MDC所谈到的“简单函数”到底指什么?

上面的问题一个也没有答案,所以我决定做个快速的研究,给这个时髦的语言加点深度。下面的就是我的发现;你会发现某些行为不是那么明显。

让代码自己说话,这里是一个简单的测试之后问题清单,试图回答这些问题。下面的简短叙述的发现你可以随意跳过。

测试(Test)

  1. 是否表达式闭包可以用来创建函数声明(而不仅仅是函数表达式)?

    答案是:是的。

    function f(x, y) x + y
    typeof f; // "function"

    上面的测试代码,正常的声明应该是:typeof(f);,而现在就是typeof f,测试结果显示是支持的。

    您可以狠狠地点击这里:表达式闭包与函数声明测试demo

    点击demo页面中的“点击我弹出结果”按钮查看效果,在firefox浏览器中可以看到如下的弹出框:

    表达式闭包测试弹出结果 张鑫旭-鑫空间-鑫生活

    因为表达式闭包是mozilla的东西,所以目前在诸如IE/Chrome浏览器下是没有效果的。火狐浏览器下直接下面代码就有效果了:

    <script>
    function f(x, y) x + y
    alert(typeof f); 
    </script>

    但是如果就上面光秃秃的代码的话,其他浏览器下会是报错的,为了友好其他浏览器,可以给javascript代码添加版本说明,就在type属性中,代码如下:

    <script type="application/javascript;version=1.8">
    function f(x, y) x + y
    alert(typeof f); 
    </script>
  2. 已命名函数表达式又如何?

    结果是:支持。

    typeof (function f(x, y) x + y); // "function"

    您可以狠狠地点击这里:表达式闭包与已命名函数表达式测试demo

    效果以及注意事项与上面一致。

  3. 表达式闭包如何影响函数表示?

    就跟平常一样,就是不包含花括号以及”return”关键字。

    String(function (x) x + x ); // "function (x) x + x;"

    您可以狠狠地点击这里:表达式闭包与函数表示测试demo

    在FireFox浏览器下点击页面上的按钮后的效果如下:

    表达式闭包与函数表示测试结果 张鑫旭-鑫空间-鑫生活
  4. 关于非标准的”name”属性?

    结果:如预期工作!

    function foo(x, y) x + y;
    foo.name; // "foo"

    您可以狠狠地点击这里:表达式闭包与非标准name属性测试demo

    结果如下图:

    表达式闭包与非标准name属性测试结果 张鑫旭-鑫空间-鑫生活
  5. 即时应用函数表达式怎么样?

    结果是:工作良好。

    (function(x, y) x + y)(1, 2); // 3

    您可以狠狠地点击这里:表达式闭包与即时应用函数表达式测试demo

    结果如下图:

    表达式闭包与即时应用函数表达式测试结果 张鑫旭-鑫空间-鑫生活
  6. 没有括号包裹的即时应用函数表达式?

    结果:啊哦,有点小疙瘩

    var f1 = (function(x, y) x + y)(1, 2);
    var f2 = function(x, y) x + y (1, 2);
     
    f1; // 3
    f2; // function(x, y) x + y(1, 2)

    正如你所看到的,x + y之后的(1, 2)实际上被当做内部的表达式了,而不是应用在外部函数上。x + y(1, 2)当做一个整体当做f2的返回值了。这是明确可以知道的。

    如果您手头上的正是Firefox浏览器,您可以狠狠地点击这里:表达式闭包与无括号包裹的即时应用函数表达式demo

  7. 表达式闭包在ECMAScript 5访问语法下是否起作用(先试试get方法)?

    结果是:起作用滴。

    var obj = ({ get foo() 1 });
    obj.foo; // 1

    您可以狠狠地点击这里:表达式闭包与ES5访问语法测试demo

    结果如下图:

    表达式闭包与ES5访问语法测试
  8. 那么set方法呢?

    答案是:同样可以。

    var _x;
    var obj = ({ set foo(x) _x = x });
    obj.foo = 5;
     
    _x; // 5

    您可以狠狠地点击这里:表达式闭包与ES5 set语法测试demo

    结果点击demo页面的测试按钮,在我的火狐浏览器下弹出结果是”5“。

  9. 要是双管齐下呢?

    结果:都ok!恩,没理由不ok啊~~

    var _x = '初始值';
    var obj = ({ get foo() _x, set foo(x) _x = x });
    obj.foo; // '初始值'
    
    obj.foo = '重置值';
    obj.foo; // '重置值'

    您可以狠狠地点击这里:表达式闭包与ES5 set/get双管齐下测试demo

    点击测试按钮后,连续弹出下图所示两弹框:

  10. 我们可以用逗号混淆分析器?

    答案是:是的。

      ({ set foo(x) bar = 1, baz = 2 }); // 解析错误

    在这个例子中,我们可能希望bar = 1, baz = 2作为函数内部的一个表达式,然而,逗号被解释成了对象初始化的一部分。当然,包裹一副括号可以解决字问题。

    ({ set foo(x) (bar = 1, baz = 2) });
  11. 语句是否允许存在于通过表达式闭包创建函数的函数体中?

    答案是:No!

    var f = function() if (true) "foo"; // 错误

    这个相当容易理解。表达式闭包中,function()后面跟着的表达式直接被当做要被return语句后面的东西给return掉了。显然,return语句是不能返回其他语句的,就是说return if (true) "foo";是行不通的。不过幸运的是,有个叫做三元运算的东西可以好好安抚这里的小错误:

    var f = function() someCondition ? "foo" : "bar";
  12. 通过表达式闭包创建的函数体是否可以忽略?

    (*^__^*) 嘻嘻……,答案是:门都没有!

      function f() // 错误

    你可能会想啊,这function f()应该等同于function f(){ return }。但是,不是滴,这里空函数体时不允许的。至于为什么会这样还不是很清楚。解析复杂,错乱如麻?或许吧。

  13. 如果不止一个表达式会怎样呢?

    结果是:只有第一个被当做柴油来烧。

    function f() alert("第一个语句"); alert("第二个语句"); alert("第三个语句"); // 弹出 "第二个语句"和"第三个语句"

    上面的语句被解析成了:

    function f() { return alert("第一个语句"); } alert("第二个语句"); alert("第三个语句");

    只有第一个表达式作为了表达式闭包的燃料使用了。

    您可以狠狠地点击这里:表达式闭包与多表达式测试demo

  14. 分号自动插入规则怎么样呢?

    答案是:规则一致。

    function foo(x) x
    (function() {
    /* ... */
    })()
    
    foo; // function foo(x) x(function(){ /* ... */})()

    这是个经典的分号自动插入实例——函数声明在新的一行同时在表达式的后面出现的奇怪表现。这里规则一致,foo函数(即使是表达式闭包)也以函数体x(function(){})()而不是x结束。

    您可以狠狠地点击这里:表达式闭包与分号自动插入规则测试demo

    结果如下图:

  15. “{“是当做声明块还是对象字面量?

    结果是:语句块。整体生产基本上符合常规的函数语法。

    function foo(x) { top: 0, left: 0 } // 语法错误

    你可能会想,function foo(x) { top: 0, left: 0 }应该被解析成function foo(x) { return { top: 0, left: 0 } },但是这里“正常”解析规则先执行,而function foo(x) { top: 0, left: 0 }回导致错误,花括号{}被解析成了函数体,而top: 0, left: 0被当做函数体里面的内容了。

  16. 如果是个表达式呢?

    结果与上面一样。

      (function(x, y) { left: x, top: y }) // 语法错误

    你必须小心地用括号包裹返回的对象,为的是花括号不会被认为是函数生成的一部分。在本例中,left: x, top: y导致了个错误。但是,如果返回的对象只包含一个值,或者甚至没有值,则会有类似下面的意想不到的行为表现:

    (function() { x: 1 } )(); // 返回 "undefined"
    (function() ({ x: 1 }) )(); // 表达式闭包; 返回带有x=1的对象
    
    (function() {} )(); // 返回 "undefined"
    (function() ({}) )(); // 表达式闭包; returns 对象
            
  17. 是否可以让函数明确严格?

    答案是:不行的!

    function foo(x, y) "use strict"; 1 + 1;
    typeof foo(); // "string" (而不是 "number")

    显然没起作用。本想使用严格指令(use strict)让函数行为表现得跟ECMAScript 5中的严格模式规则一样,然而没有任何特殊的表现。”use strict”被直接当做了字符串字面量。于是,上面的代码就直接被解析成了function foo(){ return "use strict"; },然后后面就是无止尽的1 + 1语句。

    您可以狠狠地点击这里:表达式闭包与严格模式测试demo

    结果如下截图(截自 FireFox 3.6):

    原作者也提供了上面小例子的一系列测试结果,您可以点击这里查看。

主要特征

恩,真是有意思。现在总结下一些主要特征:

  1. 函数体需一直存在
  2. 只有第一个表达式会被当做return语句
  3. 函数声明和函数表达式(显示命名还是匿名的)都可以用来被替换
  4. function idopt (argsopt) expr不完全等于function idopt (argsopt) { return expr }(依赖于上下文)
  5. 优先应用的表达式不明显。例如:(function()x+y)() vs. function()x+y()

实践?

据我所知,目前仅基于Mozilla的浏览器支持表达式闭包,其也不被诸如Google Closure Compiler或是YUI Compressor这样的工具支持,这些势必增加了其在实际项目应用中的挑战。

目前还不清楚是否有更多的工具会加入mozilla,添加对这种简写的支持。是否其可以作为和谐之路(road to Harmony)实验的一部分(以及其函数缩写语法)——下一个ECMAScript语言版本?你是否已经在你的工作/实验中使用表达式闭包,或者可能计划使用?你会改变他们的行为吗?

以上为翻译全文

大结局

虽然目前而言,表达式闭包在实际中应用不太现实。但是,本文对于拓展眼界,开阔思路还是很有帮助的。技术总在不停的发展,谁知道明天又会出来什么东西呢?

时间仓促,翻译能力有限。如果翻译不准确的地方,欢迎指正,不甚感谢。//zxx:独自饿扁啦,饭饭饭~~

(本篇完)

分享到:


发表评论(目前3 条评论)

  1. River说道:

    ie不支持 那就没有什么用啊 除非全国人民用FF了

  2. 夏雨说道:

    有点看不懂啦!

  3. Hafeyang说道:

    这个语法的改进不错,能让javascript更加简洁。