美文网首页Web前端之路JavaScript 进阶营
前端常见面试题(七)@郝晨光

前端常见面试题(七)@郝晨光

作者: 郝晨光 | 来源:发表于2019-07-10 23:16 被阅读199次

    简述同步和异步的区别

    众所周知,javascript是单线程的语言,所谓的单线程,就是从上至下,依次执行,当然这里的依次执行要抛开javascript的预解析机制。
    这样做的原因是因为javascript最初是为了操作DOM,运行在浏览器环境下的,而操作DOM的时候,不能是异步的,不然的话两个异步任务同时修改DOM结构的话,会导致浏览器不知道该执行哪一个。
    但是这样做也有缺点,当遇到一个响应时间特别长的任务时,容易导致页面加载错误或者浏览器未响应的情况。

    同步就是所有的任务都处在同一队列中,不可以插队,一个任务执行完接着开始执行下一个,相对于浏览器而言,同步的效率过低,一些耗费时间比较长的任务应该用异步来执行。

    异步就是将一个任务放入到异步队列中,当这个任务执行完成之后,再从异步队列中提取出来,插队到同步队列中,拿到异步任务的结果,可以提升代码执行的效率,不需要因为一个耗费时长的代码而一直等待。

    javascript异步的几种实现方式
    1. 回调函数 callback
      最简单的回调函数的实现方式,利用setTimeout会进入计时器队列,而不是正常队列的特性,来实现回调函数
      计时器队列与正常队列同时运行,不影响正常队列的运行,在正常队列空闲的时候,并且计时器队列的任务倒计时已经结束,就将计时器队列的任务提取到正常队列中获得最终结果。

      • function syncMethod(callback) {
          setTimeout(function() {
              callback(count);
              console.log('第一次调用回调函数!');
              setTimeout(function() {
                  callback(count);
                  console.log('第二次调用回调函数!');
              })
              for(let i=0;i<100;i++) {
                  count++;
              }
          }, 0);
          let count = 1;
          for(let i=0;i<100;i++) {
              count++;
          }
        }
        syncMethod(function(count) {
            console.log(count); // 调用两次,第一次返回101,第二次返回201
        })
        
        callback示例返回值
    2. 事件驱动

      • javascript是基于事件驱动的语言,当我们给元素绑定事件的时候,并不会立即执行,而是当触发指定的事件时,才会执行对应的方法
      •  const btn = document.querySelector('button');
         btn.addEventlistener('click', function() {
             console.log('clickBtn')
         })
        
    3. 发布者订阅者模式(观察者模式)

      • 发布者订阅者模式是通过保存事件,然后在需要使用的时候直接发布事件,就可以触发保存的回调
      •   function Watcher() {
              this.events = {}; // 定义事件对象,用来保存需要触发的事件
          };
          // $on方法,用来监听事件,订阅者
          Watcher.prototype.$on = function(event, handler) {
              this.events[event] = this.events[event] || []; 
              // 如果当前事件已经被监听,则在原基础上进行添加新的事件,如果没有的话新建一个数组,用来存储事件
              this.events[event].push({
                  handler: handler
              })
              // 将事件存储到对应的事件模型中
          };
          // $emit方法,用来发布事件,发布者
          Watcher.prototype.$emit = function(event,...arg) {
             // 如果没有订阅当前事件的话,直接返回
              if(!this.events[event]) {
                  return
              }
              // 循环遍历对应事件,将对应的事件根据填入的先后顺序触发
              for(let i = 0; i< this.events[event].length; i++) {
                  this.events[event][i].handler(...arg)
              }
          };
          // 关闭监听事件
          Watcher.prototype.$close = function(event) {
              // 如果没有订阅当前事件的话,直接返回
              if(!this.events[event]) {
                  return
              }
              // 删除订阅事件
              delete this.events[event];
          };
          // 实例化一个观察者
          let watcher= new Watcher();
          // 监听事件,并传入事件需要触发的函数
          watcher.$on('handler1', function(name, age) {
              console.log(name, age, '我是第一个函数')
          });
          // 监听不同的事件
          watcher.$on('handler2', function(sex) {
              console.log(sex)
          });
          // 监听同一个事件,触发不同的函数
          watcher.$on('handler1', function(name, age) {
              console.log(name, age, '我是第二个函数')
          });
          // 发布事件,并传参
          watcher.$emit('handler2', "男");
          watcher.$emit('handler1', '张三',20);
          // 关闭事件
          watcher.$close('handler1');
          // 事件不会触发,因为已经关闭了对应事件的监听
          watcher.$emit('handler1', '张三',20);
        
        Image 3.jpg
    4. promise 异步编程的解决方案

      • Promise是ES6新增的异步编程的解决方案,它有三种状态分别是pending进行中、resolved已完成、rejected已失败。
      • Promise可以链式调用,避免层层嵌套,异步操作更加容易方便,提升代码可读性。
      • Promise一旦创建就不可以取消,一旦在resolvedrejected中确立一种状态,就不可以再发生改变
      •  function asynMethod(flag) {
              return new Promise((resolve, reject) => {
                  setTimeout(() => {
                      if(flag) {
                          resolve('郝晨光精心写作,如有错误请指出,谢谢!');
                      }else {
                          reject('错误信息!')
                      }
                  }, 3000)
              })
          }
          asynMethod(true).then(data => {
              console.log(data);
              return asynMethod(true)
          }).then(data => {
              console.log(data);
              return asynMethod(false)
          }).catch(err =>{
              console.log(err);
          })
        

    数组去重(手写代码)

    1. 使用ES6的Set去重
      • Set是ES6新增的数据类型,Set 的成员具有唯一性
      • function distinct(arr) {
          return Array.from(new Set(arr));
        }      
        
    2. 使用ES6的Set去重(超级简化版)
      • [...new Set(arr)]   // [...new Set(需要去重的数组)]
        
    3. 使用splice配合两重for循环去重
      • function distinct(arr) {
          for(let i = 0; i < arr.length; i++) {
              for(let j = i + 1; j < arr.length; j++) {
                  if(arr[i] === arr[j]) {
                      arr.splice(j, 1);
                      j--;
                  }
              }
          }
          return arr;
        }
        
    4. 使用for循环配合indexOf去重
      •  function distinct(arr) {
             let newArr = [];
             for(let i = 0; i < arr.length; i++) {
                 if(newArr.indexOf(arr[i]) === -1) {
                     newArr.push(arr[i]);
                 }
             }
             return newArr;
        }
        
    5. 使用for循环配合sort排序去重
      • function distinct(arr) {
              arr = arr.sort();
              let newArr = [];
              for(let i = 0; i < arr.length; i++) {
                  if(arr[i] !== arr[i-1]) {
                      newArr.push(arr[i]);
                  }
              }
              return newArr;
          }
        
    6. 使用for循环配合includes去重
      •   function distinct(arr) {
              let newArr = [];
              for(let i = 0; i < arr.length; i++) {
                  if(!newArr.includes(arr[i])) {
                      newArr.push(arr[i]);
                  }
              }
              return newArr;
         }
        
    7. 使用filter配合indexOf去重
      • function distinct(arr) {
              return arr.filter((item,index, arr) => arr.indexOf(item) === index);
        }
        

    在JavaScript中什么是伪数组?如何将伪数组转化为标准数组

    javascript中的伪数组(类数组):不具有数组的push,pop等方法,但是具有length,以及可以利用for循环遍历等特性,例如函数的 arguments 参数集合,还有通过document.getElementsByTagName等方法获取的NodeList等都是类数组

    如何将伪数组转化为标准数组

    1.   let nodeList = document.getElementsByTagName('li');
        let nodeArr = Array.from(nodeList);
      
    2.    let nodeList = document.getElementsByTagName('li');
         let nodeArr = Array.prototype.slice.call(nodeList);
      

    当转化为标准数组以后,就可以调用数组实例上的方法了,如push、pop、splice、filter、map、forEach等等


    SPA路由history模式,打包上线都遇到了哪些问题?你是如何解决的?

    1. 资源路径404问题
      • vue-cli3环境下,在根目录新建vue.config.js,在该文件中写入如下
      •  module.exports = {
             publicPath: './'
         }
        
    2. 页面刷新404问题

    JavaScript中callee和caller的作用

    1. callee是函数arguments对象内的指针,它指向当前的函数,使得在函数内部递归调用当前函数时,不需要调用函数名称,减少函数内部对于函数名的依赖
      function test() {
          console.log(arguments.callee);
      }
      test();
      
    2. caller是函数的一个属性,它指向调用当前函数的函数,如果当前函数在其他函数内被调用,则返回调用它的那个函数,如果是在全局环境下被调用,则返回 null
      我们可以利用caller的特性跟踪函数的调用链
      function fn1() {
          console.log(fn1.caller);
          console.log(arguments.callee.caller);
      }
      function fn2() {
          fn1()
      }
      fn2();
      

    结言
    感谢您的查阅,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照

    相关文章

      网友评论

        本文标题:前端常见面试题(七)@郝晨光

        本文链接:https://www.haomeiwen.com/subject/cvfkkctx.html