2021 年 JS 燒腦面試題大賞
大廠技術(shù)??高級前端??Node進(jìn)階
點(diǎn)擊上方?程序員成長指北,關(guān)注公眾號
回復(fù)1,加入高級Node交流群
本文精選了20多道具有一定迷惑性的js題,主要考察的是類型判斷、作用域、this指向、原型、事件循環(huán)等知識點(diǎn),每道題都配有筆者詳細(xì)傻瓜式的解析,偏向于初學(xué)者,大佬請隨意。
第1題
let?a?=?1
function?b(a)?{
??a?=?2
??console.log(a)
}
b(a)
console.log(a)
復(fù)制代碼
答案
2、1
解析
首先基本類型數(shù)據(jù)是按值傳遞的,所以執(zhí)行b函數(shù)時(shí),b的參數(shù)a接收的值為1,參數(shù)a相當(dāng)于函數(shù)內(nèi)部的變量,當(dāng)本作用域有和上層作用域同名的變量時(shí),無法訪問到上層變量,所以函數(shù)內(nèi)無論怎么修改a,都不影響上層,所以函數(shù)內(nèi)部打印的a是2,外面打印的仍是1。
第2題
function?a?(b?=?c,?c?=?1)?{
??console.log(b,?c)
}
a()
復(fù)制代碼
答案
報(bào)錯(cuò)
解析
給函數(shù)多個(gè)參數(shù)設(shè)置默認(rèn)值實(shí)際上跟按順序定義變量一樣,所以會(huì)存在暫時(shí)性死區(qū)的問題,即前面定義的變量不能引用后面還未定義的變量,而后面的可以訪問前面的。
第3題
let?a?=?b?=?10
;(function(){?
??let?a?=?b?=?20?
})()
console.log(a)
console.log(b)
復(fù)制代碼
答案
10、20
解析
連等操作是從右向左執(zhí)行的,相當(dāng)于b = 10、let a = b,很明顯b沒有聲明就直接賦值了,所以會(huì)隱式創(chuàng)建為一個(gè)全局變量,函數(shù)內(nèi)的也是一樣,并沒有聲明b,直接就對b賦值了,因?yàn)樽饔糜蜴?,?huì)一層一層向上查找,找了到全局的b,所以全局的b就被修改為20了,而函數(shù)內(nèi)的a因?yàn)橹匦侣暶髁?,所以只是局部變量,不影響全局的a,所以a還是10。
第4題
var?a?=?{n:1}
var?b?=?a
a.x?=?a?=?{n:2}
console.log(a.x)
console.log(b.x)
復(fù)制代碼
答案
undefined、{n: 2}
解析
恕筆者不才,這道題筆者做一次錯(cuò)一次。
反正按照網(wǎng)上大部分的解釋是因?yàn)?運(yùn)算符優(yōu)先級最高,所以會(huì)先執(zhí)行a.x,此時(shí)a、b共同指向的{n: 1}變成了{(lán)n: 1, x: undefined},然后按照連等操作從右到左執(zhí)行代碼,a = {n: 2},顯然,a現(xiàn)在指向了一個(gè)新對象,然后a.x = a,因?yàn)閍.x最開始就執(zhí)行過了,所以這里其實(shí)等價(jià)于:({n: 1, x: undefined}).x = b.x = a = {n: 2}。
第5題
var?arr?=?[0,?1,?2]
arr[10]?=?10
console.log(arr.filter(function?(x)?{
??return?x?===?undefined
}))
復(fù)制代碼
答案
[]
解析
這題比較簡單,arr[10]=10,那么索引3到9位置上都是undefined,arr[3]等打印出來也確實(shí)是undefined,但是,這里其實(shí)涉及到ECMAScript版本不同對應(yīng)方法行為不同的問題,ES6之前的遍歷方法都會(huì)跳過數(shù)組未賦值過的位置,也就是空位,但是ES6新增的for of方法就不會(huì)跳過。
第6題
var?name?=?'World'
;(function?()?{
??if?(typeof?name?===?'undefined')?{
????var?name?=?"Jack"
????console.info('Goodbye?'?+?name)
??}?else?{
????console.info('Hello?'?+?name)
??}
})()
復(fù)制代碼
答案
Goodbye Jack
解析
這道題考察的是變量提升的問題,var聲明變量時(shí)會(huì)把變量自動(dòng)提升到當(dāng)前作用域頂部,所以函數(shù)內(nèi)的name雖然是在if分支里聲明的,但是也會(huì)提升到外層,因?yàn)楹腿值淖兞縩ame重名,所以訪問不到外層的name,最后因?yàn)橐崖暶魑促x值的變量的值都為undefined,導(dǎo)致if的第一個(gè)分支滿足條件。
第7題
console.log(1?+?NaN)
console.log("1"?+?3)
console.log(1?+?undefined)
console.log(1?+?null)
console.log(1?+?{})
console.log(1?+?[])
console.log([]?+?{})
復(fù)制代碼
答案
NaN、13、NaN、1、1[object Object]、1、[object Object]
解析
這道題考察的顯然是+號的行為:
1.如果有一個(gè)操作數(shù)是字符串,那么把另一個(gè)操作數(shù)轉(zhuǎn)成字符串執(zhí)行連接
2.如果有一個(gè)操作數(shù)是對象,那么調(diào)用對象的valueOf方法轉(zhuǎn)成原始值,如果沒有該方法或調(diào)用后仍是非原始值,則調(diào)用toString方法
3.其他情況下,兩個(gè)操作數(shù)都會(huì)被轉(zhuǎn)成數(shù)字執(zhí)行加法操作
第8題
var?a={},
????b={key:'b'},
????c={key:'c'}
a[b]=123
a[c]=456
console.log(a[b])
復(fù)制代碼
答案
456
解析
對象有兩種方法設(shè)置和引用屬性,obj.name和obj['name'],方括號里可以字符串、數(shù)字和變量設(shè)置是表達(dá)式等,但是最終計(jì)算出來得是一個(gè)字符串,對于上面的b和c,它們兩個(gè)都是對象,所以會(huì)調(diào)用toString()方法轉(zhuǎn)成字符串,對象轉(zhuǎn)成字符串和數(shù)組不一樣,和內(nèi)容無關(guān),結(jié)果都是[object Obejct],所以a[b]=a[c]=a['[object Object]']。
第9題
var?out?=?25
var?inner?=?{
??out:?20,
??func:?function?()?{
????var?out?=?30
????return?this.out
??}
};
console.log((inner.func,?inner.func)())
console.log(inner.func())
console.log((inner.func)())
console.log((inner.func?=?inner.func)())
復(fù)制代碼
答案
25、20、20、25
解析
這道題考察的是this指向問題:
1.逗號操作符會(huì)返回表達(dá)式中的最后一個(gè)值,這里為inner.func對應(yīng)的函數(shù),注意是函數(shù)本身,然后執(zhí)行該函數(shù),該函數(shù)并不是通過對象的方法調(diào)用,而是在全局環(huán)境下調(diào)用,所以this指向window,打印出來的當(dāng)然是window下的out
2.這個(gè)顯然是以對象的方法調(diào)用,那么this指向該對象
3.加了個(gè)括號,看起來有點(diǎn)迷惑人,但實(shí)際上(inner.func)和inner.func是完全相等的,所以還是作為對象的方法調(diào)用
4.賦值表達(dá)式和逗號表達(dá)式相似,都是返回的值本身,所以也相對于在全局環(huán)境下調(diào)用函數(shù)
第10題
let?{a,b,c}?=?{?c:3,?b:2,?a:1?}
console.log(a,?b,?c)
復(fù)制代碼
答案
1、2、3
解析
這題考察的是變量解構(gòu)賦值的問題,數(shù)組解構(gòu)賦值是按位置對應(yīng)的,而對象只要變量與屬性同名,順序隨意。
第11題
console.log(Object.assign([1,?2,?3],?[4,?5]))
復(fù)制代碼
答案
[4, 5, 3]
解析
是不是從來沒有用assign方法合并過數(shù)組?assign方法可以用于處理數(shù)組,不過會(huì)把數(shù)組視為對象,比如這里會(huì)把目標(biāo)數(shù)組視為是屬性為0、1、2的對象,所以源數(shù)組的0、1屬性的值覆蓋了目標(biāo)對象的值。
第12題
var?x=1
switch(x++)
{
??case?0:?++x
??case?1:?++x
??case?2:?++x
}
console.log(x)
復(fù)制代碼
答案
4
解析
這題考查的是自增運(yùn)算符的前綴版和后綴版,以及switch的語法,后綴版的自增運(yùn)算符會(huì)在語句被求值后才發(fā)生,所以x會(huì)仍以1的值去匹配case分支,那么顯然匹配到為1的分支,此時(shí),x++生效,x變成2,再執(zhí)行++x,變成3,因?yàn)闆]有break語句,所以會(huì)進(jìn)入當(dāng)前case后面的分支,所以再次++x,最終變成4。
第13題
console.log(typeof?undefined?==?typeof?NULL)
console.log(typeof?function?()?{}?==?typeof?class?{})
復(fù)制代碼
答案
true、true
解析
1.首先不要把NULL看成是null,js的關(guān)鍵字是區(qū)分大小寫的,所以這就是一個(gè)普通的變量,而且沒有聲明,typeof對沒有聲明的變量使用是不會(huì)報(bào)錯(cuò)的,返回'undefined',typeof對undefined使用也是'undefined',所以兩者相等
2.typeof對函數(shù)使用返回'function',class只是es6新增的語法糖,本質(zhì)上還是函數(shù),所以兩者相等
第14題
var?count?=?0
console.log(typeof?count?===?"number")
console.log(!!typeof?count?===?"number")
復(fù)制代碼
答案
true、false
解析
1.沒啥好說的,typeof對數(shù)字類型返回'number'。
2.這題考查的是運(yùn)算符優(yōu)先級的問題,邏輯非!的優(yōu)先級比全等===高,所以先執(zhí)行!!typeof count,結(jié)果為true,然后執(zhí)行true === 'number',結(jié)果當(dāng)然為false,可以點(diǎn)擊這里查看優(yōu)先級列表:點(diǎn)我[1]。
第15題
"use?strict"
a?=?1
var?a?=?2
console.log(window.a)
console.log(a)
復(fù)制代碼
答案
2、2
解析
var聲明會(huì)把變量提升到當(dāng)前作用域頂部,所以a=1并不會(huì)報(bào)錯(cuò),另外在全局作用域下使用var聲明變量,該變量會(huì)變成window的一個(gè)屬性,以上兩點(diǎn)都和是否在嚴(yán)格模式下無關(guān)。
第16題
var?i?=?1
function?b()?{
??console.log(i)
}
function?a()?{
??var?i?=?2
??b()
}
a()
復(fù)制代碼
答案
1
解析
這道題考察的是作用域的問題,作用域其實(shí)就是一套變量的查找規(guī)則,每個(gè)函數(shù)在執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)執(zhí)行上下文,其中會(huì)關(guān)聯(lián)一個(gè)變量對象,也就是它的作用域,上面保存著該函數(shù)能訪問的所有變量,另外上下文中的代碼在執(zhí)行時(shí)還會(huì)創(chuàng)建一個(gè)作用域鏈,如果某個(gè)標(biāo)識符在當(dāng)前作用域中沒有找到,會(huì)沿著外層作用域繼續(xù)查找,直到最頂端的全局作用域,因?yàn)閖s是詞法作用域,在寫代碼階段就作用域就已經(jīng)確定了,換句話說,是在函數(shù)定義的時(shí)候確定的,而不是執(zhí)行的時(shí)候,所以a函數(shù)是在全局作用域中定義的,雖然在b函數(shù)內(nèi)調(diào)用,但是它只能訪問到全局的作用域而不能訪問到b函數(shù)的作用域。
第17題
var?obj?=?{
??name:?'abc',
??fn:?()?=>?{
????console.log(this.name)
??}
};
obj.name?=?'bcd'
obj.fn()
復(fù)制代碼
答案
undefined
解析
這道題考察的是this的指向問題,箭頭函數(shù)執(zhí)行的時(shí)候上下文是不會(huì)綁定this的,所以它里面的this取決于外層的this,這里函數(shù)執(zhí)行的時(shí)候外層是全局作用域,所以this指向window,window對象下沒有name屬性,所以是undefined。
第18題
const?obj?=?{
??a:?{
????a:?1
??}
};
const?obj1?=?{
??a:?{
????b:?1
??}
};
console.log(Object.assign(obj,?obj1))
復(fù)制代碼
答案
{a: {b: 1}}
解析
這道題很簡單,因?yàn)閍ssign方法執(zhí)行的是淺拷貝,所以源對象的a屬性會(huì)直接覆蓋目標(biāo)對象的a屬性。
第19題
console.log(a)
var?a?=?1
var?getNum?=?function()?{
??a?=?2
}
function?getNum()?{
??a?=?3
}
console.log(a)
getNum()
console.log(a)
復(fù)制代碼
答案
undefined、1、2
解析
首先因?yàn)関ar聲明的變量提升作用,所以a變量被提升到頂部,未賦值,所以第一個(gè)打印出來的是undefined。
接下來是函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別,函數(shù)聲明會(huì)有提升作用,在代碼執(zhí)行前就把函數(shù)提升到頂部,在執(zhí)行上下文上中生成函數(shù)定義,所以第二個(gè)getNum會(huì)被最先提升到頂部,然后是var聲明getNum的提升,但是因?yàn)間etNum函數(shù)已經(jīng)被聲明了,所以就不需要再聲明一個(gè)同名變量,接下來開始執(zhí)行代碼,執(zhí)行到var getNum = fun...時(shí),雖然聲明被提前了,但是賦值操作還是留在這里,所以getNum被賦值為了一個(gè)函數(shù),下面的函數(shù)聲明直接跳過,最后,getNum函數(shù)執(zhí)行前a打印出來還是1,執(zhí)行后,a被修改成了2,所以最后打印出來的2。
第20題
var?scope?=?'global?scope'
function?a(){
??function?b(){?
????console.log(scope)
??}
??return?b
??var?scope?=?'local?scope'
}
a()()
復(fù)制代碼
答案
undefined
解析
這題考查的還是變量提升和作用域的問題,雖然var聲明是在return語句后面,但還是會(huì)提升到a函數(shù)作用域的頂部,然后又因?yàn)樽饔糜蚴窃诤瘮?shù)定義的時(shí)候確定的,與調(diào)用位置無關(guān),所以b的上層作用域是a函數(shù),scope在b自身的作用域里沒有找到,向上查找找到了自動(dòng)提升的并且未賦值的scope變量,所以打印出undefined。
第21題
function?fn?(){?
??console.log(this)?
}
var?arr?=?[fn]
arr[0]()
復(fù)制代碼
答案
打印出arr數(shù)組本身
解析
函數(shù)作為某個(gè)對象的方法調(diào)用,this指向該對象,數(shù)組顯然也是對象,只不過我們都習(xí)慣了對象引用屬性的方法:obj.fn,但是實(shí)際上obj['fn']引用也是可以的。
第22題
var?a?=?1
function?a(){}
console.log(a)
var?b
function?b(){}
console.log(b)
function?b(){}
var?b
console.log(b)
復(fù)制代碼
答案
1、b函數(shù)本身、b函數(shù)本身
解析
這三小題都涉及到函數(shù)聲明和var聲明,這兩者都會(huì)發(fā)生提升,但是函數(shù)會(huì)優(yōu)先提升,所以如果變量和函數(shù)同名的話,變量的提升就忽略了。
1.提升完后,執(zhí)行到賦值代碼,a被賦值成了1,函數(shù)因?yàn)橐呀?jīng)聲明提升了,所以跳過,最后打印a就是1。
2.和第一題類似,只是b沒有賦值操作,那么執(zhí)行到這兩行相當(dāng)于都沒有操作,b當(dāng)然是函數(shù)。
3.和第二題類似,只是先后順序換了一下,但是并不影響兩者的提升順序,仍是函數(shù)優(yōu)先,同名的var聲明提升忽略,所以打印出b還是函數(shù)。
第23題
function?Foo()?{
??getName?=?function?()?{?console.log(1)?}
??return?this
}
Foo.getName?=?function?()?{?console.log(2)?}
Foo.prototype.getName?=?function?()?{?console.log(3)?}
var?getName?=?function?()?{?console.log(4)?}
function?getName()?{?console.log(5)?}
//請寫出以下輸出結(jié)果:
Foo.getName()
getName()
Foo().getName()
getName()
new?Foo.getName()
new?Foo().getName()
new?new?Foo().getName()
復(fù)制代碼
答案
2、4、1、1、2、3、3
解析
這是一道綜合性題目,首先getName函數(shù)聲明會(huì)先提升,然后getName函數(shù)表達(dá)式提升,但是因?yàn)楹瘮?shù)聲明提升在線,所以忽略函數(shù)表達(dá)式的提升,然后開始執(zhí)行代碼,執(zhí)行到var getName= ...時(shí),修改了getName的值,賦值成了打印4的新函數(shù)。
1.執(zhí)行Foo函數(shù)的靜態(tài)方法,打印出2。
2.執(zhí)行g(shù)etName,當(dāng)前getName是打印出4的那個(gè)函數(shù)。
3.執(zhí)行Foo函數(shù),修改了全局變量getName,賦值成了打印1的函數(shù),然后返回this,因?yàn)槭窃谌汁h(huán)境下執(zhí)行,所以this指向window,因?yàn)間etName已經(jīng)被修改了,所以打印出1。
4.因?yàn)間etName沒有被重新賦值,所以再執(zhí)行仍然打印出1。
5.new操作符是用來調(diào)用函數(shù)的,所以new Foo.getName()相當(dāng)于new (Foo.getName)(),所以new的是Foo的靜態(tài)方法getName,打印出2。
6.因?yàn)辄c(diǎn)運(yùn)算符(.)的優(yōu)先級和new是一樣高的,所以從左往右執(zhí)行,相當(dāng)于(new Foo()).getName(),對Foo使用new調(diào)用會(huì)返回一個(gè)新創(chuàng)建的對象,然后執(zhí)行該對象的getName方法,該對象本身并沒有該方法,所以會(huì)從Foo的原型對象上查找,找到了,所以打印出3。
7.和上題一樣,點(diǎn)運(yùn)算符(.)的優(yōu)先級和new一樣高,另外new是用來調(diào)用函數(shù)的,所以new new Foo().getName()相當(dāng)于new ((new Foo()).getName)(),括號里面的就是上一題,所以最后找到的是Foo原型上的方法,無論是直接調(diào)用,還是通過new調(diào)用,都會(huì)執(zhí)行該方法,所以打印出3。
第24題
const?person?=?{
?address:?{
??country:"china",
??city:"hangzhou"
?},
?say:?function?()?{
??console.log(`it's?${this.name},?from?${this.address.country}`)
?},
?setCountry:function?(country)?{
??this.address.country=country
?}
}
const?p1?=?Object.create(person)
const?p2?=?Object.create(person)
p1.name?=?"Matthew"
p1.setCountry("American")
p2.name?=?"Bob"
p2.setCountry("England")
p1.say()
p2.say()
復(fù)制代碼
答案
it's Matthew, from England
it's Bob, from England
解析
Object.create方法會(huì)創(chuàng)建一個(gè)對象,并且將該對象的__proto__屬性指向傳入的對象,所以p1和p2兩個(gè)對象的原型對象指向了同一個(gè)對象,接著給p1添加了一個(gè)name屬性,然后調(diào)用了p1的setCountry方法,p1本身是沒有這個(gè)方法的,所以會(huì)沿著原型鏈進(jìn)行查找,在它的原型上,也就是person對象上找到了這個(gè)方法,執(zhí)行這個(gè)方法會(huì)給address對象的country屬性設(shè)置傳入的值,p1本身也是沒有address屬性的,但是和name屬性不一樣,address屬性在原型對象上找到了,并且因?yàn)槭莻€(gè)引用值,所以會(huì)成功修改它的country屬性,接著對p2的操作也是一樣,然后因?yàn)樵椭写嬖谝弥禃?huì)在所有實(shí)例中共享,所以p1和p2它們引用的address也是同一個(gè)對象,一個(gè)實(shí)例修改了,會(huì)反映到所有實(shí)例上,所以p2的修改會(huì)覆蓋p1的修改,最終country的值為England。
第25題
setTimeout(function()?{
??console.log(1)
},?0)
new?Promise(function(resolve)?{
??console.log(2)
??for(?var?i=0?;?i<10000?;?i++?)?{
????i?==?9999?&&?resolve()
??}
??console.log(3)
}).then(function()?{
??console.log(4)
})
console.log(5)
復(fù)制代碼
答案
2、3、5、4、1
解析
這道題顯然考察的是事件循環(huán)的知識點(diǎn)。
js是一門單線程的語言,但是為了執(zhí)行一些異步任務(wù)時(shí)不阻塞代碼,以及避免等待期間的資源浪費(fèi),js存在事件循環(huán)的機(jī)制,單線程指的是執(zhí)行js的線程,稱作主線程,其他還有一些比如網(wǎng)絡(luò)請求的線程、定時(shí)器的線程,主線程在運(yùn)行時(shí)會(huì)產(chǎn)生執(zhí)行棧,棧中的代碼如果調(diào)用了異步api的話則會(huì)把事件添加到事件隊(duì)列里,只要該異步任務(wù)有了結(jié)果便會(huì)把對應(yīng)的回調(diào)放到【任務(wù)隊(duì)列】里,當(dāng)執(zhí)行棧中的代碼執(zhí)行完畢后會(huì)去讀取任務(wù)隊(duì)列里的任務(wù),放到主線程執(zhí)行,當(dāng)執(zhí)行??樟擞謺?huì)去檢查,如此往復(fù),也就是所謂的事件循環(huán)。
異步任務(wù)又分為【宏任務(wù)】(比如setTimeout、setInterval)和【微任務(wù)】(比如promise),它們分別會(huì)進(jìn)入不同的隊(duì)列,執(zhí)行棧為空完后會(huì)優(yōu)先檢查微任務(wù)隊(duì)列,如果有微任務(wù)的話會(huì)一次性執(zhí)行完所有的微任務(wù),然后去宏任務(wù)隊(duì)列里檢查,如果有則取出一個(gè)任務(wù)到主線程執(zhí)行,執(zhí)行完后又會(huì)去檢查微任務(wù)隊(duì)列,如此循環(huán)。
回到這題,首先整體代碼作為一個(gè)宏任務(wù)開始執(zhí)行,遇到setTimeout,相應(yīng)回調(diào)會(huì)進(jìn)入宏任務(wù)隊(duì)列,然后是promise,promise的回調(diào)是同步代碼,所以會(huì)打印出2,for循環(huán)結(jié)束后調(diào)用了resolve,所以then的回調(diào)會(huì)被放入微任務(wù)隊(duì)列,然后打印出3,最后打印出5,到這里當(dāng)前的執(zhí)行棧就空了,那么先檢查微任務(wù)隊(duì)列,發(fā)現(xiàn)有一個(gè)任務(wù),那么取出來放到主線程執(zhí)行,打印出4,最后檢查宏任務(wù)隊(duì)列,把定時(shí)器的回調(diào)放入主線程執(zhí)行,打印出1。
第26題
console.log('1');
setTimeout(function()?{
??console.log('2');
??process.nextTick(function()?{
????console.log('3');
??});
??new?Promise(function(resolve)?{
????console.log('4');
????resolve();
??}).then(function()?{
????console.log('5');
??});
});?
process.nextTick(function()?{
??console.log('6');
});
new?Promise(function(resolve)?{
??console.log('7');
??resolve();
}).then(function()?{
??console.log('8');
});
setTimeout(function()?{
??console.log('9');
??process.nextTick(function()?{
????console.log('10');
??})?
??new?Promise(function(resolve)?{
????console.log('11');
????resolve();
??}).then(function()?{
????console.log('12')
??});
})
復(fù)制代碼
答案
1、7、6、8、2、4、9、11、3、10、5、12
解析
這道題和上一題差不多,但是出現(xiàn)了process.nextTick,所以顯然是在node環(huán)境下,node也存在事件循環(huán)的概念,但是和瀏覽器的有點(diǎn)不一樣,nodejs中的宏任務(wù)被分成了幾種不同的階段,兩個(gè)定時(shí)器屬于timers階段,setImmediate屬于check階段,socket的關(guān)閉事件屬于close callbacks階段,其他所有的宏任務(wù)都屬于poll階段,除此之外,只要執(zhí)行到前面說的某個(gè)階段,那么會(huì)執(zhí)行完該階段所有的任務(wù),這一點(diǎn)和瀏覽器不一樣,瀏覽器是每次取一個(gè)宏任務(wù)出來執(zhí)行,執(zhí)行完后就跑去檢查微任務(wù)隊(duì)列了,但是nodejs是來都來了,一次全部執(zhí)行完該階段的任務(wù)好了,那么process.nextTick和微任務(wù)在什么階段執(zhí)行呢,在前面說的每個(gè)階段的后面都會(huì)執(zhí)行,但是process.nextTick會(huì)優(yōu)先于微任務(wù),一圖勝千言:

理解了以后再來分析這道題就很簡單了,首先執(zhí)行整體代碼,先打印出1,setTimeout回調(diào)扔進(jìn)timers隊(duì)列,nextTick的扔進(jìn)nextTick的隊(duì)列,promise的回調(diào)是同步代碼,執(zhí)行后打印出7,then回調(diào)扔進(jìn)微任務(wù)隊(duì)列,然后又是一個(gè)setTimeout回調(diào)扔進(jìn)timers隊(duì)列,到這里當(dāng)前節(jié)點(diǎn)就結(jié)束了,檢查nextTick和微任務(wù)隊(duì)列,nextTick隊(duì)列有任務(wù),執(zhí)行后打印出6,微任務(wù)隊(duì)列也有,打印出8,接下來按順序檢查各個(gè)階段,check隊(duì)列、close callbacks隊(duì)列都沒有任務(wù),到了timers階段,發(fā)現(xiàn)有兩個(gè)任務(wù),先執(zhí)行第一個(gè),打印出2,然后nextTick的扔進(jìn)nextTick的隊(duì)列,執(zhí)行promise打印出4,then回調(diào)扔進(jìn)微任務(wù)隊(duì)列,再執(zhí)行第二個(gè)setTimeout的回調(diào),打印出9,然后和剛才一樣,nextTick的扔進(jìn)nextTick的隊(duì)列,執(zhí)行promise打印出11,then回調(diào)扔進(jìn)微任務(wù)隊(duì)列,到這里timers階段也結(jié)束了,執(zhí)行nextTick隊(duì)列的任務(wù),發(fā)現(xiàn)又兩個(gè)任務(wù),依次執(zhí)行,打印出3和10,然后檢查微任務(wù)隊(duì)列,也是兩個(gè)任務(wù),依次執(zhí)行,打印出5和12,到這里是有隊(duì)列都清空了。
參考資料
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FOperators%2FOperator_Precedence%23table
關(guān)于本文
來源:街角小林
https://juejin.cn/post/6989433079760683022
Node 社群
我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。
如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個(gè)小忙:
1. 點(diǎn)個(gè)「在看」,讓更多人也能看到這篇文章 2. 訂閱官方博客?www.inode.club?讓我們一起成長 點(diǎn)贊和在看就是最大的支持??
