JavaScript的闭包详解(文末有教程分享)

幸运草 2020年10月27日21:42:48使用教程评论阅读模式

2583台服务器被劫持,大规模数据泄露,NASA承包商遭DoppelPaymer勒索软件攻击

近日,DoppelPaymer勒索软件团伙在网上发布一篇博客,首先祝贺了SpaceX和NASA(美国国家航空航天局)成功发射载人火箭,然后爆料说他们已经感染了NASA的一个IT承包商(数字管理Digital Management Inc.,DMI)的网络。 D…

什么是闭包:有权访问另一个函数作用域的变量的函数

简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

  • 闭包是嵌套的内部函数
  • 闭包存在于嵌套的内部函数中

闭包的主要作用:

  • 可以读取函数内部的变量
  • 让这些变量的值始终保持在内存中,变量或参数不会被垃圾回收机制回收GC

产生闭包的条件:

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)

闭包的优点:

  • 变量长期驻扎在内存中
  • 避免全局变量的污染

闭包的缺点:

  • 常驻内存会增大内存的使用量
  • 使用不当会造成内存泄露
  • 闭包会在父函数外部,改变父函数内部变量的值

闭包使用详解:

当想要的得到f1函数的局部变量时,正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数

function f1 () {    var n = 999    function f2 () {        console.log(n) // 999    }}

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是 JavaScript 语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

function f1 () {    var n = 999    function f2 () {        console.log(n)    }    return f2}var result = f1()result() // 999

上面代码中,函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中

请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function zxxs (zxx) {    return function () {        return zxx++    }}var inc = zxxs(5)inc() // 5inc() // 6inc() // 7

上面代码中,zxx是函数zxxs的内部变量。通过闭包,zxx的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数zxxs的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖于zxxs,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

闭包的另一个用处,是封装对象的私有属性和私有方法:

function Person (name) {    var age    function setAge (n) {        age = n    }    function getAge () {        return age    }    return {        name: name,        getAge: getAge,        setAge: setAge    }}var p1 = Person('zxx')p1.setAge(18)p1.getAge() // 18

上面代码中,函数Person的内部变量age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题

函数执行完后,函数内的局部变量没有释放,占用内存时间会变长,容易造成内存泄漏(内存被占用,但是没有用上)

function fn1 () {    var arr = new Array[1000]    function fn2 () {        console.log(arr.length)    }    return fn2}var f = fn1() // 已经产生闭包f() f = null // 让内部函数成为垃圾对象-->回收闭包

习题1

var name = 'The Window'var object = {    name: 'My Object',    getNameFunc: function () {        return function () {            return this.name        }    }}alert(object.getNameFunc()())  //The Windowobject.getNameFunc() // 执行完变成一个函数,this指向window, 关于this的指向后续会更新新的文章详解var name = 'The Window'var object = {    name: 'My Object',    getNameFunc: function () {        var that = this        return function () {            return that.name        }    }}alert(object.getNameFunc()())  // My Object

习题2

function outerFun () {    var a = 0    function innerFun () {        a++        alert(a)    }    return innerFun}var obj = outerFun()obj()  // 结果为1obj()  // 结果为2var obj2 = outerFun()obj2()  // 结果为1obj2()  // 结果为2

习题3

function foo (x) {    var tmp = 3    function bar (y) {        console.log(x + y + (++tmp))    }    bar(10)}foo(2) // 16foo(2) // 16foo(2) // 16foo(2) // 16这里只是函数调用,不是闭包=== === === === === === === ===function foo (x) {    var tmp = 3    return function (y) {        console.log(x + y + (++tmp))    }}var bar = foo(2)bar(10) // 16bar(10) // 17 bar(10) // 18bar(10) // 19当你return的是内部function时,就是一个闭包。一个函数访问了它的外部变量,那么它就是一个闭包

一道经典的闭包面试题

function fun (n, o) {    console.log(o)    return {        fun: function (m) {            return fun(m, n)        }    }}var a = fun(0) // undefineda.fun(1) // 0a.fun(2) // 0a.fun(3) // 0var b = fun(0).fun(1).fun(2).fun(3) //undefined,0,1,2var c = fun(0).fun(1) //undefined,0c.fun(2) // 1c.fun(3) // 1

详解:

转换为等价代码

return返回的对象的fun属性对应一个新建的函数对象,

这个函数对象将形成一个闭包作用域,

使其能够访问外层函数的变量n及外层函数fun,

为了不将fun函数和fun属性搞混,我们将上述代码修改如下:

function _fun_(n,o){    console.log(o);    return {        fun:function(m){            return _fun_(m,n);        }    }}
var a = fun(0); // undefined 产生了闭包 n = 0a.fun(1); // 0 产生了闭包,但是由于没有绑定变量,闭包马上就消失了, 始终用的是a里面的闭包  
a.fun(2); // 0 产生了闭包,但是由于没有绑定变量,闭包马上就消失了, 始终用的是a里面的闭包 a.fun(3); // 0 产生了闭包,但是由于没有绑定变量,闭包马上就消失了, 始终用的是a里面的闭包 var b = fun(0).fun(1).fun(2).fun(3); // undefined,0,1,2 会不断产生新的闭包等价代码var b1 = b.fun(1);var b2 = b1.fun(2);var b3 = b2.fun(3); var c = fun(0).fun(1); // undefined,0,c.fun(2); // 1 当前c的闭包是 1c.fun(3); // 1 当前c的闭包是 1虽然c.fun(2)和c.fun(3)都产生了新的闭包,但是由于没有赋给新的变量,闭包接着就消失了

JavaScript视频140集

链接:https://pan.baidu.com/s/1toY_il7JnVSSKTignH7CPA

提取码:c9fk

新型PC勒索病毒?看SOLIDWORKS PDM如何保护您的设计数据

近日网络上出现了一种名为“WannaRen”的新型勒索病毒,与此前的“WannaCry”的行为类似,加密Windows系统中几乎所有文件,后缀为.WannaRen,赎金为0.05个比特币。    据贴吧和360社区反馈的消息来看,感染过程中360等杀毒软件未报…

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
幸运草
勒索病毒泛滥全球 数据方舟拯救业务 劫持

勒索病毒泛滥全球 数据方舟拯救业务

5月12日起,不法分子利用之前泄露的NSA黑客武器库中“永恒之蓝”攻击程序发起的网络攻击事件,全球范围内接二连三爆发基于Windows网络共享协议进行攻击传播的蠕虫恶意代码,危害极其严重,波及近百个国...

发表评论