惰性载入函数与惰性求值有关联不?

最近看高程书,高级技巧那里讲到了惰性载入函数,经过一天的搜索大致理解了,但是在搜索过程中发现了惰性求值这个术语,去维基百科上看,发现定义简单概括如下:
当被需要的时候才去求值并且避免了重复求值。(核心思想:延迟处理)
从避免重复求值这点上,惰性载入函数应该符合吧,但是当被需要的时候才去求值好像有点不大符合。
他俩到底是啥关系呢?
还有,惰性求值是不是需要用到闭包,那么闭包都是惰性求值吗?
求大神们讲解,在此多谢了!

阅读 2.1k
1 个回答

《高程书》与惰性载入函数

我不知道你指的是不是那本12年出版的书。我的观点是,这6年以来JavaScript已经发生了翻天覆地的变化,12年的书不再适合当做教材,而应该当做扩展书目,看完近几年的资料后有所取舍地阅读。举个例子,就“惰性载入函数”来说,对现代JavaScript引擎完全没有必要:

function alwaysIf() {
  if (document.attachEvent) {
    return document.attachEvent;
  }
  else if(document.addEventListener) {
    return document.addEventListener;
  }
  else {
    return false;
  }
}

function noIf() {
  return document.addEventListener;
}

按照“惰性载入函数”的说法,alwaysIf应该比noIf显著地慢,因此我们需要用那种花里胡哨的手段替换掉alwaysIf里面的一大串if。事实呢?请看benchmark:https://jsbench.github.io/#27...

benchmark

jsperf太麻烦了,不仅要填各种名字,还要FQ。

两者的运行速度几乎一样。我用的是Chrome67。总之事实就是,现代JavaScript引擎的分支预测能力已经相当变态了,没有必要让程序员多花精力实现那种别扭的东西

惰性求值与“重复求值”

我查到的维基百科惰性求值条目没有任何“重复求值”的字样。维基百科的定义是“在使用延迟求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值 ”。维基百科

惰性载入函数与“重复求值”

惰性载入函数符合“避免重复求值”,但的确不符合“被需要的时候才去求值”。惰性求值与惰性载入函数的关系?我看就是Java和JavaScript的关系。

惰性求值与闭包

JavaScript的普通变量没有惰性求值的概念,所以惰性求值有时候会用函数实现,而函数一般都是闭包。闭包可能是惰性求值,也可能不是。它们之间在概念上没有任何联系,只有在实现上可能有联系。

最典型的惰性求值是生成器,跟闭包(在思路上)完全没有关系:

function *gen() {
    for(let i = 0; /* 死循环 */ ; i++) {
        yield i;
    }
}

const g = gen(); // 绑定到变量之后没有立即求值,没有触发死循环
g.next(); // 取用的时候求值: 1

有时候惰性求值是用函数实现的,而这个函数正好也是个闭包:

const a = 1;
const b = 2;
const lazy_add_a_b = () => a + b;

const sum = lazy_add_a_b; // 绑定到变量之后没有立即求值,没有计算a + b
sum(); // 取用的时候求值: 3

有时候惰性求值的函数是built-in,在里世界声明,没有词法环境,那就不是闭包:

const lazy_print = console.log.bind(console, 1);

const p = lazy_print; // 绑定到变量之后没有立即求值,没有log
p(); // 取用的时候求值: 1

如果闭包立即执行,那么也不是惰性求值:

const a = 1;
const b = 2;
const add_a_b = (() => a + b)(); // 已经求值了,计算 add_a_b = a + b = 3

总结

以上三个概念之间,没有任何思路上的联系。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题