JavaScript之闭包

33 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

前言

了解以下内容有助于理解本文介绍的闭包:

执行上下文、作用域链

JavaScript中的内存管理

什么是闭包

闭包(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

参考资料

闭包-MDN