Lazy.js : 让 JavaScript 变懒

Lazy.js是相似UnderscoreLo-Dash的JavaScript东西库,然则它有一个异常奇特的特征:惰性求值。许多情况下,惰性求值都将带来庞大的机能提拔,特别是当处置惩罚庞大的数组和连锁运用多个要领的时刻。

Lazy.js的网站上展现了与Underscore、Lo-Dash比较的图表:

《Lazy.js : 让 JavaScript 变懒》

当数组异常大的时刻,关于不须要迭代全部数组的要领,比方indexOftake,Lazy.js的机能提拔更加惊人:

《Lazy.js : 让 JavaScript 变懒》

装置

Lazy.js没有外部依靠,所以加载Lazy.js异常轻易:

<script type="text/javascript" src="lazy.min.js"></script>

假如你愿望支撑DOM事宜序列的惰性求值,那末用这个:

<script type="text/javascript" src="lazy.dom.js"></script>

假如你运用Node.js:

npm install lazy.js

简介

我们建立一个包括1000个整数的数组:

var array = Lazy.range(1000).toArray();

注重我们挪用了toArray。假如没有这个,Lazy.range给我们的将不是一个数组而是一个Lazy.Sequence对象,你可以经由过程each来迭代这个对象。

如今我们盘算取每一个数字的平方,增添一下,末了掏出前5个偶数。为了坚持代码简短,我们运用这些辅佐函数:

function square(x) { return x * x; }
function inc(x) { return x + 1; }
function isEven(x) { return x % 2 === 0; }

这是一个新鲜的目的。不论怎样,我们可以用Underscore的chain要领完成它:

var result = _.chain(array).map(square).map(inc).filter(isEven).take(5).value();

注重上面这行语句做了若干事变:

  • map(square)迭代了全部数组,建立了一个新的包括1000个元素的数组
  • map(inc)迭代了新的数组,建立了另一个新的包括1000个元素的数组
  • filter(isEven)迭代了全部数组,建立了一个包括500个元素的新数组
  • take(5)这统统只是为了5个元素!

假如你须要斟酌机能,你可以不会这么干。相反,你会写出相似如许的过程式代码:

var results = [];
for (var i = 0; i < array.length; ++i) {
  var value = (array[i] * array[i]) + 1;
  if (value % 2 === 0) {
    results.push(value);
    if (results.length === 5) {
      break;
    }
  }
}

如今我们没有建立任何过剩的数组,在一次迭代中完成了统统。有什么问题么?

好吧。最大的问题在于这是一次性的代码,我们花了一点时候编写了这段代码,却没法复用。如果我们可以运用Underscore的表达力,同时获得手写的过程式代码的机能,那该多好啊!

这就是Lazy.js该发威的时刻了。用 Lazy.js,上面的代码会写成:

var result = Lazy(array).map(square).map(inc).filter(isEven).take(5);

看上去和用Underscore的代码险些一样?正是如此:Lazy.js愿望带给JavaScript开辟者熟习的体验。每一个Underscore的要领应当和Lazy.js有雷同的名字和表现,唯一的差别是Lazy.js返回一个序列对象,以及相应的each要领。

主要的是,直到你挪用了each才会发生迭代,而且不会建立中心数组。 Lazy.js将一切查询操纵组合成一个序列,终究的表现和我们最先写的过程式代码差不多。

固然,与过程式代码差别的是,Lazy.js确保你的代码是清洁的,函数式的。如许你就可以专注于构建运用,而不是优化遍历数组的代码。

特征

酷!Lazy.js还能做什么?

天生无限序列

是的,无限序列,无限无尽!一样支撑一切Lazy内建的map和filter功用。

看个例子吧。假定我们须要在1和1000之间猎取300个差别的随机数:

var uniqueRandsFrom1To1000 = Lazy.generate(function() { return Math.random(); })
  .map(function(e) { return Math.floor(e * 1000) + 1; })
  .uniq()
  .take(300);

// 输出:亲眼看看吧
uniqueRandsFrom1To1000.each(function(e) { console.log(e); });

相称不错。换一个高等点的例子吧。让我们用Lazy.js建立一个斐波那契数列。

var fibonacci = Lazy.generate(function() {
  var x = 1,
      y = 1;
  return function() {
    var prev = x;
    x = y;
    y += prev;
    return prev;
  };
}());

// 输出: undefined
var length = fibonacci.length();

// 输出: [2, 2, 3, 4, 6, 9, 14, 22, 35, 56]
var firstTenFibsPlusOne = fibonacci.map(inc).take(10).toArray();

不错,另有什么?

异步迭代

你之前多数见过如安在JavaScript中异步迭代数组的代码片断。然则你见过如许的吗?

var asyncSequence = Lazy(array)
  .async(100) // 100毫秒
  .map(inc)
  .filter(isEven)
  .take(20);

//  这个函数会立时返回,然后最先异步迭代
asyncSequence.each(function(e) {
  console.log(new Date().getMilliseconds() + ": " + e);
});

很好。另有吗?

事宜序列

我们看到,和Underscore和Lo-Dash差别,关于无限序列,Lazy.js并不须要把一个把一切数据放到内存以便迭代。异步序列也显现了它并不须要一次完成一切迭代。

如今我们要引见一个Lazy.js的小扩大lazy.dom.js(基于浏览器的环境须要包括一个零丁的文件),它组合了以上两个特征,如今,处置惩罚DOM事宜也可以运用Lazy.js的力量了。换句话说,Lazy.js让你把DOM事宜看成是一个序列——和其他序列一样——然后可以将那些用于序列的函数mapfilter运用到序列上。

下面是一个例子。比方我们盘算处置惩罚给定的DOM元素的一切mousemove事宜,同时显现它们的坐标。

// 起首我们定义事宜序列
var mouseEvents = Lazy.events(sourceElement, "mousemove");

// 将事宜序列和坐标相map
var coordinates = mouseEvents.map(function(e) {
  var elementRect = sourceElement.getBoundingClientRect();
  return [
    Math.floor(e.clientX - elementRect.left),
    Math.floor(e.clientY - elementRect.top)
  ];
});

// 关于在元素一边的鼠标事宜,在一个处所显现坐标
coordinates
  .filter(function(pos) { return pos[0] < sourceElement.clientWidth / 2; })
  .each(function(pos) { displayCoordinates(leftElement, pos); });

// 关于元素另一边的鼠标事宜,在另一处显现坐标
coordinates
  .filter(function(pos) { return pos[0] > sourceElement.clientWidth / 2; })
  .each(function(pos) { displayCoordinates(rightElement, pos); });

另有么?固然!

字符串处置惩罚

这多是你不会想到过的东西:String.matchString.split。在JavaScript中,这两个要领会返回包括子字符串的数组。假如你这么做,一般意味着JavaScrit会做一些不必要的事。然则从开辟者的角度而言,这是完成任务最快的要领。

比方,你想从一段文本中抽掏出前5行。你固然可以这么做:

var firstFiveLines = text.split("\n").slice(0, 5);

固然,这意味着将全部字符串支解成单行。假如这个字符串异常大,这很糟蹋。

有了Lazy.js,我们不必支解全部字符串,我们只需将它看成行的序列。将字符串用Lazy包裹以后再挪用split,可以获得一样的结果:

var firstFiveLines = Lazy(text).split("\n").take(5);

如许我们就可以读取恣意大小的字符串的前5行(而不须要预先天生一个庞大的数组),然后像对其他序列一样运用map/reduce

String.match同理。比方我们须要找出字符串中最前面5个数字或字母。运用Lazy.js,这很轻易!

var firstFiveWords = Lazy(text).match(/[a-z0-9]+/i).take(5);

小菜一碟。

流处置惩罚

在Node.js中,Lazy.js一样可以封装流。

给定一个可读流,你可以像封装数组一样用Lazy包裹一番:

Lazy(stream)
  .take(5) // 仅仅浏览数据中的前五块内容
  .each(processData);

为了轻易,Lazy.js也供应了处置惩罚文件流和HTTP流的特地辅佐要领。(注重:API将来可以会转变。)

// 读取文件的前5行
Lazy.readFile("path/to/file")
  .lines()
  .take(5)
  .each(doSomething);

// 从HTTP相应中读取5-10行
Lazy.makeHttpRequest("http://example.com")
  .lines()
  .drop(5)
  .take(5)
  .each(doSomething);

lines()要领将每段切割成行(固然了,切割是惰性的)。

Lazy.js是试验性的,仍在开辟中。项目主页在此

    原文作者:weakish
    原文地址: https://segmentfault.com/a/1190000000358463
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞