JavaScript 1946bv1946(10)- 函数表达式

一、函数表达式

函数表达式不同于函数声明。函数声明要求有名字,但表达式不需要。 创建一个匿名函数(也称拉姆达函数),赋值给变量。

二、递归

递归函数是一个函数通过名字调用自身的情况下构成的。例如: 上例中函数体内调用自身函数时函数名固定了,如果给函数保存给其他变量就会导致错误,更好的写法: 但严格模式下 arguments.callee 不可用,可以改写为: 以上代码创建了名为 f 的函数,再赋值给 factorial ,此时把 factorial 再赋值给另一个变量,函数 f 仍然有效,递归可以正确调用。

三、闭包

当在函数内部定义了其他函数,就创建了闭包。闭包是指有权访问包含函数内部的所有变量的函数。
1.创建闭包的常见方式
在一个函数中创建另一个函数并返回它。 上例中内部函数访问了外部函数的变量 propertyName ,即使这个内部函数被返回了,或者在其他地方被调用了,他任然可以访问变量 propertyName。这里需要知道执行环境和作用域链,可看这里
内部的细节是:在一个函数内部定义的函数(内部函数)会将包含函数(即外部函数)的活动对象添加到它的作用域链中。因此,在执行最后两行代码时,首先返回内部函数,它的作用域链被初始化为包含 createComparisonFun 函数的活动对象(activation object) 和全局变量。即使 createComparisonFun 函数执行完毕后,其执行环境的作用域链被销毁,但活动对象仍然在内存中,因为内部函数仍然在引用这个活动对象。直到内部函数被销毁后,createComparisonFun 函数的活动对象才会被销毁。
注:由于闭包会携带包含它的函数的作用域,内存占用较大,谨慎使用。
2.闭包与变量
作用域链的这种配置机制引出了一个问题:即闭包只能取得包含函数中任何变量的最终的结果,因为闭包保存的是整个变量对象而不是某个特殊的变量。例如: 预想中,arr 的每一项应该返回自己的索引值,但实际上,每个函数都返回10,因为每个函数的作用域链中都保存着 createFunction 函数的活动对象,所以都引用同一个变量 i , createFunction 函数执行结束后,变量 i 的值为 10。
可以这样改写:
3.关于 this 对象
在闭包中使用 this 对象,可能会有问题。this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 指window;当函数作为某个对象的方法调用时,this 就等于那个对象。匿名函数的执行环境具有全局性,this 通常指 window。但有时候由于编写的方式不同,看起来不会那么明显。
例如:
obj.getNameFun() 返回一个匿名函数,obj.getNameFun()()会立即调用它返回的函数,结果是返回全局的 name 属性。这是因为:每个函数再被调用时,其活动对象都会自动取得两个特殊变量,this 和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,不可能直接访问外部函数中的这两个变量。对于匿名函数,调用时内部的this指 window。
注:在通过call()和apply()改变函数执行环境的情况下,this会指向其他对象。

四、私有变量

任何函数中定义的变量,都可以认为是私有变量。私有变量包括函数的参数,局部变量,在函数内部定义的其他函数。而有权访问私有变量和私有函数的公共方法称为特权方法。
1.静态私有变量
上例中,创建了一个私有作用域,定义了私有变量、构造函数和公有方法,公有方法定义在原型上。构造函数和公有方法都可以访问私有变量name。这种模式下,name就是一个静态的、所有实例共享的属性。在一个实例上调用setName(),会影响所有实例。而调用setName()或新创建一个Person的实例都会赋予name属性一个新值。
注:使用闭包和私有变量的问题:多查找作用域链中的一个层次,就会在一定程度上影响查找速度。
2.模块模式
模块模式是为单例创建私有变量和特权方法的。单例是指只有一个实例的对象。 JavaScript是以对象字面量来创建单例对象的。模块模式通过为单例添加私有变量和特权方法能够使其得到增强。 上例中使用了一个返回对象的匿名函数。在匿名函数内部,先定义了私有变量和函数。最后将一个对象字面量做为函数值返回。这个对象就是单例的公共接口,返回的对象中包含公开的属性和方法,公共的方法有权访问上面的私有变量和函数。这种模式在需要对单例进行初始化,同时又需要维护其私有变量时很有用。
例如:
3.增强的模块模式
增强的模块模式是指:在返回对象前加入对其增强的代码。这种模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性或非法对其加以增强的情况。 上例增强的部分在创建了 app 的过程,因为它必须是 baseComponent 的实例。

小结

1.递归函数应该始终使用 arguments.callee来递归调用自身,不要使用函数名-函数名可能会有变化。
2.闭包原理:
a.在后台执行环境中,闭包的作用域链包含着自己的作用域,包含函数的作用域和全局作用域。
b.通常,函数的作用域及其所有变量都会在函数执行结束后销毁。但是,当函数返回一个闭包是,这个函数的作用域会一直保存到闭包不存在为止。
3.闭包会携带包含它的函数的作用域,内存占用较大。
评论 ( 0 )
最新评论
暂无评论
赶紧努力消灭 0 回复