面试:event Loop和防抖节流

489 阅读5分钟
  • 杭州前端岗

人事面

  • 换工作原因
  • 到岗时间
  • 个人职业规划

JavaScript 相关

一、闭包
  • 背景:Javascript语言中,函数内部可以直接读取外部或全局变量
  • 闭包优点
    • 可以读取函数内部的变量
    • 让这些变量的值始终保持在内存中(结合内存回收中的标记清除进行表述)
  • 闭包缺点
    • 内存消耗大,容易造成内存泄漏引起性能问题
二、原型链

当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,然后返回undefined,通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。

  • 联系红宝书,关联prototype(原型)constructor表述
  • __proto__和constructor属性是对象所独有的;
  • prototype属性是函数所独有的
  • 函数也是对象,所以函数(箭头函数没有)也拥有__proto__和constructor属性
  function Person(){
  }
  var person=new Person();
  
  var x=[]  
  x.constructor===Array
  /**
   * person.constructor===Person
   * person.__proto__===Person.prototype
   * person.constructor===Person
   
  */
三、 event Loop事件循环

Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

  • 宿主环境无论是Node、还是浏览器都是多线程
  • 但js是单线程语言,浏览器只分配给js一个主线程,一次只能执行一个任务
  • 为提高执行效率为http请求,浏览器的定时及事件监听等异步开辟另外的线程(任务队列)
  • 只要主线程执行完,就会读取‘任务队列’
  • 在JS中,任务分两种,宏任务(MacroTask)也叫Task,微任务(MicroTask) MacroTaskOrMicroTask.png
  • 任务队列在栈空的时候被调入的优先级是微观任务队列优于宏观任务队列
    • 补充:Promise 执行器中的代码会被同步调用,但是回调是基于微任务的
/**
  * Microtask Queue 微任务队列 
  * Message Queue 消息队列
  * call stack 调用栈
  
  宏任务典型的:整个JavaScript代码,setTimeout、setInterval、DOM事件
  微任务典型的:Process.nextTick(Node独有)、Promise、async/await
 */
  console.log('task1');
  setTimeout(()=>{
      console.log('taskPlus1');
  })
  Promise.resolve().then(()=>{
      console.log('taskPlus2');
  })
  setTimeout(()=>{
      console.log('taskPlus3');
  })
  console.log('task2');
// task1  task2 taskPlus2 taskPlus1 taskPlus3
/**
  *  首先浏览器执行js第一个宏任务进入主线程
  * 遇到 console.log() 直接执行         所以先输出 --- task1
  * 遇到 setTimeout  分发到宏任务Event Queue中
  * 遇到 Promise,执行then 被分发到微任务Event Queue中
  * 遇到 setTimeout  分发到宏任务Event Queue中
  * 遇到 console.log() 直接执行           再输出   ---task2
  * 第一轮宏任务执行结束,执行微任务Promise 再输出------'taskPlus2' 
  * 微任务全部执行完毕,执行第二轮宏事件setTimeout,输出 --'taskPlus1'
  * 执行微服务,没有微服务,执行第三轮宏事件setTimeout,输出--'taskPlus3'
 */
  • 可以看出Promise确实会比setTimeout()先执行。
  • 因为Promise定义之后便会立即执行,其后的.then( ) 是异步里面的微任务。
四、 防抖和节流
  • 防抖
    • 概念:利用setTimeout,在一定时间间隔内,将多次触发变为一次触发
    • 前置条件:必须熟知JS中this指向问题,及函数内类数组对象arguments
        btn.addEventListener('click',debounce(submit,3000))
        function submit(e){
            // Do something
            console.log('---');
        }
        function debounce(cbk,wait){
            let timer=null;
            return(...args)=>{
                //如果定时器存在就清除上一个,避免多次执行
                if(timer) clearTimeout(timer)
                //必须使用箭头函数,避免函数自身污染外部arguments
                timer=setTimeout(() => {
                  timer=null ;
                  cbk.apply(this,args)
                }, wait);
            }
        }
    
  • 节流
    • 概念:借助flag元素和setTimeout实现在一定时间内,只执行一次方法
    • 着重点间隔的判断及时间戳更新
        btn.addEventListener('click',throttle(submit,3000))
        function submit(e){
          // Do something
          console.log('---');
        }
        function throttle(cbk,delay){
          // 设置初始值
          var begin=0;
          return(...args)=>{
              // 当前点击的时间戳
              var cur=new Date().getTime();
              //每次点击的时间和上次的时间大于定义间隔则执行
              if(cur-begin>delay){
                  cbk.apply(this,args)
                  //更新初始值
                  begin=cur;
              }
          }
        }
    
五、 ES6新语法 ?
  • 变量声明中的let、const
    • 块级作用域:二者只在当前声明下的大括号下可见
    • 重复声明:二者均不支持同作用域重复声明
    • 调用:二者不属于全局对象属性,也不支持全局this调用
    • 变量提升:二者与var不同,不存在变量提升,提前使用报错
      console.log(potato);//undefined
      console.log(tomato);//ReferenceError: tomato is not defined
      var potato='YES';
      let tomato='NO';
      const cucumber='LIKE'
      ------------------
      this.potato // "YES"
      this.tomato // undefined 
      this.cucumber // Identifier 'cucumber' has already been declared
    
  • 模板字符串[ ` ]
    • 结合${}引入变量,更便于字符拼接
  • 唯一值定义 Symbol
    • 通过Symbol函数生成的值都是独一无二,且其类型为"symbol"
      var fruit1=Symbol('apple')
      var fruit2=Symbol('apple')
      var fruit3='apple';
      typeof fruit2 // "symbol"
      typeof fruit3 // "string"
      fruit1==fruit2 //false
      fruit1==fruit3 //false
      fruit1===fruit2 //false
    
  • 箭头函数 =>
    • 使用它本质是为了避免函数中this的管理过于混乱
      • 箭头函数不会创建自己的this,会从自己的作用域链的上一层继承this
      • 箭头函数没有自己的thisarguments
      • 不支持call/apply改变this指向,见下
    • 普通函数中this指向大多和自身所处环境及被引用有关
      • 箭头函数中this指向在定义时就被固定
      • 通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定this---译者注)
  • 解构赋值 [ ]或者展开运算符 ...
    • 常用于简化数据交互及赋值操作,或者二者搭配进行数据解析
      let first=[1,2,3];
      let last=[6,7,8];
      let concat=[...first,...last]
      concat // [1, 2, 3, 6, 7, 8]
    
  • Set数据结构
    • Set数据容器,这是一个能够存储无重复值的有序列表。
      • 可以利用它的无重复特性,进行数组去重
      var unique=[1,2,3,4,5,5,6,6,8];
      // Set对象非数组,利用展开运算符转数组
     [...unique]=new Set(unique);
     console.log(Array.isArray(unique));// true
    
  • Promise 对象
    • 静态方法有四种resolve()、reject()、all()和race()
    • 状态有三种,pending(进行中)、fulfilled(已成功)和rejected(已失败)。
    • 状态有不可逆性
      • pending变为fulfilled(成功)
      • pending变为rejected(失败)
    • 创建:利用关键字 new 进行生成
    • 参数:对象接收两个函数,分别是resolve和reject。
      • 异步操作成功,结果会传入resolve( )
      • 异步操作失败,错误会传入reject( )
      • 无论哪种,状态都会发生变化
    • 调用:当Promise状态为fulfilled时,会调用实例的.then方法
      • 实例的.then其实是继承于Promise.prototype上的方法
      • .then(value)获取的参数是resolve(params)中传递过来的
    • Promise.all异步请求并行操作
      • 当所有结果成功返回时按照请求顺序返回成功;
      • 当其中有一个失败方法时,则进入失败方法;
      • Promise.all可以将多个Promise实例包装成一个新的Promise实例。
        • 成功的时候返回的是一个结果数组
        • 失败的时候则返回最先被reject失败状态的值。
六、 数组方法
  • filter :过滤返回数组中符合条件的值
    let numbers=[1,2,3,4,3,2,1]
    let result=numbers.filter(item=>{
      return item!=2;
   })
  console.log(result);//[1, 3, 4, 3, 1]
  • map:回一个新数组,数组中的元素为原始数组元素调用函数处理后的值
    • map() 不会对空数组进行检测,不会改变原始数组。
    • map()方法会得到一个新的数组并返回。
    let numbers=[1,2,3,4,3,2,1]
    let result=numbers.map(item=>{
      return item!=2;
    })
    let process=numbers.map(item=>{
      return item+=1;
    })
  console.log(result);// [true, false, true, true, true, false, true]
  console.log(process);//[2, 3, 4, 5, 4, 3, 2]

map返回对当前元素调用函数后的结果,filter 会将符合条件的元素存到新的数组中并返回。

  • forEach:类似于for循环遍历数组中每一项,但不支持break,无法终止循环
    • forEach()会修改原来的数组
    let numbers=[1,2,3,4,3,2,1]
      numbers.forEach((item,index,data)=>{
          data[index]+=1
        })
   console.log(numbers);//[2, 3, 4, 5, 4, 3, 2]

map有返回值且是新数组,forEach没有返回值且改变原数组。

  • reduce:用于计算数组元素相加后的总和
    let numbers=[1,2,3,4,3,2,1]
    var sum = numbers.reduce((prev, cur)=> {return prev + cur });
   console.log(sum);//16
  • for of遍历的是数组元素值
    • for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象
    • 可正确响应break、continue和return语句
      let numbers=[1,3,3,2]
      for (var index of numbers) {
      console.log(index);//  1 3 3 2 元素值
      }
    
  • for in遍历的是数组的索引(即键名)
    • for in也可以循环数组但是特别适合遍历对象
      let numbers=[1,3,3,2]
      for (var index in numbers) {
      console.log(index);// 0 1 2 3 索引
    }
    

for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。

Vue相关

一、 Vue组件及组件嵌套的生命周期钩子函数

之前写过,点击 → Vue生命周期及嵌套

二、 vue的computed和watch的区别
  • computed计算属性的作用
    • 变量可不在 data中定义,定义在computed中,写法跟写方法一样,有返回值
    • 在页面模板中渲染直接写函数名,不用加括号调用
    • 根据传入的变量的变化,进行结果的更新
    • 性能方面
      • methods中每调用一次就会重新计算,为了进行不必要的资源消耗
      • 值未发生变化时,计算属性调用的是缓存数据,提高性能
    <div id="app">
    {{ getMoney }} // 展示总额
    </div>
    <script>
    vm = newVue({
    el: "#app",
    data:{
      myGoodsCount:3,
      price:'25'
    },
    computed:{
      getMoney(){
        return this.myGoodsCount*this.price;
        }
      }
    })
</script>
  • watch 监听属性
    • watch是在值发生改变的时候才会触发。
    • 当有些数据需要随着其它数据变动而联动时
    • 或数据变化时执行异步时,可以使用 watch。
  • 二者的区别
    • 计算属性变量在computed中定义,watch属性监听在data中定义
    • computed支持数据缓存,watch不支持数据缓存
    • computed值多个值变化影响当前值(多对一),watch是当前一个值变化影响多个值(一对多)
    • 计算属性是声明一个值依赖其他值,依赖的值改变后重新计算结果更新DOM视图
    • 属性监听的是data定义的变量,当定义的值发生变化时,执行相对应的函数
三、vue路由模式
  • 路由的两种模式
    • hash模式 :默认模式,通过路径中的hash值来控制路由跳转,路径带有#号
    • history模式:H5新增的 history API,不会显示#号,但是必须服务器端进行配置
  • 重要补充
    • hash模式下,前端路由修改的是#中的信息,刷新没有问题
    • history模式下,刷新由于发起了服务请求,因此会报404,所以需求服务端给路由到初始页面
    • APP端,不支持#号,因此不支持hash模式
  • routeroute和router的区别
    • $route为当前router跳转对象里面可以获取name、path、query、params等
    • routerVueRouter实例,包含路由的跳转方法,钩子函数等,如导航到不同URL,用router为VueRouter实例,包含路由的跳转方法,钩子函数等,如导航到不同URL,用router.push

HTML及CSS相关

一、样式优先级
  • 优先级关系:内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器
  • 图示如下,来源互联网 CSSImportant.jpg
二、盒模型
  • W3C的标准盒模型
    • 标准盒模型中元素的width=content
  • IE怪异盒模型
    • IE怪异盒模型中元素的width=content+padding+border
  • 切换当前盒模型
    • box-sizing: content-box 是W3C盒子模型
    • box-sizing: border-box 是IE怪异盒模型

知乎链接,讲解更详细——>,知乎

三、 垂直居中
  • 使用绝对定位来实现(子绝父相),同时利用 transform

.parent{
    position: relative;/*父相*/


    height: 360px;
}
.son{
    position: absolute; /*子绝*/
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%); 
}
  • 使用flex布局会更加的方便
.parent{
  display: flex;
  height: 360px;
  background:hotpink;
}
.son{
    margin: auto;
    background: rosybrown;
}

HTTP相关

之前总结过,点击 → HTTP状态码及请求头

一、 跨域解决方案

跨域原因:为了浏览器安全采取的同源策略,指"协议+域名+端口"三者相同。

  • jsonp
    • 动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。
  • Ajax中设置dataType
    • dataType: 'jsonp', //请求方式为jsonp
  • 跨域资源共享(CORS) - 服务端设置Access-Control-Allow-Origin即可
    res.header("Access-Control-Allow-Origin", "*"); //设置请求来源不受限制
    
  • nginx代理

拓展

一、API设计规范?比如RESTful API
二、工作中Git的使用
  • 冲突如何解决,以及如何选用多个commit的其中几个
    • cherry-pick
三、拓展内容
  • 平时通过哪些渠道获取技术信息,学习习惯
想了解的东西?
  • 公司目前的业务方向。