JavaScript学习之路--命名函数表达式

函数表达式和函数声明

在ECMAjavascript中,建立函数最经常使用的两个方法是函数表达式和函数声明,这二者的区别并非很明显。由于ECMA规范只明确了一点:函数声明必须带有标识符(identifier,也就是常说的函数名称),而函数表达式能够省略标识符。javascript

//函数声明
  function 函数名称:必须 (函数参数:可选){};
  
  //函数表达式
  function 函数名称:可选(函数参数:可选){}

也就是说,若是没有函数名称,那么确定是函数表达式,经常使用于:java

var foo = function(){};

那么若是有函数名称的时候,就要根据上下文来判断了。
若是是做为赋值表达式的一部分的话,那就是表达式。
若是是被包含在一个函数体内,或者位于程序最顶部,那么就是声明。json

var foo = function(){};// 函数表达式 

function foo (){}; // 函数声明

new function(){}; // 匿名类表达式

(function(){
  function foo(){}; // 声明 由于位于函数体内
})();

还有一种不太常见的表达式写法,就是被括号括住的(function(){}),这是由于在括号是一个分组操做符,在它内部只能是表达式。浏览器

function foo(){};//函数声明

(function foo(){}) //函数表达式,由于位于分组操做符内部

try{
    (var temp = 3);
}
catch(error){
    alert(error);
}
//这里会抛出异常,由于在()中只能放入表达式,而var是声明。

一样的,在使用eval方法,将string类型的json字符串转换为对象的时候,使用eval('('+str +')')也是这个缘由。把str放在括号内,就可使编译器把"{}"解析成表达式而不是代码块。ide

表达式和声明还有一个很大的差别就是:声明会在同一个做用域内,最先的表达式执行前执行解析。即便函数声明在最后一行,也会优先解析。函数

alert(foo()); 

 function foo(){
    return "abc";
 }
 
 //这也就是为何结果是"abc"的缘由,声明虽然写在下面,可是在alert表达式以前已经被执行了。
 
 // 可是若是foo不是以声明的方式定义,而是以表达式的方式定义的话,就会出现问题了:
 
 alert(foo());
 var foo = function(){
    return "abc";
 };
 
 //Uncaught TypeError: foo is not a function 会出现这个错误,由于在alert调用foo的时候,foo并无被解析。

另外,虽然在条件语句内部可使用函数声明,可是并无人测试过兼容性,所以,若是须要“重载”函数,最好仍是使用表达式:测试

if(true){
    function foo(){
    }
  }
  else{
    function foo(){
    }
  }
  //这种写法存在必定风险,最好使用如下表达式的写法
  var foo;
  if(true){
    foo = function(){};
  }
  else{
  }

命名函数表达式

上面说了一堆函数声明和函数表达式的区别,如今终于言归正传说到正题了。能够看到,上面函数表达式的例子里面,都是没有名字的。调试

//常规写法:
var foo = function(){};

//命名函数表达式:
var foo = function f(){};
f就是这个表达式的名字,可是,它的做用域仅仅在于函数内部

var foo = function f(){
    alert(typeof f);
};
foo();
alert(f);//Uncaught ReferenceError: f is not defined

既然如此,命名还有什么用呢?
答案就是在调试的时候,能够很爽。。。。code

var f1 = function (){
    return "abc";
};

var f2 = function(){
    return f1() + "edf";
};

var f3 = function(){
    return f2();
};

alert(f3());

表达式不命名时的堆栈信息

命名表达式时的堆栈信息

能够看到,当使用命名表达式时,Call Stack会使用该名称。不然会自动起一个名字。能够确定的是,当状况复杂的时候,解析器并不会返回你指望的那个名字,这也就是为何要使用命名表达式的缘由。对象

早期版本下,命名函数表达式的bug

  1. 表达式标识符的做用域泄露到外部做用域,也就是说在函数外部,该标识符也是可见的
  2. 将函数表达式同时看成了函数声明,即将函数表达式的解析也提到最前面了
  3. 命名函数表达式会建立两个不一样的对象。
var foo = function f(){};
alert(foo === f);// 结果是false,固然这个只有在低版本的ie浏览器下才会看到,正常状况下f应该是undefined

了解到这些bug之后,最好的解决方案就是:当标识符不存在。
由于自己就是方便调试的,那么代码里面直接无视就是最好的办法。

替代方案

若是不想使用“命名”的方式的话,能够还有一种简单的方式来替代,就是在函数表达式中,定义一个函数声明,而后将这个函数声明返回。

var hasClassName = (function(){
    var cache = {};
    
        var _className = '(?:^|\\s+)' + className + '(?:\\s+|$)';
      var re = cache[_className] || (cache[_className] = new RegExp(_className));
      return re.test(element.className);
    } 
    
    return hasClassName;
    
 })();
相关文章
相关标签/搜索