万网做网站,内蒙古城乡住房建设厅网站,asp网站空间,企业名称查重序言 学习JavaScript切勿好高骛远。正所谓贪多嚼不烂#xff0c;前端标准和工具这几年的飞速发展#xff0c;以及不时冒出的“新鲜玩意”让众多前端从业者惊呼#xff1a;“学不动啦学不动啦#xff01;学习速度跟不上技术发展速度#xff01;我感到手忙脚乱、力不从心……… 序言 学习JavaScript切勿好高骛远。正所谓贪多嚼不烂前端标准和工具这几年的飞速发展以及不时冒出的“新鲜玩意”让众多前端从业者惊呼“学不动啦学不动啦学习速度跟不上技术发展速度我感到手忙脚乱、力不从心……如果你有以上“症状”请勿着急这不过是你内心不安造成的。你为何追新你又何苦追新在根基不牢的情况下就算盖楼盖到18层再往上堆一块砖都可能导致大楼坍塌这结果绝非你预期。所以此时你应该沉下心来苦练基础。而非死钻牛角尖。硬要及时掌握那些业界最新冒出来的“玩意儿”对你无益处。 前言 我们知道作用域链查找标识符的顺序是从当前作用域开始一级一级往上查找。因此通过作用域链JavaScript函数内部可以读取函数外部的变但反过来函数的外部通常则无法读取函数内部的变量。在实际应用中有时需要真正在函数外部访问函数内部的局部变量此时最常用的方法就是使用闭包。那么什么是闭包所谓闭包就是 同时含有对函数对象以及作用域对象引用的对象。闭包主要是用来获取作用域链或原型链上的变量或值。创建闭包最常见的方式是在一个函数中声明内部函数(也称嵌套函数)并返回内部函数。此时在函数外部就可以通过调用函数得到内部函数。虽然按照闭包的概念所有访问了外部变量的JavaScript函数都是闭包。但我们平常绝大部分时候所谓的闭包其实指的就是内部函数闭包。闭包可以将一些数据封装私有属性以确保这些变量的安全访问这个功能给应用带来了极大的好处。需要注意的是闭包如果使用不当也会带来一些意想不到的问题。下面就通过几个示例来演示一下闭包的创建、使用和可能存在的问题及其解决方法。 示例1 创建闭包。 htmlhtmlhead title闭包titleheadbodyscript typetext/javascriptfunction outer(argument) {var b0;return function inner (){ b;console.log(内部的bb); } }var func outer();//1 通过外部变量引用函数返回的内部函数console.log(func);//2 输出内部函数定义代码 func();//3 通过闭包访问局部变量b此时b1;console.log(外部函数中bb); //4 出错报引用错误。scriptbodyhtml上述代码在外部函数 outer中声明内部函数 inner并返回内部函数同时在 outer函数外面变量 func引用了 outer函数返回的内部函数所以内部函数 inner是一个闭包。该闭包访问了外部函数的局部变量 b。1处代码通过调用外部函数返回内部函数并赋给外部变量 func使 func变量引用内部函数所以2处代码将输出 inner函数的整个定义代码。3处代码通过对外部变量 func添加一对小括号后调用内部函数 inner,从而达到在函数外部访问局部变量 b的目的。执行4处的代码时将报 ReferenceError错误因为 b是局部变量不能在函数外部直接访问局部变量。我们知道函数执行完毕时运行期上下文会被销毁与之关联的活动对象也会随之销毁因此离开函数后属于活动对象的局部变量将不能被访问。但是为什么上述示例中的 outer函数执行完后它的局部变量还能被内部函数访问呢这个问题我们可以用作用域链来解释。当执行1处代码调用 outer函数时JavaScript引擎会创建 outer函数执行上下文的作用域链这个作用域链包含了 outer函数执行时的活动对象同时JavaScript引擎也会创建一个闭包而闭包因为需要访问 outer函数的局部变量因而其作用链也会引用 outer的活动对象。这样当 outer函数执行完后它的作用域对象因为有闭包的引用而依然存在固而可以提供给闭包访问。上述示例中的内部函数虽然有名称但在调用是并没有用到这个名称所以内部函数的名称可以缺省即可以将内部函数修改为匿名函数从而简化代码。 示例2 经典闭包问题 htmlhtmlheadtitle经典闭包问题titlescript typetext/javascriptwindow.onloadfunction () {var abtn document.getElementsByTagName(button);for (var i 0; i abtn[i].onclickfunction(){ alert(按钮(i1)); } } }scriptheadbodybutton按钮1buttonbutton按钮2buttonbutton按钮3buttonbodyhtml该示例期望实现的功能是单击每个按钮时在弹出的警告对话框中显示相应的标签内容即单击3个按钮时将分别显示“按钮1”、“按钮2”、“按钮3”。上述示例页面加载完后触发窗口加载事件从而执行外层匿名函数外层匿名函数执行完循环语句后使活动对象中的局部变量i的值修改为 3。外层匿名函数执行完后撤销但由于其活动对象中的 abtn和 i变量被内层匿名函数引用因而外层匿名函数的活动对象仍然存在堆中供内层匿名函数访问。每执行一次循环都将创建一个闭包这些闭包都引用了外层匿名函数的活动对象因而访问变量i时都得到 3这样最后的结果是单击每个按钮在警告对话框中显示的文字都是“按钮4” (i131)与期望的功能不一致。造成这个问题的原因是每个闭包都引用一个变量如果我们使不同的闭包引用不同的变量就可以实现输出的结果不一样。这个需求可使用多种方法实现在此介绍使用立即调用函数表达式( IIFE)和ES6中的 let创建块即变量的方法。 IIFE指的是在定义函数的时候直接执行即此时函数定义变成了一个函数调用的语句。要让一个函数定义语句变成函数调用语句就需要将定义语句变为一个函数表达式然后在该表达式后面再加一对圆括号()即可。将函数定义语句变为一个函数表达式的最常用方法就是将整个定义语句放在一对圆括号中。 1、IIFE中的函数为一个匿名函数 (function(name){ console.log(helloname);})(maomin);JS引擎执行上述代码时会调用匿名同时将后面圆括号中的参数 maomin传给 name虚参结果得到hellomaomin。 2、IIFE中的函数为一个有名函数 (function func (name) { console.log(I amname);})(maomin) 上述代码跟匿名函数完全一样。 示例3 使用立即调用函数表达式解决经典闭包问题 htmlhtmlhead title使用立即调用表达式解决经典闭包问题title script typetext/javascriptwindow.onloadfunction () {var abtn document.getElementsByTagName(button);for (var i 0; i (function(num){ abtn[num].onclickfunction(){ alert(按钮(num1)); } })(i) } }scriptheadbodybutton按钮1buttonbutton按钮2buttonbutton按钮3buttonbodyhtml上述代码中第二个匿名函数为 IIFE每次调用该匿名函数时将生成一个对应该函数的活动对象。该对象中包含可一个函数参数值为当次循环的循环变量值。上述示例中IIFE共执行了 3次因而共生成了 3个活动对象活动对象中包含的参数值分别为 0、 1和 2依次对应 IIFE的 3次执行。每次执行 IIFE时将会产生一个闭包该闭包会引用对应按钮索引顺序执行 IIFE的活动对象而闭包引用的活动对象中的参数值刚好等于按钮的索引值因而单击 3个按钮将在弹出的警告框中分别显示按钮1、“按钮2”、“按钮3”。 示例4使用ES6中的let关键字创建块级变量解决经典闭包问题 htmlhtmlhead title使用ES6中的let关键字解决经典闭包问题title script typetext/javascriptwindow.onloadfunction () {var abtn document.getElementsByTagName(button);for (let i 0; i abtn[i].onclickfunction(){ alert(按钮(i1)); } } }scriptheadbodybutton按钮1buttonbutton按钮2buttonbutton按钮3buttonbodyhtml上述代码中循环变量使用 let声明因而每次循环时都会产生一个新的块级变量所以在页面加载完执行外层匿名函数时产生的活动对象中包含了 3个对应循环变量的块级变量变量值分为 0、 1和 2。每执行一次循环将会产生一个闭包该闭包中的变量 i会引用外层匿名函数的活动对象对应按钮索引的块级变量因而单击 3个按钮时将在弹出的警告对话框中分别显示“按钮1”、“按钮2”、“按钮3”。 下一期更精彩 结语 欢迎关注我的公众号回复关键词【电子书】即可获取近十几本前端热门电子书。更有精品文章等着你哦。你还可以加我微信我拉拢了很多IT大佬创建了一个技术交流、文章分享群欢迎你的加入。 作者Vam的金豆之路主要领域前端开发我的微信maomin9761微信公众号前端历劫之路