Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function Functions 相关源码拾遗 #24

Open
lessfish opened this issue Oct 14, 2016 · 0 comments
Open

Function Functions 相关源码拾遗 #24

lessfish opened this issue Oct 14, 2016 · 0 comments

Comments

@lessfish
Copy link
Owner

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();

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant