这个库让JavaScript变“懒”了,但性能飞升啦

JavaScript/前端
213
0
0
2024-01-20

在前端开发过程中,为了提高开发效率,我们会经常使用到第三方工具库。

尤其是在一些业务比较复杂的场景,原生 JavaScript 实现可能很啰嗦,但使用第三方工具库,通常一行代码就搞定了。

之前我们介绍过Underscore、Lodash、Ramda 多个第三方库工具库。今天,再给大家分享一个类似但非常独特的库:Lazy.js

简介

Lazy.js是类似Underscore或Lo-Dash的JavaScript工具库,但是它有一个非常独特的特性:惰性求值

很多情况下,惰性求值都将带来巨大的性能提升,特别是当处理巨大的数组和连锁使用多个方法的时候。Lazy.js的网站上展示了与Underscore、Lo-Dash比较的图表:

当数组非常大的时候,对于不需要迭代整个数组的方法,例如indexOf和take,Lazy.js的性能提升更为惊人:

安装

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个元素!

如果你需要考虑性能,你可能不会这么干。因为,它生成了中间数组,且这些中间数组仅仅是为了最后的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的表达力,同时得到手写的过程式代码的性能,那该多好啊!

这就是懒惰.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.js 还有其他特性,诸如生成无穷序列,异步迭代,处理Dom事件(小扩展lazy.dom.js),字符串处理,流处理等功能。

详细的使用,请大家参阅下方链接。

官方地址 https://danieltao.com/lazy.js/ Github地址 https://github.com/dtao/lazy.js/

注意官网首页最后一句话:This library is experimental and still a work in progress. Lazy.js是试验性的,仍在开发中。

因此,在正式环境中,大家酌情使用哦~