博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【前端工程师手册】JavaScript之闭包
阅读量:7173 次
发布时间:2019-06-29

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

闭包确实是一个说烂了的概念,校招社招都会被问到,今天总结一番。

先下定义,闭包是函数和该函数的词法作用域的组合。其实这个定义是比较教条的,可以直白的理解为闭包是一个函数,且这个函数使用了既没在它内部声明且不是它的参数的变量。
举个栗子,

function foo() {     var a = 2;    function bar() {         console.log( a );    }    return bar; }var baz = foo();baz(); // 2

按照常理,foo函数在执行完毕之后会销毁掉其内部的变量a,但是bar函数内部保持着对a的引用,所以通过调用foo()把bar的引用赋给了baz,运行baz()依然可以打印出a。在这个栗子里,函数bar以及它对变量a的引用就构成了闭包。

闭包和作用域

对于闭包和作用域的关系,我的理解是闭包其实就是作用域的延伸

由于在JavaScript中函数内部可以使用函数外部的变量,所有有时候会不知不觉的产生闭包,假如在上面那个代码片段中,不允许函数内部使用函数外部的变量,闭包也就无从谈起了。

闭包有什么用?

模拟私有变量和私有方法

var Dog = (function(){    var privateVal = 'dog'    function doing(val) {        console.log(privateVal + ' ' + val)    }    return {        run: function(){            doing('run')        },        bark: function(){            doing('bark')        }    }})()Dog.run()    // dog runDog.bark()   //  dog bark

可以看到的是,run和bark这两个闭包分享了同一个词法作用域,且都引用了私有方法doing。这样,我们就可以只向外暴露run和bark两个公共接口而隐藏私有的变量和方法。

闭包与循环

或许这是面试中出现最多的问题...

for(var i = 1;i <= 5;i++) {    setTimeout(function() {        console.log(i)    }, i*1000)}// 每隔一秒打印一个6,共打印5次

为什么事与愿违,而不是按照我们所想的依次的间隔1秒打印出1,2,3,4,5呢?首先,这段循环产生了5个闭包,而且最重要的是这5个闭包都处在同一个作用域中,也就是说它们引用的是同一个i,当for循环结束时,i变成了6。所以,5个匿名函数执行时会依次的去打印那同一个i,所以就打印出了5个6。

如何解决?
之前也说了让这5个闭包处于不同的作用域且让它们在各自的作用域中拥有它们各自的i即可。
可以使用自执行函数来创建一个新的作用域

for(var i = 1;i <= 5;i++) {    (function(k){        setTimeout(function() {        console.log(k)    }, k*1000)    })(i)}

在这个代码片段中,每一个setTimeout都处于一个独立的作用域中,且都引用了它们各自的k,并不是指向了外层作用域的i,所以就会打印出1,2,3,4,5

也可以使用let

for(let i = 1;i <= 5;i++) {    setTimeout(function() {        console.log(i)    }, i*1000)}

for 循环头部的 let 不仅将 i 绑定到了 for 循环的块中,事实上它将其重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值。

其实使用let的本质是

for(let i = 1;i <= 5;i++) {    let i = 上次迭代结束的i    setTimeout(function() {        console.log(i)    }, i*1000)}

其实闭包就这么多东西,而且主要是作用域的概念,作用域明白了,闭包也就明白了。

that's all, thank you.

参考资料

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

你可能感兴趣的文章
java JDK6的可变参数
查看>>
初入职场程序员的五大钻石法则
查看>>
Node.js学习笔记(一)概述
查看>>
split的3种方法
查看>>
忽略PNG透明区域的事件(AS/Flash)
查看>>
文本框只能输入正整数(大于0的整数)代码
查看>>
一步一个脚印学习WCF系列之WCF概要—WCF服务的创建与调用HelloWorld实例,通过配置文件方式(六)...
查看>>
只需简单一步,android自带的示例程序 BluetoothChat 变蓝牙串口助手
查看>>
在WIN7下安装运行mongodb
查看>>
(C#)与Windows用户账户信息的获取
查看>>
thrift之TTransport层的内存缓存传输类TMemoryBuffer
查看>>
使用pull方式解析xml文件示例:
查看>>
学习jQuery的免费资源:电子书、视频、教程和博客
查看>>
找出数列中个数大于总数一半的元素(编程之美2.3)
查看>>
断路器(CircuitBreaker)设计模式
查看>>
SQL中利用DMV进行数据库性能分析
查看>>
讨论:程序员高手和菜鸟的区别是什么?
查看>>
SQL Server里的 ISNULL 与 NULLIF
查看>>
Linux Systemcall By INT 0x80、Llinux Kernel Debug Based On Sourcecode
查看>>
imageNamed 与 imageWithContentsOfFile的区别
查看>>