分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 运营维护

js的闭包

发布时间:2023-09-06 01:27责任编辑:熊小新关键词:js闭包
function a(){ ???var n = 0; ???this.inc = function () { ???????n++; ????????console.log(n); ???};}var c = new a();c.inc(); ???//控制台输出1c.inc(); ???//控制台输出2

什么是闭包?这就是闭包!!有权访问另一个函数作用域内变量的函数都是闭包。当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

这里 inc 函数访问了构造函数 a 里面的变量 n,所以形成了一个闭包。

function a(){ ???var n = 0; ???function inc(){ ??????n++; ???????console.log(n); ???} ???return inc;}var c = a();c(); ???//控制台输出1c(); ???//控制台输出2

执行过程:

var c=a();这里的a()返回的是函数inc,那这句等同于var c = inc;c()相当于inc();函数名只是一个标识(指向函数的指针),而()才是执行函数。

为什么要用闭包?

js的每个函数都是一个个小黑屋,它可以获取外界信息,但是外界却无法直接看到里面的内容。将变量 n 放进小黑屋里,除了 inc 函数之外,没有其他办法能接触到变量 n,而且在函数 a 外定义同名的变量 n 也是互不影响的,这就是所谓的增强“封装性”。而之所以要用 return 返回函数标识 inc,是因为在 a 函数外部无法直接调用 inc 函数,所以 return inc 与外部联系起来,第一个代码中的 this 也是将 inc 与外部联系起来而已。

常见的陷阱

function createFunctions(){ ???var result = new Array(); ???for (var i=0; i < 10; i++){ ???????result[i] = function(){ ???????????return i; ???????}; ???} ???return result;}var funcs = createFunctions();for (var i=0; i < funcs.length; i++){ ???console.log(funcs[i]());}

原本想要输出0~9,但是最后的结果是10个10,为什么呢?

这里的陷阱就是:函数带()才是执行函数! 单纯的一句 var f = function() { alert(‘Hi‘); }; 是不会弹窗的,后面接一句 f(); 才会执行函数内部的代码。上面代码翻译一下就是:

var result = new Array(), i;result[0] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!result[1] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!...result[9] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!i = 10;funcs = result;result = null;console.log(i); // funcs[0]()就是执行 return i 语句,就是返回10console.log(i); // funcs[1]()就是执行 return i 语句,就是返回10...console.log(i); // funcs[9]()就是执行 return i 语句,就是返回10

为什么只垃圾回收了 result,但却不收了 i 呢? 因为 i 还在被 function 引用着啊。这就是闭包的神奇之处,内部作用域依然存在,因此没有被回收。result依然持有对该作用域的引用,这个引用就叫做闭包!

更改方法:

function createFunctions(){ ???var result = new Array(); ???for (var i=0; i < 10; i++){ ???????result[i] = function(x){ ???????????return function(){ ???????????????return x; ???????????} ???????}(i); ???} ???return result;}var funcs = createFunctions();for (var i=0; i < funcs.length; i++){ ???console.log(funcs[i]());}

没有直接把闭包赋值给数组,而是定义了一个匿名函数,并通过立即执行该匿名函数的结果赋值给数组,并带了for循环的参数i进去,让x能找到传入的参数值为0-10,这就解释了函数参数是按值传递的,所以会将变量i的当前值复制给参数x。而这个匿名函数内部又创建并返回了一个访问x的闭包。这样以来result数组中的每个函数都有自己x变量的一个副本,所以会符合我们的预期输出不同的值。

闭包的应用场景

闭包应用的两种情况:函数作为返回值,函数作为参数传递

1、函数作为返回值

function fn(){ ???var max=10; ???return function bar(x){ ???????if (x>max){ ????????????console.log(x); ???????} ???}; ?}var f1=fn();f1(15);

bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值。

2、函数作为参数被传递

var max = 10;var fn=function(x){ ??if(x>max){ ?????console.log(x); ??}};(function(f){ ??var max = 100; ??f(15);})(fn);

fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100,因为要去创建这个函数的作用域取值,而不是“父作用域”。

当一个函数被调用完成之后,其执行上下文环境将被销毁,其中的变量也会被同时销毁。但有些情况下,函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。如下:

第一步,代码执行前生成全局上下文环境,并在执行时对其中的变量进行赋值。此时全局上下文环境是活动状态。

第二步,执行第17行代码时,调用fn(),产生fn()执行上下文环境,压栈,并设置为活动状态。

第三步,执行完第17行,fn()调用完成。按理说应该销毁掉fn()的执行上下文环境,但是这里不能这么做。注意,重点来了:因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。

因此,这里的fn()上下文环境不能被销毁,还依然存在与执行上下文栈中。——即,执行到第18行时,全局上下文环境将变为活动状态,但是fn()上下文环境依然会在执行上下文栈中。另外,执行完第18行,全局上下文环境中的max被赋值为100。如下图:

第四步,执行到第20行,执行f1(15),即执行bar(15),创建bar(15)上下文环境,并将其设置为活动状态。

执行bar(15)时,max是自由变量,需要向创建bar函数的作用域中查找,找到了max的值为10。这个过程在作用域链一节已经讲过。

这里的重点就在于,创建bar函数是在执行fn()时创建的。fn()早就执行结束了,但是fn()执行上下文环境还存在与栈中,因此bar(15)时,max可以查找到。如果fn()上下文环境销毁了,那么max就找不到了。

这里明显可以看到,使用闭包会增加内容开销!

第五步,执行完20行就是上下文环境的销毁过程。

总结

闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。

js的闭包

原文地址:https://www.cnblogs.com/lmjZone/p/9400300.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved