(万文)最全、最细前端面试问题总结(答题思路分析、答案解析)


theme: fancy

😱前言

已经进入秋招了,作者本人也在着急的准备面试,凭着之前的面试经验,我总结了一份完整的面试题,其中分析了答题思路和答案,还有一些手写题,希望可以帮到所有参加面试的小伙伴!!!

可能有些地方不严谨,写的也比较急,所以希望大家可以评论留言批评指正!!!

写作不易,我已经毫无保留分享自己的毕生所学,那你是不是应该毫不吝啬的给个赞吧!!!

持续更新中,还没有总结完。。。

😊(冲!!!)ES6 篇

😃1. 请你说下为什么ES6中引入了 const,let

回答思路:

  • 没有 const、let 之前有什么弊端
  • const、let解决了什么问题

扩展问题:

  • let、const 存储方式

开始回答

  1. 在没有 const、let之前,我们使用 var 来定义变量,因此我们的作用域只分为两种,也就是全局作用域和块级作用域,因此,我们在使用像for或者if这种关键词时,会有很大的隐患,可能造成变量冲突。
    比如说:
  • for: 正常来说,我们在 for 循环中定义其他变量,在结束后 for 中定义的变量应该被销毁,不会修改全局变量,但是由于没有块级作用域的限制,使得本应该销毁的变量没有被销毁。

  • if: 正常我们在fun函数中会打印x的值1,但是由于var变量提升,并且没有块级作用域的限制,虽然if中的代码不会执行,但是变量 x 会覆盖fun外部的值,导致x打印undefined

      var i = 5;                               
      for(var i = 3; i < 10; i ++){            
          // 其他操作                                 
      }                                        
      console.log(i) 
      ---------------------------
      var x = 1
      function fun (){
          console.log(x)
          if(false){
              var x = 1
          }
          console.log(x)
      }
    
  1. 在引入let、const之后,我们引入了新的概念,块级作用域,很好的解决了变量提升和无块级作用域带来的变量冲突的问题。那是如何解决的呢?我们引入新的问题。

扩展回答:

  1. 在js代码执行之前会经历编译阶段,而在编译阶段会确定当前执行上下文所需要的信息,变量环境,词法环境,this,作用域链,其中,变量环境用来存放 varfunction定义的变量,并且初始化值为undefined地址值,而在执行前变量已经定义好了,也就是所谓的变量提升。而constlet定义的变量存储在词法环境中,在词法环境中维护一个小型栈,按照块的形式,将每一个块级变量压入栈顶,执行后弹出,并且不会初始化,因此在一个块级作用域中提前访问变量会报错,也就是所谓的暂时性死区

变量查找顺序:先从词法环境中从栈顶到栈底,然后到变量环境中查找,之后沿着作用域链查找。

相关题型:
  • 给代码看输出

注意点varfunction 变量提升,并且function优先于var,const 、let块级作用域中的暂时性死区。

😃2. 请你说下Promise?

回答思路

  • 首先回答 Promise 是什么?
  • Promise 为什么出现,没出现前有什么问题,出现后解决了什么问题?

拓展问题:

  • Promise 执行机制,微任务宏任务
  • Promise的缺陷,async/await 语法糖的出现

开始回答:

  1. 首先,从字面意思来理解,Promise 期约, 是一个用来执行将来要发生的或者即将要发生的事件的对象,它自身有三种状态,pending, fulfilledrejected,同一时刻只能有一种状态,一旦状态改变,则不能再更改,也不可逆。通过构造函数的方式使用,传入一个执行器回调函数,以此来决定Promise的执行状态。之后通过then方法来决定执行什么样的操作,并且then方法返回一个值会自动包装为Promise从而实现对象的链式调用。(之后可以介绍下其他方法 race, all, resolve, reject
  2. Promise 出现之前,我们在书写异步代码时,通过回调的方式来拿到异步返回的值,代码抒写逻辑不连续,除此之外,如果下一次的执行需要依赖上一次的执行结果,会导致代码嵌套,如果嵌套次数太多,就造成了新的问题,回调地狱,使得代码难看难以维护,Promise的出现,通过 then方法,解决了函数嵌套的问题,then方法的链式调用,也将回调地狱的问题迎刃而解。

上面回答,只是简单的介绍了下Promise的用法,没有深入的去讲 Promise ,有能力的话我们应该扩展去讲它周边的知识

扩展:

1. Promise执行机制

Promise除了解决异步回调的问题之外,还有一个特性,就是它的执行时机,微任务,和微任务对应的还有宏任务,说到这我们不得不讲下浏览器的异步实现机制,为了解决 js 单线程(可能涉及问题:js为什么是单线程?)同步执行的效率问题,我们引入了异步执行机制,而异步执行依赖于 v8引擎 中的消息队列和事件循环机制,也就是js在执行过程中遇到异步任务时,不会立即执行,而是将该事件存放到消息队列当中,而消息队列中存放的任务也就是我们所谓的宏任务,然后继续执行js代码,当所有同步代码执行完毕之后,通过事件循环,也就是一个循环代码for或者while来不停的从消息队列中取出一个事件来执行,这就是异步任务的执行机制。消息队列有一个缺点,就是所有任务都是按顺序执行,因此,如果我们需要执行一些时间粒度小的任务,比如监听DOM的变化去做相应的业务逻辑的时候,再使用消息队列的话会造成严重的效率问题,因为消息队列是先进先出的结构,在该事件添加到消息队列尾部时,消息队列内部可能已经有很多任务了,所以宏任务的执行效率不高,这是就引入了新的概念微任务,在宏任务中包含一个微任务队列,来存放需要执行效率高的事件,在每次宏任务执行完之后,不会着急执行下一个宏任务,而是将该宏任务中的微任务执行完毕,再去执行下一个宏任务

  • js
    • 添加微任务:Promise, MutationObserver
    • 添加宏任务:script,定时器,I/O,交互事件
  • node
    • 添加微任务:: nextTick
2. async/await 语法糖

Promise虽然可以链式调用来解决回调地狱,但是还是不够完美,依然是回调的方式书写代码,为了更加符合代码逻辑,推出了async/await语法糖,来用同步的方式书写异步代码。使用 async 修饰的函数代表异步函数,会自动包装返回一个Promise类型的对象,await 关键字用来暂停执行逻辑。通过try/cache来捕获异常,介绍完后,我们来说下它的执行原理。
执行原理:(自执行的生成器 + 协程)

  • 生成器(Generator):yield/* ,通过调用 * 函数来返回一个对象,其中有next,return,trow方法。next 方法用来恢复*函数的执行,yield来暂停 *函数的执行。详细使用大家请自己查阅,这里只做大致介绍。
  • 协程:协程是一种比线程更加轻量级的存在,可以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程,协程不是被操作系统内核所管理,而完全是由程序所控制。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

通过*函数来创建一个协程,但是该协程不会立刻执行,当调用next方法时,将控制权交给该协程,开始执行,遇到yield关键字后,停止协程,并将控制权返回给父协程,并把 yield 后的值传递给父协程,父协程通过next(value)传值给该子协程。这就是生成器可以实现暂停执行的原理。

Promise 相关手写题:

说明:Promise底层实现了微任务的调用,我们没办法用代码实现,只能通过js模拟执行

  1. Promise 的实现
        const PENDING = 'pending';
        const FULFILLED = 'fulfilled';
        const REJECTED = 'rejected';
        class MyPromise {
   
   
            constructor(excutor) {
   
   
                this.state = PENDING
                this.value = null
                this.err = null
                this.onFulfilledCallback = null
                this.onRejectedCallback = null
                excutor(this.Resolve, this.Reject)
            }
            Resolve = val => {
   
   
                if (this.state == PENDING) {
   
   
                    this.state = FULFILLED
                    this.value = val
                    this.onFulfilledCallback && this.onFulfilledCallback(this.value)
                }
            }
            Reject = err => {
   
   
                if (this.state == PENDING) {
   
   
                    this.state = REJECTED
                    this.value = err
                    this.onRejectedCallback && this.onFulfilledCallback(this.err)
                }
            }
            then = (onFulfilled, onRejected) => {
   
   
                if (this.state == FULFILLED) {
   
   
                    const res = onFulfilled(this.value)
                    return MyPromise.resolve(res)
                }
                if (this.state == REJECTED) {
   
   
                    const err = onRejected(this.err)
                    return MyPromise.reject(err)
                }
                if (this.state == PENDING) {
   
   
                    this.onFulfilledCallback = onFulfilled
                    this.onRejectedCallback = onRejected
                }

            }
            static resolve(val) {
   
   
                return new MyPromise((res, rej) => {
   
   
                    res(val)
                })
            }
            static reject(err) {
   
   
                return new MyPromise((res
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员-石头山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值