1. 1. 面试总结

腾讯一面面经(2019-4-15)

  1. 作用域链是什么

    当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即 arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延 续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象

    除了函数作用域在Js里面还有什么作用域?

    块级作用域

    什么时候可以形成块级作用域

    let,const

  2. 状态提升是什么

    在一个函数的内部无论在什么地方声明一个变量,都可以在使用这个函数的时候识别这个变量

    以下输出什么

    1
    2
    console.log(a);
    var a = 1;

    undefined (当时回答错了说成了1)

    函数提升和变量提升哪个优先级高

    函数提升(当时说错了)具体见博客

  3. dns的整个过程

    host查询 =》 浏览器缓存查询 =》 dns解析(递归查询和迭代查询)具体见下图

    如果在域名查询中任何一级都没有查到会发生什么?

  4. ajax的整个请求过程

    (new XMLHttpRequest => onreadyStateChange监听变化,当时少说了一个)创建对象,发送请求,响应请求

    readState的状态码除了4还有什么,描述的是什么

    (描述的是请求的状态,具体忘了)具体见下图

    再说一下你知道的http状态码

    2xx 3xx 4xx 5xx

    什么情况下会遇到304

    不知道

  5. js的单线程与eventloop

    js单线程,浏览器多线程,浏览器内部提供了许多线程:js引擎线程,UI渲染线程,事件触发线程等。对于同步的操作都是放在了主线程里面,这是一个队列,先进先出,然后异步的操作有定时器,之后放在任务队列里面,主线程的操作结束之后根据任务队列的优先级操作(当时没有说到异步的触发是回调函数)

    优先级是什么样的

    忘了(应该是宏任务和微任务)

  1. 缓存:serviceWorker有了解吗

    完全没有了解,只提到create-react-app里面自动生成,,,

  2. 同源机制是什么?什么是跨域,请求什么资源会跨域,跨域会引起什么后果,如何解决跨域,cross头部有哪些字段?

    浏览器为了安全起见的机制,同协议,同域名同子域名,同端口称为同源。

    跨域的后果

    访问不到资源

    什么资源

    ajax请求,xml, img

    如何解决

    access-control-allow-origin,jsonp,代理

    jsonp的解决方式需要后端做什么

    不知道

    cross头部有什么字段?

    不知道

    跨域的话能访问cookie吗?

    不能

    不同的标签页的localstorage能跨域访问吗?

    不能

    怎么操作才能让一个根域下面的子域之间不会跨域

    document.domain(‘’) 当场被反驳

  3. cdn有什么好处

    不知道

  4. 你还有什么问题:

    问了两个一个是做什么业务,什么技术栈。一个是看重实习生的基础还是经验,很疑惑没有问到很基础的红宝书的问题

  5. 最开始做了自我介绍

    大三,前端一年,react

面试总结

  1. 关于状态提升:

    • 首先需要说明变量的声明,使用var声明的变量会自动添加到最接近的环境变量中,在函数内部,最接近的环境就是函数的局部环境。如果省略了关键词var,这个变量依然存在,因为在寻找这个变量的过程中,直到找到顶层作用域链都没有找到就会在顶层声明这个变量,于是就会变成全局变量

    • 再谈查询标识符:当某个环境中为了读取或写入而引入一个标识符时,必须通过搜索来确定该标识符实际代表什么,搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符,如果在某个局部环境中找到了这个标识符,搜索就会停止,如果在全局环境中也没有找到标识符,则意味着该变量未声明

    • 现在说变量提升:代码中声明变量的所在行并不是真正的所在行,javascript编译时会统一将声明提升到作用域的最上方。这里需要注意的一点是,编译器会将变量声明提升到代码顶部,但赋值仍然在原来的地方,所以以下示例的输出就显而易见了

      1
      2
      console.log(a) //undefined
      var a = 1;
      1
      2
      3
      4
      5
      6
      var a = 3;
      function test(){
      console.log(a); //3
      a = 4;
      console.log(a); //4
      }
    • 同名的局部变量或参数优先级会高于同名局部变量:这是由于作用域链的搜索过程是先局部再全局

      1
      2
      3
      4
      5
      6
      7
      var a = 3;
      function test(){
      console.log(a); //undefined
      var a = 4;
      console.log(a); //4
      }
      test();
    • 函数声明:函数声明不同的是不但声明会提升在作用域顶端,就连函数的内容也会提升。注意:函数声明分为函数声明(函数式声明)和函数表达式(变量式声明),只有前者才会有函数提升

      1
      2
      3
      4
      5
      6
      7
      8
      9
      function test(){
      test1();
      var b = 5;
      function test1(){
      console.log(b); //undefined
      }
      }

      test();
      1
      2
      3
      4
      5
      // 函数声明 执行函数提升
      function a(){}

      // 函数表达式 不执行函数提升 但可以看成是变量提升
      var b = function(){}
    • 关于优先级:函数提升优先级高于变量提升,且不会被变量声明覆盖,但是会被变量赋值覆盖。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      var a;
      function a(){
      console.log(2); //2
      }

      console.log(a); // function a()
      a();
      a = 3;
      console.log(a); // 3
  1. 关于http状态码304

    • 304是什么:资源为修改。客户端中所请求资源的缓存仍然是有效的,也就是说该资源从上次缓存到现在并没有被修改过.条件请求可以在确保客户端的资源是最新的同时避免因每次都请求完整资源给服务器带来的性能问题
    • 辨别条件请求:当客户端缓存了目标资源但不确定该缓存资源是否是最新版本的时候,就会发送一个条件请求。在Headers Inspector查找相关请求头,这样就可以辨别出一个请求是否是条件请求在进行条件请求时,客户端会提供给服务器一个If-Modified-Since请求头,其值为服务器上次返回的Last-Modified响应头中的日期值,还会提供一个If-None-Match请求头,值为服务器上次返回的ETag响应头的值。服务端会根据客户端的这两个字段判断客户端缓存的资源是否是最新的,如果是最新的,就会返回304,如果不是最新的就会返回200并且返回响应体就是该资源当前最新的。只有客户端发送的请求中有以上两个字段才会去判断即发送条件请求,否则就会无条件请求改资源,服务器也会返回完整资源
    • 为什么使用条件请求:访问网页加速。但是仍然需要发送条件请求,只有当收到304的时候才会显示出缓存的资源。
    • 避免条件请求:清除浏览器缓存,删除所有请求中的条件请求相关的请求头以及所有响应中的缓存时间相关的响应头,在每个请求中添加Pragma: no-cache请求头来阻止浏览器缓存资源
  2. eventloop中的优先级

    • 首先知道宏任务与微任务的优先级:多个宏任务组成的队列就是一个任务队列,任务队列中都是进行完的异步操作的回调。在宏任务进行的过程中可能又会产生微任务,任务队列中执行的优先级就是宏任务的依次进入顺序,另外当前宏任务附带的微任务没有进行完是不会进行下一个宏任务的。参照如下示例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      setTimeout(_ => console.log(4))
      new Promise(resolve => {
      resolve()
      console.log(1)
      }).then(_ => {
      console.log(3)
      })

      console.log(2)
      `

      以上输出的顺序是1234setTimeout是作为宏任务的,而Promise.then是典型的微任务。所有进入任务队列的都是异步操作的回调函数,也就是说new Promise在实例化的过程中都是同步操作,在then中被注册的回调才是异步执行的。由于eventloop是首先进行同步操作,之后才会去检查任务队列中的回调,也就是同步阶段输出1,2之后就会去任务队列检查微任务这个时候输出3,最后再输出4

    • 什么是宏任务和微任务:

  3. serviceWorker