【高频面试题】大厂你来挑(一)

377 阅读6分钟

目录

  1. 如何实现sleep的效果(es5或者es6)
  2. 箭头函数中this指向举例
  3. Promise.allSettled的实现
  4. 事件循环demo-【promise中是否执行了resolve或者reject】
  5. 构造函数class 内部this的区别
  • 疑问: 五题中 2)的第一段的代码执行是undefined的原因是?

一、如何实现sleep的效果(es5或者es6)

(1)while循环的方式

function sleep(ms){
   var start=Date.now(),expire=start+ms;
   while(Date.now()<expire);
   console.log('1111');
   return;
}

执行sleep(1000)之后,休眠了1000ms之后输出了1111。上述循环的方式缺点很明显,容易造成死循环。

(2)通过promise来实现

function sleep(ms){
  var temple=new Promise(
  (resolve)=>{
  console.log(111);setTimeout(resolve,ms)
  });
  return temple
}
sleep(500).then(function(){
   //console.log(222)
})
//先输出了111,延迟500ms后输出222

(3)通过async封装

function sleep(ms){
  return new Promise((resolve)=>setTimeout(resolve,ms));
}
async function test(){
  var temple=await sleep(1000);
  console.log(1111)
  return temple
}
test();
//延迟1000ms输出了1111

(4).通过generate来实现

function* sleep(ms){
   yield new Promise(function(resolve,reject){
             console.log(111);
             setTimeout(resolve,ms);
        })  
}
sleep(500).next().value.then(function(){console.log(2222)})

二、 箭头函数中this指向举例

箭头函数定时绑定,外层函数的this为自己的this

1)普通函数


var a=11;
function Test2(){
  this.a=22;
  console.log('this1:', this);
  let b=function(){  
      console.log('this2:', this);
      console.log(this.a);
  };
  setTimeout(function(){
     console.log('this3:', this);
     console.log(this.a);
  },1000);
  
  b();
}
var t1=new Test2();

可以看到构造函数内部函数的this全部指向了 window image.png

2)构造函数中函数改为箭头函数

var a=11;
function Test2(){
  this.a=22;
  console.log('this1:', this);
  let b=()=>{  
      console.log('this2:', this);
      console.log(this.a);
  };
  setTimeout(()=>{
     console.log('this3:', this);
     console.log(this.a);
  },1000);
  
  b();
}
var t1=new Test2();

可以看到构造函数内部箭头函数的this全部指向了他的外层构造函数Test2image.png

三、Promise.allSettled的实现

1) 回顾: Promise 状态

给定一个返回Promise的异步操作,以下这些是Promise的可能状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
  • Settled: Promise要么被完成,要么被拒绝。Promise一旦达成,它的状态就不再改变。
1.1) 什么是组合

又称部分-整体模式,将对象整合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,它基于两种函数:

  • 基元函数(简短:基元)创建原子块。
  • 组合函数(简称:组合)将原子和/或复合件组合在一起以形成复合件。

对于 JS 的 Promises 来说

  • 基元函数包括:Promise.resolve()Promise.reject()
  • 组合函数:Promise.all(), Promise.race(), Promise.allSettled()

2) 实现 allSettled

allSettled 对于每个结果对象,都有一个 status 字符串。如果它的值为 fulfilled,则结果对象上存在一个 value 。如果值为 rejected,则存在一个 reason 。value(或 reason )反映了每个 promise 决议(或拒绝)的值。

MyPromise.allSettled = function (promises) {
    return new MyPromise((resolve, reject) => {
      promises = Array.isArray(promises) ? promises : []
      let len = promises.length
      const argslen = len
      // 如果传入的是一个空数组,那么就直接返回一个resolved的空数组promise对象
      if (len === 0) return resolve([])
      // 将传入的参数转化为数组,赋给args变量
      let args = Array.prototype.slice.call(promises)
      // 计算当前是否所有的 promise 执行完成,执行完毕则resolve
      const compute = () => {
        if(--len === 0) { 
          resolve(args)
        }
      }
      function resolvePromise(index, value) {
        // 判断传入的是否是 promise 类型
        if(value instanceof MyPromise) { 
          const then = value.then
          then.call(value, function(val) {
            args[index] = { status: 'fulfilled', value: val}
            compute()
          }, function(e) {
            args[index] = { status: 'rejected', reason: e }
            compute()
          })
        } else {
          args[index] = { status: 'fulfilled', value: value}
          compute()
        }
      }
   
      for(let i = 0; i < argslen; i++){
        resolvePromise(i, args[i])
      }
    })
  }

四、事件循环demo

1) demo1

需要注意,Promise构造器中resolve()的位置在Promise的构造器中无论是在resolve前或者后都是与同步任务一样去执行的。只有then中的才是 微任务

console.log('begin')
setTimeout(function () {
    console.log('setTimeout 0');
});
new Promise(function (resolve) {
    console.log('promise1');
    for (let i = 0; i < 1000; i++) {
        i === 99 && resolve();
    }
    console.log('promise2');
}).then(function () {
    console.log('then1');
    setTimeout(() => {
        console.log('setTimeout2 between promise1&2')
    })
}).then(() => {
    console.log('promise 3')
});
console.log('end')

image.png

2) demo2 Promise状态是pending 没有resolve,也没有reject。

导致成功和失败的回调都不会被执行。因为在Promise的实现中只有在构造器中执行了resolve或者reject。状态成会变更。才会赋值。 注意这里注释了resolve。导致then后面的成功CB不会被执行。

console.log('begin')
setTimeout(function () {
    console.log('setTimeout 0');
});
new Promise(function (resolve) {
    console.log('promise1');
    // resolve()   // 注意这里注释了resolve。导致then后面的成功CB不会被执行。
    console.log('promise2');
}).then(function () {
    console.log('then1');
    setTimeout(() => {
        console.log('setTimeout2 between promise1&2')
    })
}).then(() => {
    console.log('promise 3')
}).catch((err)=>{
    console.log('reject:', err)
});
console.log('end')

image.png

3) demo3 Promise.reject()

实例化时自执行函数执行了reject,那么,失败回调会被执行。

reject失败回调,首先会在then中会被捕获,其次才会在catch中会被捕获。 如果then中捕获了,catch将不会在捕获。

console.log('begin')
setTimeout(function () {
    console.log('setTimeout 0');
});
new Promise(function (resolve,reject) {
    console.log('promise1');
    // resolve()   // 注意这里注释了resolve。导致then后面的成功CB不会被执行。
    reject(0);
    console.log('promise2');
}).then(function () {
    console.log('then1');
    setTimeout(() => {
        console.log('000')
    })
}).then(() => {
    console.log('promise 3')
},(err)=>{
    console.log('第二个then中的reject:', err)
}).catch((err)=>{
    console.log('catch 中 reject:', err)
});
console.log('end')

image.png

构造函数class 内部this的区别

1) 构造函数内部this

window.name = 'aaa';
function Atest1(){
    this.name = 123;    
};
Atest1.prototype={
    getA() { 
          console.log(this);
          return this.name + 1; 
    },
    getB:()=>{ 
          console.log(this);
          return this.name + 1; 
    }
}
let a = new Atest1();
let funcA = a.getA;
let funcB = a.getB;
funcA();
funcB();

image.png

此调用方式,相当于window.funcA(),所以,无论原型上的方法是否是箭头函数。那么,this,指向的都是widow。

换一种调用方式

window.name = 'aaa';
function Atest1(){
    this.name = 123;    
};
Atest1.prototype={
    getA() { 
          console.log(this,this.name);
          return this.name + 1; 
    },
    getB:()=>{ 
          console.log(this);
          return this.name + 1; 
    }
}
let a = new Atest1();
let funcA = a.getA();

这种调用方式很明显,getA是a调用的所以指向a image.png

image.png

箭头函数

理论上箭头函数如果是普通函数的话 其中的 this 会指向 a, 实际上因为是箭头函数所以 指向的是 window

a.getB();

image.png

这种调用方式很明显,getA是a调用的所以指向a

2)(?) class内部this

window.name = 'aaa';
class Atest {
        constructor() {
          this.name = 123;
        }
        getA() { 
          console.log(this);
          return this.name + 1; 
        }
        getB=()=>{ 
          console.log(this);
          return this.name + 1; 
        }
};
let a = new Atest();
let funcA = a.getA;
let funcB = a.getB;
funcA();
funcB();

image.png

class中此种调用方式this 指向的是undefined。首先和super()没有关系,super是子类当中使用的。

感谢评论区同学给的建议

调用时改为window.funA()时,才能让class中的this指向window,理论上默认funA应该就是在window上吧?

image.png

常规对象方式调用

window.name = 'aaa';
class Atest {
        constructor() {
          this.name = 123;
        }
        getA() { 
          console.log(this);
          return this.name + 1; 
        }
        getB=()=>{ 
          console.log(this);
          return this.name + 1; 
        }
};
let a = new Atest();
a.getA();
a.getB();

image.png

参考

总结

  • 箭头函数定时绑定 外层函数的this自己的this
  • 每一个函数都是一个独立的盒子,都有自己的this比如函数嵌套时,函数返回函数时,函数做为参数时除了箭头函数是定义时绑定外层函数的this其他场景都是看当前函数是如何调用的,从而决定this的指向
  • 需要注意,Promise构造器中resolve()的位置在Promise的构造器中无论是在resolve前或者后都是与同步任务一样去执行的。只有then中的才是 微任务
  • 注意:promise实例化传入的自执行成功或者失败回调不执行的话会导致then``成功和失败的回调都不会被执行。因为在Promise的实现中只有在构造器中执行了resolve或者reject状态成会变更。才会赋值
  • reject失败回调首先会在then中会被捕获其次才会在catch中会被捕获如果then中捕获了catch将不会在捕获

疑问

  • 五题中 2)的第一段的代码执行是undefined的原因是?