JavaScript 函数表达式

  JavaScript中建立函数主要有两种方法:函数声明和函数表达式。这两种方式都有不一样的适用场景。这篇笔记主要关注的是函数表达式的几大特色以及它的使用场景,下面一一描述。ide

  主要特色

  • 可选的函数名称

  函数名称是函数声明的必需组成部分,这个函数名称至关于一个变量,新定义的函数会复制给这个变量,之后函数的调用都须要经过这个变量进行。而对于函数表达式来讲,函数的名称是可选的,例以下面的例子:模块化

var sub = function(a1,a2){
    return a1-a2;
}

  这个例子中函数表达式没有名称,属于匿名函数表达式。再看下面的例子:函数

var sub = function f(a1,a2){
    return a1-a2;
}
console.log(f(5,3));   //错误调用方式
console.log(sub(5,3));   //正确调用方式

  在这个例子中,函数表达式的名称为f,这个名称f实际上变成了函数内部的一个局部变量,而且指代函数对象自己,在函数递归的时候有很大用处,后面会详细讲到。this

  • 在执行阶段建立(区别于函数声明)

   这个特色是函数表达式明显区别于函数声明的地方。对象

  解释器在解析JavaScript代码时对于这两种方式并非一视同仁的。解释器会首先读取函数声明,并使其在执行任何代码以前可用;而对于函数表达式,则必须等到解释器执行到它所在的代码行,才会被真正解析执行。例如:递归

console.log(add(1,2));   //"3"
console.log(sub(5,3));   //"unexpected identifier",报错
function add(a1,a2){
    return a1+a2;
}
var sub = function(a1,a2){
    return a1-a2;
}

  第一条语句彻底能够正常执行。对代码求值时,JavaScript引擎在第一遍就会声明函数并经过一个名为函数声明提高的过程将它们放到源代码树的顶部。也就是说在执行环境的建立阶段(函数被调用但尚未开始执行)就会对函数声明进行"hosting"操做。因此,即便声明函数的代码在调用它的代码后面,JavaScript引擎也会把函数声明提高到顶部。可是若是把函数声明更改成函数表达式,就会在执行期间报错。缘由在于在执行到函数所在的语句以前,变量sub中并不会包含对函数的引用。也就是说在代码执行阶段,变量sub才会被赋值。除了以上不一样,在其它方面函数声明和函数表达式的语法是等价的。接口

  • 不会影响变量对象
var sub = function f(a1,a2){
    console.log(typeof f);  //"function"
    return a1-a2;
}
console.log(typeof f);   //"Uncaught ReferenceError: f is not defined(…)"

  经过上面的例子能够看到,函数名称f只能在函数对象内部使用,函数表达式的函数名称并不存在于变量对象中。ip

  使用场景

  函数表达式的使用场景不少。下面主要描述的是函数递归以及代码模块化方面的应用。作用域

  • 函数递归

  看下面的例子:get

function factorial(num){
    if(num <= 1){
         return 1; 
    }else{
         return num * factorial(num - 1);
    }
}

  这是一个经典的阶乘函数,可是这个例子存在的一个问题是函数名称factorial与函数体紧密耦合在一块儿,执行下面的语句就会报错:

var anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(5));   //"Uncaught TypeError: factorial is not a function"

  报错的缘由在于在函数体内部会调用factorial函数,而变量factorial对函数的引用已经被解除因此报错。这种状况的解决方法通常可使用arguments.callee来解决,arguments.callee始终指向当前的函数,例如:

function factorial(num){
    if(num <= 1){
         return 1; 
    }else{
         return num * arguments.callee(num - 1);
    }
}

  这样在此执行anotherFactorial函数就能够获得正确结果了。可是在严格模式"strict"下,arguments.callee是不能经过脚本访问的,这是就可使用函数表达式来解决这个问题了,例如:

var factorial = (function f(num){
    if(num <= 1){
         return 1; 
    }else{
         return num * f(num - 1);
    }
});
console.log(factorial(5));   //"120"
  • 代码模块化

  JavaScript中没有块级做用域,但咱们可使用函数表达式模块化JavaScript代码。模块化代码中能够封装没必要让使用者知道的细节,只暴露给使用者相关接口,同时能够避免对全局环境的污染,例如:

var person = (function(){
    var _name = "";
    return{
        getName:function(){
             return _name;
        },
        setName:function(newname){
             _name = newname;
        }
    };
}());
person.setName("John");
person.getName();   //"John"

  这个例子中建立了一个匿名函数表达式,这个函数表达式中包含了模块自身的私有变量和函数;这个函数表达式的执行结果返回一个对象,对象中包含了模块暴露给使用者的公共接口。代码模块化的具体形式还有不少,例如在一些经常使用的JavaScript库中一般都会使用相似下面例子的当即执行函数:

(function(){
    var _name = "";
    var root = this;
    var person = {
        getName: function(){
            return _name;
        },
        setName: function(newname){
            _name = newname;
        }
    };
    root.person = person;
}.call(this));
person.setName("John");
person.getName();   //"John"

  这种方式直接将包含模块公共接口的对象做为全局对象的一个属性,这样在其它地方直接可使用全局对象的这个属性来使用这个模块了。

  这篇笔记只包含了JavaScript中函数表达式的一部份内容,可能存在不许确或错误的地方,但愿可以指出!

相关文章
相关标签/搜索