20190423 前端面试总结

题目概览

  • 使用 last-child 找到 ul 的最后一个 li 元素
  • 闭包的概念 & 缺点 & 解决方法
  • 作用域 & let var区别 & 变量提升
  • 如何判断数组还是对象 & ‘’ instanceof String 结果 & 数组去重的方法
  • 解决跨域问题的方案 & 前端可以做代理么
  • …怎么使用 & 应用场景 & arguments
  • 是否了解 promise & 如何改变promise的状态 & 解决异步问题的其他方案
  • generator & async & 区别
  • for…of & 机制
  • 移动端解决方案 & vw 转换成px的配置
  • 下拉加载更多的实现方法 & 节流 & 去抖
  • 有没有自己封装过组件 & 高阶组件应用
  • React DOM & React Router DOM 区别
    (电面 施强)

具体分析

Promise

概念:通过链式调用的方式处理异步问题,避免了层层嵌套的回调对象。

状态:Promise的状态一旦确定就不能再更改了;两种:从pending到resolve和从pending到rejected

改变:可以直接声明一个resolve的promise:promise.resolve

延伸:还有哪些处理异步问题的方案

回调,事件监听,Promise,Generator,Async

Generator/yield

next 没有返回参数的例子(重点理解1、2、3、4步):

function* oddGenerator() {
  // 1. function关键字后添加*标识的函数
  yield 1; // 3. 遇到yield/return关键字就会暂停执行,直到下次触发 next() 方法
  yield 3;

  return 5;
}

let iterator = oddGenerator(); // 2. 这个函数并不会立即执行,而是返回一个Generator对象,通过调用对象的 next() 方法执行

// 4. next() 方法会返回一个带有 value 和 done 两个属性的对象,value 是返回值,done 是布尔类型,标识是否继续返回值。
let first = iterator.next(); // { value: 1, done: false }
let second = iterator.next(); // { value: 3, done: false }
let third = iterator.next(); // { value: 5, done: true  }

next 返回了参数的例子:

function* outputGenerator() {
  let ret1 = yield 1;
  console.log(`got ret1: ${ret1}`);
  let ret2 = yield 2;
  console.log(`got ret2: ${ret2}`);
}

let iterator = outputGenerator();

iterator.next(1);
iterator.next(2); // got ret1: 2
iterator.next(3); // got ret2: 3

过程:

  1. 先执行 = 号右边,遇到 yield 关键字暂停执行
  2. 在下次触发 next 方法后继续向下执行,也就是 = 左边的部分 let ret1 = XXX;这也解释了为什么 第二次调用 next()的参数 会被 第一次 yield 赋值的变量 接收到。

Generator 对象其实是一个迭代器,可以直接用 for … of 循环迭代 generator 对象

延伸:如何处理异步问题

yield 表达式后面跟promise对象。手动调用next方法。

Async/await

概念:本身就是为了解决异步编程而产生的,Async 函数始终返回一个 Promise,使用 await 获取返回值。

区别 Generator:

  1. Async 内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。
  2. 更好的语义。async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示后面的表达式需要等待结果。
  3. 更广的适用性。co 函数库约定,yield 后面只能是 Thunk 函数或 Promise 对象,而 await 后面,可以跟 Promise 对象或者原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)

迭代器

目的:为所有的数据结构提供统一的访问机制。比如 for…of 循环。

可迭代的数据结构:一种数据结构,只要部署了 Iterator 接口,这种数据结构就是可迭代的。比如:

  1. 原生的数据结构:Array String Map Set arguments TypedArray generator 对象 DOM NodeList 对象
  2. 手动部署了 Iterator 接口 的数据结构
    const obj = {
      [Symbol.iterator]: function() {
        return {
          next: function() {
            return {
              value: 1,
              done: true
            };
          }
        };
      }
    };
    

迭代的过程:

  1. 通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置;
  2. 调用 next 方法,返回当前位置的对象 { value: 当前属性的值, done: 当前遍历是否结束}
  3. 当 done 为 true 的时候遍历结束

需要调用 Iterator 的场景

  • 解构赋值
    let [first, ...rest] = [1, 2, 3];
    
  • 扩展运算符
    var str = "hello";
    [...str]; //  ['h','e','l','l','o']
    

for…of

概念:新引入的循环方式;支持新的迭代协议;用于可以被迭代的数据结构;不能遍历普通对象,只能遍历可迭代对象。

原理:

  1. 当 for…of 循环某种数据结构时,这个循环会自动寻找 Iterator 接口,返回一个遍历器对象
  2. 调用对象的 next 方法,返回带有 value 和 done 两个属性的对象,value 是返回的值,done 是布尔类型,判断是够继续遍历
  3. 当 done 为 true 时结束遍历

与 for-in 的区别: for-in 为普通对象设计的。

for-in 的缺点:除了遍历数组元素以外,还会遍历自定义属性,甚至原型链上的属性。

扩展运算符

形式:...

扩展运算符可以看做是 rest 参数的逆运算

console.log(...[1, 2, 3]); // 1 2 3

应用:

普通函数的调用;合并数组;将字符串转为数组;将类数组转换成真正的数组;代替 apply 方法调用函数;
  • 普通函数调用:

    function add(x, y) {
      return x + y;
    }
    var numbers = [4, 38];
    add(...numbers); // 42
    
  • 合并数组

    var arr1 = ['a', 'b'];
    var arr2 = ['c', 'd'];
    // ES5的合并数组
    arr1.concat(arr1, arr2)  // [ 'a', 'b', 'c', 'd' ]
    // ES6的合并数组
    [...arr1, ...arr2]  // [ 'a', 'b', 'c', 'd' ]
    
  • 将字符串转为数组

    var str = "hello";
    // ES5
    var arr1 = str.split(""); // [ "h", "e", "l", "l", "o" ]
    // ES6
    var arr2 = [...str]; // [ "h", "e", "l", "l", "o" ]
    
  • 替代 apply 方法调用函数

    // ES5 的写法
    Math.max.apply(null, [14, 3, 77]);
    // ES6 的写法
    Math.max(...[14, 3, 77]);
    // 等同于
    Math.max(14, 3, 77);
    
  • 将实现了 Iterator 接口的对象转换成真正的数组

    var nodeList = document.querySelectorAll("div");
    var array = [...nodeList];
    

rest 参数

形式:...变量名

作用:获取函数的多余参数

rest参数和 arguments 对象的区别:

  • rest 参数没有对应形参的实参;arguments 包含传给函数的所有实参
  • rest 是真实的 array 实例;arguments 不是一个真正的数组;
  • arguments 对象还有一些附加的属性,比如:callee(当前函数的引用)、length、Symbol(Symbol.iterator)

举例:

function add(first, ...values) {
  console.log(values); // [2,3]
  console.log(arguments); // [1, 2, 3, callee: 当前函数的引用, length: 3, Symbol(Symbol.iterator): 迭代接口]
}
add(1, 2, 3);

React DOM & React Router DOM 区别

React DOM 提供了针对 DOM 的方法,可以在应用的顶层调用。比如:

  • render:渲染一个 React 元素成为 DOM,放到所提供的 container 里。

    ReactDOM.render(element, container[, callback])
    

    第一次被调用时,内部存在的任何 DOM 元素都会被替换掉之后的调用会使用 React 的 DOM差分算法进行高效的更新。

  • unmountComponentAtNode():从 DOM 元素中移除已挂载的 React 组件,清除它的事件处理器和 state。

    ReactDOM.unmountComponentAtNode(container);
    

下拉加载更多的实现

componentDidMount() {
  this.props.monitorStore.getErrorFlowList();
  // 监听内容滚动事件
  this.refs.scroll.addEventListener('scroll', this.handleScroll.bind(this), false);
}

handleScroll(e){
  if(e.target.scrollTop>=e.target.scrollHeight-e.target.clientHeight-100){
    this.props.monitorStore.getErrorFlowList();
  }
}
// 移除监听事件
componentWillUnmount() {
  this.refs.scroll.removeEventListener('scroll', this.handleScroll.bind(this), false);
}

store文件:

// 1.判断是否正在加载中,若正在请求,则退出
if (this.fetchStatus == "ing") return;
// 2. 判断errorFlowList的长度与总条数是否相等,若相等(即全部加载完成),则退出
if (this.errorTotalCount === this.errorFlowList.length) {
  this.fetchStatus = "finished";
  return;
}

节流 Throttle & 去抖 Debounce

去抖:函数在一段时间之后再执行,如果这个时间段内有新的调用了,则取消上次调用,重新开始新的调用

  • 窗口缩放
  • 文本输入的验证

节流:函数在执行之后,在规定的时间内不能进行下一次执行

  • DOM 元素的拖拽功能实现
  • 监听滚动事件判断是否到页面底部自动加载更多内容

区别:节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

react 封装组件的经历

比如:弹框、弹层提示tooltip、带分类的下拉组件、搜索项的提取封装

proxy 解决跨域问题

nodejs 代理:http-proxy-middleware 把请求代理转发到其他服务器的中间件

使用 webpack 配置代理代码devServer

module.exports = {
  //...
  // dev-server 使用了非常强大的 http-proxy-middleware 包
  devServer: {
    proxy: {
      "/api": "http://localhost:3000"
    }
  }
};

猜你喜欢

转载自blog.csdn.net/m_review/article/details/89510393