定义 函数有 2 种形式
- 函数声明,它有一个重要特就是函数声明提升,即在执行代码之前会先读取函数声明。
- 函数表达式,创建一个函数并将其赋值给变量,此种情况下创建的函数叫匿名函数,因为 function 关键字后面没有标识符
闭包
闭包
是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,在一个函数内部创建另一个函数。
作用域链
作用域链的本质是一个指向变量对象的指针列表。无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。
当某个函数被调用时,会创建一个执行环境及相应的作用域链,这个作用域链被保存在内部的[[Scopes]]
属性中。然后使用arguments
和其他命名参数的值函数的活动对象,并将该活动对象放至作用域链前端,外部函数的活动对象位于作用域链第二位,外部函数的外部函数的活动对象处于第三位,….直至作为作用域链终点的全局执行环境。
1 | function createComparisonFunction(propertyName) { |
在匿名函数从 createComparisonFunction()中返回后,它的作用域链被初始化位包含 createComparisonFunction()函数的活动对象和全局变量对象。并且在 createComparisonFunction()函数执行完毕后,其执行环境的作用域链会被销毁,但它的活动对象会保留在内存中,因为匿名函数的作用域链仍然在引用这个活动对象。直到匿名函数被消耗,createComparisonFunction()的活动对象才会被销毁。
闭包作用
闭包有两个作用:
- 可以读取自身函数外部的变量(沿着作用域链寻找)
- 让这些外部变量始终保存在内存中
闭包与变量
一个值的注意的地方:闭包只能取得包含函数中任何变量的最后一个值。闭包所保存的是整个变量对象,而不是某一个特殊的变量。
1 | function createFunctions() { |
上述函数会返回一个函数数组,表面是,似乎每个函数都应返回自己的索引值,但实际上,每个函数都返回 10.因为每个函数的作用域链中都保存着 createFunctions()函数的活动对象,所以它们引用的都是同一变量 i,当 createFunctions()返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量对象,所以每个函数内部的 i 的值都是 10
要解决此问题,可以通过创建另一个匿名函数,并立即执行匿名函数,将其结果赋值给数组。这里的匿名函数中有一个参数 num,也就是最终函数要返回的值,即返回了一个返回 num 的闭包。
1 | function createFunctions() { |
关于 this 对象
this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 指向 window,而当函数被作为某个对象的方法调用时,this 指向那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window
另外,每个函数在被调用时都会自动获取 2 个特殊变量:this 和 arguments。内部函数在搜索这 2 个 变量时,只会搜索到其活动对象为止,因此永远不能直接访问外部函数中的这两个变量。不过,可以把外部函数中的 this 对象保存在一个闭包能访问的变量里,就可以让闭包访问该对象了
1 | var name = "The Window"; |
模仿块级作用域
JavaScript 没有块级作用域的概念,立即执行的匿名函数可以用来模仿块级作用域,语法如下:
1 | (function() { |
这种技术经常用于全局作用域中的外部函数,从而限制向全局作用域中添加过多的变量和函数。通过创建私有作用域,每个开发人员既可以使用 自己的变量,又不必担心同全局变量和函数发生命名冲突。
此种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就立即销毁其作用域链了
1 | (function(){ |
私有变量
任何函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。它包括函数的参数,局部变量,和在函数定义的其他函数。
如果在函数内部创建一个闭包,那么闭包通过作用域链就可以访问这些变量,利用这一点,就可以创建用于访问私有变量的公用方法。
有权访问私有变量和私有函数的公用方法称为特权方法
在构造函数中定义特区方法
1 | function Person(name) { |
2 个特权方法: getName()、 setName(),都可在构造函数外部使用,有权访问私有变量name
.因为这 2 个方法都在构造函数内部定义,它们作为闭包能够通过作用域链访问到name
.
模块模式
模块模式是为单例(只要一个实例的对象)创建私有变量和特权方法。 JavaScript 是以对象字面量的方式来创建单例对象的。
模块模式通过为单例添加私有变量和特权方法使其得到增强,语法如下:
1 | var singleton = (function() { |