摘要:提示,很顯然,出了循環的的大括號對應的作用域之后,就會被自動銷毀。那么呢,也是這樣么我們來看個例子這段代碼執行結果是估計有人也會比較奇怪。這邊我解釋下執行這段代碼的過程。提出了用關鍵字來代替關鍵字,具體的話可以參考阮一峰的而教程。
從一道題說起
最近又有人問我下面這道題目,題目是這樣的,首先是一個DOM結構如下:
12345
非常easy的dom結構,在來一小段js,如下:
var nodes = document.getElementsByTagName("div"); for(var i = 0,len = nodes.length; i < len; i++){ nodes[i].onclick = function(){ console.log(i); } }
好了,問題來了,依次點擊div,結果是多少?答案并不是1,2,3,4,5,而是點擊任何一個div都會輸出5.
分析先來說下為什么最后執行的結果都是5.首先我們要明白,js中沒有塊級作用域,講人話,就是js中不存在{}這種代碼塊的東西。各位估計會反駁我說,上面例子中不是明明白白的寫的for(){}這種代碼,怎么這邊就開始說js不存在{}這種東西呢?我先舉個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; }
這么寫是沒有問題的,下面我再加點東西
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;
這么寫,編譯器直接就報錯了。提示 error: use of undeclared identifier "i",很顯然,出了for循環的{}的大括號對應的作用域之后,i就會被自動銷毀。那么JS呢,也是這樣么?我們來看個例子
for(var i = 0;i< 5;i++){ console.log(i); } console.log(i);
這段代碼執行結果是0,1,2,3,4,5.估計有人也會比較奇怪。這邊我解釋下JS執行這段代碼的過程。
首先是變量提升,js把var i = 0;分解成兩句話,var i;i =0;并且把var i;提到最近一個function的頂部,這個時候,這段代碼就變成了這樣
var i; for(i=0;i<5;i++){ console.log(i); } console.log(i);
這樣各位對于上面執行出來的0,1,2,3,4,5估計就沒啥疑問了。
看完這個例子之后,我也希望各位注意下我前面說的js沒有塊級作用域,以及js會做變量提升,把變量的申明提升到最近的一個function的頂部
由于js會做變量提升,自動將變量的申明提升到最近的一個function的頂部,所以{}根據不會構成所謂的塊級作用域,對js里面的變量而言,只有function才會是其作用域。
好了,講完js的變量提升,我們再回頭來看最開始的這個問題。首先是變量提升,提升之后我們得到
var nodes = document.getElementsByTagName("div"); var i; for(i = 0,len = nodes.length; i < len; i++){ nodes[i].onclick = function(){ console.log(i); } }
執行過程中,我們對每個node[i]節點都綁定了一個onclick事件,但是for循環執行的過程中,我們并沒有出發這個click事件,for循環執行結束之后,i變為5。當用戶點擊div的時候,這個時候執行對應的onclick函數,也就是console.log(i),這個時候,會自動找到被js變量提升過的i,所以大家都會輸出5.
解決總結下,上面的問題之所以會產生,就是因為所有的onclick事件都去引用被js變量提升的i,那么如果我們想要解決這個問題,應該怎么辦呢。一個就是我們可以通過JS的IIFE(immediately-invoked-function-expression)來構造一個作用域,讓onclick函數引用我們構造出來作用域里面的i。ok,我們來解決下
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) }
這種做法把整個綁定事件的過程都給包起來了,由于IIFE會馬上執行,for循環的i相當于一個輸入參數,在綁定完事件只有,也形成了一個作用域,并且這個作用域中存在一個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) }
除此之外,我們可能會想到,如果js能夠有這種塊級作用于就好了,我們綁定的事件一定是在{}作用域下面,一定可以引用到for循環中的每個i,而不是應用哪個被變量提升的i。ES6提出了用let關鍵字來代替var關鍵字,具體的話可以參考阮一峰的而ES6教程。上個代碼,這邊代碼用了一個inbrowser的es6轉碼器,可以測試用,如果想要生產環境中使用需要提前將es6代碼編譯成es5的代碼。
12345>
引用了一個inbrower級別的es6轉碼器。具體可以參考babel-standalone項目.改進后的代碼與原來的代碼的區別在于,將var i = 0換成了let i = 0.
下面我在看下,通過轉碼之后,到底生成了什么樣的js代碼,通過es6轉碼器,我們最終生成了如下的代碼
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); }
原來ES6幫我們構造了一個function的作用域報過了node[i].onclick的事件綁定過程,跟我們上面的解決方法其實是一樣的!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82225.html
JavaScript在創建變量(數組、字符串、對象等)是自動進行了分配內存,而且當它沒有被使用的狀態下,會自動的釋放分配的內容;其實這樣基層語言,如C語言,他們提供了內存管理的接口,比如malloc()用于分配所需的內存空間、free()釋放之前所分配的內存空間。 釋放內存的過程稱為垃圾回收,例如avaScript這類高級語言可以提供了內存自動分配和自動回收,其實這個自動儲存不會占用太多空間...
學習一門知識,有些內容必須要提前明白,比如在學習js中同步異步的問題前,需要明白,js是單線程的,為什么它得是單線程的呢?現在先從它應用的場景來說,就是用來讓用戶與頁面進行交互的吧。假如有js是多線程的,那在這個線程里面,用戶點擊某個按鈕會增加一個DOM節點,在另一個線程里面,用戶點擊這個按鈕又會刪除一個DOM節點,那么此時js就不知道該聽誰的了。這就是為什么會出現同步異步。假設沒有異步,那么...
學習JS,就應該知道數據結構與算法這個詞。現在我們就說說: 數據結構與算法在編程中是十分需要,主要是沒有很好的數據結構與算法的功底,就影響后續學習和工作,這是為什么那?是因為隨著項目的復雜,數據量也隨之變大,數據結構與算法可以更優雅的處理這些數據。 程序=數據結構+算法,是計算機科學界的一個經典名句,這句話也體現了一個應用程序是與數據結構和算法密不可分的。 數據結構 其實數據結構簡單說...
背景 在使用useEffect中用啦回調函數中使用 async...await... 這時候就會報錯。 上面代碼可以看到,在報錯,effect function 應該返回一個銷毀函數(effect:是指return返回的cleanup函數),如果 useEffect 第一個參數傳入 async,返回值則變成了 Promise,結果就是會導致 react 在調用銷毀函數的時候報錯。 React...
閱讀 3666·2021-09-30 09:59
閱讀 2318·2021-09-13 10:34
閱讀 584·2019-08-30 12:58
閱讀 1513·2019-08-29 18:42
閱讀 2206·2019-08-26 13:44
閱讀 2931·2019-08-23 18:12
閱讀 3324·2019-08-23 15:10
閱讀 1630·2019-08-23 14:37