一:什么是做用域链javascript
要理解做用域链,须要先清楚下面两个概念:前端
其中执行环境是javascript中最为重要的一个概念。在《javascript高级程序设计》中对于它们的解释以下:java
执行环境定义了变量或函数有权访问的其余数据,决定了它们各自的行为。
每一个执行环境都有一个与之关联的变量对象(variable object)。
环境中定义的全部变量和函数都保存在这个对象中。
复制代码
顾名思义,执行环境是一系列变量、函数及其行为规则的集合,变量对象则是执行环境的介质。全局执行环境是最外围的执行环境,每一个函数都有本身的执行环境。当某个执行环境的全部代码执行完毕后,该环境被销毁,在该执行环境中保存和定义的全部变量和函数也随之销毁,全局执行函数直到应用程序退出才被销毁。bash
这一部份内容刚开始接触可能仍是比较抽象的,哈哈,反正我是重复了不少遍才记忆深入。推荐一种记忆方法吧,能够在熟记几回以后试着讲给别人听,在给别人讲解的过程当中本身也会成长很快。闭包
下面再来看一下做用域链吧。模块化
当执行流进入一个函数时,这个函数的执行环境就被推入环境栈中,函数执行完毕后再从环境栈中弹出。这里不难看出,环境栈最上层即为当前执行代码所在的执行环境,最下层则为全局执行环境。而这一系列环境栈所对应的变量对象集合即为做用域链。做用域链保证了对执行环境有权访问的全部变量和函数的有序访问。函数
因而可知,做用域链的成员实际上是各个执行环境的变量对象。做用域链最前端即为当前执行环境的变量对象,若是当前执行环境是函数,则将其活动对象做为变量对象。做用域链的最末端即为全局执行环境的变量对象。性能
在做用域链中,内部环境能够经过做用域链访问全部的外部环境,可是外部环境不能访问内部环境中的任何变量和函数。这些环境的联系是线性的有次序的。不难想到,做用域链就是一个单向链表。ui
值得注意的一点是,在javascript中函数的参数也被当作变量来处理,其访问规则与执行环境中的其它变量相同。而且函数的参数是按值传递的,即便在函数内部修改了参数的值,其原始的引用仍然保持不变。spa
二:闭包
闭包便可以访问其余做用域变量的函数。首先,闭包是一个函数;其次,它能够访问到其它做用域内的变量。在js中,闭包的实现通常为在一个函数内定义一个函数。举个栗子:
function outer() {
var a = 1
var inner = function() {
a++;
console.log(a)
}
return inner
}
var get = outer();
get(); //2
get(); //3
复制代码
inner函数内定义了一个函数inner并做为返回值返回,此时inner函数即为一个闭包。不妨这样理解,外层函数的做用域即为外层函数内变量的集合,当在该做用域中又定义了一个函数时,内层函数做为变量也包含在外层做用域中,但同时它又有本身的做用域,javascript做用域链的特性使得它能够访问到上层函数做用域内的变量。当内层函数做为返回值时,至关于返回了整个内层做用域,包括其引用过的上层函数做用域内的变量(这一段好像有点绕2333)。
var get = outer();
实际上是建立了一份outer函数的执行环境,返回了内层函数。可是因为该内层函数做为返回值赋给了get变量,因此在get变量销毁以前,该内层函数执行环境始终存在,而且其做用域内的变量也因为被引用而不被销毁。这也解释了为何闭包使得函数拥有私有变量成为了可能。
不妨看一下下面这段代码:
function outer() {
var a = 1
console.log("外层函数会执行吗?", a)
var inner = function() {
a++;
console.log(a)
}
return inner
}
var get = outer(); //外层函数会执行吗 1
get(); //2
get(); //3
复制代码
结果很明显,只有执行outer函数的时候输出了外层函数会执行吗 1
,再执行get时至关于只在执行inner,可是变量a却被保留了下来。
事实上,经过适当使用闭包,咱们的代码能够变得更优雅,更简洁,在模拟面向对象的代码风格上用处十分普遍,也为模块化开发提供了强有力的支持。可是因为变量一直保存在内存中,因此对性能的消耗也很明显,在使用的时候要注意权衡利弊。