`
yiminghe
  • 浏览: 1437901 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

function declaration in firefox

阅读更多

条件函数定义与异常作用域

好像以前没记录过,罕有情景 firefox 被ie 系列(<=8)以及chrome v8,safari,opera 孤立了:

经典如: 

if(true){
		function god(){
			
		}
	}else{
		function god(){
			alert("what the hell!");
		}
}
alert(god);

不常见如:

alert(typeof z);
try{
	var x=1;
}catch(e){
	function z(){}
}

alert(typeof z);

总结:

函数声明在非firefox下是神,无视障碍会穿越(最后声明的就是事实)!哪个是标准?

 

updated 2010-10 :

标准与实现

ie,chrome 中函数都会根据 ecma 规范被提前申明,例如:

 

 

if(false) {
   function f(){..}
}
 

等价于

 

function f(){..}
if(false){}

 

但是 ie 对于具名函数表达式处理有差异。ie 中

 

if(false){
   var f=function f(){
      ....
   };
}

 

相当于:

 

function f(){
...
}

if(false) {
   var f=function(){...}
}
 

chrome 最符合标准,具名表达式中的名字只能用于该函数内部递归调用,而不能在当前作用域起作用,则相当于:

 

if(false){
 var f=function(){...};
}
 

firefox 由于自 javascript1.5 引入了 Conditional function declarations ,则函数申明也可以条件化( firefox 把这种不叫做声明,而叫做表达式

 

if(false){
function f(){..}
}

相当于:

 

if(false){
//不一定是window,只是在当前作用域动态定义
window.f=function(){...}
}
 

详细可见这里的讨论 以及针对jscript的实验 。(相等比较部分看这里 。)

 

updated 2011-03-04 :

 

引擎实现

 

根据 Narcissus 的实现(基于 firefox ),函数定义可分为三种方式:


DECALRED_FORM :

 

function t(){}
 

STATEMENT_FORM :

 

{
 function t(){
 }
}
 

EXPRESSED_FORM :

 

var x = function t(){}
 

而对于 DECALRED_FORM 各个浏览器自然没有疑问。这次主要看看 firefox 的整体函数实现:

 

switch (n.type) {
      case FUNCTION:
        if (n.functionForm != DECLARED_FORM) {
           /*
              function(){}
              或
              {
                     function t(){
                     }
              }
           */
           if (!n.name || n.functionForm == STATEMENT_FORM) {
                v = new FunctionObject(n, x.scope);
                //如果是有名字,则在当前作用域中加入名字
                if (n.functionForm == STATEMENT_FORM)
                    x.scope.object[n.name] = v;
            } 

          /**
              var x=function t(){};
          **/
          else {
                //新生成一个作用域,里面只有名字 t ,当前作用域引不到
                x.scope = {object: {}, parent: x.scope};
                try {
                    //只能在函数中引到本函数的名字 t
                    v = new FunctionObject(n, x.scope);
                    // t 加入到新的作用域
                    x.scope.object[n.name] = v;
                } finally {
                    //恢复
                    x.scope = x.scope.parent;
                }
            }
        }
        break;

 

 

update 2011-08-16

function and function scope

 

细读 function and function scope @ mdc :

 

function x(){}

 

相当于函数名为 x ,并且在当前 scope 加了一个变量 x 指向函数 x.

 

看起来和函数表达式情况类似:

 

var x=function x(){}
 

明显了除了解析阶段不同,还有一个微小的差别:

 

函数声明不会绑定函数名到函数体作用域,而函数表达式则会将将函数名绑定到函数体作用域,而函数名是不可变的,可变的只是那个在当前作用域指向函数的变量:

 

函数声明没有绑定函数名到函数体:

 

(function () {
        function x(zz) {
            if (zz == 2) {
                alert("o");
                return;
            }
            x(2);
    
        }
        var z = x;
        x = 1;
        z();
    })();

 函数表达式绑定了函数名到函数体:

 

(function () {
        var x = function x(zz) {
            if (zz == 2) {
                alert("o");
                return;
            }
            x(2);
    
        }
        var z = x;
        x = 1;
        z();
    })();

 

catch 内变量声明问题:

 

try{
var x=1;
throw x;
}catch(e){
   var x=2;
}
alert(typeof e);
alert(x);

 

除了 ie<9 ( ie9 e 也会 scope leak 到上一层作用域 )外其中 e 是没问题的,但是x就很奇怪了,根据ecma规范,catch 会新建一个词法环境,e以及在里面申明的变量不会和外边的混在一起的?catch退出后新的词法环境就消失了,怎么x会被修改呢?

The production Catch : catch ( Identifier ) Block is evaluated as follows:
1.    Let C be the parameter that has been passed to this production.

2.    Let oldEnv be the running execution context’s LexicalEnvironment.

3.    Let catchEnv be the result of calling NewDeclarativeEnvironment passing oldEnv as the argument .

4. Call the CreateMutableBinding concrete method of catchEnv passing the Identifier String value as the
argument.
5.    Call the SetMutableBinding concrete method of catchEnv passing the Identifier, C, and false as arguments. Note that the last argument is immaterial in this situation.
6.    Set the running execution context’s LexicalEnvironment to catchEnv.

7.    Let B be the result of evaluating Block.

8.    Set the running execution context’s LexicalEnvironment to oldEnv.

9.    Return B.

 

updated 2011-08-16:

 

异常 e 应该是插入到新建的申明作用域头,但这个声明作用域比较特殊,则其内声明的变量其实会声明在父作用域,这个新的作用域在查找时起作用,只有在这个作用域能够找到 e

 

Refer:

block scope in javascript

 

catch warning in jslint


http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
 
http://www.dustindiaz.com/javascript-function-declaration-ambiguity/

 

分享到:
评论
1 楼 厨房男 2010-07-15  
functon 确定了词作用域,javascript没有块级作用域概念

相关推荐

Global site tag (gtag.js) - Google Analytics