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

資訊專欄INFORMATION COLUMN

由 for 循環(huán)經(jīng)典面試題延伸的 js 相關(guān)知識

justjavac / 3165人閱讀

摘要:經(jīng)過上網(wǎng)友的點(diǎn)撥,這個問題涉及到中的兩個問題,作用域鏈和事件執(zhí)行機(jī)制。常見的異步任務(wù)有定時器和事件回調(diào)等。異步任務(wù)三張圖片的事件依次觸發(fā),回調(diào)函數(shù)進(jìn)入任務(wù)隊(duì)列,等主線程的循環(huán)執(zhí)行完畢之后,依次執(zhí)行這三個任務(wù)。

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

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)   
        //最終打印出來都是最后一個圖片
        //
        //
        //
    }
}

這個問題其實(shí)跟之前經(jīng)常碰到的一個面試題本質(zhì)上是一致的。我們可以在上面函數(shù)中打印索引值,會發(fā)現(xiàn)打印出來的值都是3。經(jīng)過 segmentfault 上網(wǎng)友的點(diǎn)撥,這個問題涉及到 js 中的兩個問題,作用域鏈事件執(zhí)行機(jī)制

作用域鏈

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

當(dāng)執(zhí)行環(huán)境中要用到某個變量或者函數(shù)時,會從自己作用域鏈的起點(diǎn)也就是自己的變量對象中開始搜索相應(yīng)的變量名或者函數(shù)名,如果搜索不到就接著在作用域鏈的上一級搜索,一直到找到相關(guān)變量名或者到作用域鏈的末尾為止。

js 事件執(zhí)行機(jī)制

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

瀏覽器內(nèi)核線程

瀏覽器內(nèi)核是多線程的,通常包含以下線程:

GUI 渲染線程:
負(fù)責(zé)渲染網(wǎng)頁,當(dāng)頁面需要重繪時,該線程就會執(zhí)行。

JavaScript 引擎線程:
也就是 JS 內(nèi)核,負(fù)責(zé)解析和運(yùn)行 JS 代碼。

定時器觸發(fā)器線程:
通過這個線程計(jì)時來確定什么時候觸發(fā)定時器。

事件觸發(fā)線程:
監(jiān)控某個事件是否觸發(fā),事件觸發(fā)之后會被添加到任務(wù)隊(duì)列中。

異步 HTTP 請求線程:
監(jiān)控 AJAX 的狀態(tài)變更時,就會把相應(yīng)的任務(wù)添加到任務(wù)隊(duì)列中。

同步和異步

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

事件循環(huán)機(jī)制(event loop)

js 中事件執(zhí)行基本按照下面這三步進(jìn)行循環(huán)。

主線程先按照代碼順序執(zhí)行同步任務(wù)

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

主線程上同步任務(wù)執(zhí)行完之后,會依次執(zhí)行任務(wù)隊(duì)列中的任務(wù)

回到開頭

在最上面的例子中,for 循環(huán)是同步任務(wù),會立即執(zhí)行,圖片的 onload 事件是異步任務(wù),需要等另行觸發(fā)。這個例子中代碼的執(zhí)行順序是這樣:

同步任務(wù):循環(huán)創(chuàng)建三張圖片,每個圖片賦予各自的 src 值,并且都注冊了一個 onload 事件。
異步任務(wù):三張圖片的 onload 事件依次觸發(fā),回調(diào)函數(shù)進(jìn)入任務(wù)隊(duì)列,等主線程的 for 循環(huán)執(zhí)行完畢之后,依次執(zhí)行這三個任務(wù)。

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

解決辦法

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

方法1:創(chuàng)建多帶帶的執(zhí)行環(huán)境
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)
}

這個方法實(shí)現(xiàn)的原理是:for 循環(huán)中立即執(zhí)行函數(shù)每次都會創(chuàng)建一個新的執(zhí)行環(huán)境,三張圖片的 onload 事件函數(shù)的作用域鏈的包含環(huán)境分別是這三個立即執(zhí)行函數(shù),這三個立即執(zhí)行函數(shù)里面保存的是不同的 img 變量和不同的參數(shù) index,當(dāng)三個 onload 回調(diào)函數(shù)執(zhí)行時,分別在自己的作用域鏈上尋找各自對應(yīng)的 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)
}

也可以不通過傳參,而是在立即執(zhí)行函數(shù)內(nèi)部創(chuàng)建一個變量來接收每次循環(huán)中的 i 值,原理都是一樣的。

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

這個方法的原理是:函數(shù)內(nèi)部在執(zhí)行過程中會有一個默認(rèn)的 this 變量會把函數(shù)的調(diào)用對象保存起來,通過函數(shù)內(nèi)部的 this 就可以訪問調(diào)用函數(shù)的對象。或者可以通過 event 事件對象的 currentTarget 屬性訪問到事件觸發(fā)節(jié)點(diǎn),原理是一樣的。

方法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 中規(guī)定了一個新的變量聲明命令 let,let 會創(chuàng)建一個塊級作用域,用 let 聲明的變量只在 let 所在的代碼塊中有效。這個例子中,三次循環(huán)會創(chuàng)建三個塊級作用域,每個塊級作用域中有各自的變量 i 和 img,互相獨(dú)立,每個 onload 回調(diào)函數(shù)執(zhí)行時都會獲取各自代碼塊中的 i 和 img ,最終能實(shí)現(xiàn)我們想要的結(jié)果。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82587.html

相關(guān)文章

  • 8道經(jīng)典JavaScript面試解析,你真掌握J(rèn)avaScript了嗎?

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

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

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

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

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

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

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

    stefan 評論0 收藏0
  • 用9種辦法解決 JS 閉包經(jīng)典面試for 循環(huán)取 i

    摘要:閉包正確的說應(yīng)該是指一個閉包域每當(dāng)聲明了一個函數(shù)它就產(chǎn)生了一個閉包域可以解釋為每個函數(shù)都有自己的函數(shù)棧每個閉包域?qū)ο蠖加幸粋€不是屬性內(nèi)默認(rèn)有個名為的全局引用有了這個引用就可以直接調(diào)用的屬性或方法凡是在閉包域內(nèi)聲明的變量或方法外部無法直接訪問 閉包 正確的說,應(yīng)該是指一個閉包域,每當(dāng)聲明了一個函數(shù),它就產(chǎn)生了一個閉包域(可以解釋為每個函數(shù)都有自己的函數(shù)棧),每個閉包域(Function...

    Betta 評論0 收藏0

發(fā)表評論

0條評論

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