lessfish / underscore-analysis

【NO LONGER UPDATE】underscore-1.8.3.js 源码解读 & 系列文章(完)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Function Functions 相关源码拾遗

lessfish opened this issue · comments

underscore 源码解读之 function 的扩展方法部分即将进入尾声,我们重点剖析了解了 bind 方法的使用场景以及兼容方式,函数节流、函数去抖,以及 memoize 方法,本文我们来看看 underscore 还为 function 扩展了哪些有用(有意思)的方法。

_.delay & _.defer

_.delay 能使得函数延迟执行,其实就是封装了一个定时器。

// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
// 延迟触发某方法
// _.delay(function, wait, *arguments)
//  如果传入了 arguments 参数,则会被当作 func 的参数在触发时调用
// 其实是封装了「延迟触发某方法」,使其复用
_.delay = function(func, wait) {
  // 获取 *arguments
  // 是 func 函数所需要的参数
  var args = slice.call(arguments, 2);
  return setTimeout(function(){
    // 将参数赋予 func 函数
    return func.apply(null, args);
  }, wait);
};

_.defer 其实就是设定了一个 0ms 的定时器(源码中其实设定的是 1ms)

// Defers a function, scheduling it to run after the current call stack has
// cleared.
// 和 setTimeout(func, 0) 相似(源码看来似乎应该是 setTimeout(func, 1))
// _.defer(function, *arguments)
// 如果传入 *arguments,会被当做参数,和 _.delay 调用方式类似(少了第二个参数)
// 其实核心还是调用了 _.delay 方法,但第二个参数(wait 参数)设置了默认值为 1
// 如何使得方法能设置默认值?用 _.partial 方法
_.defer = _.partial(_.delay, _, 1);

.partial 可以理解为一个「占位方法」,.delay 至少需要两个参数,第一个参数用 _ 来占位( _.defer 的参数),第二个参数为 1,表示 _.delay 的 wait 参数为 1,也就是说设定了一个 1ms 的定时器。应用在哪里?其实平时你需要用到 setTimeout(function(){}, 0) 的地方,都可以用 _.defer 方法代替,比如为了避免 UI 卡顿的场景。

_.after & _.before & _.once

思考这样一个场景,如果有 N 个异步请求,全部请求完成后需要触发某个方法,怎么做?以前做爬虫碰到过这样的场景,当时用了 eventproxy 模块,ES6 的 Promise 应该也提供了相应的 API,underscore 的 _.after 方法也能做到。

function print() {
  console.log('hello world');
}

var renderNotes = _.after(3, print);

renderNotes();
renderNotes();
// 第三次后才真正触发 print 方法
renderNotes();  // hello world

如果不仔细看,可能会错误地使用(N 次之后每次都会执行方法)。

function print() {
  console.log('hello world');
}

var renderNotes = _.after(3, print);

renderNotes();
renderNotes();
renderNotes();  // hello world
renderNotes();  // hello world

原来的文档有点问题,我报了个 bug https://github.com/jashkenas/underscore/issues/2607,提了个 pr https://github.com/jashkenas/underscore/pull/2608,被合并了。_.after 源码非常简单,就不贴了。

_.before 和 .after 相反,其实两个方法的参数相同。假设传入第一个参数 times 的值为 N,.after 是在 [N, +∞) 次触发方法时执行,而 _.before 是在 [1, N) 触发时执行方法,刚好互为补集。

function print() {
  console.log('hello world');
}

var renderNotes = _.before(3, print);

renderNotes(); // hello world
renderNotes(); // hello world
renderNotes();
renderNotes();

也就是 _.before 应用于 至多触发某函数 N-1 次。

而 _.once 基于 _.before 进行了封装,用于某函数至多只能被执行一次的场景。

function print() {
  console.log('hello world');
}

var renderNotes = _.once(print);

// 至多执行一次 print
renderNotes(); // hello world
renderNotes();
renderNotes();

适用于类似初始化的场景,只需要被执行一次。