开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
前言
了解以下内容有助于理解本文介绍的闭包:
什么是闭包
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。 --- MDN
通俗地讲:在内层函数作用域中访问外部函数作用域就会形成 闭包。
function makeFunc() {
var name = "closure";
function printName() {
console(name);
}
return printName;
}
var myFunc = makeFunc();
myFunc();
makeFunc
函数执行时,会创建 printName
函数并将其 引用
返回给 myFunc
。当 myFunc
函数执行时,会输出 closure
, 说明变量 name
仍然可以访问。
上列代码的 执行上下文
:
// 1. 创建 GO
GO = {
makeFunc: ƒ makeFunc(){},
myFunc: undefined
}
// 2. 执行 全局代码 var myFunc = makeFunc(),创建makeFuncVO
GO = {
makeFunc: ƒ makeFunc(){},
myFunc: ƒ printName(){}
}
// 3. 创建makeFuncVO
makeFuncVO = {
name: undefined,
printName: ƒ printName(){}
}
// 4. 执行全局代码 myFunc(),创建 printNameVO
printNameVO = {}
// 5. 执行 printName,函数内部访问变量 name,当前作用域查找无对应变量;依次向上级作用域查找,即查看makeFunc作用域,存在变量name,输出该变量name的值。
通过上面对 执行上下文
创建和执行阶段的分析,可以发现 JavaScript
的函数中的变量并不仅仅存在于该函数执行期间,在函数执行结束后仍然可以被访问。
闭包的应用
例1:在页面上添加一些可以调整字号的按钮
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
// 函数工厂,创建指定字体大小的函数
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
例2:防抖、节流工具函数
代码略
错误的闭包应用
将上面 例1
中的js
部分稍加改造:
function makeSizer(size) {
document.body.style.fontSize = size + 'px';
}
var els = document.getElementsByTagName('a');
for(var i = 0; i < els.length; i ++) {
var size = 12 + i; // 重点
els[i].onclick = function() {
makeSizer(size)
}
}
var size = 12 + i
会发生变量提升,这里的变量size
相当于定义到全局作用域中- 每次循环都会给
size
重新赋值 a
标签的回调函数中访问外部作用域的变量size
,形成闭包。- 最终
a
元素点击事件回调函数中使用的size
是在点击事件触发时决定,此时size
已经被赋值为15