html

<body>
<p>产品一</p>
<p>产品二</p>
<p>产品三</p>
<p>产品四</p>
<p>产品五</p>
</body>

js

function init() {
var pAry = document.getElementsByTagName("p");
for( var i=0; i<pAry.length; i++ ) {
pAry[i].onclick = function() {
alert(i);
}
}
}
window.onload = init;

上面代码点击每个p,输出都是5,因为这里的 i 是个引用。演示地址

要想输出对应的i,有以下方法:

1、将变量 i 保存给在每个段落对象(p)上

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
pAry[i].i = i;
pAry[i].onclick = function() {
alert(this.i);
}
}
}
window.onload = init;

演示地址

2、将变量 i 保存在匿名函数自身

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
(pAry[i].onclick = function() {
alert(arguments.callee.i);
}).i = i;
}
}
window.onload = init;

演示地址

caller返回一个函数的引用,这个函数调用了当前的函数;

callee返回正在执行的函数本身的引用,它是arguments的一个属性

3.加一层闭包,i以函数参数形式传递给内层函数

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
(function(arg) {
pAry[i].onclick = function() {
alert(arg);
};
})(i); //调用时参数
}
}
window.onload = init;

演示地址

4、加一层闭包,i以局部变量形式传递给内存函数

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
(function() {
var temp = i; //调用时局部变量
pAry[i].onclick = function() {
alert(temp);
}
})();
}
}
window.onload = init;

演示地址

5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
pAry[i].onclick = function(arg) {
return function() { //返回一个函数
alert(arg);
}
}(i);
}
}
window.onload = init;

演示地址

6、用Function实现,实际上每产生一个函数实例就会产生一个闭包

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
pAry[i].onclick = new Function("alert(" + i + ");"); //new一次就产生一个函数实例
}
}
window.onload = init;

演示地址

7、用Function实现,注意与6的区别

function init() {
var pAry = document.getElementsByTagName("p");
for (var i = 0; i < pAry.length; i++) {
pAry[i].onclick = Function('alert(' + i + ')')
}
} window.onload = init;

演示地址

看了这篇文章,又想到用es5,es6的两个方法:

8、用es5 forEach函数

这里用forEach也行成了一个所谓的闭包,forEach里的执行函数也行成了一个闭包,每个执行体里,index都是一局部作用域,那为什么用Array.from呢,我们也可以用[].slice.call(node)我们类数组对象转化成真正的数组

function init() {
var pAry = document.getElementsByTagName("p");
//pAry = Array.from(pAry);
pAry = Array.prototype.slice.call(pAry);
pAry.forEach(function(currentItem,index){
currentItem.onclick = function(){
alert(index);
}
})
}
window.onload = init;

演示地址

9、用es6的let声明块级变量

function init() {
var pAry = document.getElementsByTagName("p");
for( let i=0; i<pAry.length; i++ ) {
pAry[i].onclick = function() {
alert(i);
}
}
}
window.onload = init;

演示地址

知乎关于这个问题也有一些回答

https://www.zhihu.com/question/33468703

JavaScript闭包 循环输出i的更多相关文章

  1. JavaScript利用闭包循环绑定事件

    我们经常在做前端面试题的时候,会遇到循环绑定事件后,输出打印结果,很多人总是搞不清楚,今天借此机会跟大家梳理一下闭包相关作用. 1.首先我们举一个简单的例子. html部分: <a href=& ...

  2. 从javascript的循环问题来看待闭包本质

    第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然又想起了这个问题,在这个春气盎然的周末,我就坐下来研究下并把结果和大家分享下: 先看代码:demo.html < ...

  3. JavaScript学习笔记-循环输出菱形,并可菱形自定义大小

    var Cen = 6;//定义菱形中部为第几行(起始值为0) //for循环输出菱形 document.write("<button onclick='xh()'>点我for循 ...

  4. JavaScript闭包深入解析

    for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } --上面这段 ...

  5. JavaScript 闭包系列二(匿名函数及函数的闭包)

    一. 匿名函数 1. 函数的定义,可分为三种 1) 函数声明方式 function double(x) {     return 2*x; } 2)Function构造函数,把参数列表和函数体都作为字 ...

  6. JavaScript闭包模型

      JavaScript闭包模型 -----  [原创翻译]2016-09-01  09:32:22 < 一>  闭包并不神秘 本文利用JavaScript代码来阐述闭包,目的是为了使普通 ...

  7. JavaScript 闭包整合

    初遇闭包感觉很困惑,上网查看了些许介绍,有很多没看懂,就想先对能懂的东西整整 首先觉得要了解闭包,要先对一.JavaScript的变量作用域和作用域链有基本了解 1.变量的作用域分为:全局变量和局部变 ...

  8. JavaScript ——闭包理解

    昨天晚上听别人谈起闭包这个东西,虽然对js有一点了解但却丝毫没有印象,今天也没什么事就顺便研究了一下满足好奇宝宝.整合于网上的理解,记录一下. 一.闭包的作用域 要理解闭包,首先必须理解Javascr ...

  9. Javascript闭包的一些研究

    原文:Javascript闭包的一些研究 本文不谈闭包的概念,因为概念容易把人搞晕,本文希望通过几个鲜活的例子来探究闭包的性质,相信对理解闭包会有所帮助. 程序1 var f = (function( ...

随机推荐

  1. 自动发现项目中的URL,django1版本和django2版本

    一.django 1 版本 routers.py import re from collections import OrderedDict from django.conf import setti ...

  2. g++编译器的使用(转载)

    关于g++ g++  是GNU组织开发出的编译器软件集合(GCC)下的一个C++编译器.它是Unix 和 Linux  系统下标配的 基于命令行的 C++编译器.如果你的系统是Windows,可以按照 ...

  3. P1309 瑞士轮

    题目背景 在双人对决的竞技性比赛,如乒乓球.羽毛球.国际象棋中,最常见的赛制是淘汰赛和循环赛.前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高.后者的特点是较为公平,偶然性较低,但比赛过程往往十分 ...

  4. 菜鸟学Linux - Linux文件属性

    在Linux中,文件的属性是一个很重要的概念,用户或者用户组对一个文件所拥有的权限,都可以从文件的属性得知. 我们可以通过ls -al命令,列出某个文件夹下面的所有文件(包括以.开头的隐藏文件).下面 ...

  5. laravel5.2总结--本地化以及常量的使用

    1.本地化 Laravel 的本地化功能提供方便的方法来获取多语言的字符串,让你的网站可以简单的支持多语言. 语言包存放在 resources/lang 文件夹的文件里.每一个子目录应该对应一种语言 ...

  6. 设计模式之第19章-中介者模式(Java实现)

    设计模式之第19章-中介者模式(Java实现) “测试妹纸找你,你的代码出问题了.”“美工妹纸让你看看界面怎么样.”身为程序员总要和各种人打交道,但是如果再分为前端.后端工程师的话,那么关系就会错综复 ...

  7. 纯js国际化(i18n)

    i18n,是internationalization单词的简写,中间18个字符略去,简称i18n,意图就是实现国际化,方便产品在不同的场景下使用 目标:可以点击切换语言或者ChangeLanguage ...

  8. 为什么对多线程编程这么怕?pthread,sem,mutex,process

    转自http://blog.chinaunix.net/uid-20788636-id-1841334.html 1.线程创建和退出创建线程实际上就是确定调用该线程函数的入口点,这里通常使用的函数是p ...

  9. .net学习笔记--设计模式

    设计模式都有哪些? 按照GOF提出,23种,按照目的分为:创建型(creational).结构性(structural).行为型(behavioral). 一.创建型: 1.Singleton 单例模 ...

  10. jira从windows迁移到linux

    说明:迁移的就是 jira安装路径/atlassian/jira/atlassian-jira/WEB-INF/classes/jira-application.properties文件中的jira_ ...