国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

由 for 循環經典面試題延伸的 js 相關知識

justjavac / 3172人閱讀

摘要:經過上網友的點撥,這個問題涉及到中的兩個問題,作用域鏈和事件執行機制。常見的異步任務有定時器和事件回調等。異步任務三張圖片的事件依次觸發,回調函數進入任務隊列,等主線程的循環執行完畢之后,依次執行這三個任務。

之前工作中碰到一個需求,需要根據從后臺獲取到的圖片路徑獲得這些圖片的 base64 文件。實現過程中遇到一個問題,代碼如下:

var src=["http://www.w3school.com.cn/i/site_photoref.jpg",
    "http://www.w3school.com.cn/i/site_photoexa.jpg",
    "http://www.w3school.com.cn/i/site_photoqe.jpg"] 
for(var i=0;i<3;i++){
    var img=new Image();
    img.src=src[i];
    img.onload=function(){
        console.log(img)   
        //最終打印出來都是最后一個圖片
        //
        //
        //
    }
}

這個問題其實跟之前經常碰到的一個面試題本質上是一致的。我們可以在上面函數中打印索引值,會發現打印出來的值都是3。經過 segmentfault 上網友的點撥,這個問題涉及到 js 中的兩個問題,作用域鏈事件執行機制

作用域鏈

在 js 中,每個函數都有自己的執行環境,每個執行環境都有一個與之關聯的變量對象。當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈。每個作用域鏈的起點都是當前執行代碼所在的執行環境的變量對象,作用域鏈的下一個變量對象來自包含環境,一直延續到全局執行環境(瀏覽器中是指 window 對象)。
如果執行環境是函數,那么它的變量對象就包括活動對象,活動對象在一開始只包括 arguments 對象(函數的參數對象)。

當執行環境中要用到某個變量或者函數時,會從自己作用域鏈的起點也就是自己的變量對象中開始搜索相應的變量名或者函數名,如果搜索不到就接著在作用域鏈的上一級搜索,一直到找到相關變量名或者到作用域鏈的末尾為止。

js 事件執行機制

js 是一種單線程語言,在主線程中同一時間只能執行一個任務。

瀏覽器內核線程

瀏覽器內核是多線程的,通常包含以下線程:

GUI 渲染線程:
負責渲染網頁,當頁面需要重繪時,該線程就會執行。

JavaScript 引擎線程:
也就是 JS 內核,負責解析和運行 JS 代碼。

定時器觸發器線程:
通過這個線程計時來確定什么時候觸發定時器。

事件觸發線程:
監控某個事件是否觸發,事件觸發之后會被添加到任務隊列中。

異步 HTTP 請求線程:
監控 AJAX 的狀態變更時,就會把相應的任務添加到任務隊列中。

同步和異步

js 中每個任務的操作可以簡化為發起調用獲得結果兩步,根據這兩步可以把js 中的任務可以分為同步任務和異步任務。所有任務的執行都在主線程進行。
同步任務:發起調用之后,立即就會執行來獲取結果的任務。調用之后會一直等待直到返回結果,在這期間主線程不能進行其他操作。
異步任務:發起調用之后,并不會立即執行相關函數,而是需要額外的操作滿足相關條件之后進行觸發。相關任務被觸發之后會進入任務隊列等待主線程任務執行完成后按順序進入主線程,調用和執行之間的時間可以介入其他異步任務。常見的異步任務有定時器、ajax和事件回調等。

事件循環機制(event loop)

js 中事件執行基本按照下面這三步進行循環。

主線程先按照代碼順序執行同步任務

在異步任務被注冊之后,瀏覽器的其他線程(事件觸發線程、定時器觸發線程、異步 HTTP 請求線程)監控異步任務的觸發條件,按照觸發順序把這些異步任務放在任務隊列中

主線程上同步任務執行完之后,會依次執行任務隊列中的任務

回到開頭

在最上面的例子中,for 循環是同步任務,會立即執行,圖片的 onload 事件是異步任務,需要等另行觸發。這個例子中代碼的執行順序是這樣:

同步任務:循環創建三張圖片,每個圖片賦予各自的 src 值,并且都注冊了一個 onload 事件。
異步任務:三張圖片的 onload 事件依次觸發,回調函數進入任務隊列,等主線程的 for 循環執行完畢之后,依次執行這三個任務。

當開始執行異步任務時,每個函數都需要用到 img 這個變量,就開始在自己的作用域鏈上開始尋找 img,自身變量對象中不存在,接著在包含環境中找到,由于 for 循環并不會創造一個新的執行環境,所以這個例子中包含環境其實就是全局執行環境。而在 for 循環完之后,img 變量的值已經經過兩次覆蓋變成了最后一個索引對應的圖片。所以每個圖片的 onload 函數都會打印出同一個 img 。打印 i 值出現的結果也是一樣。

解決辦法

弄清楚出現這個問題的原因,解決這個問題可以用下面的辦法:

方法1:創建多帶帶的執行環境
for(var i=0;i<3;i++){
    (function(index){   
        var img=new Image();
        img.src=src[i];
        img.onload=function(){
            console.log(index)
            console.log(img)   
        }
    })(i)
}

這個方法實現的原理是:for 循環中立即執行函數每次都會創建一個新的執行環境,三張圖片的 onload 事件函數的作用域鏈的包含環境分別是這三個立即執行函數,這三個立即執行函數里面保存的是不同的 img 變量和不同的參數 index,當三個 onload 回調函數執行時,分別在自己的作用域鏈上尋找各自對應的 img 變量。利用同樣的原理,也可以寫成這樣:

for(var i=0;i<3;i++){
    var img=new Image();
    img.src=src[i];
    img.onload=function(index,img){
        return function(){
            console.log(index)
            console.log(img) 
        }  
    }(i,img)
}

也可以不通過傳參,而是在立即執行函數內部創建一個變量來接收每次循環中的 i 值,原理都是一樣的。

方法2:訪問事件觸發節點
for(var i=0;i<3;i++){
    var img=new Image();
    img.src=src[i];
    img.onload=function(){
        console.log(this)   
    }
}

這個方法的原理是:函數內部在執行過程中會有一個默認的 this 變量會把函數的調用對象保存起來,通過函數內部的 this 就可以訪問調用函數的對象。或者可以通過 event 事件對象的 currentTarget 屬性訪問到事件觸發節點,原理是一樣的。

方法3:ES6 的新語法 let
for(let i=0;i<3;i++){
    let img=new Image();
    img.src=src[i];
    img.onload=function(){
        console.log(img)   
        console.log(i)
    }
}

在 ES6 中規定了一個新的變量聲明命令 let,let 會創建一個塊級作用域,用 let 聲明的變量只在 let 所在的代碼塊中有效。這個例子中,三次循環會創建三個塊級作用域,每個塊級作用域中有各自的變量 i 和 img,互相獨立,每個 onload 回調函數執行時都會獲取各自代碼塊中的 i 和 img ,最終能實現我們想要的結果。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/82587.html

相關文章

  • 8道經典JavaScript面試解析,你真掌握JavaScript了嗎?

    摘要:瀏覽器的主要組成包括有調用堆棧,事件循環,任務隊列和。好了,現在有了前面這些知識,我們可以看一下這道題的講解過程實現步驟調用會將函數放入調用堆棧。由于調用堆棧是空的,事件循環將選擇回調并將其推入調用堆棧進行處理。進程再次重復,堆棧不會溢出。 JavaScript是前端開發中非常重要的一門語言,瀏覽器是他主要運行的地方。JavaScript是一個非常有意思的語言,但是他有很多一些概念,大...

    taowen 評論0 收藏0
  • Deep in JS - 收藏集 - 掘金

    摘要:今天同學去面試,做了兩道面試題全部做錯了,發過來給道典型的面試題前端掘金在界中,開發人員的需求量一直居高不下。 排序算法 -- JavaScript 標準參考教程(alpha) - 前端 - 掘金來自《JavaScript 標準參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡介 算法實現 選擇排序 簡介 算法實現 ... 圖例詳解那道 setTimeout 與循環閉包的經典面...

    enali 評論0 收藏0
  • 程序語言

    摘要:一面應該還問了其他內容,但是兩次面試多線程面試問題和答案采訪中,我們通常會遇到兩個主題采集問題和多線程面試問題。多線程是關于并發和線程的。我們正在共享重要的多線程面試問題和答案。。 2016 年末,騰訊,百度,華為,搜狗和滴滴面試題匯總 2016 年未,騰訊,百度,華為,搜狗和滴滴面試題匯總 【碼農每日一題】Java 內部類(Part 2)相關面試題 關注一下嘛,又不讓你背鍋!問:Ja...

    mtunique 評論0 收藏0
  • 程序語言

    摘要:一面應該還問了其他內容,但是兩次面試多線程面試問題和答案采訪中,我們通常會遇到兩個主題采集問題和多線程面試問題。多線程是關于并發和線程的。我們正在共享重要的多線程面試問題和答案。。 2016 年末,騰訊,百度,華為,搜狗和滴滴面試題匯總 2016 年未,騰訊,百度,華為,搜狗和滴滴面試題匯總 【碼農每日一題】Java 內部類(Part 2)相關面試題 關注一下嘛,又不讓你背鍋!問:Ja...

    stefan 評論0 收藏0
  • 用9種辦法解決 JS 閉包經典面試for 循環取 i

    摘要:閉包正確的說應該是指一個閉包域每當聲明了一個函數它就產生了一個閉包域可以解釋為每個函數都有自己的函數棧每個閉包域對象都有一個不是屬性內默認有個名為的全局引用有了這個引用就可以直接調用的屬性或方法凡是在閉包域內聲明的變量或方法外部無法直接訪問 閉包 正確的說,應該是指一個閉包域,每當聲明了一個函數,它就產生了一個閉包域(可以解釋為每個函數都有自己的函數棧),每個閉包域(Function...

    Betta 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<