切换语言为:繁体

理解 JavaScript 的惰性求值与惰性函数以及两者之间的异同点

  • 爱糖宝
  • 2024-06-04
  • 2093
  • 0
  • 0

惰性求值

什么是惰性求值?

首先来看下维基百科上对这个概念的定义:

在编程语言理论中,惰性求值(英语:Lazy Evaluation),又译为惰性计算懒惰求值,也称为传需求调用(call-by-need),是计算机编程中的一个概念,目的是要最小化计算机要做的工作。

这段话中提到“最小化计算机要做的工作”,而具体是怎么实现的呢?下面我举一个非常简单的例子: 首先是最容易理解的例子:

let v = 1 + 1; // 立刻计算为2
let lazyV = () => 1 + 1 // 当调用lazyValue时,计算结果为2


是的,就如 传需求调用(call-by-need 这种说法一样,在需要的时候再进行求值,这样就能体现出“最小化计算机要做的工作”,进而使得在一些场景下,数据的计算效率会有大幅度提升。

惰性求值的特点

  • 延迟计算,在需要值的时候才进行计算,而不是值在被定义的时候进行计算。

  • 节约资源:通过延迟计算,最小化计算机要做的工作,节约了计算资源和内存占用。

  • 提高效率:惰性计算可以提高程序的执行效率,特别是在处理大数据集或复杂计算时更为明显。

为什么需要惰性求值?

请看下面这段代码:

function lazyAdd(a, b) {
  return () => {
    return a + b;
  }
}

const add = lazyAdd(2, 3);
console.log("result:", add()); // result: 5


上面这个例子中,调用lazyAdd这个方法的时候不会执行加法操作,而是会返回一个函数,该函数在实际调用的时候才会执行“加”的操作,通过这种思路我们可以用在类似的地方,比如我们可以通过这种思路,结合生成器写一个生成斐波那契数列的函数:

function* fibonacciGenerator() {
  let a = 0, b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fibonaccIns = fibonacciGenerator();

// 每当调用fibonaccIns.next()的时候才会求值
console.log(fibonaccIns.next().value); // 0
console.log(fibonaccIns.next().value); // 1
console.log(fibonaccIns.next().value); // 1
console.log(fibonaccIns.next().value); // 2
console.log(fibonaccIns.next().value); // 3
console.log(fibonaccIns.next().value); // 5


再比如常见的JavaScript开发工具库Lodash就使用了惰性计算的编码思路,从而对数据进行细化的处理,这样可以显著的提高数据的处理性能,如果你对这块知识感兴趣的话或者是你对惰性求值的特点具体是怎么体现的感兴趣的话,可以去参考这篇文章,相信你会对这块的知识有进一步的理解!

惰性求值-小结

  • 惰性求值是一种计算策略,指在需要值的时候才会进行计算(延迟计算),而不是在每个值被定义时都立即计算,提高程序的执行效率,特别是在处理大数据集或复杂计算时更为明显。

惰性函数

什么是惰性函数?

惰性函数指的是函数在第一次对函数进行调用的时候会进行一些额外的操作,将函数替换为另一个函数,这样下一次调用时候就会执行替换后的那个函数了,从而减少程序在执行时进行额外操作时的重复开销。

惰性函数的常见用途

惰性函数比较常用在浏览器检测的代码中,可以参考下面这个案例:

例:浏览器环境检测

比如下面封装一个浏览器绑定事件的函数:

let bindEvent = function (ele, type, fn, isCapture) {
  if (window.addEventListener) {
    ele.addEventListener(type, fn, isCapture)
  } else if (window.attachEvent) {
    ele.attachEvent('on' + type, fn)
  }
}


通过上面的代码我们可以知道每次调用该函数的时候,都需要对当前的浏览器环境进行对应的判断,多次调用的情况下,这些判断就会显得有些多余,所以现在需要将该函数改造成惰性函数进行简化,若要将函数改造成惰性函数,则有两种思路:

let bindEvent = (function () {
  if (window.addEventListener) {
    return function (ele, type, fn, isCapture) {
      ele.addEventListener(type, fn, isCapture)
    }
  } else if (window.attachEvent) {
    return function (ele, type, fn) {
      ele.attachEvent('on' + type, fn)
    }
  }
})()


  • 思路2:在第一次调用函数的时候将函数进行重新赋值

function bindEventJr(ele, type, fn) {
  if (window.bindEventJrListener) {
    bindEventJr = function (ele, type, fn) {
      ele.bindEventJrListener(type, fn);
    };
  } else if (window.attachEvent) {
    bindEventJr = function (ele, type, fn) {
      ele.attachEvent("on" + type, fn);
    };
  } else {
    bindEventJr = function (ele, type, fn) {
      ele["on" + type] = fn;
    };
  }
  return bindEventJr(ele, type, fn);
}


惰性函数-小结

  • 惰性函数是一种函数的写法,具体指的是在首次对函数进行调用的时候会进行一些额外的操作,将函数替换为另一个函数,下一次调用时候就会执行替换后的那个函数了,从而减少程序在执行时进行额外操作时的重复开销,常用于浏览器环境检测与性能优化。

总结

相同点:

  • 惰性求值和惰性函数都可以对代码进行性能上的优化,避免不必要的计算或执行

不同点:

  • 惰性函数是一种具体的函数写法,而惰性求值是一种求值的策略

  • 惰性求值通常在整个编程语言中生效,而惰性函数是特定函数的属性

参考资料

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.