JavaScript 经典面试题解析(持续更新)

1.说出并解释下列代码的输出结果:

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()

直接调用 Foo 的静态方法 ,Foo.getName被重新复制,所以打印2

(2). Foo().getName()

Foo() 就是普通函数,返回的this是window,后面调用window.getName(),而winsow下的getName在第一步调用的时候被重新复制,所以打印1

(3). getName()

调用window中的getName,由于window中的getName在(2)中执行的时候被重新赋值,所以打印1.
(如果将(3)放在(2)前面执行,打印结果就为4)

(4). new Foo.getName()

同(1)效果一样,直接调用 Foo 的静态方法 ,Foo.getName被重新复制,所以打印2

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

通过实例访问 Foo原型链上的方法,所以打印3

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

首先 new Foo() 得到一个空对象 {}
第二步向空对象中添加一个属性 getName,值为一个函数
第三步 new {}.getName()
等价于 var bar = new (new Foo().getName)(); console.log(bar) 先 new Foo 得到的实例对象上的 getName 方法,再将这个原型上 getName 方法当做构造函数继续 new ,所以执行原型上的方法,打印 3

2.写出打印结果,并分析出原因

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

解析

getPro是一个箭头函数的方法,在箭头函数中不会创建自己的this,只会集成作用于链上一层的this,所以这里的this指向为window,打印结果为10

3.写出打印结果,并分析出原因

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

解析

首先,双等号的执行顺序是从左到右
那么a.x = a = {n:2} 就等价于 a.x = a ; a={n:2}
由于b = a 属于浅拷贝,那么再对a赋值时也会传递给b, 经过a.x处理过后b.x = {n:1},然后将{n:2}赋值给a同时会传递给b.x
此时的a={n:2} , b={n: 1,x: {n: 2}}
所以打印结果依次为 undefined , {n: 2}

4.写出打印结果,并分析出原因

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和一个函数fn . length赋值为10。
接下来是fn函数,输出this.length,对象obj中obj.length是5,obj.method是一个函数,这个函数里面调用了fn函数,arguments是一个伪数组,代表method函数的实参
method函数当中调用的fn函数是全局当中的函数,所以this指向的是window,this.length就是10,arguments0代表的就是调用arguments里面的第一项,也就是传参进来的fn,所以这个this指向的就是arguments,arguments.length的值为2
所以最后的打印结果为10 ,2

5.写出打印结果,并分析出原因

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,和一个函数a,函数a当中的this.x等于接受到的参数,返回this,.这里要注意,返回的是this,不是this.x 接下来我们给X赋值 值为a(5) ,又给y赋值,值为a(6).最后我们输出x.x 和y.x

分析完代码的定义, 我们来看输出结果, a(5) 执行的时候传递一个参数为5,调用函数a, 返回this 此时的this指向window, 所以 x = window , 但是后面又执行了y=a(6), 再次调用fn 将 this.x修改为6, 所以此时window中的 x 为6, 返回结果为window, 所以y=window,
由于 x =6 所以x.x = undefined, y=window, window中的x为6 , 所以y.x 为 6
打印结果依次为 undefined 和 6

6.阅读下面代码,写出打印结果的先后顺序

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')
        })
打印结果

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

解析:

注:下文中的轮次只是为了方便理解
首先,我们来看最外层的两个newPromise,里面同步语句会先执行,所以最先打印出来的是A和J
因为每个Promise都会产生一个微任务,所以到第一轮的微任务中Promise的第一个then会进入到第一轮的微任务中,下面我们来单独看这两个then. 第一个Promise的第一个then里面又new了一个新的Promise,这个新的Promise产生一个微任务,本轮的微任务已经在执行中了,所以这个微任务会排到下一个微任务队列的第一位,还是先执行里面的同步语句,打印B和H ,之后运行低二个Promise的第一个then,打印K

第一轮的微任务执行完毕,开始第二轮微任务,先执行第三个Promise的第一个then,打印C,继续执行第一个Promise的第二个then,打印I,最后执行第二个Promise的第二个then,打印K
第三轮微任务开始,执行第三个Promise的第二个then,这个then里面又new了一个新的Promise,同理,新的Promise(第四个)产生的微任务放入下一轮第一个执行,此时执行同步语句,打印D和G
第四轮微任务开始执行第四个Promise的第一个then,打印E
第五轮微任务开始执行第四个Promise的第二个then,打印F
综上,我们最后得到的结果就是:
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')
打印结果

ACDMQEFGKJBLNPHO

解析

首先,我们要知道微任务会先于宏任务执行,知道了这一点,我们来看下面的代码.
现在,我们将微任务列表和宏任务列表分开梳理,最后再合并结果
先看微任务,第一层为Promise--promise--console.log,第一个Promise中的setTimeOut创建宏任务1,最外层的setTimeOut创建宏任务2
所以第一轮按从上到下顺序打印 A,C,D,M,Q

然后看第二轮微任务,分别为第一个Promise中的then,和第二个Promise中的then,由于第二个Promise中的then中执行setTimeOut,创建宏任务3,所以第二轮先打印 E,然后创建Promsie,打印F和G,目前的及结果为A,C,D,M,Q,E,F,G

然后看第三轮,由于第二个Promise中的then中创建了宏任务,所以第三轮微任务只在第一个promise中的第二个then中,从代码中可以看出,第一个Promise中的第一个then执行后,有两个then,前者创建宏任务4,打印I,后者打印K,所以目前打印顺序为A,C,D,M,Q,E,F,G,I,K

然后第四轮微任务只剩下第三轮中的第二个then所调用的then了,打印结果为J,至此,微任务已经执行完毕,结果顺序为:
A,C,D,M,Q,E,F,G,I,K,J

开始执行宏任务,第一轮按上方创建顺序为宏任务1,2,3,4
宏任务1打印B,宏任务2打印L,宏任务3 第一轮微任务打印N,然后第二轮创建宏任务5,第三轮打印P, 宏任务4打印H
第一轮宏任务 打印顺序为B,L,N,P,H

第二轮宏任务执行宏任务3创建的宏任务5,打印O
至此,宏任务执行完毕,结果为B,L,N,P,H

由于宏任务是在微任务执行完成后执行,所以需要将宏任务的打印结果连接在微任务后方,结果为
A
C
D
M
Q
E
F
G
I
K
J
B
L
N
P
H
O

8.力扣 - 柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为5美元
顾客排队购买你的产品,(按账单bills支付的顺序)一次购买一杯
每位顾客只买一杯柠檬水,然后向你付5美元、10美元或者20美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付5美元
注意,一开始你手头没有任何零钱
如果你能给每位顾客正确找零,返回true,否则返回false

function func(bills) {
  let a = 0 //5元
  let b = 0 //10元
  //循环所有数据
  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元足够的时候优先找一个10元一个5元
        //10元减一,5元减一
        a -= 1
        b -= 1
      } else {
        //10元不够是找三个5元, 5元减3
        a -= 3
      }
    }
    //当5元也不够时说明没有零钱了,返回false
    if (a < 0) {
      return false
    }
  }
  //循环完成,说明零钱足够,返回true
  return true
}

let result = func([5, 5, 5, 10, 20])
console.log(result)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容