摘要:一概要作用域和作用域鏈?zhǔn)侵蟹浅V匾奶匦裕P(guān)系到理解整個(gè)體系,閉包是對作用域的延伸,其他語言也有閉包的特性。作用域鏈的作用他保證了變量對象的有序訪問。
一、概要
作用域和作用域鏈?zhǔn)莏s中非常重要的特性,關(guān)系到理解整個(gè)js體系,閉包是對作用域的延伸,其他語言也有閉包的特性。
那什么是作用域?作用域指的是一個(gè)變量和函數(shù)的作用范圍。
1、js中函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)始終是可見的;
2、在ES6中有全局作用域和局部作用域,但是沒有沒有塊級作用域(catch只在其內(nèi)部生效);
3、局部變量的優(yōu)先級高于全局變量。
二、作用域我們來舉幾個(gè)栗子:
2.1變量提升var scope="global"; function scopeTest(){ console.log(scope); var scope="local" } scopeTest(); //undefined
上面的代碼輸出是undefined,這是因?yàn)榫植孔兞縮cope變量提升了,等效于下面
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(); //local2.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](); // 32.3作用域鏈
每個(gè)函數(shù)都有自己的執(zhí)行上下文環(huán)境,當(dāng)代碼在這個(gè)環(huán)境中執(zhí)行時(shí)候,會(huì)創(chuàng)建變量對象的作用域鏈,
那什么是作用域鏈?作用域鏈?zhǔn)绞且粋€(gè)對象列表。
作用域鏈的作用?他保證了變量對象的有序訪問。
作用域鏈開始的地方:當(dāng)前代碼執(zhí)行環(huán)境的變量對象,常被稱之為“活躍對象”(AO),變量的查找會(huì)從第一個(gè)鏈的對象開始,如果對象中包含變量屬性,那么就停止查找,如果沒有就會(huì)繼續(xù)向上級作用域查找,直到找到全局對象中,如果找不到就會(huì)報(bào)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ù)中反悔了兩個(gè)閉包,這兩個(gè)閉包都維持著對外部作用域的引用,因此不管在哪調(diào)用都是能夠訪問外部函數(shù)中的變量。在一個(gè)函數(shù)內(nèi)部定義的函數(shù),閉包中會(huì)將外部函數(shù)的自由對象添加到自己的作用域中,所以可以通過內(nèi)部函數(shù)訪問外部函數(shù)的屬性,這就是js模擬私有變量的一種方式。
注意:由于閉包會(huì)額外的附帶函數(shù)的作用域(內(nèi)部匿名函數(shù)攜帶外部函數(shù)的作用域),因此,閉包會(huì)比其他函數(shù)多占用些內(nèi)存空間,過度使用會(huì)導(dǎo)致內(nèi)存占用增加。
三、閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起了一個(gè)副作用,如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。
var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); // 3 data[1](); // 3 data[2](); // 3
如果想強(qiáng)制返回逾期結(jié)果,怎么整?
方法一:立即執(zhí)行函數(shù)
for (var i = 0; i < 3; i++) { (function(num) { setTimeout(function() { console.log(num); }, 1000); })(i); } // 0 // 1 // 2
方法二:返回一個(gè)匿名函數(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ù)還是返回一個(gè)匿名函數(shù)賦值,原理上都是因?yàn)樽兞康陌粗祩鬟f,所以會(huì)將變量i的值賦值給實(shí)參num,在匿名函數(shù)的內(nèi)部又創(chuàng)建了一個(gè)用于訪問num的匿名函數(shù),這樣每一個(gè)函數(shù)都有一個(gè)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)建一個(gè)數(shù)組data;
// 進(jìn)入第一次循環(huán)
{
let i = 0; // 注意:因?yàn)槭褂胠et使得for循環(huán)為塊級作用域 // 此次 let i = 0 在這個(gè)塊級作用域中,而不是在全局環(huán)境中 data[0] = function() { console.log(i); };
}
循環(huán)時(shí),let聲明了i,所以整個(gè)塊是塊級作用域,那么data[0]這個(gè)函數(shù)就成了一個(gè)閉包,這里用{}表述,只是希望通過它來說明let存在的時(shí)候,這個(gè)for循環(huán)塊是塊級作用域,而不是全局作用域。 上面的塊級作用域,就像函數(shù)作用域一樣,寒暑表執(zhí)行完畢,其中的變量會(huì)被銷毀,但是因?yàn)檫@個(gè)代碼塊中存在一個(gè)閉包,閉包的作用域鏈中引用著塊級作用域,所以在閉包被調(diào)用之前,這個(gè)塊級作用域內(nèi)部的變量不會(huì)被銷毀。
// 進(jìn)入第二次循環(huán)
{
let i = 1; // 因?yàn)?let i = 1 和上面的 let i = 0 // 在不同的作用域中,所以不會(huì)相互影響 data[1] = function(){ console.log(i); };
}
當(dāng)執(zhí)行data[1]()時(shí),進(jìn)入下面的執(zhí)行環(huán)境。
{
let i = 1; data[1] = function(){ console.log(i); };
}
在上面這個(gè)執(zhí)行環(huán)境中,它會(huì)首先尋找該執(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
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104232.html
摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起的一個(gè)副作用就是如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了...
摘要:使用上一篇文章的例子來說明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)...
摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來說只有當(dāng)函數(shù)以返回值返回或者當(dāng)函數(shù)以參數(shù)形式使用或者當(dāng)函數(shù)中自由變量在函數(shù)外被引用時(shí)才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會(huì)問的問題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書籍等書中給出的概念,這些概念雖然...
摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問外鏈,點(diǎn)擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對象,在全局環(huán)境中定義的變量就會(huì)綁定到全局對象中。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第6天。 本...
摘要:對數(shù)組函數(shù)而言,相當(dāng)于產(chǎn)生了個(gè)閉包。關(guān)于對象在閉包中使用對象也會(huì)導(dǎo)致一些問題。不過,匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其對象通常指向。由于聲明函數(shù)時(shí)與聲明函數(shù)時(shí)的值是不同的,因此閉包與閉包貌似將會(huì)表示各自不同的值。 這幾天看到閉包一章,從工具書到各路大神博客,都各自有著不同的理解,以下我將選擇性的抄(咳咳,當(dāng)然還是會(huì)附上自己理解的)一些大神們對閉包的原理及其使用文章,當(dāng)作是自己初步理解...
閱讀 1392·2019-08-30 12:54
閱讀 1878·2019-08-30 11:16
閱讀 1621·2019-08-30 10:50
閱讀 2455·2019-08-29 16:17
閱讀 1275·2019-08-26 12:17
閱讀 1387·2019-08-26 10:15
閱讀 2396·2019-08-23 18:38
閱讀 792·2019-08-23 17:50