JS高程第七章笔记 --函数表达式

JavaScript 高级程序设计-第七章-函数表达式

函数表达式的特征

定义函数有两种方式:

  1. 函数声明:
function functionName(arg0, arg1, arg2){
}
//只在 Firefox、Safari、Chrome 和 Opera 有效 
alert(functionName.name); //"functionName"

函数声明有一个特征是函数声明提升,详见笔记—《JS 变量和函数的声明提升》。

  1. 函数表达式,最常见的函数表达式写法:
var functionName = function(arg0, arg1, arg2){
}

创建一个函数并赋给一个变量,很像常规的变量赋值语句,赋给那个变量的函数叫匿名函数(有时匿名函数也叫拉姆达函数),匿名函数的 name 属性是空字符串。

函数声明和函数表达式的区别详见笔记—《JS 变量和函数的声明提升》。

使用函数实现递归

递归函数是一个函数调用自身的构成的,一个经典的递归阶乘函数,但是会报错!,例子:

function factorial(num) {
    if (num <= 1) {
        return 1
    } else {
        return num * factorial(num - 1)
    }
}
var anotherFactorial = factorial
factorial = null
alert(anotherFactorial(4))  // 出错!

报错原因是,factorial = null 这一步使指向原始函数的引用就一个,但在执行 anotherFactorial 函数时需要调用 factorial 函数,但此时 factorial 为 null,所以报错(当然这里直接执行 factorial(4) 是没问题的)。

arguments.callee 是一个指向正在执行的函数的指针,用它来实现函数的递归调用,例子:

function factorial(num) {
    if (num <= 1) {
        return 1
    } else {
        return num * arguments.callee(num - 1)
    }
}
var anotherFactorial = factorial
factorial = null
alert(anotherFactorial(4))  // 12

警告:严格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。当一个函数必须调用自身的时候, 避免使用 arguments.callee(),要么给函数表达式一个名字,要么使用一个函数声明。

例子修改为:

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

这种方式在严格模式和非严格模式下都行得通。

使用闭包定义私有变量

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式是在一个函数中创建另一个函数,例子:

function createComparisonFunction(propertyName) {
    return function (object1, object2) {
        var value1 = object1[propertyName]
        var value2 = object2[propertyName]
        if (value1 < value2) {
            return -1
        } else if (value1 > value2) {
            return 1
        } else {
            return 0
        }
    }
}
var compareNames = createComparisonFunction("name")
var result = compareNames({ name: "Nicholas" }, { name: "Greg" })  // result===1
//解除对匿名函数的引用(以便释放内存) 
compareNames = null;

内部函数之所以能访问外部函数的参数,是因为当一个函数被调用时会创建一个执行环境和相应的作用域链,作用域链的起点是被调用函数的活动对象(arguments 和命名参数),外部函数的活动对象在第二位,再外部函数的活动对象在第三位,直到作用域链的终点(全局执行环境)。作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

一般来说函数执行完毕,局部活动对象会被销毁,内存只保留全局作用域。但是闭包不同,例子中匿名函数的作用域链为匿名函数的活动对象—> createComparisonFunction 函数的活动对象—> 全局执行环境。因此匿名函数可以访问 createComparisonFunction 函数的 propertyName 参数,而且在匿名函数被赋给变量 compareNames 后(此时 createComparisonFunction 函数已经执行完了)createComparisonFunction 函数的活动对象不会被销毁,因为它还在匿名函数的作用域中。如例子所示,销毁对匿名函数的引用,createComparisonFunction 函数的活动对象才会被销毁。

虽然像 V8 等优化后的 JavaScript 引擎会尝试回收被闭包占用的内存,但请大家 还是要慎重使用闭包。

闭包与变量

经典的闭包例子:

function createFunctions(){
    var result = new Array();
    for (var i=0; i < 3; i++){
        result[i] = function(){
          return i; 
        };
    }
    console.log(result[0](),result[1](),result[2]());
}
createFunctions() // 3 3 3

次数为3次的遍历中的每一个匿名函数都作用域链的第二位都是 createFunctions() 的活动对象,引用的是同一个变量 i,程序运行结束的时候 i 是3,所以输出的结果都是3。

修改后:

function createFunctions(){
    var result = [];
    for (var i=0; i < 3; i++){
        result[i] = function(num){
            return function(){
              return num;
            }
        }(i)
    }
    console.log(result[0](),result[1](),result[2]());
}
createFunctions() // 0 1 2

每一个匿名函数的作用域链的第二位是有着参数为 num 的立即执行的匿名函数的活动对象,num 参数是由处于作用域链第三位的 createFunctions() 的 i 值传递传递进来的,因此最后输出的是 0 1 2。

关于 this 对象

  1. this 对象是运行时基于函数的执行环境绑定的;
  2. 在全局环境中 this 等于 window,当函数作为某个对象的方法被调用时,this 等于这个对象;
  3. 匿名函数的执行环境有全局性,this 等于 window。
var name = 'The Window'
var object = {
    name: 'My Object',
    getName: function () {
        // var that = this; 如果下面的 this 改成 that 的话就可以指向 object 对象
        // 每个函数调用时都会自动获得 this 和 arguments,这里的 this 还是指向 object,下面匿名函数的 this 就指向全局了
        return function () {
            return this.name
        }
    }
}
console.log(object.getName()())  //  "The Window"

var name2 = 'The Window'
var object2 = {
    name2: 'My Object',
    getName2: function () {
        return this.name2
    }
}
console.log(object2.getName2())  //  "My Object"

我的理解是第一个最终调用的匿名函数,匿名函数执行环境具有全局性,因此输出是”The Window”,第二个最终调用的 getName 方法是作为对象 object2 的方法被调用的,this 等于 object2,因此输出是“My Object”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值