JavaScript 經(jīng)典面試題解析(持續(xù)更新)

1.說出并解釋下列代碼的輸出結(jié)果:

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);
}
Foo.getName(); //2
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3

解析

(1) . Foo.getName()

直接調(diào)用 Foo 的靜態(tài)方法 ,Foo.getName被重新復制,所以打印2

(2). Foo().getName()

Foo() 就是普通函數(shù),返回的this是window,后面調(diào)用window.getName(),而winsow下的getName在第一步調(diào)用的時候被重新復制,所以打印1

(3). getName()

調(diào)用window中的getName,由于window中的getName在(2)中執(zhí)行的時候被重新賦值,所以打印1.
(如果將(3)放在(2)前面執(zhí)行,打印結(jié)果就為4)

(4). new Foo.getName()

同(1)效果一樣,直接調(diào)用 Foo 的靜態(tài)方法 ,Foo.getName被重新復制,所以打印2

(5). new Foo().getName()

通過實例訪問 Foo原型鏈上的方法,所以打印3

(6) . new new Foo().getName()

首先 new Foo() 得到一個空對象 {}
第二步向空對象中添加一個屬性 getName,值為一個函數(shù)
第三步 new {}.getName()
等價于 var bar = new (new Foo().getName)(); console.log(bar) 先 new Foo 得到的實例對象上的 getName 方法,再將這個原型上 getName 方法當做構(gòu)造函數(shù)繼續(xù) new ,所以執(zhí)行原型上的方法,打印 3

2.寫出打印結(jié)果,并分析出原因

var a = 10
var obj = {
  a: 100,
  pro: {
    getPro: () => {
      console.log(this.a)
    },
  },
}
obj.pro.getPro() //10

解析

getPro是一個箭頭函數(shù)的方法,在箭頭函數(shù)中不會創(chuàng)建自己的this,只會集成作用于鏈上一層的this,所以這里的this指向為window,打印結(jié)果為10

3.寫出打印結(jié)果,并分析出原因

var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
console.log(a.x) // undefined
console.log(b.x)  // {n:2}

解析

首先,雙等號的執(zhí)行順序是從左到右
那么a.x = a = {n:2} 就等價于 a.x = a ; a={n:2}
由于b = a 屬于淺拷貝,那么再對a賦值時也會傳遞給b, 經(jīng)過a.x處理過后b.x = {n:1},然后將{n:2}賦值給a同時會傳遞給b.x
此時的a={n:2} , b={n: 1,x: {n: 2}}
所以打印結(jié)果依次為 undefined , {n: 2}

4.寫出打印結(jié)果,并分析出原因

var length = 10 

function fn(){
    console.log(this.length)
}
var obj = {
    length : 5 ,
    method :function (fn){
        fn();
        arguments[0]()
    }
}

obj.method(fn,1)

解析

首先,我在全局定義了一個變量length,一個對象obj和一個函數(shù)fn . length賦值為10。
接下來是fn函數(shù),輸出this.length,對象obj中obj.length是5,obj.method是一個函數(shù),這個函數(shù)里面調(diào)用了fn函數(shù),arguments是一個偽數(shù)組,代表method函數(shù)的實參
method函數(shù)當中調(diào)用的fn函數(shù)是全局當中的函數(shù),所以this指向的是window,this.length就是10,arguments0代表的就是調(diào)用arguments里面的第一項,也就是傳參進來的fn,所以這個this指向的就是arguments,arguments.length的值為2
所以最后的打印結(jié)果為10 ,2

5.寫出打印結(jié)果,并分析出原因

function a(xx){
    this.x = xx
    return this
}

var x = a(5)
var y = a(6)

console.log(x.x)
console.log(y.x)

解析

首先,我們在全局定義了一個變量x,一個變量y,和一個函數(shù)a,函數(shù)a當中的this.x等于接受到的參數(shù),返回this,.這里要注意,返回的是this,不是this.x 接下來我們給X賦值 值為a(5) ,又給y賦值,值為a(6).最后我們輸出x.x 和y.x

分析完代碼的定義, 我們來看輸出結(jié)果, a(5) 執(zhí)行的時候傳遞一個參數(shù)為5,調(diào)用函數(shù)a, 返回this 此時的this指向window, 所以 x = window , 但是后面又執(zhí)行了y=a(6), 再次調(diào)用fn 將 this.x修改為6, 所以此時window中的 x 為6, 返回結(jié)果為window, 所以y=window,
由于 x =6 所以x.x = undefined, y=window, window中的x為6 , 所以y.x 為 6
打印結(jié)果依次為 undefined 和 6

6.閱讀下面代碼,寫出打印結(jié)果的先后順序

new Promise(function (resolve, reject) {
        console.log('A')
        resolve()
      }) //A J B H K C I L D G E F
        .then(function (resolve, reject) {
          new Promise(function (resolve, reject) {
            console.log('B')
            resolve()
          })
            .then(function () {
              console.log('c')
            })
            .then(function () {
              new Promise(function (resolve, reject) {
                console.log('D')
                resolve()
              })
                .then(function () {
                  console.log('E')
                })
                .then(function () {
                  console.log('F')
                })
              console.log('G')
            })
          console.log('H')
        })
        .then(function () {
          console.log('I')
        })

      new Promise(function (resolve, reject) {
        console.log('J')
        resolve()
      })
        .then(function () {
          console.log('K')
        })
        .then(function () {
          console.log('L')
        })
打印結(jié)果

A J B H K C I L D G E F

解析:

注:下文中的輪次只是為了方便理解
首先,我們來看最外層的兩個newPromise,里面同步語句會先執(zhí)行,所以最先打印出來的是A和J
因為每個Promise都會產(chǎn)生一個微任務(wù),所以到第一輪的微任務(wù)中Promise的第一個then會進入到第一輪的微任務(wù)中,下面我們來單獨看這兩個then. 第一個Promise的第一個then里面又new了一個新的Promise,這個新的Promise產(chǎn)生一個微任務(wù),本輪的微任務(wù)已經(jīng)在執(zhí)行中了,所以這個微任務(wù)會排到下一個微任務(wù)隊列的第一位,還是先執(zhí)行里面的同步語句,打印B和H ,之后運行低二個Promise的第一個then,打印K

第一輪的微任務(wù)執(zhí)行完畢,開始第二輪微任務(wù),先執(zhí)行第三個Promise的第一個then,打印C,繼續(xù)執(zhí)行第一個Promise的第二個then,打印I,最后執(zhí)行第二個Promise的第二個then,打印K
第三輪微任務(wù)開始,執(zhí)行第三個Promise的第二個then,這個then里面又new了一個新的Promise,同理,新的Promise(第四個)產(chǎn)生的微任務(wù)放入下一輪第一個執(zhí)行,此時執(zhí)行同步語句,打印D和G
第四輪微任務(wù)開始執(zhí)行第四個Promise的第一個then,打印E
第五輪微任務(wù)開始執(zhí)行第四個Promise的第二個then,打印F
綜上,我們最后得到的結(jié)果就是:
A
J
B
H
K
C
I
L
D
G
E
F

7.閱讀下面代碼,寫出他們的打印順序

 new Promise((resolve,reject)=>{
  console.log('A')
  setTimeout(() => {
    console.log('B')
  }, 0);
  console.log('C')
  resolve()
  console.log('D')
}).then((resolve,reject)=>{
  console.log('E')
  new Promise((resolve,reject)=>{
    console.log("F")
    resolve()
    console.log('G')
  }).then(()=>{
     setTimeout(()=>{
       console.log('H')
     })
    console.log('I')
  }).then(()=>{
    console.log('J')
  })
}).then(()=>{
  console.log('K')
})
setTimeout(() => {
  console.log('L')
}, 0);
new Promise((resolve,reject)=>{
  console.log('M')
  resolve()
}).then(()=>{
  setTimeout(() => {
    new Promise((resolve,reject)=>{
      console.log('N')
      resolve()
    }).then(()=>{
       setTimeout(()=>{
         console.log('O')
       },0)
    }).then(()=>{
      console.log('p')
    })
  }, 0);
})
console.log('Q')
打印結(jié)果

ACDMQEFGKJBLNPHO

解析

首先,我們要知道微任務(wù)會先于宏任務(wù)執(zhí)行,知道了這一點,我們來看下面的代碼.
現(xiàn)在,我們將微任務(wù)列表和宏任務(wù)列表分開梳理,最后再合并結(jié)果
先看微任務(wù),第一層為Promise--promise--console.log,第一個Promise中的setTimeOut創(chuàng)建宏任務(wù)1,最外層的setTimeOut創(chuàng)建宏任務(wù)2
所以第一輪按從上到下順序打印 A,C,D,M,Q

然后看第二輪微任務(wù),分別為第一個Promise中的then,和第二個Promise中的then,由于第二個Promise中的then中執(zhí)行setTimeOut,創(chuàng)建宏任務(wù)3,所以第二輪先打印 E,然后創(chuàng)建Promsie,打印F和G,目前的及結(jié)果為A,C,D,M,Q,E,F,G

然后看第三輪,由于第二個Promise中的then中創(chuàng)建了宏任務(wù),所以第三輪微任務(wù)只在第一個promise中的第二個then中,從代碼中可以看出,第一個Promise中的第一個then執(zhí)行后,有兩個then,前者創(chuàng)建宏任務(wù)4,打印I,后者打印K,所以目前打印順序為A,C,D,M,Q,E,F,G,I,K

然后第四輪微任務(wù)只剩下第三輪中的第二個then所調(diào)用的then了,打印結(jié)果為J,至此,微任務(wù)已經(jīng)執(zhí)行完畢,結(jié)果順序為:
A,C,D,M,Q,E,F,G,I,K,J

開始執(zhí)行宏任務(wù),第一輪按上方創(chuàng)建順序為宏任務(wù)1,2,3,4
宏任務(wù)1打印B,宏任務(wù)2打印L,宏任務(wù)3 第一輪微任務(wù)打印N,然后第二輪創(chuàng)建宏任務(wù)5,第三輪打印P, 宏任務(wù)4打印H
第一輪宏任務(wù) 打印順序為B,L,N,P,H

第二輪宏任務(wù)執(zhí)行宏任務(wù)3創(chuàng)建的宏任務(wù)5,打印O
至此,宏任務(wù)執(zhí)行完畢,結(jié)果為B,L,N,P,H

由于宏任務(wù)是在微任務(wù)執(zhí)行完成后執(zhí)行,所以需要將宏任務(wù)的打印結(jié)果連接在微任務(wù)后方,結(jié)果為
A
C
D
M
Q
E
F
G
I
K
J
B
L
N
P
H
O

8.力扣 - 檸檬水找零

在檸檬水攤上,每一杯檸檬水的售價為5美元
顧客排隊購買你的產(chǎn)品,(按賬單bills支付的順序)一次購買一杯
每位顧客只買一杯檸檬水,然后向你付5美元、10美元或者20美元。你必須給每個顧客正確找零,也就是說凈交易是每位顧客向你支付5美元
注意,一開始你手頭沒有任何零錢
如果你能給每位顧客正確找零,返回true,否則返回false

function func(bills) {
  let a = 0 //5元
  let b = 0 //10元
  //循環(huán)所有數(shù)據(jù)
  for (let i = 0; i < bills.length; i++) {
    //如果顧客給的是5元
    if (bills[i] === 5) {
      // 手中的5元加一
      a += 1
    } else if (bills[i] === 10) {
      //顧客給的是10元
      //手中的5元減一,10元加一
      b += 1
      a -= 1
    } else {
      //顧客給的是20元,有兩種找零方式
      // 找3個5元, 或者找1個10元1個5元
      if (b >= 1) {
        //10元足夠的時候優(yōu)先找一個10元一個5元
        //10元減一,5元減一
        a -= 1
        b -= 1
      } else {
        //10元不夠是找三個5元, 5元減3
        a -= 3
      }
    }
    //當5元也不夠時說明沒有零錢了,返回false
    if (a < 0) {
      return false
    }
  }
  //循環(huán)完成,說明零錢足夠,返回true
  return true
}

let result = func([5, 5, 5, 10, 20])
console.log(result)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容