W3C home > Mailing lists > Public > public-html-ig-zh@w3.org > December 2012

RE: 关于函数定义和函数表达式的[[Scope]]问题

From: 权一 <qjy1111@hotmail.com>
Date: Wed, 12 Dec 2012 11:18:26 +0000
Message-ID: <BLU158-W9A971A22A0498653FB063AD4F0@phx.gbl>
To: <otakustay@live.com>, <public-html-ig-zh@w3.org>
CC: <kanghaol@oupeng.com>









问题分析:


函数 test 内会创建  词法环境,变量环境. 初期他俩是一个玩意. 只是语意不同.
变量环境负责绑定 变量 函数 以及形参. 
词法环境则一般来说都和变量环境是一个东西, 他们也统称为词法环境(复数).


那么标准中阐述他俩的区别是, 词法环境是可变的.而变量环境不可变.比如 上面的with statement 就会导致 创建一个新的词法环境,然后把 当前词法环境的值设为新的词法环境. 直到 with statement 的代码执行完毕.再恢复回去.


这似乎也没啥问题. 但是ES5又规定 函数声明和函数表达式 创建函数对象时.采用其所在执行环境的 词法环境不一致.  函数声明,使用用的是 变量环境, 而函数表达式则使用词法环境. 这就带来了问题


eval代码中明确规定 非严格模式下, 其内部的 词法环境即为其所在执行环境的词法环境, 变量环境,即为其所在执行环境的变量环境.
那么就有趣了.  这样看起来. foo 内部和  bar 内部访问的标识符 x 应该分别属于两个东西.   foo为函数声明,应该访问对应的变量环境. 也就是  x = 1 . 而函数表达式则访问当前词法环境 . 则是2.  


但是 IE10-, Safari6- ,FF17- ,Chrome24- 等ES5引擎实现的结果 与我们期待值不符.   而Opera12-ES5实现 则奇葩的是.两个都是1 . 这个不予讨论.


根据kenny的信息,好在ES6 的草案目前似乎统一了 函数声明,和函数表达式依赖的 环境差异.  也许是有人意识到 标准的不合理吧?






另外一个值得注意的是. with (expression) statement   通过产生式的分析发现. 其实  statement 中是不能出现函数声明的(所有引擎都实现错误). 当然, 这并不影响上面的demo. 因为eval并不在这个限制范围内.
我们可以考虑 这里应该是一个疏忽. 因为5.1制定时可能考虑到. statement 中不可能出现函数声明(遗漏了eval的场景).. 所以并没想过会引发类似的问题吧?  但是我更加疑惑的是.函数表达式和函数声明区分初始化环境的做法的目的...暂时记录下吧.

个人吐槽: ES3 啥都交代的不清楚, ES5试图啥都交代清楚.但是似乎更绕了.让人不好懂. 且此处还疑似出现bug. 至少各个引擎实现不符. ES6还在草案中...
期待ES6的 正是版本吧... 


From: otakustay@live.com
To: public-html-ig-zh@w3.org
CC: kanghaol@oupeng.com
Date: Wed, 12 Dec 2012 07:45:33 +0000
Subject: 关于函数定义和函数表达式的[[Scope]]问题




根据[ECMA标准第13节](http://ecma-international.org/ecma-262/5.1/#sec-13), 创建**函数定义**和**函数表达式**的过程几乎一样,唯一的区别是调用[创建函数对象](http://ecma-international.org/ecma-262/5.1/#sec-13.2)这一过程时会传递不同的**Scope**参数,**函数定义**用的是**VariableEnvironment**,**函数表达式**用的是**LexicalEnvironment**。不过现实好像有点不一样。 来看下面的代码:     function test() {        var x = 1;        var o = { x: 2 };        with (o) {            eval('function foo() { console.log(x); }');            eval('var bar = function() { console.log(x); }');        }        foo();        bar();    }    test(); 我没理解错的话,这段代码:
- 在`with`语句外面,**VariableEnvironment**和**LexicalEnvironment**指向的是同一个对象,我这里叫它`outerEnv`。- 在`with`语句里面,会有一个新的**LexicalEnvironment**产生(是个object environment),而**VariableEnvironment**保持不变,这个新的**LexicalEnvironment**就叫它`innerEnv`。
关键的问题是,当2个`eval`调用时,这个显然是直接调用,并且不是严格模式。这种情况下,`eval`会共享外层环境的**LexicalEnvironment**和**VariableEnvironment**,因此它的代码中,**LexicalEnvironment**和**VariableEnvironment**是不同的对象,即**LexicalEnvironment**为`innerEnv`,**VariableEnvironment**是`outerEnv`。
然后在`eval`中,分别创建了一个**函数定义`foo`**和一个**函数表达式`bar`**,那么它们的`[[Scope]]`应该不一样,其中`foo`的是`outerEnv`,而`bar`的是`innerEnv`。
因此当这2个函数被执行时,`foo`应该输出`1`,`bar`输出`2`。不过事实是,Chrome、Firefox的最新版本,IE7-10,统一都是输出`2`和`2`。
同样的问题也出在`catch`语句中:     function test() {        var x = 1;        try {            throw 2;        }        catch (x) {            eval('function foo() { console.log(x); }');            eval('var bar = function() { console.log(x); }');        }        foo();        bar();    }    test();
现在的问题是,为什么ECMA要定义出一个和几乎所有浏览器(Opera和Safari等我没测,感觉八九不离十)都不同的行为。这种算是标准有问题,还是所有浏览器都应该修复呢?
其实我觉得,正常来说,**函数定义**也应该用**LexicalEnvironment**就合理了,而且**函数定义**通常是在进入函数时就被创建的,此时**LexicalEnvironment**和**VariableEnvironment**肯定是相同的,仅有`eval`配合`with`或`catch`才会出现这种情况。
Gray Zhang-------------------------------------------otakustay@live.comhttp://www.otakustay.com 		 	   		   		 	   		  
Received on Thursday, 13 December 2012 10:47:16 UTC

This archive was generated by hypermail 2.3.1 : Tuesday, 6 January 2015 20:43:51 UTC