2019-06 中旬 ✅

# 2019-06-20: react 性能优化 ✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    • 利用 shouldComponentUpdate 和 PureComponent 避免过多 render function
    • 不要直接改变 setState 数据
    • 虚拟化长列表  & 常用库  react-virtualized
    • 合理组织 React Component:React Component Patterns
    • 在希望发生重新渲染的 DOM 上设置同级唯一 key  以触发重新渲染
    • 尽量将 props 和 state 摊平,只传递 component 需要的 props,慎将 component 当作 props 传入

# 2019-06-19: react hooks, 前景 ✅✅

好好想想先 😌

# 2019-06-18: jsx 语法为什么 需要 import react 包?✅✅

好好想想先 😌

# 2019-06-17: React 的 Fiber 架构,reconciler 阶段和 commit 阶段各做什么?💊✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    • React 16 之前是栈调度器
      • Stack reconciler 的工作流程很像函数的调用过程。父组件里调子组件,可以类比为函数的递归(这也是为什么被称为 stack reconciler 的原因)。在 setState 后,react 会立即开始 reconciliation 过程,从父节点(Virtual DOM)开始遍历,以找出不同。将所有的 Virtual DOM 遍历完成后,reconciler 才能给出当前需要修改真实 DOM 的信息,并传递给 renderer,进行渲染,然后屏幕上才会显示此次更新内容。对于特别庞大的 vDOM 树来说,reconciliation 过程会很长(x00ms),在这期间,主线程是被 js 占用的,因此任何交互、布局、渲染都会停止,给用户的感觉就是页面被卡住了。
      • Fiber reconciler 允许渲染过程分段完成,而不必须一次性完成,中间可以返回至主进程控制执行其他任务。
    • ReactFiber 基于时间分片的限量更新 requetIdleCallback
    • reconciliation: 简单来说就是找到需要更新的工作,通过 Diff Fiber Tree 找出要做的更新工作,这是一个 js 计算过程,计算结果可以被缓存,计算过程可以被打断,也可以恢复执行
    • commit: 提交更新并调用对应渲染模块(react-dom)进行渲染,为了防止页面抖动,该过程是同步且不能被打断

# 2019-06-16: React 16 新增的生命周期,为什么要废弃以前的生命周期?✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    • 废弃
      • componentWillMount
      • componentWillReceiveProps
      • componentWillUpdate
    • 新增 static getDerivedStateFromProps getSnapshotBeforeUpdate componentDidCatch
    • react 打算在 17 版本推出新的 Async Rendering,提出一种可被打断的生命周期,而可以被打断的阶段正是实际 dom 挂载之前的虚拟 dom 构建阶段

# 2019-06-15: 函数柯里化有什么作用?✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    • 柯里化,可以理解为提前接收部分参数,延迟执行,不立即输出结果,而是返回一个接受剩余参数的函数
    • 优点:参数复用、延迟运行、扁平化

# 2019-06-14: 不用 call 和 apply 如何实现 bind? ✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    • 利用 eval 实现
    • 利用中间对象实现
  • 代码实现
    Function.prototype.myCall = function (context) {
      // 如果没有传或传的值为空对象 context指向window
      context = context || window
      context.fn = this //给context添加一个方法 指向this
      // 处理参数 去除第一个参数this 其它传入fn函数
      let arg = [...arguments].slice(1) //[...xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组
      context.fn(...arg) //执行fn
      delete context.fn //删除方法
    }
    

# 2019-06-13: 什么是原型和原型链,实现继承有哪些方式?✅✅

好好想想先 😌
  • 参考链接

  • 一句话

    • 每个实例对象( object )都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( __proto__ ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节
    • obj.__proto__ === Obj.prototype
  • 代码实现

    • 原型链继承
    function Parent() {
      this.name = '张三'
    }
    Parent.prototype.getName = function () {
      console.log(this.name)
    }
    function Child() {}
    Child.prototype = new Parent()
    var child1 = new Child()
    
    • 构造函数继承
    function Parent() {
      this.names = ['张三', '李四']
    }
    function Child() {
      Parent.call(this)
    }
    var child1 = new Child()
    

# 2019-06-12: instanceof 原理和代码实现? ✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    • js 在底层存储变量的时候,会在变量的机器码的低位 1-3 位存储其类型信息,对于 undefined 和 null 来说,这两个值的信息存储是有点特殊的,由于 null 的所有机器码均为 0,因此直接被当做了对象来看待
    • obj.__proto__ === Obj.prototype
  • 代码实现
    function instanof(left, right) {
      return left.__proto__ === right.prototype
    }
    

# 2019-06-11: 深拷贝和浅拷贝有什么不同,代码实现 ✅✅

好好想想先 😌
  • 参考链接
  • 一句话
    深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同,基本数据类型存放在栈中,基本数据类型值不可变;引用类型存放在堆中,引用类型值可变
  • 代码实现
    function extend(target, source, deep) {
      for (key in source) {
        if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
          if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
            target[key] = {}
          }
          if (isArray(source[key]) && !isArray(target[key])) {
            target[key] = []
          }
          extend(target[key], source[key], deep)
        } else if (source[key] !== undefined) {
          target[key] = source[key]
        }
      }
    }