定义函数的两种方式
(1)函数声明
- 函数声明提升(执行代码之前会先读取函数声明,因此可以把函数声明放在调用它的语句后面);
- 不能出现在判断、循环等位置。
1 | function functionName(arg0, arg1,arg2){ |
(2)函数表达式
1 | //匿名函数(拉姆达函数) |
7.1 递归
不健壮的递归
1 | function factorial(num){ |
通过arguments.callee规避错误(非严格模式下)
1 | function factorial(num){ |
通过命名函数表达式(严格或非严格模式都正常工作)
1 | var factoriall = (function f(num){ |
7.2 闭包
说明:指有权访问另一个函数作用域中的变量的函数。
普通函数 : 不访问其他作用域的变量的函数
作用域中引用的变量对象 | 产生 | 消亡 | 作用域链中的位置 | 包含属性 |
---|---|---|---|---|
全局变量对象 | 创建compare()函数时,会创建一个预先包含全局变量对象的作用域链 | 关闭网页 | 1 | this 、result 和compare |
本地活动对象 | 第一次调用cpmpare() 时,为函数创建一个执行环境,然后通过赋值函数的[[Scope]] 属性中的对象构建起执行环境的作用域链。此后, 活动对象被创建并推入作用域链前端 |
函数执行完毕后 | 0 | this 、arguments 、value1 和value2 |
1 | /** |
创建闭包:创建闭包的常见方式就是在一个函数内部创建另一个函数。
问题:会携带外部函数的作用域,因此占用更多内存。
1 | /** |
闭包作用域链中引用的变量对象 | 产生 | 消亡 | 作用域链中的位置 | 包含属性 |
---|---|---|---|---|
全局变量对象 | 外部函数被执行后 | 关闭网页 | 2 | this 、compare 、result 和ComparisionFunction |
外部函数的本地活动对象 | 外部函数被执行后 | 闭包失去引用 | 1 | prototyName |
本地活动对象 | 闭包被调用时 | 闭包执行完毕 | 0 | this 、arguments 、value1 、value2 |
7.2.1 闭包与变量
闭包局限性:闭包只能取得任何变量的最后一个值。
1 | function createFunctions(){ |
避开局限性:通过立即执行匿名函数创建函数避开局限性
1 | function createFunctions(){ |
7.2.2 关于this对象
闭包的问题 : 内部函数只在活动对象中搜索this和arguments属性。
1 | var name = 'The Window'; |
解决办法:把外部作用域中的this对象保存在一个闭包能够访问的变量里。
1 | var name = 'The Window'; |
7.2.3 内存泄露
IE9- : 对JScript
对象和COM
对象使用不同的垃圾收集例程,如果闭包的作用域链中保存着一个HTML
元素,意味着该元素无法被销毁。
闭包引用外部函数活动对象导致内存泄漏
1 | function assignHandler(){ |
7.3 模仿块级作用域
将函数声明转换为函数表达式:只要函数执行完毕,就可以立即销毁其作用域链。
1 | function outputNumbers(count){ |
7.4 私有变量
特权方法(privileged method):有权访问私有变量和私有函数的公有方法。
构造函数中定义特权方法:缺点是为每个实例都重新创建一遍方法。
1 | function Person(name){ |
7.4.1 静态私有变量
静态:因为变量所在的活动对象在构造函数被载入的时候,就被添加到构造函数的作用域链中,因此会被所有实例共享。
私有: 因为变量只存在于作用域链中,无法直接访问,只能通过特定方法访问。
1 | (function(){ |
7.1.2 模块模式: 通过为单例添加私有变量和特权方法使其增强
单例(对象字面量)
1 | var singleton = { |
模块模式:为单例添加私有变量和特权方法
说明:因为返回字面对象,所以每个单例都是Object的实例;
适用:适用于必需创建一个对象并需要将其初始化,同时还要公开一些能够访问这些私有数据的方法的场合。
1 | var singleton = function(){ |
7.4.3 增强的模块模式
- 返回特定类型的对象;
- 需要添加某些属性或方法。
1 | var singleton = function(){ |