View on GitHub

Cycle263 Blog

Stay hungry, stay foolish.

高阶组件(HOC)

高阶组件可以看作React对装饰模式的一种实现,是一个没有副作用的纯函数。其本质就是一个函数接受一个组件作为参数,并返回一个新的组件,新的组件主要是扩展它的行为。例如:高阶组件通过包裹(wrapped)被传入的React组件,经过一系列处理,最终返回一个相对增强(enhanced)的React组件,供其他组件调用。

  function showComp(WrappedComp) {
    return class extends Component {
      render() {
        const { visible, ...props } = this.props;
        return visible ? <WrappedComp {...props} /> : null;
      }
    }
  }

高阶组件作为一个函数,它可以更加纯粹地关注业务逻辑层面的代码,比如数据处理,数据校验,发送请求等,可以改善目前代码里业务逻辑和UI逻辑混杂在一起的现状。父组件则是UI层的东西,我们先前经常把一些业务逻辑处理放在父组件里,这样会造成父组件混乱的情况。为了代码进一步解耦,可以考虑使用高阶组件这种模式。

高阶函数

javascript中,函数可以当做参数传递,也可以被当做返回值返回,高阶函数就是这一类的函数。有哪些函数属于高阶函数呢?

connect(mapStateToProps, mapDispatchToProps)(MyComponent);

function connect(...otherProps) {
  return (MyComponent) => {
    return <MyComponent {...this.props} {...otherProps} />
  }
}

将函数当做返回值输出的典型应用就是偏函数。类型判断函数都是典型的偏函数

isType = function(type) {
    return function(obj) {
        return Object.prototype.toString.call(obj) === "[object " + type + "]";
    }
}

isString = isType('String');

一个currying的函数首先会接受一些参数,接受这些参数之后,函数并不会立即求值,而是继续返回另一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。柯里化的过程是将函数拆分成功能更具体化的函数,那反柯里化的作用则在于扩大函数的适用性,使本来作为特定对象所拥有的功能函数可以被任意对象所使用。

var currying = function(fn) {
  var args = [];

  return function() {
    if (arguments.length === 0) {
      return fn.applay(this, args);
    } else {
      args = args.concat(arguments);
      return arguments.callee;
    }
  }
}

某些事件可能会被重复的触发,但事件处理函数并不需要每次都执行,这个时候就可以使用节流函数。

// method 1
function throttle(fn, interval) {
  var doing = false;

  return function() {
    if (doing) {
      return;
    }
    doing = true;
    fn.apply(this, arguments);
    setTimeout(function() {
      doing = false;
    }, interval);
  }
}

// method 2
var throttle = function (fn, delay, atleast) {
  var timer = null;
  var previous = null;

  return function () {
    var now = +new Date();
    if ( !previous ) previous = now;
    if ( now - previous > atleast ) {
      fn();
      // 重置上一次开始时间为本次结束时间
      previous = now;
    } else {
      clearTimeout(timer);
      timer = setTimeout(function() {
        fn();
      }, delay);
    }
  }
};
// method 1
function debounce(fn, interval) {
  var timer = null;

  function delay() {
    var target = this;
    var args = arguments;
    return setTimeout(function(){
      fn.apply(target, args);
    }, interval);
  }

  return function() {
    if (timer) {
      clearTimeout(timer);
    }

    timer = delay.apply(this, arguments);
  }
};

// method 2
function _debounce (fn, delay, context) {
  var timer = null

  if (delay === 0) {
    return fn
  }
  return function () {
    var eContext = context || this
    var args = arguments
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(eContext, args)
    }, delay)
  }
}