闭包(closure) – 延伸了变量的作用域
-
闭包:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。指有权访问另一个函数作用域中变量的函数。
-
闭包可以在一个内层函数中访问到其外层函数的作用域。延伸了变量的作用域
-
在JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
-
示例1:
// 外层函数 -- 声明一个变量;return 一个内部函数 funciton fn() { var num = 0; // 内层函数 return function() { // 调用外部函数 fn()的变量 num console.log(++num) // 延伸了num 作用域 } } var fun = fn(); fun(); // 打印结果为 1 fun(); // 打印结果为 2
在本例子中,
fun
是执行fn 时创建的函数实例(return 的函数)的引用。该函数的实例维持了一个对它的词法环境(变量
num存在于其中)的引用。因此,当
fun被调用时,变量
num` 仍然可用,所以打印结果为 1 -
词法作用域
词法(lexical):指的是,词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。
嵌套函数可访问声明于它们外部作用域的变量。
-
示例2:
function makeAdder(x) { return function(y) { return x + y; } } var add5 = makeAdder(5); var add10 = makeAdder(10); console.log(add5(2)); // 打印结果为 5 console.log(add10(2)); // 打印结果为 12 // 解析 add5 = makeAdder(5) = function(y) = 5+y add5(2) = makeAdder(5) = function(2) = 5+2 = 7 add10 = makeAdder(10) = function(y) = 10+y add10(2) = makeAdder(10) = function(2) = 10+2 = 12
add5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5 的环境中,x为5。而在add10中,x则为10。
-
用闭包模拟私有方法 – 面试时可能会问道
私有方法的好处:
- 利于限制对代码的访问;
- 提供了管理全局命名空间的强大能力;
- 避免非核心的方法弄乱代码的公共接口部分。
-
在循环中创建闭包:一个常见错误 – 面试时可能会问道
在for循环中创建闭包时,可能会因为闭包共享一个词法作用域,造成错误。
解决方法:
-
为每个闭包创建单独的词法环境
-
使用匿名闭包
示例:
function showHelp(help) { document.getElementById('help').innerHTML = help; } function setupHelp() { var helpText = [ {'id': 'email', 'help': 'Your e-mail address'}, {'id': 'name', 'help': 'Your full name'}, {'id': 'age', 'help': 'Your age (you must be over 16)'} ]; for (var i = 0; i < helpText.length; i++) { (function() { var item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); } })(); // 马上把当前循环项的item与事件回调相关联起来 } } setupHelp();
-
使用关键词 let 而非 var (let 关键字有作用域;var 关键字无作用域)
-
可选方案 – 使用forEach()遍历数组
function showHelp(help) { document.getElementById('help').innerHTML = help; } function setupHelp() { var helpText = [ {'id': 'email', 'help': 'Your e-mail address'}, {'id': 'name', 'help': 'Your full name'}, {'id': 'age', 'help': 'Your age (you must be over 16)'} ]; helpText.forEach(function(text) { document.getElementById(text.id).onfocus = function() { showHelp(text.help); } }); } setupHelp()
-
-
性能考量
闭包的缺点:闭包在处理速度和内存消耗方面对脚本性能具有负面影响。