91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在javascript中閉包是不是作用域

發布時間:2021-09-07 17:16:07 來源:億速云 閱讀:143 作者:chen 欄目:web開發

本篇內容介紹了“在javascript中閉包是不是作用域”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!


本教程操作環境:windows7系統、javascript1.8.5版、Dell G3電腦。

我們知道,作用域鏈查找標識符的順序是從當前作用域開始一級一級往上查找。因此,通過作用域鏈,JavaScript 函數內部可以讀取函數外部的變量,但反過來,函數的外部通常則無法讀取函數內部的變量。在實際應用中,有時需要在函數外部訪問函數的局部變量,此時最常用的方法就是使用閉包。

閉包是 JavaScript 的重要特性之一,在函數式編程中有著重要的作用,本節介紹閉包的結構和基本用法。

那么什么是閉包?

閉包是一個能夠持續存在的函數上下文活動對象,是同時含有對函數對象以及作用域對象引用的對象。閉包主要是用來獲取作用域鏈或原型鏈上的變量或值。創建閉包最常用的方式是在一個函數中聲明內部函數(也稱嵌套函數),并返回內部函數。

此時在函數外部就可以通過調用函數得到內部函數,進而調用內部函數來實現對函數局部變量的訪問。此時的內部函數就是一個閉包。雖然按照閉包的概念,所有訪問了外部變量的 JavaScript 函數都是閉包,但我們平常絕大部分時候所謂的閉包其實指的就是內部函數閉包。

閉包可以將一些數據封裝為私有屬性以確保這些變量的安全訪問,這個功能給應用帶來了極大的好處。需要注意的是,閉包如果使用不當,也會帶來一些意想不到的問題。下面就通過幾個示例來演示一下閉包的創建、使用和可能存在的問題及其解決方法。

形成原理

函數被調用時,會產生一個臨時上下文活動對象。它是函數作用域的頂級對象,作用域內所有私有方法有變量、參數、私有函數等都將作為上下文活動對象的屬性而存在。

函數被調用后,在默認情況下上下文活動對象會被立即釋放,避免占用系統資源。但是,若函數內的私有變量、參數、私有函數等被外界引用,則這個上下文活動對象暫時會繼續存在,直到所有外界引用被注銷。

但是,函數作用域是封閉的,外界無法訪問。那么在什么情況下,外界可以訪問到函數內的私有成員呢?

根據作用域鏈,內部函數可以訪問外部函數的私有成員。如果內部函數引用了外部函數的私有成員,同時內部函數又被傳給外界,或者對外界開放,那么閉包體就形成了。這個外部函數就是一個閉包體,它被調用后,活動對象暫時不會被注銷,其屬性會繼續存在,通過內部函數可以持續讀寫外部函數的私有成員。

閉包結構

典型的閉包體是一個嵌套結構的函數。內部函數引用外部函數的私有成員,同時內部函數又被外界引用,當外部函數被調用后,就形成了閉包。這個函數也稱為閉包函數。

下面是一個典型的閉包結構。

function f(x) {  //外部函數
    return function (y) {  //內部函數,通過返回內部函數,實現外部引用
        return x + y;  //訪問外部函數的參數
    };
}
var c = f(5);  //調用外部函數,獲取引用內部函數
console.log(c(6));  //調用內部函數,原外部函數的參數繼續存在

解析過程簡單描述如下:

  • 在 JavaScript 腳本預編譯期,聲明的函數 f 和變量 c,先被詞法預解析。

  • 在 JavaScript 執行期,調用函數 f,并傳入值 5。

  • 在解析函數 f 時,將創建執行環境(函數作用域)和活動對象,并把參數和私有變量、內部函數都映射為活動對象的屬性。

  • 參數 x 的值為 5,映射到活動對象的 x 屬性。

  • 內部函數通過作用域鏈引用了參數 x,但是還沒有被執行。

  • 外部函數被調用后,返回內部函數,導致內部函數被外界變量 c 引用。

  • JavaScript 解析器檢測到外部函數的活動對象的屬性被外界引用,無法注銷該活動對象,于是在內存中繼續維持該對象的存在。

  • 當調用 c,即調用內部函數時,可以看到外部函數的參數 x 存儲的值繼續存在。這樣就可以實現后續運算操作,返回 x+y=5=6=11。

如下結構形式也可以形成閉包:通過全局變量引用內部函數,實現內部函數對外開放。

var c;  //聲明全局變量
function f(x) {  //外部函數
    c = function (y) {  //內部函數,通過向全局變量開放實現外部引用
        return x + y;  //訪問外部函數的參數
    };
}
f(5);  //調用外部函數
console.log(c(6));  //使用全局變量c調用內部函數,返回11

閉包變體

除了嵌套函數外,如果外部引用函數內部的私有數組或對象,也容易形成閉包。

var add;  //全局變量
function f() {  //外部函數
    var a = [1,2,3];  //私有變量,引用型數組
    add = function (x) {  //測試函數,對外開放
        a[0] = x * x;  //修改私有數組的元素值
    }
    return a;  //返回私有數組的引用
}
var c = f();
console.log(c[0]);  //讀取閉包內數組,返回1
add(5);  //測試修改數組
console.log(c[0]);  //讀取閉包內數組,返回25
add(10);  //測試修改數組
console.log(c[0]);  //讀取閉包內數組,返回100

與函數相同,對象和數組也是引用型數據。調用函數 f,返回私有數組 a 的引用,即傳值給局部變量 c,而 a 是函數 f 的私有變量,當被調用后,活動對象繼續存在,這樣就形成了閉包。

這種特殊形式的閉包沒有實際應用價值,因為其功能單一,只能作為一個靜態的、單向的閉包。而閉包函數可以設計各種復雜的運算表達式,它是函數式變成的基礎。

反之,如果返回的是一個簡單的值,就無法形成閉包,值傳遞是直接復制。外部變量 c 得到的僅是一個值,而不是對函數內部變量的引用。這樣當函數調用后,將直接注銷對象。

function f(x) {  //外部函數
    var a = 1;  //私有變量
    return a;
}
var c = f(5);
console.log(c);  //僅是一個值,返回1

使用閉包

下面結合示例介紹閉包的簡單使用,以加深對閉包的理解。

示例1

使用閉包實現優雅的打包,定義存儲器。

var f = function () {  //外部函數
    var a = [];  //私有數組初始化
    return function (x) {  //返回內部函數
        a.push(x);  //添加元素
        return a;  //返回私有數組
    };
} ()  //直接調用函數,生成執行環境
var a = f(1);  //添加值
console.log(a);  //返回1
var b = f(2);  //添加值
console.log(b);  //返回1,2

在上面示例中,通過外部函數設計一個閉包,定義一個永久的存儲器。當調用外部函數生成執行環境之后,就可以利用返回的匿名函數不斷地的向閉包體內的數組 a 傳入新值,傳入的值會持續存在。

示例2

在網頁中事件處理函數很容易形成閉包。

<script>
function f() {
    var a = 1;
    b = function () {
        console.log("a =" + a);
    }
    c = function () {
        a ++;
    }
    d = function () {
        a --;
    }
}
</script>
<button onclick="f()">生成閉包</button>
<button onclick="b()">查看 a 的值</button>
<button onclick="c()">遞增</button>
<button onclick="d()">遞減</button>

在瀏覽器中瀏覽時,首先點擊“生成閉包”按鈕,生成一個閉包;點擊“查看 a 的值”按鈕,可以隨時查看閉包內私有變量 a 的值;點擊“遞增”“遞減”按鈕時,可以動態修改閉包內變量 a 的值,效果如圖所示。

在javascript中閉包是不是作用域

閉包的局限性

閉包的價值是方便在表達式運算過程中存儲數據。但是,它的缺點也不容忽視。

  • 由于函數調用后,無法注銷調用對象,會占用系統資源,在腳本中大量使用閉包,容易導致內存泄漏。解決方法:慎用閉包,不要濫用。

  • 由于閉包的作用,其保存的值是動態,如果處理不當容易出現異常或錯誤。

示例

設計一個簡單的選項卡效果。HTML 結構如下:

<div class="tab_wrap">
    <ul class="tab" id="tab">
        <li id="tab_1" class="hover">Tab1</li>
        <li id="tab_2" class="normal">Tab2</li>
        <li id="tab_3" class="normal">Tab3</li>
    </ul>
    <div class="content" id="content">
        <div id="content_1" class="show"><img scr="image/1.jpg" height="200" /></div>
        <div id="content_2" class="show"><img scr="image/2.jpg" height="200" /></div>
        <div id="content_3" class="show"><img scr="image/3.jpg" height="200" /></div>
    </div>
</div>

下面請看 JavaScript 腳本。

window.onload = function () {
    var tab = document.getElementById("tab").getElementsByTagName("li"),
        content = document.getElementById("content").getElementByTagName("div");
    for (var i = 0; i < tab.length;i ++) {
        tab[i].addEventListener("mouseover"), function () {
            for (var n = 0; n < tab.length; n ++) {
                tab[n].className = "normal";
                content[n].className = "none";
            }
            tab[i].className = "hover";
            content[i].className = "show";
        });
    }
}

在 load 事件處理函數中,使用 for 語句為每個 li 屬性元素綁定 mouseover 事件;在 mouseover 事件處理函數中重置所有選項卡 li 的類樣式,然后設置當前 li 選項卡高亮顯示,同時顯示對應的內容容器。

但是在瀏覽器中預覽時,會發現瀏覽器拋出異常。

SCRIPT5007:無法設置未定義或 null 引用的屬性"className"

在 mouseover 事件處理函數中跟蹤變量 i 的值,i 的值都變為了 3,tab[3] 自然是一個 null,所以也不能夠讀取 className 屬性。

【原因分析】

上面 JavaScript 代碼是一個典型的嵌套函數結構。外部函數為 load 事件處理函數,內部函數為 mouseover 事件處理函數,變量 i 為外部函數的私有變量。

通過事件綁定,mouseover 事件處理函數被外界引用(li 元素),這樣就形成了一個閉包體。雖然在 for 語句中為每個選項卡 li 分別綁定事件處理函數,但是這個操作是動態的,因此 tab[i] 中 i 的值也是動態的,所以就出現了上述異常。

【解決方法】

解決閉包的缺陷,最簡單的方法是阻斷內部函數對外部函數的變量引用,這樣就形成了閉包體。針對本示例,我們可以在內部函數(mouseover 事件處理函數)外邊增加一層防火墻,不讓其直接引用外部變量。

window.load = function () {
    var tab = document.getElementById("tab").getElementsByTagName("li"),
        content = document.getElementById("content").getElementsByTagName("div");
    for (var i = 0; i < tab.length; i ++ ) {
        (function (j) {
            tab[j].addEventListener("number", function () {
                for (var n = 0; n < tab.length; n ++) {
                    tab[n].className = "normal";
                    content[n].className = "none";
                }
                tab[j].className = "hover";
                conteng[j].className = "show";
            });
        }) (i);
    }
}

在 for 語句中,直接調用匿名函數,把外部函數的 i 變量傳給調用函數,在調用函數中接收這個值,而不是引用外部變量 i,規避了閉包體帶來的困惑。

“在javascript中閉包是不是作用域”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

锦州市| 镇原县| 阿巴嘎旗| 漯河市| 文山县| 遵义县| 东阿县| 临沂市| 阿拉善右旗| 万荣县| 习水县| 沧州市| 郴州市| 永胜县| 平谷区| 石首市| 特克斯县| 上饶县| 尉氏县| 高台县| 庆元县| 商城县| 儋州市| 桂平市| 林芝县| 乌苏市| 重庆市| 水富县| 济宁市| 荆门市| 故城县| 静宁县| 许昌市| 门头沟区| 伽师县| 汉寿县| 昆山市| 南岸区| 辽宁省| 石屏县| 青阳县|