1. 程式人生 > >js 函數閉包=bug

js 函數閉包=bug

執行 理解 object nbsp onclick 代碼執行 存在 銷毀 ret

1.錯誤理解

閉包就是個bug,閉包其實是利用了一個變量退出作用域的時候,暫時沒有被銷毀,它的值還在,如果後面有變量也叫這個名字,那這個數據會被重新利用起來。你會發現,後面你使用的這個名字一樣的變量是有初值的。下面的例子可以看出問題來。

<p>局部變量計數。</p>
<button type="button" onclick="myFunction()">計數!</button>
<p id="demo">0</p>
<script>
{
    var tmp = 2; //理論上在退出語句塊後,這個變量要被釋放掉的。包括內存可能被回收。但事實並非如此,會影響後面和他同名的變量
}

var add = (function () {
    //var counter = 0; //這裏註釋掉.其實和上面的tmp一樣的道理。這裏在函數自己執行完後就應該銷毀了的。
    //return function () {return counter  += 1;} //這裏的counter已經不是上面的counter了,是一個全局變量。有初值,受上面影響,初值為0
    return function () {return tmp += 1;} //這裏tmp就是個全局變量。它是有初值的。為上面的2

})();
function myFunction(){
    document.getElementById("demo").innerHTML = add();//3
    document.getElementById("demo").innerHTML = add();//4
    document.getElementById("demo").innerHTML = add(); //5
}

</script>

執行上面的代碼可以看的很清楚。計數器一樣正常工作。

所以,閉包就是利用了個bug: 退出作用域的變量不會立即銷毀。會影響後面的同名變量。

但不知道後面的javascript版本會不會修改這種機制。

所以代碼這麽寫是不可靠的。太依賴於javascript的內部實現了。

其實要實現全局計數器的需求。正常的做法是:

定義一個全局對象。這個對象定義自己的屬性和方法add。

用的時候 對象.add 就可以了。這個才正常。

上面用閉包的方式太過巧妙,給人的感覺就是在夾縫中求生存。用起來也不舒服吧。

這個 bug 用著真不痛快,針對這個例子寫了點代碼:

局部變量計數。

<button type="button" onclick="myFunction()">計數!</button>
<p id="demo">0</p>
<script>
var add = new Object();
add.count = 0;
add.plus = function()
{
    this.count++;
}
function myFunction(){
    add.plus();
    document.getElementById("demo").innerHTML = add.count;
}
</script>

感覺可以用正常的手段解決這類問題了。

2.正解

{ var tmp = 2 } JS 中語句塊不能獨立作用域,這樣寫等同於聲明了一個全局變量 tmp = 2,所以不存在該語句塊內代碼執行完畢後 tmp 就被銷毀的情況。

變量 add,以及 add 的 count 屬性、plus() 方法都是公共的,這意味著其他代碼可以直接修改你的 count 屬性,造成不必要的麻煩

閉包要解決的問題是:一個函數可以擁有私有變量,並且外部可以通過閉包訪問該私有變量,如特權方法(類似 JavaBean 的寫法):

<script>
function Student(value) {
    var name = value;
    this.getName = function() { return name; };
    this.setName = function(value) {name = value; };
}
<script>

js 函數閉包=bug