阳谷网站建设,wordpress换主题后打不开,智慧团建官网登录口手机版,深圳市住房和建设局网站和市住宅租赁管理服务中心大厂面试题分享 面试题库后端面试题库 #xff08;面试必备#xff09; 推荐#xff1a;★★★★★地址#xff1a;前端面试题库闭包的背景由于js中只有两种作用域#xff0c;全局作用域和函数作用域#xff0c;而在开发场景下#xff0c;将变量暴露在全局作用域下的时候…大厂面试题分享 面试题库后端面试题库 面试必备 推荐★★★★★地址前端面试题库闭包的背景由于js中只有两种作用域全局作用域和函数作用域而在开发场景下将变量暴露在全局作用域下的时候是一件非常危险的事情特别是在团队协同开发的时候变量的值会被无意篡改并且极难调试分析。这样的情况下闭包将变量封装在局部的函数作用域中是一种非常合适的做法这样规避掉了被其他代码干扰的情况。闭包的使用下面是一种最简单直接的闭包示例//妈妈本体functionmother(){//口袋里的总钱数let money  100//消费行为returnfunction (pay){//返回剩余钱数return money - pay}
}
//为儿子消费let payForSon  mother()
//打印最后的剩余钱数console.log(payForSon(5))
复制代码为了便于理解我们将外部函数比喻为妈妈本体里面保存着总钱数这个变量和消费这个行为通过创建为儿子消费的这个行为对象然后执行这个行为花费5元返回剩余的95元。这个就是为了将变量money保存在mother本体内而避免暴露在外部的全局环境作用域中只能通过mother()创建消费行为来影响money这个变量。由此可以归纳总结使用闭包的三个步骤用外层函数包裹变量函数外层函数返回内层函数外部用变量保存外部函数返回的内层函数目的是为了形成一个专属的变量只在专属的作用域中操作。上述的闭包代码示例中有一个缺陷的场景是在后续不需要money变量的情况下没有释放该变量造成内存泄露。原因是payForSon这个函数的作用域链引用着money对象解决的办法是将payForSon  null就可以释放方法作用域进而解除对money的引用最后释放money变量。闭包的扩展函数柯里化在开发的场景中有时需要通过闭包来实现函数的柯里化调用。调用示例如下alert(add(1)(2)(3))
复制代码这种连续的传参调用函数叫做函数柯里化。通过闭包的实现方式如下functionadd(a){//保存第一个参数let sum  afunctiontmp(b){//从第二个函数开始递加sum  sum  b//返回tmp让后续可以继续传参执行return tmp}tmp.toString  function(){return sum}//返回加法函数return tmp
}
alert(add(1)(2)(3))
复制代码下面我们来一步步分析add(1)执行时保存第一个参数到sum变量中返回tmp函数add(1)(2)执行等于tmp(2),将2的值加到了变量sum上返回tmp函数本身add(1)(2)(3)执行等同于上述步骤的加到比变量sum上返回tmp函数本身alert(add(1)(2)(3))执行时alert需要将值转为string显示最后的tmp函数执行tmp.toString返回sum的值。矩阵点击应用该例子的demo代码在我的github上可以自行取阅需求在一个4*4的矩阵方块中实现点击每个按钮时记录下各自的点击次数相互之间互不干扰。思路在按钮事件中使用闭包创建独立的存储变量空间。注意下列的方案1到方案3是逐次演进的优化方案需要按照方案标号的次序逐层理解更有利于理解最终的优化方案方案1div idcontainer/div
...
let container  document.getElementById(container)
for (let r  0; r  arr.length; r) {for (let c  0; c  arr[r].length; c) {let cell  document.createElement(div)cell.innerHTML  (${r},${c})container.append(cell)cell.onclick  (function () {let n  0return function () {ncell.innerHTML  点${n}}})()}
}
复制代码在每个按钮上通过onclick绑定闭包方法存储操作独立的n变量这样就可以单独记录每个按钮的点击次数缺点这样做有一个不足的地方是外部无法获取内部的n变量不能实现与外部的交互比如按钮间的相互影响。方案2为了改善方案1的缺点我们引入外部数据arr来操作管控按钮点击数。 代码示例如下let arr  [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],]
let container  document.getElementById(container)
for (let r  0; r  arr.length; r) {for (let c  0; c  arr[r].length; c) {let cell  document.createElement(div)cell.innerHTML  (${r},${c})container.append(cell)cell.onclick  (function (r, c) {return function () {arr[r][c]cell.innerHTML  点${arr[r][c]}}})(r, c)}
}
复制代码参照方案1 改动点包含两个新增arr二维数组来记录点击数这样可以达到与外部交互的目的onclick绑定的事件新增rc两个参数并且执行时传参进入这样就可以把行列参数传递到方法内部onclick的执行环境作用域与r,c所在的环境不一致所以无法直接使用这样改进完以后外部可以通过操作arr来与每个按钮的点击次数进行交互。缺点这样会将arr暴露在全局作用域下可以在console控制台访问到很容易被其他人或者模块误操作也不利于封装方案3基于方案2的改进实现为用一个立即执行的函数包裹住整个执行代码这样就构建了一个函数作用域来封装arr变量为私有。代码如下(function () {let arr  [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],]let container  document.getElementById(container)for (let r  0; r  arr.length; r) {for (let c  0; c  arr[r].length; c) {let cell  document.createElement(div)cell.innerHTML  (${r},${c})container.append(cell)cell.onclick  (function (r, c) {return function () {arr[r][c]cell.innerHTML  点${arr[r][c]}}})(r, c)}}})()
复制代码这样一个相对完整的按钮点击次数的方案就完成了。使用call实现bind这个需要有call和bind的使用知识的前提可以自行百度哈废话不多说直接上代码Function.prototype.bind  function(obj){console.log(调用自定义bind函数);//保存当前函数对象let fun  this//去除第一个obj参数并且转换为js数组let outerArg  Array.prototype.slice.call(arguments,1)returnfunction(){//将arguments转为js数组let innerArg  Array.prototype.slice.call(arguments)//汇总所有参数let totalArg  outerArg.concat(innerArg)//调用外部保存的函数并且传参fun.call(obj,...totalArg)}
}//调用示例let zhangsan  {name:wawawa}
functiontotal(s1,s2){console.log(this.name  s1  s2);
}
let bindTotal  total.bind(zhangsan,100)
bindTotal(200)
复制代码重写函数类的bind函数先将函数对象也就是下面示例中的total函数保存在fun变量中等于闭包外层保存了funobj以及其他绑定的参数由于arguments是类数组对象需要转换为数组且去除第一个函数obj然后返回匿名函数在匿名函数中将外部和内部的参数进行转换和拼接最后通过fun.call(obj,...totalArg)调用保存的函数对象fun并且通过call来实现传递绑定的作用域obj和其他参数totalArg注意arguments是类数组对象不能直接使用数组方法需要转化为数组操作外层函数arguments转化时需要剔除掉obj因为下面的fun.call需要单独传递obj作为函数作用域totalArg传递给call函数时需要通过...语法糖摊开数组大厂面试题分享 面试题库后端面试题库 面试必备 推荐★★★★★地址前端面试题库