Javascript-函数表达式

理解函数声明和函数表达式

1、函数声明

function functionName(arg0,arg1,arg2){
}

重要特征:函数声明的提升,即在代码执行前解析器会先读取函数声明

2、函数表达式

var functionName = function(){
  //函数体
};

匿名函数:像等号右边这样没有函数名的的函数就是匿名函数

一、递归

递归函数是在一个函数通过名字调用自身的情况下构成的

function factorial(num) {
    if(num <= 1){
        return 1;
    }else{
        return num * factorial(num - 1);
    }
}
var anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(4));

以上代码运行会报错,因为当运行anotherFactorial的时候,函数体内部的factorial已经是null,所以要通过arguments.callee来解决,arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用

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

但是在“use strict”模式下不能访问arguments.callee。所以我们要换一种方法解决,使用命名函数表达式来达成相同的结果

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

以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial。即便把函数赋值给了另一个变量,函数的名字f 仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。

二、闭包

1、概念

闭包是(指有权访问另一个函数作用域中的变量的)函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数

变量对象:每创建一个执行环境都有一个对应的变量对象,这个对象包含着这个作用域中的所有变量。在浏览器中,全局变量对象就是window

作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象

无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是,闭包的情况又有所不同。

看了一遍JS红皮书的这一章,感觉还是有点懵逼。就我现在所知道的闭包的知识:

用途:

1、访问函数内部变量。

2、让变量的值始终保持在内存中。

三、模仿块级作用域

匿名函数可以用来模仿块级作用域并避免这个问题。用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。

(function(){
    //这里是块级作用域
})();

以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。

function outputNumbers(count) {
    (function(){
        for(var i=0; i<count; i++){
            console.log(i);
        }
    })();
    console.log(i);
}
outputNumbers(6);  //Uncaught ReferenceError: i is not defined

四、私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。 私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

我们把有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。
在对象上创建特权方法的方式: