【3分鐘帶你學】JS閉包

【3分鐘帶你學】JS閉包

一、什麼是閉包?


var n=99;
function f1(){
    console.log(n);
}
f1();    //99

​ JavaScript中有兩種作用域,全局作用域和局部作用域,函數內部可以直接讀取全局變量。函數 f1 可以讀取全局變量 n。但是,在函數外部無法讀取函數內部聲明的變量。

function f1() {
    var n = 99;
} 
    f1( );
console.log(n);    //undefined

​ 但是,有時我們卻需要在函數外部訪問函數內部的變量,正常情況下,這是辦不到的,只有通過變通方法才能實現。那就是在函數的內部, 再定義一個函數。

function f1() {
    var n = 99;
    var f2 = function() {
        console.log(n);
    }
    return f2;
} 
var f = f1();
f();

​ 上面代碼中, 函數f2就在函數f1內部, 這時f1內部的所有局部變量,對f2都是可見的。但是反過來就不行, f2內部的局部變量,對f1就是不可見的。

​ 這就是JavaScript語言特有的“鏈式作用域”結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。既然f2可以讀取f1的局部變量, 那麼只要把f2作爲返回值,我們不就可以在f1外部讀取它的內部變量了嗎!

​ 閉包就是函數f2, 即能夠讀取其他函數內部變量的函數。

​ 由於在JavaScript語言中, 只有函數內部的子函數才能讀取內部變量, 因此可以把閉包簡單理解成“定義在一個函數內部的函數”。閉包最大的特點, 就是它可以“記住”誕生的環境, 比如f2記住了它誕生的環境f1, 所以從f2可以得到f1的內部變量。

在本質上, 閉包就是將函數內部和函數外部連接起來的一座橋樑;

二、垃圾回收機制(GC)及閉包


先看一個demo

function f1( ) {
    var n = 99;
    console.log(++n);
}
     f1( ); //100
     f1( ); //100

這裏寫圖片描述

​ 當我們在函數內部引入一個變量或函數時,系統都會開闢一塊內存空間;

​ 還會將這塊內存的引用計數器進行初始化,初始化值爲0;

​ 如果外部有全局變量或程序引用了這塊空間,則引用計數器會自動進行+1操作;

​ 當函數執行完畢後,變量計數器重新歸零,系統會運行垃圾回收機制,將函數運行產生的數據銷燬;

​ 如計數器不是 0 ,則不會清除數據;

​ 這個過程就稱之爲 “JS的垃圾回收機制” ;

但是,如果將代碼改爲閉包形式:

function f1() {
    var n = 99;
    function f2(){
    console.log(++n);
    }
 return f2;
}
var f = f1();
f(); //100
f(); //101

​ 運行代碼發現,函數調用一次,其變量 n 變化一次;因函數f1被調用時,返回的結果是f2函數體,也就是說,f2函數被當作值返回給f1的調用者,但是f2函數並沒有在此時被調用執行,所以整個 f1 函數體,無法判斷子函數f2會對其產生何種影響,無法判斷變量n是否會被使用;即使f1函數被調用結束,整個f1函數始終保留在內存中不會被垃圾回收機制回收;

三、閉包的作用


閉包的最大用處有2個,

  1. 一個是可以讀取函數內部的變量

  2. 另一個就是讓這些變量始終保持在內存中.

    此外,還保護變量的安全,因爲函數內的變量只能通過函數內部訪問,其他途徑不能訪問即閉包可以使得它誕生環境一直存在;

    注意外層函數每次運行,都會生成一個新的閉包,而這個閉包又會保留外層函數的內部變量,所以內存消耗很大。因此不能濫用閉包, 否則會造成網頁的性能問題。

下附一個實際應用案例 tab切換

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
* {
    padding-bottom: 0px;
    margin: 0px;
    padding-left: 0px;
    padding-right: 0px;
    font-size: 12px;
    padding-top: 0px;
}
BODY {
    padding-left: 20px;
    padding-top: 20px;
}
.wid240 {
    width: 242px;
    margin-bottom: 20px;
}
.wid180 {
    width: 182px;
}
.tab {
    border-bottom: #000 1px solid;
    border-left: #000 1px solid;
    border-top: #000 1px solid;
    border-right: #000 1px solid;
}
.tab UL {
    zoom: 1;
    clear: both;
}
.tab UL:after {
    display: block;
    height: 0px;
    visibility: hidden;
    clear: both;
    content: "";
}
.tab UL LI {
    text-align: center;
    line-height: 26px;
    width: 60px;
    display: inline;
    background: #000;
    float: left;
    height: 26px;
    color: #fff;
}
.tab UL LI.on {
    background: #fff;
    color: #000;
}
.tabList {
    border-bottom: #000 1px solid;
    border-left: #000 1px solid;
    height: 150px;
    border-top: #000 1px;
    border-right: #000 1px solid;
}
.tabList .one {
    padding-bottom: 10px;
    padding-left: 10px;
    padding-right: 10px;
    display: none;
    color: #ff0000;
    padding-top: 10px;
}
.tabList .block {
    display: block;
}
</style>
<meta name="GENERATOR" content="MSHTML 8.00.7600.16535">
</head>
<body>
<div class="wid240">
  <div class="tab">
    <ul>
      <li id="one1" class="on">one1 </li>
      <li id="one2">one2 </li>
      <li id="one3">one3 </li>
      <li id="one4">one4 </li>
    </ul>
  </div>
  <div class="tabList">
    <div id="cont_one_1" class="one block"> cont_one_1</div>
    <div id="cont_one_2" class="one"> cont_one_2</div>
    <div id="cont_one_3" class="one"> cont_one_3</div>
    <div id="cont_one_4" class="one"> cont_one_4</div>
  </div>
</div>
</div>
</body>
<script type="text/javascript">
        //獲取li標籤
        var lis = document.getElementsByTagName('li');
        //綁定懸浮事件
        for(var i=0;i<lis.length;i++) {
            lis[i].onmouseover = (function(index){
                return function() {
                    //切換div.one
                    var num = parseInt(index+1);
                    var div = document.getElementById('cont_one_'+num);
                    //判斷
                    var divs = document.querySelectorAll('.one');
                    for(var i=0;i<divs.length;i++) {
                        divs[i].style.display = 'none';
                    }
                    div.style.display = 'block';
                }
            })(i);
        }
</script>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章