欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

你真的了解JavaScript的作用域與閉包嗎

 更新時(shí)間:2022年03月30日 15:27:47   作者:~小猿  
這篇文章主要為大家詳細(xì)介紹了JavaScript作用域與閉包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

一、作用域

1.作用域總體來說就是根據(jù)名稱查找變量的一套規(guī)則。JS查找變量的方式有兩種:LHS和RHS。

LHS(left hand side)大致可以理解為給某個(gè)變量賦值,在賦值符(=)的左邊;RHS(right hand side)是指找到某個(gè)變量的源值,如在賦值符(=)的右邊。

舉個(gè)例子說明,對于 a = 10; a = b; 這兩個(gè)語句。

var a 這里就是LHS查詢,查找當(dāng)前作用域是否有a這個(gè)變量來給a進(jìn)行賦值,如果有則進(jìn)行賦值;如果沒有,在非嚴(yán)格模式下JS引擎會(huì)自動(dòng)創(chuàng)建一個(gè)變量a來進(jìn)行賦值;在嚴(yán)格模式下JS引擎會(huì)拋出ReferenceError錯(cuò)誤。

a = b;這個(gè)語句,對于b這個(gè)變量是進(jìn)行RHS查詢,查找b這個(gè)變量的值。如果成功查詢,則進(jìn)行賦值操作;如果查詢失敗,則拋出ReferenceError錯(cuò)誤。如果查詢成功,但是對這個(gè)變量的操作有問題,就會(huì)拋出TypeError錯(cuò)誤。如b只是一個(gè)普通的變量,卻當(dāng)成函數(shù)使用(b())。

2.JS是基于詞法作用域的。指在你寫代碼時(shí)將變量和函數(shù)寫在哪里決定的,而不是調(diào)用順序決定的。

舉個(gè)例子

var num = 1;
function a() {
    console.log(num); // 1
}
function b() {
    var num = 2;
    a();
}
b();

當(dāng)按照順序執(zhí)行b()時(shí),在b函數(shù)作用域中定義了一個(gè)值為2的num變量,然后在b函數(shù)中調(diào)用a函數(shù),來到a函數(shù)的作用域進(jìn)行調(diào)用(而不是在b函數(shù)作用域),找到全局變量num = 1。

3.不同作用域間是互相不會(huì)影響的,重復(fù)定義變量是不會(huì)發(fā)生錯(cuò)誤的。

舉個(gè)例子

var i = 1;
var j = 2;
function a(){
    var i = 3;
    var j = 4;
    console.log(i,j); // 3,4
}
function b(){
    var i = 5;
    var j = 6;
    console.log(i,j); // 5,6
}
console.log(i,j);// 1,2
a();
b();

4.for循環(huán)中,var聲明的變量在全局作用域能夠訪問,并且值是退出循環(huán)的那個(gè)值;let聲明的變量會(huì)產(chǎn)生塊作用域,它將值重新綁定到循環(huán)迭代中。

for(var i = 0;i<5;i++){
    console.log(i); // 0 1 2 3 4
}
console.log(i); // 5
 
for(let i = 0;i<5;i++){
    console.log(i); // 0 1 2 3 4
}
console.log(i); // ReferenceError

5.每個(gè)作用域都會(huì)發(fā)生提升操作;函數(shù)聲明會(huì)發(fā)生提升,但是函數(shù)表達(dá)式不會(huì)被提升;

b() // TypeErrorn() // ReferenceError,因?yàn)椴粫?huì)變量提升// 函數(shù)聲明function a(){    console.log(num); // undefined    var num = 1;}// 函數(shù)表達(dá)式var b = function n(){    console.log('b');}

6.函數(shù)聲明首先被提升,變量聲明在后;

函數(shù)首先被提升可以認(rèn)為是函數(shù)的優(yōu)先級高于變量,重復(fù)聲明的變量會(huì)被函數(shù)替代。但是其他函數(shù)聲明可以覆蓋之前的函數(shù)聲明。

b() // TypeError
n() // ReferenceError,因?yàn)椴粫?huì)變量提升
// 函數(shù)聲明
function a(){
    console.log(num); // undefined
    var num = 1;
}
// 函數(shù)表達(dá)式
var b = function n(){
    console.log('b');
}

二、閉包

1.閉包是基于詞法作用域書寫代碼時(shí)的必然結(jié)果。

不論函數(shù)在哪里執(zhí)行,函數(shù)都會(huì)在代碼書寫的位置(即函數(shù)本身在哪)來進(jìn)行作用域鏈的查找。當(dāng)函數(shù)進(jìn)行作用域查找時(shí),就形成了閉包。

也可以說是當(dāng)函數(shù)被當(dāng)作值進(jìn)行傳遞的時(shí)候(如參數(shù)傳遞、返回值等),就會(huì)形成閉包。

舉個(gè)例子

function foo() {
    var a = 2;
    function bar() {
        console.log(a);
    }
    return bar;
}
var a = 3;
var baz = foo();
baz(); // 2 

當(dāng)foo函數(shù)執(zhí)行時(shí),會(huì)返回bar函數(shù),當(dāng)bar函數(shù)在全局作用域執(zhí)行時(shí),會(huì)沿著bar函數(shù)定義的地方開始進(jìn)行RHS查找a變量的值。這時(shí)就產(chǎn)生了閉包。閉包的產(chǎn)生準(zhǔn)確來說應(yīng)該是函數(shù)在函數(shù)本身所處作用域之外執(zhí)行,對于IIFE(立即執(zhí)行函數(shù))來說雖然創(chuàng)建了閉包,但是卻沒有真正使用閉包,因?yàn)榱⒓磮?zhí)行函數(shù)的執(zhí)行作用域是函數(shù)定義的作用域,雖然是閉包,但是沒有使用閉包的功能。

閉包的功能:函數(shù)在所處作用域之外執(zhí)行時(shí),會(huì)沿著函數(shù)定義時(shí)的作用域鏈進(jìn)行查找。

var a = 1;
// 立即執(zhí)行函數(shù)
(
    function(){
        console.log(a);
     }
)();

2.循環(huán)與閉包

觀察下面的例子

for(var i = 0;i<3;i++){
    setTimeout(()=>{
        console.log(i);
    },i*500)
}

輸出結(jié)果為 3 3 3,并不是想象中的 0 1 2。因?yàn)樵趂or循環(huán)結(jié)束時(shí),setTimeout開始執(zhí)行,在執(zhí)行的時(shí)候進(jìn)行變量 i 的RHS查詢,但是變量 i 只能在全局作用域中查到,這是變量 i 的值是退出循環(huán)的值 i = 3;

可能與你的想象結(jié)果有些許不同,如果要輸出 0 1 2 該怎么做?

(1).使用IIFE形成閉包是否可行?

for (var i = 0; i < 3; i++) {
    (function () {
        setTimeout(() => {
            console.log(i);
        }, i*500)
    })()
}

結(jié)果依然是 3 3 3。因?yàn)殡m然使用IIFE形成了閉包,但是在閉包中并沒有 i 的值,還會(huì)沿著作用域鏈查找到全局 i = 3;

(2).在IIFE使用一個(gè)變量臨時(shí)保存 i 值或傳參

for (var i = 0; i < 3; i++) {
    (function () {
        var j = i;
        setTimeout(() => {
            console.log(j);
        }, i*500)
    })()
}
 
// 或
for (var i = 0; i < 3; i++) {
    (function (i) {
        setTimeout(() => {
            console.log(i);
        }, i*500)
    })(i)
}

結(jié)果為 0 1 2,第一個(gè)for循環(huán)閉包中有變量 j ,第二個(gè)for循環(huán)有形參 i ,都不會(huì)到達(dá)全局作用域。

(3).使用 let 生成塊作用域

for(let i = 0;i<3;i++){
    setTimeout(()=>{
        console.log(i);
    },i*500)
}

結(jié)果為 0 1 2,let聲明的變量會(huì)包含在塊作用域中,并且作為循環(huán)變量時(shí),每一次的迭代都會(huì)聲明一次變量并將變量結(jié)果保存,當(dāng)訪問變量時(shí),由于閉包的作用,會(huì)得到每次迭代的值。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

最新評論