JavaScript函数表达式

函数表达式

  1. 函数声明:函数声明提升,执行代码前会先读取函数声明

    1
    2
    3
    4
    sayHi(); // 正常执行
    function sayHi(){
    console.log("hi");
    }
  2. 函数表达式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 创建一个函数并将它赋值给变量 functionName,这种叫做匿名函数,name 属性是空字符串
    var functionName = function(){
    // 函数体
    }

    sayHi(); //错误:函数还不存在
    var sayHi = function(){
    console.log("hi");
    }

递归

  1. 递归函数是在一个函数通过名字调用自身的情况下构成的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function factorial(num){
    if(num <= 1){
    return 1;
    } else {
    return num * factorial(num - 1); // 跟函数名耦合
    return num * arguments.callee(num - 1); // 解耦 严格模式不支持
    }
    }

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

闭包

  1. 闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数
  2. 闭包只能取得包含函数中任何变量的最后一个值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    function createFunctions() {
    var result = new Array();

    for (var i = 0; i < 10; i++) {
    result[i] = function(){
    return i;
    }
    }

    return result;
    }
    console.log(createFunctions()[0]()); // 10

    function createFunctions() {
    var result = new Array();

    for (var i = 0; i < 10; i++) {
    result[i] = function(num){
    return function(){
    return num;
    }
    }(i);
    }

    return result;
    }
    console.log(createFunctions()[0]()) // 0

    var adder = (function (){
    var n = 0
    return function(){
    return n += 1
    }
    })()
    console.log(adder()) // 1
    console.log(adder()) // 2
  3. 在闭包中使用 this 对象也可能导致一些问题。this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等于那个对象。匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var name = "The Window";
    var object = {
    name: "My Object",
    getNameFunc: function(){
    var that = this // this 指向的是window
    return function(){
    return that.name;
    return this.name; // 非严格模式下"The Window"
    }
    }
    }

    console.log(object.getNameFunc()()) // "My Object"
  4. 闭包会引用包含函数的整个活动对象,因此要解除对象引用释放内存

    1
    2
    3
    4
    5
    6
    7
    8
    function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
    console.log(id);
    };
    element = null; // 解除引用
    }

模仿块级作用域

JavaScript 没有块级作用域。这意味着在块语句中定义的变量,实际上是包含函数中而非语句中创建的

1
2
3
4
5
6
7
8
9
10
function outputNumber(count){
for(var i = 0;i<count;i++){
console.log("for:" + i)
}
var i; // 视而不见
// var i = 0 ; 会执行
console.log("output:" + i);
}

outputNumber(10) // for: 1 for: 2 for: 3... output: 10

JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到上述情况,它只会对后续的声明视而不见(变量初始化会执行)。匿名函数可以用来模仿块级作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function(){
// 这里是块级作用域
})();

function outputNumber(count){
(function(){
for(var i = 0;i<count;i++){
console.log("for:" + i)
}
}
)();
console.log("output:" + i);
}

outputNumber(10) // i is not defined

私有变量

JavaScript 中没有私有成员的概念;所有对象属性都是公有的。但是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。包括函数的参数、局部变量和在函数内部定义的其他函数
(1) 即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量
(2) 有权访问私有变量的公有方法叫做特权方法
(3) 可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式、增强的模块模式来实现单例的特权方法