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

函数表达式和函数声明

在ECMAjavascript中,创建函数最常用的两个方法是函数表达式和函数声明,这两者的区别并不是很明显。因为ECMA规范只明确了一点:函数声明必须带有标识符(identifier,也就是常说的函数名称),而函数表达式可以省略标识符。

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

也就是说,如果没有函数名称,那么肯定是函数表达式,常用于:

var foo = function(){};

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

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放在括号内,就可以使编译器把"{}"解析成表达式而不是代码块。

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

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

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

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;
    
 })();

转载于:https://www.cnblogs.com/liuzhuang-blog/p/5122671.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值