博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript笔记——闭包
阅读量:6276 次
发布时间:2019-06-22

本文共 2605 字,大约阅读时间需要 8 分钟。

前言

秋招大大小小若干场面试,几乎都问了这个问题,当然不知道闭包也不能说会js。所以,不要逃避,好好地来梳理一下。

闭包是什么?

来看看MDN官网上关于闭包的定义:

A closure is the combination of a function and the lexical environment within which that function was declared.

闭包是函数以及函数声明所在的词法环境的组合。这个定义有点干涩,个人认为可以结合闭包的实际场景简单地这么理解:函数调用结果返回一个子函数,同时该子函数使用了外部函数的局部变量,使得变量保存在内存中,形成了闭包。

举个例子?

举个最简单的栗子~

function func(){  var count = 0;  return function(){    console.log(count++);  }}var fn = func();fn(); // 0fn(); // 1

func 函数执行返回一个函数,其中调用了func的局部变量count,导致func函数执行完成后,count变量仍保留在内存空间,未被销毁,形成了闭包。

闭包的作用?

从上一个例子,可以看到闭包的特点是读取函数内部局部变量,并将局部变量保存在内存,延长其生命周期。利用这个特点可以使用闭包实现以下功能:

  1. 解决类似循环绑定事件的问题

    在实际开发中,经常会遇到需要循环绑定事件的需求,比如上一篇博客的例子,在id为container的元素中添加5个按钮,每个按钮的文案是相应序号,点击打印输出对应序号。
    其中第一个方法很容易错误写成:

    var container = document.getElementById('container');for(var i = 1; i <= 5; i++) { var btn = document.createElement('button'),     text = document.createTextNode(i); btn.appendChild(text); btn.addEventListener('click', function(){   console.log(i); }) container.appendChild(btn);}

    虽然给不同的按钮分别绑定了事件函数,但是5个函数其实共享了一个变量 i。由于点击事件在 js 代码执行完成之后发生,此时的变量 i 值为6,所以每个按钮点击打印输出都是6。

    为了解决这个问题,我们可以修改代码,给各个点击事件函数建立独立的闭包,保持不同状态的i。

    var container = document.getElementById('container');for(var i = 1; i <= 5; i++) { (function(_i) {   var btn = document.createElement('button'),       text = document.createTextNode(_i);   btn.appendChild(text);   btn.addEventListener('click', function(){     console.log(_i);   })   container.appendChild(btn); })(i);}

    注:解决这个问题更好的方法是使用 ES6 的 let,声明块级的局部变量。

  2. 封装私有变量

    经典的计数器例子:

    function makeCounter() {  var value = 0;  return {    getValue: function() {      return value;    },    increment: function() {      value++;    },    decrement: function() {      value--;    }  }}var a = makeCounter();var b = makeCounter();b.increment();b.increment();b.decrement();b.getValue(); // 1a.getValue(); // 0a.value; // undefined

    每次调用makeCounter函数,环境是不相同的,所以对b进行的increment/decrement操作不会影响a的value属性。同时,对value属性,只能通过getValue方法进行访问,而不能直接通过value属性进行访问。

闭包的问题?

使用闭包会将局部变量保持在内存中,所以会占用大量内存,影响性能。所以在不再需要使用这些局部变量的时候,应该手动将这些变量设置为null, 使变量能被回收。

当闭包的作用域中保存一些DOM节点时,较容易出现循环引用,可能会造成内存泄漏。原因是在IE9以下的浏览器中,由于BOM 和DOM中的对象是使用C++以COM 对象的方式实现的,而COM对象的垃圾收集机制采用的是引用计数策略,当出现循环引用时,会导致对象无法被回收。当然,同样可以通过设置变量为null解决。

举例如下:

function func() {  var element = document.getElementById('test');  element.onClick = function() {      console.log(element.id);  };}

func 函数为 element 添加了闭包点击事件,匿名函数中又对element进行了引用,使得 element 的引用始终不为0。解决办法是使用变量保存所需内容,并在退出函数时将 element 置为 null。

function func() {  var element = document.getElementById('test'),      id = element.id;  element.onClick = function() {      console.log(id);  };  element = null;}

参考文章

转载地址:http://ulgpa.baihongyu.com/

你可能感兴趣的文章
简单易懂的谈谈 javascript 中的继承
查看>>
Spark学习之Spark 集群资源调度
查看>>
京东Taro:用技术解放小程序生产力 | 点评家
查看>>
Dart编程语言入门学习
查看>>
小程序登录逻辑
查看>>
vscode透明主题、霓虹灯字体
查看>>
多线程基础知识
查看>>
iOS汇编基础(四)指针和macho文件
查看>>
Laravel 技巧锦集
查看>>
Android 使用 ViewPager+RecyclerView+SmartRefreshLayout 实现顶部图片下拉视差效果
查看>>
Flutter之基础Widget
查看>>
写给0-3岁产品经理的12封信(第08篇)——产品运营能力
查看>>
ArcGIS Engine 符号自动化配置工具实现
查看>>
小程序 · 跳转带参数写法,兼容url的出错
查看>>
开源干货!!!.NET Core + Vue.js(iview-admin) 通用动态权限(RBAC)管理系统框架[DncZeus]开源啦!!!...
查看>>
flutter error
查看>>
Flask框架从入门到精通之模型数据库配置(十一)
查看>>
10年重新出发
查看>>
2019年-年终总结
查看>>
聊聊elasticsearch的RoutingService
查看>>