准确来说,闭包是基于正常的垃圾回收处理机制下的。也就是说,一般情况一个函数(函数作用域)执行完毕,里面声明的变量会全部释放,被垃圾回收器回收。但闭包利用一个技巧,让作用域里面的变量,在函数执行完之后依旧保存没有被垃圾回收处理掉。

闭包

定义

MDN定义

javascriptkit

词法作用域

作用域链

  • 函数在执行的过程中,先从自己内部找变量如果找不到,再从创建当前函数所在的作用域(词法作用域)去找, 以 此往上注意找的是变量的当前的状态

作用域链的博客

函数连同它作用域链上的要找的这个变量,共同构成闭包

一般情况下使用闭包主要是为了

  • 封装数据
  • 暂存数据

一个典型的闭包案例


function car(){
var speed = 0
function fn(){
speed++
console.log(speed)
}
return fn
}
var speedUp = car()
speedUp() //1
speedUp() //2

当函数内部没有执行以下的代码时


function fn(){
speed++
console.log(speed)
}
return fn

在代码执行完成后,函数内部的局部变量speed就会被销毁,由于全局标量speedUp一直存在(除非关闭当前页面,否则全局变量一直存在),那么函数内部的作用域就没有办法被销毁,里面有东西一直被使用,这点与浏览器的垃圾回收机制相仿,当我们执行speedUp(),他会在函数的词法作用域下去寻找,函数里面又返回了一个fn,因而形成闭包,简单的理解为


var speed = 0
function fn(){
speed++
console.log(speed)
}

这一段代码形成一个闭包,如果不return fn,那函数内部的局部变量就会被销毁。

我们可以看看上述代码利用立即执行语句和立即执行函数可以怎么演变:


function car(){
var speed = 0
function fn(){
speed++
console.log(speed)
}
return fn
}
var speedUp = car()
//1
function car(){
var speed = 0
return function (){
speed++
console.log(speed)
}
}
var speedUp = car()
//2
function car(speed){
return function (){
speed++
console.log(speed)
}
}
var speedUp = car(3)
//3
function car(){
var speed = arguments[0]
return function (){
speed++
console.log(speed)
}
}
var speedUp = car()
//4
function car(){
var speed = 0
return function (){
speed++
console.log(speed)
}
}
//5 car可以不写,则为匿名函数
var speedUp = (function car(speed){
return function (){
speed++
console.log(speed)
}
}
)(3)

闭包的相关案例

如下代码输出多少?如果想输出3,那如何改造代码?


var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
};
}
console.log( fnArr[3]() ) // 10

同等演变

假设只有两层循环:


var fnArr = []
for (var i = 0; i < 2; i ++) {
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}
fnArr[3]()
//1
var fnArr = []
fnArr[0] = (function(j){
return function(){
return j
}
})(0)
}
fnArr[1] = (function(j){
return function(){
return j
}
})(1)
}
fnArr[3]()
//2
var a = (function(j){
return function(){
return j
}
})(0)
}
var b = (function(j){
return function(){
return j
}
})(1)
}
b()
//3
var a = (function(j){
return function(){
return j
}
})(0)
}
function fn2(j){
return function(){
return j
}
}
var b = fn2(1)
//4
var a = (function(j){
return function(){
return j
}
})(0)
}
function fn2(j){
return function(){
return j
}
return f
}
var b = fn2(1)
//5
var a = (function(j){
return function(){
return j
}
})(0)
}
function fn2(j){
var j = arguments[0]
function f(){
return j
}
return f
}
var b = fn2(1)

改造后(立即执行语句,演变过程)


var fnArr = []
for (var i = 0; i < 10; i ++) {
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (var i = 0; i < 10; i ++) {
(function(i){
fnArr[i] = function(){
return i
}
})(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (let i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
}
}
console.log( fnArr[3]() ) // 3

封装一个 Car 对象


var Car = (function(){
var speed = 0;
function set(s){
speed = s
}
function get(){
return speed
}
function speedUp(){
speed++
}
function speedDown(){
speed--
}
return {
setSpeed: setSpeed,
get: get,
speedUp: speedUp,
speedDown: speedDown
}
})()
Car.set(30)
Car.get() //30
Car.speedUp()
Car.get() //31
Car.speedDown()
Car.get() //3

如下代码输出多少?如何连续输出 0,1,2,3,4


for(var i=0; i<5; i++){
setTimeout(function(){
console.log('delayer:' + i )
}, 0)
}

输出结果为:delayer:5(连续输出5个),执行setTimeout时,代码会挂到任务队列中区,待i遍历完成之后执行,而此时i = 5,所以输出delayer:5(连续输出5个)

修改后


for(var i=0; i<5; i++){
(function(j){
setTimeout(function(){
console.log('delayer:' + j )
}, 0)//1000-1000*j
})(i)
}

或者


for(var i=0; i<5; i++){
setTimeout((function(j){
return function(){
console.log('delayer:' + j )
}
}(i)), 0)
}

如下代码输出多少?


function makeCounter() {
var count = 0
return function() {
return count++
};
}
var counter = makeCounter()
var counter2 = makeCounter();
console.log( counter() ) // 0
console.log( counter() ) // 1
console.log( counter2() ) // 0
console.log( counter2() ) // 1

补全代码,实现数组按姓名、年纪、任意字段排序


var users = [
{ name: "John", age: 20, company: "Baidu" },
{ name: "Pete", age: 18, company: "Alibaba" },
{ name: "Ann", age: 19, company: "Tecent" }
]
users.sort(byName)
users.sort(byAge)
users.sort(byField('company'))

解答


function byName(user1, user2){
return user1.name > user2.name
}
function byAge (user1, user2){
return user1.age > user2.age
}
function byFeild(field){
return function(user1, user2){
return user1[field] > user2[field]
}
}
users.sort(byField('company'))

写一个 sum 函数,实现如下调用方式


console.log( sum(1)(2) ) // 3
console.log( sum(5)(-1) ) // 4

最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。

原文地址:https://segmentfault.com/a/1190000016807005

js闭包实例展示的更多相关文章

  1. js闭包实例汇总

    本文是通过实例来帮助大家深刻理解js闭包,是篇非常不错的文章,这里推荐给大家,有需要的小伙伴可以参考下 Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取 ...

  2. js 闭包实例

    var db = (function() { // 创建一个隐藏的object, 这个object持有一些数据 // 从外部是不能访问这个object的 var data = {}; // 创建一个函 ...

  3. js闭包理解实例小结

    Js闭包 闭包前要了解的知识  1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...

  4. Js闭包的用途

    本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...

  5. js闭包vs Java内部类

    前言: 昨天写了一个关于Java内部的博客,在内部类的最后一点中谈到了Java闭包的概念,他是这样定义闭包的:闭包是一个可调用的对象,它记录了一些信息,这些信息来自创建它的作用域.结合Java的内部类 ...

  6. js闭包的作用

    js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文   我们来看 ...

  7. window.location.hash 页面跳转,精确定位,实例展示:

    window.location.hash 页面跳转,精确定位,实例展示: (1).index.phtml,页面用于传参 <script id="bb_list_template&quo ...

  8. JS闭包、作用域链、垃圾回收、内存泄露相关知识小结

    补充: 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 闭包的三个特性: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变 ...

  9. js闭包的用途详解

    js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中 我们来看看闭包的用途.事实上,通过使用闭包,我们可以做很多事情.比如模拟 ...

随机推荐

  1. echarts中国地图

    echarts中国地图效果图: =================== 需要引入echarts的js文件:(1.echarts.min.js:2.china.js) 下载地址: echarts.min ...

  2. 你不知道的JavaScript(二)数组

    作为一种线性数据结构,几乎每一种编程语言都支持数组类型.和c++.java这些强类型的语言相比,JavaScript数组有些不同,它可以存放任意类型的值.上节中有提到过JS中任意类型的值都可以赋值给任 ...

  3. NEON基本知识

    http://blog.csdn.net/EmSoftEn/article/details/51834171 http://blog.csdn.net/yxnyxnyxnyxnyxn/article/ ...

  4. 3ds Max修改桌面快捷方式为中文语言

    通过上篇教程的学习,我们已经会切换3ds Max 2018的界面语言了,相关内容可参阅3ds Max如何设置中文界面.但是如果每次都要从开始菜单进行操作,岂不麻烦?简单一点,可以在桌面添加3ds Ma ...

  5. echars自定义提示框位置

    tooltip: { trigger: 'item', formatter: "{a} <br/>{b}: {c} ({d}%)", position:function ...

  6. MVC 单元测试xUnit初探

    对于.NET项目 Web Api的业务逻辑后台开发[特别是做Web Api接口]而言,编写单元测试用例,会极大的减轻代码帮助与运行的方式.然而使用测试框架,相对于自带的,我更加推荐是用xUnit.ne ...

  7. json转换方法

    import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.ut ...

  8. svn文件管理器的使用

    服务器端: 客户端 使用SVN的注意事项 做任何操作之前,先update一下 不要修改其他人的文件 不要在SVN里直接打开.编辑文件 不要在打开.编辑文件的时候,进行操作 SVN客户端的安装,非常简单 ...

  9. 集团公司(嵌入ETL工具)財务报表系统解决方式

    集团公司(嵌入ETL工具)財务报表系统解决方式 一.项目背景: 某集团公司是一家拥有100多家子公司的大型集团公司,旗下子公司涉及各行各业,包含:金矿.铜矿.房产.化纤等.因为子公司在业务上的差异.子 ...

  10. Android开发学习之事件处理和Button具体解释

    Android的事件处理机制:  1.基于监听器的事件处理 --- 组件绑定特定的事件监听器   --- 重点   2.基于回调的事件处理   --- 主要做法是重写Android组件特定的回调函数, ...