用9种办法解决 JS 闭包经典面试题之 for 循环取 i

 

2017-01-06 Tomson JavaScript

转自 https://segmentfault.com/a/1190000003818163

 

闭包

1.正确的说,应该是指一个闭包域,每当声明了一个函数,它就产生了一个闭包域(可以解释为每个函数都有自己的函数栈),每个闭包域(Function 对象)都有一个 function scope(不是属性),function scope内默认有个名为Global的全局引用(有了这个引用,就可以直接调用 Global 的属性或方法)

2.凡是在闭包域内声明的变量或方法,外部无法直接访问

3.闭包域可以访问外部的变量或方法 
(上图为 chrome 下 debug 环境)

当在一个闭包域内包含另一个闭包域时(简单的说就是在一个函数内有另一个函数,当然这个内部函数的生命周期是依附于外部函数的), 此时,若子闭包域(内部的闭包域,内部函数)使用了父闭包域(外部闭包域,外部函数)的私有变量(在父闭包域中声明的变量,父闭包域的外部空间无法直接访问,但子闭包域可以访问),子闭包域即当前的子函数的 function scope 会产生一个 closure 对象属性,这个对象属性内包含的是子闭包域对父闭包域的所有引用(只要子闭包域(内部函数)还存活,其父闭包域(外部函数)就依旧存活),倘若在父闭包域存活期间对其私有变量内容进行修改,则对这些父闭包域的私有变量进行引用的子闭包域中 function scope 的 closure 对象属性的内容也会发生变化,因为这只是引用.

举例:

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title></title>

</head>

<body>

   <script type="text/javascript" charset="utf-8">

       //函数 a 有一个私有变量 p 和一个内部函数 innerA

       function a() {                      //外部闭包域 ,一个名为 a 的 Function 对象

           var p = 0;                      //私有变量 p

           var innerA = function () {      //内部闭包域 ,一个名为 innerA 的 Function 对象

               console.log(p);             //对外部闭包域的私有变量进行了引用,故 innerA 对象的 function scope 会产生一个名为 closure 的对象属性,closure 对象内含有一个名为 p 的引用

           }

 

           innerA();//输出0

           p++;

           innerA();//输出1

       }

       a();

   </script>

</body>

</html>

结果如下:

第一次调用innerA

第二次调用 innerA

控制台输出

回到主题 面试经典问题

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title></title>

   <script type="text/javascript">

       //面试经典问题:

 

       function onMyLoad(){

           /*

           抛出问题:

               此题的目的是想每次点击对应目标时弹出对应的数字下标 0~4,但实际是无论点击哪个目标都会弹出数字5

           问题所在:

               arr 中的每一项的 onclick 均为一个函数实例(Function 对象),这个函数实例也产生了一个闭包域,

               这个闭包域引用了外部闭包域的变量,其 function scope 的 closure 对象有个名为 i 的引用,

               外部闭包域的私有变量内容发生变化,内部闭包域得到的值自然会发生改变

           */

           var arr = document.getElementsByTagName("p");

           for(var i = 0; i < arr.length;i++){

               arr[i].onclick = function(){

                   alert(i);

               }

           }

       }

   </script>

</head>

<body onload="onMyLoad()">

   <p>产品一</p>

   <p>产品二</p>

   <p>产品三</p>

   <p>产品四</p>

   <p>产品五</p>

</body>

</html>

解决办法:

解决办法一

/*

解决思路:

   增加若干个对应的闭包域空间(这里采用的是匿名函数),专门用来存储原先需要引用的内容(下标),不过只限于基本类型(基本类型值传递,对象类型引用传递)

*/

for(var i = 0;i<arr.length;i++){

 

   //声明一个匿名函数,若传进来的是基本类型则为值传递,故不会对实参产生影响,

   //该函数对象有一个本地私有变量arg(形参) ,该函数的 function scope 的 closure 对象属性有两个引用,一个是 arr,一个是 i

   //尽管引用 i 的值随外部改变 ,但本地私有变量(形参) arg 不会受影响,其值在一开始被调用的时候就决定了.

   (function (arg) {

       arr[i].onclick = function () {  //onclick函数实例的 function scope 的 closure 对象属性有一个引用 arg,

           alert(arg);                 //只要 外部空间的 arg 不变,这里的引用值当然不会改变

       }

   })(i);                              //立刻执行该匿名函数,传递下标 i(实参)

}

解决办法二

/*

解决思路:

   将下标作为对象属性(name:"i",value:i的值)添加到每个数组项(p对象)中

*/

for(var i = 0;i<arr.length;i++){

   //为当前数组项即当前 p 对象添加一个名为 i 的属性,值为循环体的 i 变量的值,

   //此时当前 p 对象的 i 属性并不是对循环体的 i 变量的引用,而是一个独立p 对象的属性,属性值在声明的时候就确定了

   //(基本类型的值都是存在栈中的,当有一个基本类型变量声明其等于另一个基本变量时,此时并不是两个基本类型变量都指向一个值,而是各自有各自的值,但值是相等的)

   arr[i].i = i;

   arr[i].onclick = function () {

       alert(this.i);

   }

}

解决办法三

/*

解决思路:

   与解决办法一有点相似但却有点不太相似.

   相似点:同样是增加若干个对应的闭包域空间用来存储下标

   不同点:解决办法一是在新增的匿名闭包空间内完成事件的绑定,而此例是将事件绑定在新增的匿名函数返回的函数上

 

   此时绑定的函数中的 function scope 中的 closure 对象的 引用 arg 是指向将其返回的匿名函数的私有变量 arg

*/

for(var i = 0; i<arr.length;i++){

   arr[i].onclick = (function(arg){

       return function () {

           alert(arg);

       }

   })(i);

}

解决办法四

/*

解决思路与解决办法一相同

*/

for(var i = 0; i<arr.length;i++){

   (function(){

      var temp = i;

       arr[i].onclick = function () {

           alert(temp);

       }

   })();

}

解决办法五

/*

解决思路与解决办法三及四相同

*/

for(var i = 0;i<arr.length;i++){

   arr[i].onclick = (function () {

       var temp = i;

       return function () {

           alert(temp);

       }

   })();

}

解决办法六

/*

解决思路:

   将下标添加为绑定函数的属性

*/

for(var i = 0;i<arr.length;i++){

   (arr[i].onclick = function () {

       alert(arguments.callee.i);      //arguments 参数对象  arguments.callee 参数对象所属函数

   }).i = i;

}

解决办法七

/*

解决思路:

   通过 new 使用 Function 的构造函数 创建 Function 实例实现,由于传入的函数体的内容是字符串,故 Function 得到的是一个字符串拷贝,而没有得到 i 的引用(这里是先获取 i.toString()然后与前后字符串拼接成一个新的字符串,Function 对其进行反向解析成 JS 代码)

*/

for(var i = 0;i<arr.length;i++){

   arr[i].onclick = new Function("alert("+i+");");//每 new 一个 Function 得到一个 Function 对象(一个函数),有自己的闭包域

}

解决办法八

/*

解决思路:

   直接通过 Function 返回一个函数

   与解决办法七的不同之处在于:

       解决办法七使用 new,使用了 new,此时 Function 函数就被当成构造器可以用来构造一个 Function 实例返回

       当前解决办法没有使用 new ,即将 Function 函数当成一个函数,传入参数返回一个新函数;

       其实此处 new 与不 new 只是的区别在于:

           使用了 new 即 Function 函数充当构造器,由 JS 解析器生产一个新的对象,构造器内的 this 指向该新对象;

           不实用 new 即 Function 函数依旧是函数,由函数内部自己生产一个实例返回.

*/

for(var i = 0;i<arr.length;i++){

   arr[i].onclick = Function("alert("+i+");");

}

解决办法九 
使用ES6新语法 let 关键字 由于几新东西 各浏览器支持不同 
chrome 及 opera支持以下语法

<script type="application/javascript">

   "use strict";//使用严格模式,否则报错 SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

   var arr = document.getElementsByTagName("p");

   for(var i = 0;i<arr.length;i++){

       let j = i;//创建一个块级变量

       arr[i].onclick = function () {

           alert(j);

       }

   }

</script>

在 chrome 查看

可以在控制台看到 j 变量是一个 block 级的变量

待函数绑定完成后看数组项:

此时的该数组项的<function scope>的 Block 域有个 j 存储的就是对应的数组下标 
firefox支持一下语法

<script type="application/javascript;version=1.7">

   var arr = document.getElementsByTagName("p");

   for(var i = 0;i<arr.length;i++){

       let j = i;

       arr[i].onclick = function () {

           alert(j);

       }

   }

</script>

由于新语法各大厂商的支持尚未规范故暂不不推荐使用

解决办法大同小异,只要理解其中的实质,可以写出多多的解决办法

 

点击阅读全文,查看更多

转载于:https://www.cnblogs.com/meinan588/p/6401978.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/393831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bzoj 2296: 【POJ Challenge】随机种子

Time Limit: 1 Sec Memory Limit: 128 MBSec Special JudgeDescription1tthinking除了随机算法&#xff0c;其他什么都不会。但是他还是可以ac很多题目&#xff0c;他用的是什么呢&#xff1f;他会选择一个好的随机种子&#xff0c;然后输出答案。往往他选择的一个好的种子可…

英特尔第十代处理器为什么不支持win7_5GHz动力澎湃 高主频多核处理器成就巅峰玩家...

频率之争永远是处理器领域无法回避的话题。高主频在游戏中所带来的高速运行&#xff0c;稳定帧数等特性永远是玩家们所追求的目标。随着英特尔第十代桌面及移动版酷睿处理器的发布&#xff0c;无论是台式整机或是笔记本平台&#xff0c;都已全面进入了5GHz时代。选择英特尔处理…

leetcode46. 全排列(回溯)

给定一个 没有重复 数字的序列&#xff0c;返回其所有可能的全排列。 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 代码 class Solution {List<List<Integer>> cListnew ArrayList<>();public List<List<…

初级算法-12.反转字符串

题目描述: 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 你可以假设数组中的所有字符都是 ASCII 码表中的可打…

linux python源码目录结构,TensorFlow0.8源码阅读 -- 代码目录结构讲解

TensorFlow0.8发布以来受到了大量机器学习领域爱好者的关注&#xff0c;目前其项目在github上的follow人数在同类项目中排名第一。作为google的第一个开源项目&#xff0c;TensorFlow的源码结构较为清晰&#xff0c;相关的代码注释覆盖较全。本文首先从代码结构入手&#xff0c…

在VirtualBox里复制VDI文件[转]

原文地址:http://blog.sina.com.cn/s/blog_591a2c940100aree.html 在VirtualBox的快速修复界面里&#xff0c;可以随时生成当前状态的备份。当生成了备份之后&#xff0c;会在Snapshots目录下创建一个新的VDI文件&#xff0c;之后对当前状态所做的一切操作都将针对最新的VDI文件…

软件开发重要性_在软件开发中考虑时间的重要性

软件开发重要性by Crunch Tech通过Crunch Tech 在软件开发中考虑时间的重要性 (The importance of time to think in Software Development) Modern Technology teams operate in a fast-paced environment. With a Technology team of only 35 people, we average over 50 re…

自动登录360,百度

方便登录&#xff0c;写的小工具 1 import win.ui;2 import web.ui;3 /*DSG{{*/4 var winform ..win.form(text"AAuto Form";right599;bottom399)5 winform.add(6 button{cls"button";text"百度";left41;top25;right163;bottom59;z1};7 button2…

arm linux 开机电路_【技术角度看问题之一】ARM到底是个啥?

【小宅按】近期公司推出来基于ARM芯片的服务器&#xff0c;本文就一些基本概念&#xff0c;比如ARM&#xff0c; ARM64, ARMv8, ARM7&#xff0c;ARMv7, 64位等让人费解的概念进行了粗浅地分析&#xff0c;涉及的关键字已用粗体标出。文中观点仅仅是一家之言&#xff0c;拙劣之…

leetcode77. 组合(回溯)

给定两个整数 n 和 k&#xff0c;返回 1 … n 中所有可能的 k 个数的组合。 示例: 输入: n 4, k 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 代码 class Solution {List<List<Integer>> cListnew ArrayList<>();public List<List<I…

linux系统配置脚本,Linux系统配置脚本开机自启

在日常使用过程中&#xff0c;当有时候需要运行开机自启脚本&#xff0c;同时想看到输出结果的时候&#xff0c;不免就需要将系统配置为免密登录模式&#xff0c;本文主要介绍再init3模式(多用户命令行界面)&#xff0c;运行开机自启脚本及输出脚本指令运行数据结果。本文使用的…

WPF多线程UI更新

前言 在WPF中&#xff0c;在使用多线程在后台进行计算限制的异步操作的时候&#xff0c;如果在后台线程中对UI进行了修改&#xff0c;则会出现一个错误&#xff1a;&#xff08;调用线程无法访问此对象&#xff0c;因为另一个线程拥有该对象。&#xff09;这是很常见的一个错误…

iOS------App之间传递数据的几种方式

UIDocumentInteractionController UIActivityViewController Shared Keychain Access Custom URL Scheme Web Service iCloud API UIPasteboard 参考 http://enharmonichq.com/sharing-data-locally-between-ios-apps/http://stackoverflow.com/questions/9425706/share-data-b…

了解如何使用Vue.js CLI

Interested in learning Vue.js? Get my ebook at vuehandbook.com有兴趣学习Vue.js吗&#xff1f; 在vuehandbook.com上获取我的电子书 One of them is the Vue Command Line Interface (CLI).其中之一是Vue命令行界面(CLI)。 Note: There is a huge rework of the CLI goin…

rabbitmq学习——队列

public class Send { public static final String routingKey "wuqidi_task_durable"; /*工作队列 也叫任务队列 目的是将任务发送到队列中 由工作者进行处理 在后台的多个工作者中 任务是共享的*/ public static void main(String[] args) throws Exception{ Conne…

python 战舰_简单Python战舰

我最近开始学习python&#xff0c;并决定尝试制作我的第一个项目。我正在尝试做一个战舰游戏&#xff0c;随机放置两个3块长的船在一块板上。但效果不太好。我为2号飞船做了一个临时的循环&#xff0c;它应该检查一下旁边的两个空间是否空闲&#xff0c;然后在那里建立自己的空…

leetcode面试题 08.12. 八皇后(回溯)

设计一种算法&#xff0c;打印 N 皇后在 N N 棋盘上的各种摆法&#xff0c;其中每个皇后都不同行、不同列&#xff0c;也不在对角线上。这里的“对角线”指的是所有的对角线&#xff0c;不只是平分整个棋盘的那两条对角线。 注意&#xff1a;本题相对原题做了扩展 示例: 输…

linux 进入redis 数据库,Linux下Redis数据库的安装方法与自动启动脚本分享

安装Redis(1) 下载Rediswget http://redis.googlecode.com/files/redis-2.2.11.tar.gztar xzvf redis-2.2.11.tar.gz(2) 编译并安装Redismake && make install(3) 复制并修改配置文件cp redis.conf /etc/redis.confvi /etc/redis.conf注意修改以下几项&#xff1a;daem…

Flutter 36: 图解自定义 View 之 Canvas (三)

小菜继续学习 Canvas 的相关方法&#xff1a; drawVertices 绘制顶点 小菜上次没有整理 drawVertices 的绘制方法&#xff0c;这次补上&#xff1b;Vertice 即顶点&#xff0c;通过绘制多个顶点&#xff0c;在进行连线&#xff0c;多用于 3D 模型中&#xff1b; drawVertices 包…

sphinx 项目根目录_如何使用Sphinx工具记录Django项目

sphinx 项目根目录I recently visited a company where I had a nice talk with one of its employees. We talked about technology and programming. Then we touched the subject of project documentation. Specifically how React does it automatically but Django doesn…