溫馨提示×

溫馨提示×

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

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

js中的作用域和閉包詳細介紹

發(fā)布時間:2021-09-08 18:27:37 來源:億速云 閱讀:138 作者:chen 欄目:開發(fā)技術

本篇內容主要講解“js中的作用域和閉包詳細介紹”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“js中的作用域和閉包詳細介紹”吧!

作用域

JS中有兩種作用域:全局作用域|局部作用域

栗子1

console.log(name);      //undefinedvar name = '波妞';var like = '宗介'console.log(name);      //波妞function fun(){    console.log(name);  //波妞    console.log(eat)    //ReferenceError: eat is not defined    (function(){        console.log(like)   //宗介        var eat = '肉'    })()}fun();

1. name定義在全局,在全局可以訪問到,所以 (2) 打印能夠正確打印;

2. 在函數(shù)fun中,如果沒有定義name屬性,那么會到它的父作用域去找,所以 (3) 也能正確打印。

3. 內部環(huán)境可以通過作用域鏈訪問所有外部環(huán)境,但外部環(huán)境不能訪問內部環(huán)境的任何變量和函數(shù)。類似單向透明,這就是作用域鏈,所以 (4) 不行而 (5) 可以。

那么問題來了,為什么第一個打印是"undefined",而不是"ReferenceError: name is not defined"。原理簡單的說就是JS的變量提升

變量提升:JS在解析代碼時,會將所有的聲明提前到所在作用域的最前面


栗子2

console.log(name);      //undefinedvar name = '波妞';console.log(name);      //波妞function fun(){    console.log(name)   //undefined    console.log(like)   //undefined    var name = '大西瓜';    var like = '宗介'}fun();

相當于

var name;console.log(name);      //undefinedname = '波妞';console.log(name);      //波妞function fun(){    var name;    var like;    console.log(name)   //undefined    console.log(like)   //undefined    name = '大西瓜';    like = '宗介'    console.log(name)   //大西瓜    console.log(like)   //宗介}fun();

注意:是提前到當前作用域的最前面


栗子3

printName();     //printName is not a functionvar printName = function(){    console.log('波妞')}printName();       //波妞

相當于

var printName;printName();     //printName is not a functionprintName = function(){    console.log('波妞')}printName();       //波妞

這樣一來就好理解了,函數(shù)表達式在聲明的時候還只是個變量


栗子4

{    var name = '波妞';}console.log(name)   //波妞(function(){    var name = '波妞';})()console.log(name)   //ReferenceError: name is not defined{    let name = '波妞';}console.log(name)   //ReferenceError: name is not defined

從上面的栗子可以看出,不可以草率的認為JS中var聲明的變量的作用范圍就是大括號的起止范圍,ES5并沒有塊級作用域,實質是函數(shù)作用域;ES6中有了let、const定義后,才有了塊級作用域。


栗子5

function p1() {     console.log(1);}function p2() {     console.log(2);}(function () {     if (false) {        function p1() {            console.log(3);        }    }else{        function p2(){            console.log(4)        }    }    p2();    p1()})();       //4//TypeError: print is not a function

這是一個非常經典的栗子,聲明提前了,但是因為判斷條件為否,所以沒有執(zhí)行函數(shù)體。所以會出現(xiàn)"TypeError: print is not a function"。while,switch,for同理

閉包

函數(shù)與對其狀態(tài)即詞法環(huán)境(lexical environment)的引用共同構成閉包(closure)。也就是說,閉包可以讓你從內部函數(shù)訪問外部函數(shù)作用域。在JavaScript中,函數(shù)在每次創(chuàng)建時生成閉包。

上面的定義來自MDN,簡單講,閉包就是指有權訪問另一個函數(shù)作用域中變量的函數(shù)。


● 閉包的關鍵在于:外部函數(shù)調用之后其變量對象本應該被銷毀,但閉包的存在使我們仍然可以訪問外部函數(shù)的變量對象.,

//舉個例子function makeFunc() {    var name = "波妞";    function displayName() {        console.log(name);    }    return displayName;}var myFunc = makeFunc();myFunc();

JavaScript中的函數(shù)會形成閉包。 閉包是由函數(shù)以及創(chuàng)建該函數(shù)的詞法環(huán)境組合而成。這個環(huán)境包含了這個閉包創(chuàng)建時所能訪問的所有局部變量

在例子中,myFunc 是執(zhí)行 makeFunc 時創(chuàng)建的 displayName 函數(shù)實例的引用,而 displayName 實例仍可訪問其詞法作用域中的變量,即可以訪問到 name 。由此,當 myFunc 被調用時,name 仍可被訪問,其值 '波妞' 就被傳遞到console.log中。創(chuàng)建閉包最常見方式,就是在一個函數(shù)內部創(chuàng)建另一個函數(shù)


● 通常,函數(shù)的作用域及其所有變量都會在函數(shù)執(zhí)行結束后被銷毀。但是,在創(chuàng)建了一個閉包以后,這個函數(shù)的作用域就會一直保存到閉包不存在為止

//例二function makeAdder(x) {  return function(y) {    return x + y;  };}var add5 = makeAdder(5);var add10 = makeAdder(10);console.log(add5(2));  // 7console.log(add10(2)); // 12//釋放對閉包的引用add5 = null;add10 = null;

從本質上講,makeAdder 是一個函數(shù)工廠 — 他創(chuàng)建了將指定的值和它的參數(shù)相加求和的函數(shù)。在上面的示例中,我們使用函數(shù)工廠創(chuàng)建了兩個新函數(shù) — 一個將其參數(shù)和 5 求和,另一個和 10 求和。

add5 和 add10 都是閉包。它們共享相同的函數(shù)定義,但是保存了不同的詞法環(huán)境。在 add5 的環(huán)境中,x 為 5。而在 add10 中,x 則為 10。

閉包的作用域鏈包含著它自己的作用域,以及包含它的函數(shù)的作用域和全局作用域。


● 閉包只能取得包含函數(shù)中的任何變量的最后一個值

//栗子1function arrFun1(){    var arr = [];    for(var i = 0 ; i < 10 ; i++){        arr[i] = function(){            return i        }    }    return arr}console.log(arrFun1()[9]());     //10console.log(arrFun1()[1]());     //10//栗子2function arrFun2(){    var arr = [];    for(var i = 0 ; i < 10 ; i++){        arr[i] = function(num){            return function(){                return num            };        }(i)    }    return arr}console.log(arrFun2()[9]());     //9console.log(arrFun2()[1]());     //1

栗子 1 中,arr數(shù)組中包含10個匿名函數(shù),每個函數(shù)都可以訪問外部的變量 i , arrFun1 執(zhí)行后,其作用域被銷毀,但它的變量依然存在內存中,能被循環(huán)中的匿名函數(shù)訪問,這是的 i 為 10;

栗子 2 中,arr數(shù)組中有是個匿名函數(shù),其匿名函數(shù)內還有匿名函數(shù),最內層匿名函數(shù)訪問的 num 被 上一級匿名函數(shù)保存在了內存中,所以可以訪問到每次的 i 的值。

到此,相信大家對“js中的作用域和閉包詳細介紹”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

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

js
AI