深入理解javascript作用域,作用域鏈,閉包的面試題

一、概要
作用域和作用域鏈是js中非常重要的特性,關系到理解整個js體系,閉包是對作用域的延伸,其他語言也有閉包的特性。
那什么是作用域?作用域指的是一個變量和函數(shù)的作用范圍。
1、js中函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)始終是可見的;
2、在ES6中有全局作用域和局部作用域,但是沒有沒有塊級作用域(catch只在其內(nèi)部生效);
3、局部變量的優(yōu)先級高于全局變量。
文章首發(fā):https://www.mwcxs.top/page/574.html
二、作用域
我們來舉幾個栗子:
2.1變量提升
var scope="global"; function scopeTest(){ console.log(scope); var scope="local" } scopeTest(); //undefined
上面的代碼輸出是undefined
,這是因為局部變量scope
變量提升了,等效于下面
var scope="global"; function scopeTest(){ var scope; console.log(scope); scope="local" } scopeTest(); //undefined
注意,如果在局部作用域中忘記var,那么變量就被聲明為全局變量。
var scope="global"; function scopeTest(){ console.log(scope); scope="local" } scopeTest(); //global var scope="global"; function scopeTest(){ scope="local" console.log(scope); } scopeTest(); //local
2.2沒有塊級作用域
和我們其他常用語言不同的是,js中沒有塊級作用域
var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); // 3 data[1](); // 3 data[2](); // 3
2.3作用域鏈
每個函數(shù)都有自己的執(zhí)行上下文環(huán)境,當代碼在這個環(huán)境中執(zhí)行時候,會創(chuàng)建變量對象的作用域鏈,
那什么是作用域鏈?作用域鏈式是一個對象列表。
作用域鏈的作用?他保證了變量對象的有序訪問。
作用域鏈開始的地方:當前代碼執(zhí)行環(huán)境的變量對象,常被稱之為“活躍對象”(AO),變量的查找會從第一個鏈的對象開始,如果對象中包含變量屬性,那么就停止查找,如果沒有就會繼續(xù)向上級作用域查找,直到找到全局對象中,如果找不到就會報ReferenceError。
2.4閉包
function createClosure(){ var name = "jack"; return { setStr:function(){ name = "rose"; }, getStr:function(){ return name + ":hello"; } } } var builder = new createClosure(); builder.setStr(); console.log(builder.getStr()); //rose:hello
上面在函數(shù)中反悔了兩個閉包,這兩個閉包都維持著對外部作用域的引用,因此不管在哪調(diào)用都是能夠訪問外部函數(shù)中的變量。在一個函數(shù)內(nèi)部定義的函數(shù),閉包中會將外部函數(shù)的自由對象添加到自己的作用域中,所以可以通過內(nèi)部函數(shù)訪問外部函數(shù)的屬性,這就是js模擬私有變量的一種方式。
注意:由于閉包會額外的附帶函數(shù)的作用域(內(nèi)部匿名函數(shù)攜帶外部函數(shù)的作用域),因此,閉包會比其他函數(shù)多占用些內(nèi)存空間,過度使用會導致內(nèi)存占用增加。
三、閉包面試題解
由于作用域鏈機制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個值,這引起了一個副作用,如果內(nèi)部函數(shù)在一個循環(huán)中,那么變量的值始終為最后一個值。
var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); // 3 data[1](); // 3 data[2](); // 3
如果想強制返回逾期結(jié)果,怎么整?
方法一:立即執(zhí)行函數(shù)
for (var i = 0; i < 3; i++) { (function(num) { setTimeout(function() { console.log(num); }, 1000); })(i); } // 0 // 1 // 2
方法二:返回一個匿名函數(shù)賦值
var data = []; for (var i = 0; i < 3; i++) { data[i] = (function (num) { return function(){ console.log(num); } })(i); } data[0](); // 0 data[1](); // 1 data[2](); // 2
無論上是立即執(zhí)行函數(shù)還是返回一個匿名函數(shù)賦值,原理上都是因為變量的按值傳遞,所以會將變量i的值賦值給實參num,在匿名函數(shù)的內(nèi)部又創(chuàng)建了一個用于訪問num的匿名函數(shù),這樣每一個函數(shù)都有一個num的副本,互不影響。
方法三:使用es6的let
var data = []; for (let i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();
解釋一下原理:
var data = [];// 創(chuàng)建一個數(shù)組data; // 進入第一次循環(huán) { let i = 0; // 注意:因為使用let使得for循環(huán)為塊級作用域 // 此次 let i = 0 在這個塊級作用域中,而不是在全局環(huán)境中 data[0] = function() { console.log(i); }; }
循環(huán)時,let聲明了i,所以整個塊是塊級作用域,那么data[0]這個函數(shù)就成了一個閉包,這里用{}表述,只是希望通過它來說明let存在的時候,這個for循環(huán)塊是塊級作用域,而不是全局作用域。
上面的塊級作用域,就像函數(shù)作用域一樣,寒暑表執(zhí)行完畢,其中的變量會被銷毀,但是因為這個代碼塊中存在一個閉包,閉包的作用域鏈中引用著塊級作用域,所以在閉包被調(diào)用之前,這個塊級作用域內(nèi)部的變量不會被銷毀。
// 進入第二次循環(huán) { let i = 1; // 因為 let i = 1 和上面的 let i = 0 // 在不同的作用域中,所以不會相互影響 data[1] = function(){ console.log(i); }; }
當執(zhí)行data[1]()
時,進入下面的執(zhí)行環(huán)境。
{ let i = 1; data[1] = function(){ console.log(i); }; }
在上面這個執(zhí)行環(huán)境中,它會首先尋找該執(zhí)行環(huán)境中是否存在i,沒有找到,就沿著作用域鏈繼續(xù)向上找,在其所在的塊級作用域執(zhí)行環(huán)境中,找到i=1,于是輸出1。
四、思考題
代碼1:
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()(); //local scope
代碼2:
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); foo(); //local scope
四、參考
1、https://segmentfault.com/a/1190000000618597
2、https://www.cnblogs.com/zhuzhenwei918/p/6131345.html
總結(jié)
以上就是深入理解javascript作用域,作用域鏈,閉包的面試題的詳細內(nèi)容,更多關于js 作用域,作用域鏈,閉包的面試題請關注腳本之家其它相關文章!
相關文章
- 這篇文章主要介紹了新手怎么學JS?JavaScript基礎語法入門要學什么?本文給大家介紹一個大致的學習路線和方向,需要的朋友趕緊一起來看看吧2020-03-19
- 這篇文章主要介紹了2020年12道高頻JavaScript手寫面試題及答案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編2020-01-06
- 這篇文章主要介紹了JavaScript關于數(shù)組的四道面試題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習2019-12-23
- 這篇文章主要介紹了11道JS選擇題(聽說第一題就難倒80%的人),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-12-18
- JS 初學者總是對this關鍵字感到困惑,因為與其他現(xiàn)代編程語言相比,JS 中的這this關鍵字有點棘手。今天小編給大家?guī)?0個比較流行的JavaScript面試題 ,感興趣的朋友一起2019-07-12
- 這篇文章主要介紹了10個JavaScript筆試題解析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2020-06-02