js中的闭包

js中为何要使用闭包?浏览器

先介绍一下全局变量和局部变量的优缺点:闭包

全局变量:在全局环境下声明的变量为全局变量,全局变量在任何地方均可访问,且一直保存在内存中只到应用程序退出(关闭网页或浏览器)时才被销毁。可是过多的声明全局变量容易形成全局污染,且全局变量容易被修改。函数

局部变量:在函数环境下声明的变量为局部变量,局部变量仅在函数内部可访问,当函数执行完毕时就会被销毁。局部变量不会形成全局污染也不容易被修改。this

从上面能够看出全局变量和局部变量的优缺点恰好是相对的,闭包的出现正好结合了全局变量和局部变量的优势。闭包可以使已经执行结束的函数中的局部变量仍然留在内存中,且能被重复访问使用。对象

闭包的定义是什么?blog

闭包是指有权访问另外一个函数做用域中的变量的函数。建立闭包的常见方式就是在一个函数内部建立另外一个函数。以一个函数为例:ip

js中的闭包

示例内存

加粗的两行代码是内部函数(一个匿名函数)中的代码,这两行代码访问了外部函数中的变量propertyName。作用域

js中的闭包

示例io

在匿名函数从createComparisonFunction()中被返回后,他仍然能够访问在createComparisonFunction()中定义的全部变量。更为重要的是,createComparisonFunction()函数在执行完毕后,其变量对象也不会被销毁,由于匿名函数的做用域链仍然在引用这个变量对象。

什么是做用域?

要理解什么是做用域,要先理解什么是执行环境

1.什么是执行环境(执行上下文)

当代码在JavaScript中运行的时候,代码在环境中被执行是很是重要的,它会被评估为如下之一类型来运行:

全局代码:默认环境,代码第一时间在这儿运行。

函数代码:当执行流进入一个函数体的时候。

Eval代码:在eval()函数中的文本。

来看一个例子:

js中的闭包

示例

全局环境由紫色边框表示,还有三个不一样的函数环境分别由绿色边框,蓝色边框和橙色边框表示。这里只能有一个全局环境,全局环境能够被其余环境访问。能够有不少的函数环境,每一个函数都会建立一个新的函数环境,在新的函数环境中,会建立一个私有做用域,在这个函数中建立的任何声明都不能被当前函数做用域以外的地方访问。

2.执行环境的详情

一个函数被调用就会建立一个新的执行环境。然而解释器的内部,每次调用执行环境会有两个阶段:

1). 建立阶段

- 当函数被调用,可是为执行内部代码以前:

- 建立一个做用域链。

- 建立变量,函数和参数。

- 肯定this的值。

2). 激活/代码执行阶段

> - 赋值,引用函数,解释/执行代码。

这意味着每一个执行环境在概念上做为一个对象并带有三个属性

executionContextObj = {

scopeChain: { /* variableObject + all parent execution context's variableObject */ },

//做用域链:{变量对象+全部父执行环境的变量对象}

variableObject: { /* function arguments / parameters, inner variable and function declarations */ },

//变量对象:{函数形参+内部的变量+函数声明(但不包含表达式)}

this: {}

}

看下面例子:

在调用foo(22)时,建立阶段像下面这样:

fooExecutionContext = {

scopeChain: { ... },

variableObject: {

arguments: {

0: 22,

length: 1

},

i: 22,

c: pointer to function c()

a: undefined,

b: undefined

},

this: { ... }

}

建立阶段处理了定义属性的变量名,可是并不把值赋给变量,不包括形参和实参。一旦建立阶段完成,执行流进入函数而且激活/代码执行阶段,在函数执行结束以后,看起来像这样:

fooExecutionContext = {

scopeChain: { ... },

variableObject: {

arguments: {

0: 22,

length: 1

},

i: 22,

c: pointer to function c()

a: 'hello',

b: pointer to function privateB()

},

this: { ... }

}

上述过程也能够印证了js中的变量提高,即变量和函数声明会被提高到它们函数做用域的顶端。

理解了什么是执行环境和做用域链以后,回到上文所讲的闭包实例。

为何createComparisonFunction()函数在执行完毕后,其变量对象不会被销毁。

js中的闭包

示例

在匿名函数从createComparisonFunction()被返回后,它的做用域链被初始化为包含createComparisonFunction()函数的变量对象和全局变量对象。这样,匿名函数就能够访问在createComparisonFunction()中定义的全部变量。当createComparisonFunction()函数返回后,其执行环境的做用域链会被销毁,但它的变量对象仍然会留在内存中;只到匿名函数销毁后,createComparisonFunction()函数的变量对象才会被销毁。下图展现了调用compareNames()过程当中产生的做用域链之间的关系:

js中的闭包

示例

解除对匿名函数的引用,只需compareNames=null便可,此时createComparisonFunction()函数的变量对象会被销毁。

闭包的做用?

事实上,经过使用闭包,咱们能够作不少事情。好比模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提高代码的执行效率。这些须要感兴趣的人本身去实践和探索,此处不一一列举。

闭包的缺点?

因为闭包会携带包含它的函数的做用域,所以会比其余函数占用更多的内存。过分使用闭包可能会致使内存占用过多,因此要慎重使用闭包。

相关文章
相关标签/搜索