0

0

JavaScript中的闭包

韦小宝

韦小宝

发布时间:2018-03-07 14:08:28

|

1343人浏览过

|

来源于php中文网

原创

闭包就那么一两句话谁都能背出来。可是闭包偏偏就是那种初学者十次面试八次可能会遇到,答不上来就是送命题、答得出来也不加分题。为了不让我们前端开发从入门到放弃,我还是来谈谈我认为的 js 里面的闭包。

闭包是什么

闭包创建一个词法作用域,这个作用域里面的变量被引用之后可以在这个词法作用域外面被自由访问,是一个函数和声明该函数的词法环境的组合

还有一种说法,闭包就是引用了自由变量的函数,这个自由变量与函数一同存在,即使脱离了创建它的环境。所以你常看到的说闭包就是绑定了上下文环境的函数,也大概是这个意思。只要想明白了,就觉得其实是很简单的一个东西,并没有多么高深。

在下面的介绍中,我还是比较偏向闭包是一个函数和声明该函数的词法环境的组合这种解释,所以也会基于这种解释去阐述。

立即学习Java免费学习笔记(深入)”;

闭包其实是计算机科学里面的一个概念,并不是JS里面独有的。闭包的概念出现于60年代,最早实现闭包的程序语言是Scheme。(别问我 Scheme 是什么,问了我也不知道,这段是从维基上抄的。)之后,闭包被广泛使用于函数式编程语言

JS里面的闭包

现在,我就发大绝招了,徒手撸一个闭包。

function sayHello(name) {
  let str = `Hello,${name}`;
  function say() {
    console.log(str);
  }
  return say;
}

let myHello = sayHello('abby');
myHello(); // Hello,abby

上面这段代码,其实就形成了一个闭包,其中在 sayHello 这个函数里面定义的函数 say 和其声明它的词法环境就形成了一个闭包,因为它引用了sayHello 里面定义的一个变量 str,并且将 say 这个函数 return 了出去,这样在 sayHello 这个函数的外面也能访问它里面定义的变量 str,就好像 say 这个函数和这个变量绑定了一样。

看到这里可能会疑问为什么在外部还能访问到这个变量呢,因为在有些语言中,一般认为函数的局部变量只在函数的执行期间可访问。说到这里又不得不说到执行环境,不太了解的朋友可能先去看我这篇文章:你不知道的执行上下文。其实当执行到let myHello = sayHello('abby');这段代码的时候按理会销毁掉 sayHello()的执行环境,但是这里却没有,原因是因为 sayHello() 返回的是一个函数,这个函数里面的 str 引用了外部的变量 str,如果销毁了就找不到了,因此 sayHello() 这个函数的执行环境会一直在内存中,所以会有闭包会增加内存开销balabala之类的。

其实说到这里,闭包就应该是说完了的,但是可能有好多东西看的都是一脸懵,那么我们就继续来讲几个例子吧!

举个例子

例子1:闭包并不是一定需要 return 某个函数

虽然常见的闭包都是 return 出来一个函数,但是闭包并不一定非要 return,return 出一个函数只是为了能在作用域范围之外访问一个变量,我们用另一种方式也能做到,比如:

let say;
function sayHello(name) {
  let str = `Hello,${name}`;
  say = function() {
    console.log(str);
  }
}
let myHello = sayHello('abby');
say(); // Hello,abby

在这个例子里面,say和声明它的词法环境其实也形成了一个闭包,在它的作用域里面持有了 sayHello 这个函数里面定义的 str 变量的引用,因此也能在 str 变量定义的作用域之外访问它。只要弄清楚闭包的本质即可。

但是在 JS 里面,最常用的形成闭包的方式便是在一个函数里面嵌套另一个函数,另一个函数持有父作用域里面定义的变量。

NT80 购物系统
NT80 购物系统

功能说明:1 会员可申请开店功能2 购买在线扣除金额3 冲值卡自动生成4 支持2级分类5 数据库压缩和备份6 会员分5个级别7 商品带讨论8 自带融合论坛,可关闭打开9 密码找回功能10 新闻``滚动新闻``帮助中心11 后台设置前台会员的上传权限12 可关闭/打开商店13 会员自助发布商品功能14 用户问题咨询管理

下载

 例子2:同一个调用函数生成同一个闭包环境,在里面声明的所有函数同时具有这个环境里面自由变量的引用。

这句话说起来很绕,其实我给个很简单的例子就可以了。

let get, up, down
function setUp() {
  let number = 20
  get = function() {
    console.log(number);
  }
  up = function() {
    number += 3
  }
  down = function() {
    number -=2;
  }
}
setUp();
get(); // 20
up();
down();
get(); // 21

在这个例子里面,我们用setUp这个函数生成了一个闭包环境,在这个环境里面的三个函数共享了这个环境里面的 number 变量的引用,因此都可以对 number 进行操作。

例子3:每一个调用函数都会创建不同的闭包环境。

还是给一个很简单的例子。

function newClosure() {
  let array = [1, 2];
  return function(num) {
    array.push(num);
    console.log(`array:${array}`);
  }
}
let myClosure = newClosure();
let yourClosure = newClosure();
myClosure(3); // array:1,2,3
yourClosure(4); // array:1,2,4
myClosure(5); // array:1,2,3,5

上面这个例子里面, myClosure 和 yourClosure 的赋值语句,也就是 newClosure 这个函数被调用了两次,因此创建了两个不同的闭包环境,因此里面的变量是互不影响的。

例子4:在循环里面创建闭包

function newClosure() {
  for(var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(i);
      })
  }
}
newClosure(); // 5个5

打印的结果大家也知道是5个5,因为 setTimeout 里面的函数保持对 i 的引用,在setTimeout的回调函数被执行的时候这个循环早已经执行完成,这里我之前在另一篇文章里面做过更深入的介绍:深入浅出Javascript事件循环机制(上)。

这里我要说的是我们如何才能得到我们想要的01234,在这里有两种做法。

一种是 创建一个新的闭包对象,这样每个闭包对象里面的变量就互不影响。例如下面的代码种每次 log(i)都会创建不同的闭包对象,所有的回调函数不会指向同一个环境。

function log(i) {
  return function() {
    console.log(i);
  }
}
function newClosure() {
  for(var i = 0; i < 5; i++) {
    setTimeout(log(i));
  }
}
newClosure(); // 0 1 2 3 4

另一种做法就是使用自执行函数,外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。写法如下:

function newClosure() {
  for(var i = 0; i < 5; i++) {
    (function(e) {
      setTimeout(function() {
        console.log(e);
      })
    })(i)  
  }
}
newClosure(); // 0 1 2 3 4

看看,写这么多,多累是不是,还是let省事,所以赶紧拥抱 es6 吧。。。

好了,这次是真的结束了,我所理解的闭包大概就是这样了,如果理解有所偏差,欢迎指出,谁当初不是从颗白菜做起的呢,学习前端的小伙伴们可以看看哦!

关于闭包:

js中作用域与函数闭包实例讲解

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

4

2025.12.31

视频文件格式
视频文件格式

本专题整合了视频文件格式相关内容,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

不受国内限制的浏览器大全
不受国内限制的浏览器大全

想找真正自由、无限制的上网体验?本合集精选2025年最开放、隐私强、访问无阻的浏览器App,涵盖Tor、Brave、Via、X浏览器、Mullvad等高自由度工具。支持自定义搜索引擎、广告拦截、隐身模式及全球网站无障碍访问,部分更具备防追踪、去谷歌化、双内核切换等高级功能。无论日常浏览、隐私保护还是突破地域限制,总有一款适合你!

7

2025.12.31

出现404解决方法大全
出现404解决方法大全

本专题整合了404错误解决方法大全,阅读专题下面的文章了解更多详细内容。

42

2025.12.31

html5怎么播放视频
html5怎么播放视频

想让网页流畅播放视频?本合集详解HTML5视频播放核心方法!涵盖<video>标签基础用法、多格式兼容(MP4/WebM/OGV)、自定义播放控件、响应式适配及常见浏览器兼容问题解决方案。无需插件,纯前端实现高清视频嵌入,助你快速打造现代化网页视频体验。

4

2025.12.31

关闭win10系统自动更新教程大全
关闭win10系统自动更新教程大全

本专题整合了关闭win10系统自动更新教程大全,阅读专题下面的文章了解更多详细内容。

3

2025.12.31

阻止电脑自动安装软件教程
阻止电脑自动安装软件教程

本专题整合了阻止电脑自动安装软件教程,阅读专题下面的文章了解更多详细教程。

3

2025.12.31

html5怎么使用
html5怎么使用

想快速上手HTML5开发?本合集为你整理最实用的HTML5使用指南!涵盖HTML5基础语法、主流框架(如Bootstrap、Vue、React)集成方法,以及无需安装、直接在线编辑运行的平台推荐(如CodePen、JSFiddle)。无论你是新手还是进阶开发者,都能轻松掌握HTML5网页制作、响应式布局与交互功能开发,零配置开启高效前端编程之旅!

2

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.1万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 1.9万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号