<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          2021 年 JS 燒腦面試題大賞

          共 8200字,需瀏覽 17分鐘

           ·

          2022-02-22 00:21


          大廠技術(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ì)列都清空了。

          參考資料

          [1]

          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)贊和在看就是最大的支持??

          瀏覽 61
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩国产一区二区 | 国产激情无码视频网站在线 | 久久人妻电影 | 日韩一级网站 | 欧美黑人又大又长 |