<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
function onMyLoad(){
var arr = document.getElementsByTagName("p");
for(var i = 0; i < arr.length; i++){
arr[i].onclick = function(){
alert(i);//5次均輸出5
}
}
}
</script>
</head>
<body "onMyLoad()">
<p>0</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
</body>
</html>
以上代碼指望依次輸出0,1,2,3,4,實際卻輸出5,5,5,5,5,這是由於閉包致使,下來來具體介紹:
for循環是一個外部閉包,依次綁定的點擊事件是一個函數實例,也產生了一個閉包域,它引用了外部閉包的變量i,外部閉包域中i的最終值爲5,點擊事件觸發時引用外部閉包變量i(此時i=5),因此輸出的值全爲5。javascript
解決方案
方法一:增長若干個對應的閉包域空間(採用匿名函數實現)專門用來存儲原先須要引用的內容(下標值),只限於基本類型(基本類型值傳遞,對象類型引用傳遞)html
for(var i = 0; i<arr.length; i++){
(function (arg){//這個函數對象有一個本地私有變量arg(形參),該函數的function scope的closure對象屬性有兩個引用:arr和i。i的值隨外部改變,可是本地的私有變量(形參)arg不會受影響,其值在一開始被調用時就決定了
arr[i].onclick = function () {//onclick函數實例的function scope的closure對象屬性有一個引用arg
alert(arg);//只要外部空間的arg不變,這裏的引用值就不會改變
}
})(i);//當即執行匿名函數,傳遞下標i(實參)
}
方法二:將下標做爲對象屬性(name:「i」,value:i的值)添加到每一個數組項(p對象)中java
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);
}
方法四:使用ES6的let關鍵字閉包
"use strict";
var arr = document.getElementsByTagName("p");
for(var i = 0; i<arr.length; i++){
let j = i;//塊級變量
arr[i].onclick = function () {
alert(j);
}
}
---------------------
原文:https://blog.csdn.net/sinat_40172076/article/details/87161221 函數