摘要:提示,很顯然,出了循環(huán)的的大括號(hào)對(duì)應(yīng)的作用域之后,就會(huì)被自動(dòng)銷(xiāo)毀。那么呢,也是這樣么我們來(lái)看個(gè)例子這段代碼執(zhí)行結(jié)果是估計(jì)有人也會(huì)比較奇怪。這邊我解釋下執(zhí)行這段代碼的過(guò)程。提出了用關(guān)鍵字來(lái)代替關(guān)鍵字,具體的話可以參考阮一峰的而教程。
從一道題說(shuō)起
最近又有人問(wèn)我下面這道題目,題目是這樣的,首先是一個(gè)DOM結(jié)構(gòu)如下:
12345
非常easy的dom結(jié)構(gòu),在來(lái)一小段js,如下:
var nodes = document.getElementsByTagName("div"); for(var i = 0,len = nodes.length; i < len; i++){ nodes[i].onclick = function(){ console.log(i); } }
好了,問(wèn)題來(lái)了,依次點(diǎn)擊div,結(jié)果是多少?答案并不是1,2,3,4,5,而是點(diǎn)擊任何一個(gè)div都會(huì)輸出5.
分析先來(lái)說(shuō)下為什么最后執(zhí)行的結(jié)果都是5.首先我們要明白,js中沒(méi)有塊級(jí)作用域,講人話,就是js中不存在{}這種代碼塊的東西。各位估計(jì)會(huì)反駁我說(shuō),上面例子中不是明明白白的寫(xiě)的for(){}這種代碼,怎么這邊就開(kāi)始說(shuō)js不存在{}這種東西呢?我先舉個(gè)C++的例子吧
int arr[] = {1,2,3,4,5}; vectorv = vector (arr,arr+sizeof(arr)/sizeof(int)); for(int i = 0; i < v.size(); i++){ std::cout << i << std::endl; }
這么寫(xiě)是沒(méi)有問(wèn)題的,下面我再加點(diǎn)東西
int arr[] = {1,2,3,4,5}; vectorv = vector (arr,arr+sizeof(arr)/sizeof(int)); for(int i = 0; i < v.size(); i++){ std::cout << i << std::endl; } std::cout << i;
這么寫(xiě),編譯器直接就報(bào)錯(cuò)了。提示 error: use of undeclared identifier "i",很顯然,出了for循環(huán)的{}的大括號(hào)對(duì)應(yīng)的作用域之后,i就會(huì)被自動(dòng)銷(xiāo)毀。那么JS呢,也是這樣么?我們來(lái)看個(gè)例子
for(var i = 0;i< 5;i++){ console.log(i); } console.log(i);
這段代碼執(zhí)行結(jié)果是0,1,2,3,4,5.估計(jì)有人也會(huì)比較奇怪。這邊我解釋下JS執(zhí)行這段代碼的過(guò)程。
首先是變量提升,js把var i = 0;分解成兩句話,var i;i =0;并且把var i;提到最近一個(gè)function的頂部,這個(gè)時(shí)候,這段代碼就變成了這樣
var i; for(i=0;i<5;i++){ console.log(i); } console.log(i);
這樣各位對(duì)于上面執(zhí)行出來(lái)的0,1,2,3,4,5估計(jì)就沒(méi)啥疑問(wèn)了。
看完這個(gè)例子之后,我也希望各位注意下我前面說(shuō)的js沒(méi)有塊級(jí)作用域,以及js會(huì)做變量提升,把變量的申明提升到最近的一個(gè)function的頂部
由于js會(huì)做變量提升,自動(dòng)將變量的申明提升到最近的一個(gè)function的頂部,所以{}根據(jù)不會(huì)構(gòu)成所謂的塊級(jí)作用域,對(duì)js里面的變量而言,只有function才會(huì)是其作用域。
好了,講完js的變量提升,我們?cè)倩仡^來(lái)看最開(kāi)始的這個(gè)問(wèn)題。首先是變量提升,提升之后我們得到
var nodes = document.getElementsByTagName("div"); var i; for(i = 0,len = nodes.length; i < len; i++){ nodes[i].onclick = function(){ console.log(i); } }
執(zhí)行過(guò)程中,我們對(duì)每個(gè)node[i]節(jié)點(diǎn)都綁定了一個(gè)onclick事件,但是for循環(huán)執(zhí)行的過(guò)程中,我們并沒(méi)有出發(fā)這個(gè)click事件,for循環(huán)執(zhí)行結(jié)束之后,i變?yōu)?。當(dāng)用戶(hù)點(diǎn)擊div的時(shí)候,這個(gè)時(shí)候執(zhí)行對(duì)應(yīng)的onclick函數(shù),也就是console.log(i),這個(gè)時(shí)候,會(huì)自動(dòng)找到被js變量提升過(guò)的i,所以大家都會(huì)輸出5.
解決總結(jié)下,上面的問(wèn)題之所以會(huì)產(chǎn)生,就是因?yàn)樗械膐nclick事件都去引用被js變量提升的i,那么如果我們想要解決這個(gè)問(wèn)題,應(yīng)該怎么辦呢。一個(gè)就是我們可以通過(guò)JS的IIFE(immediately-invoked-function-expression)來(lái)構(gòu)造一個(gè)作用域,讓onclick函數(shù)引用我們構(gòu)造出來(lái)作用域里面的i。ok,我們來(lái)解決下
var nodes = document.getElementsByTagName("div"); for(var i = 0,len = nodes.length; i < len; i++){ (function(i){ nodes[i].onclick = function(){ console.log(i); } })(i) }
這種做法把整個(gè)綁定事件的過(guò)程都給包起來(lái)了,由于IIFE會(huì)馬上執(zhí)行,for循環(huán)的i相當(dāng)于一個(gè)輸入?yún)?shù),在綁定完事件只有,也形成了一個(gè)作用域,并且這個(gè)作用域中存在一個(gè)i的值。
同樣的道理,我再給一種解法,如下:
var nodes = document.getElementsByTagName("div"); for(var i = 0,len = nodes.length; i < len; i++){ nodes[i].onclick = (function(i){ return function(){ console.log(i); } })(i) }
除此之外,我們可能會(huì)想到,如果js能夠有這種塊級(jí)作用于就好了,我們綁定的事件一定是在{}作用域下面,一定可以引用到for循環(huán)中的每個(gè)i,而不是應(yīng)用哪個(gè)被變量提升的i。ES6提出了用let關(guān)鍵字來(lái)代替var關(guān)鍵字,具體的話可以參考阮一峰的而ES6教程。上個(gè)代碼,這邊代碼用了一個(gè)inbrowser的es6轉(zhuǎn)碼器,可以測(cè)試用,如果想要生產(chǎn)環(huán)境中使用需要提前將es6代碼編譯成es5的代碼。
12345>
引用了一個(gè)inbrower級(jí)別的es6轉(zhuǎn)碼器。具體可以參考babel-standalone項(xiàng)目.改進(jìn)后的代碼與原來(lái)的代碼的區(qū)別在于,將var i = 0換成了let i = 0.
下面我在看下,通過(guò)轉(zhuǎn)碼之后,到底生成了什么樣的js代碼,通過(guò)es6轉(zhuǎn)碼器,我們最終生成了如下的代碼
var nodes = document.getElementsByTagName("div"); var _loop = function _loop(i, len) { nodes[i].onclick = function () { console.log(i); }; }; for (var i = 0, len = nodes.length; i < len; i++) { _loop(i, len); }
原來(lái)ES6幫我們構(gòu)造了一個(gè)function的作用域報(bào)過(guò)了node[i].onclick的事件綁定過(guò)程,跟我們上面的解決方法其實(shí)是一樣的!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/82225.html
JavaScript在創(chuàng)建變量(數(shù)組、字符串、對(duì)象等)是自動(dòng)進(jìn)行了分配內(nèi)存,而且當(dāng)它沒(méi)有被使用的狀態(tài)下,會(huì)自動(dòng)的釋放分配的內(nèi)容;其實(shí)這樣基層語(yǔ)言,如C語(yǔ)言,他們提供了內(nèi)存管理的接口,比如malloc()用于分配所需的內(nèi)存空間、free()釋放之前所分配的內(nèi)存空間?! ♂尫艃?nèi)存的過(guò)程稱(chēng)為垃圾回收,例如avaScript這類(lèi)高級(jí)語(yǔ)言可以提供了內(nèi)存自動(dòng)分配和自動(dòng)回收,其實(shí)這個(gè)自動(dòng)儲(chǔ)存不會(huì)占用太多空間...
學(xué)習(xí)一門(mén)知識(shí),有些內(nèi)容必須要提前明白,比如在學(xué)習(xí)js中同步異步的問(wèn)題前,需要明白,js是單線程的,為什么它得是單線程的呢?現(xiàn)在先從它應(yīng)用的場(chǎng)景來(lái)說(shuō),就是用來(lái)讓用戶(hù)與頁(yè)面進(jìn)行交互的吧。假如有js是多線程的,那在這個(gè)線程里面,用戶(hù)點(diǎn)擊某個(gè)按鈕會(huì)增加一個(gè)DOM節(jié)點(diǎn),在另一個(gè)線程里面,用戶(hù)點(diǎn)擊這個(gè)按鈕又會(huì)刪除一個(gè)DOM節(jié)點(diǎn),那么此時(shí)js就不知道該聽(tīng)誰(shuí)的了。這就是為什么會(huì)出現(xiàn)同步異步。假設(shè)沒(méi)有異步,那么...
學(xué)習(xí)JS,就應(yīng)該知道數(shù)據(jù)結(jié)構(gòu)與算法這個(gè)詞。現(xiàn)在我們就說(shuō)說(shuō): 數(shù)據(jù)結(jié)構(gòu)與算法在編程中是十分需要,主要是沒(méi)有很好的數(shù)據(jù)結(jié)構(gòu)與算法的功底,就影響后續(xù)學(xué)習(xí)和工作,這是為什么那?是因?yàn)殡S著項(xiàng)目的復(fù)雜,數(shù)據(jù)量也隨之變大,數(shù)據(jù)結(jié)構(gòu)與算法可以更優(yōu)雅的處理這些數(shù)據(jù)。 程序=數(shù)據(jù)結(jié)構(gòu)+算法,是計(jì)算機(jī)科學(xué)界的一個(gè)經(jīng)典名句,這句話也體現(xiàn)了一個(gè)應(yīng)用程序是與數(shù)據(jù)結(jié)構(gòu)和算法密不可分的?! ?shù)據(jù)結(jié)構(gòu) 其實(shí)數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單說(shuō)...
一、瀏覽器安全無(wú)風(fēng)險(xiǎn)的世界不存在,包括瀏覽器,我們知道Web世界是開(kāi)放的,包容的。但是開(kāi)放和風(fēng)險(xiǎn)是對(duì)立的。Web 世界會(huì)是開(kāi)放的,任何資源都可以接入其中,我們的網(wǎng)站可以加載并執(zhí)行別人網(wǎng)站的腳本文件、圖片、音頻 / 視頻等資源,甚至可以下載其他站點(diǎn)的可執(zhí)行文件。比如你打開(kāi)了一個(gè)銀行站點(diǎn),然后又一不小心打開(kāi)了一個(gè)惡意站點(diǎn),如果沒(méi)有安全措施,惡意站點(diǎn)就可以做很多事情:修改站點(diǎn)的 DOM、CSSOM 等信...
背景 在使用useEffect中用啦回調(diào)函數(shù)中使用 async...await... 這時(shí)候就會(huì)報(bào)錯(cuò)?! ∩厦娲a可以看到,在報(bào)錯(cuò),effect function 應(yīng)該返回一個(gè)銷(xiāo)毀函數(shù)(effect:是指return返回的cleanup函數(shù)),如果 useEffect 第一個(gè)參數(shù)傳入 async,返回值則變成了 Promise,結(jié)果就是會(huì)導(dǎo)致 react 在調(diào)用銷(xiāo)毀函數(shù)的時(shí)候報(bào)錯(cuò)。 React...
閱讀 3706·2021-09-30 09:59
閱讀 2373·2021-09-13 10:34
閱讀 590·2019-08-30 12:58
閱讀 1522·2019-08-29 18:42
閱讀 2215·2019-08-26 13:44
閱讀 2936·2019-08-23 18:12
閱讀 3332·2019-08-23 15:10
閱讀 1637·2019-08-23 14:37