美文网首页
7.尾调用优化和闭包

7.尾调用优化和闭包

作者: 原来哥哥是万家灯火 | 来源:发表于2020-07-03 12:54 被阅读0次

1.尾调用
函数 f1 调用函数 f2,如果调用 f2 就是 f1 的最后一步操作的话,那么这就叫做尾调用。

下面二种情况不属于尾调用:

第一种
function f1(x){
  f2(x);
}

因为 f1 的最后一步其实是 return undefined;
第二种
function f1(x){
  return f2(x) + 1;
}

只有其最后一句是return f2()才算尾调用:

function f1(x){
  return f2(x);
}

2.尾调用优化:
因为只有代码执行完毕,其执行期上下文才会被移出执行栈。f1 调用函数 f2,那么执行栈中就会保存这两个执行期上下文。(即使f1、f2是同一个函数)但是,f1 的执行期上下文已经没有任何意义留着了,如果执行引擎聪明一点,它就该把f1的执行期上下文移出,这就叫做尾调用优化

执行栈和调用栈是同一个东西。参考 wikistackoverflow

不幸的是,虽然ES6已经把尾调用优化写进了规范里(需要使用严格模式)。但是时至今日(2020-7-5),真正实现了尾调用优化的执行引擎并不多。比如桌面浏览器里,只有safari13及以上版本实现了。chrome实现了但是又有bug,所以默认不开启。


不用引擎的尾调用优化支持情况

测试:分别使用safari 13.0或以上和chrome执行下面两段代码

递归求和,未使用尾调用

"use strict";

function sum(n) {
    if (n === 0) { return 0}
    return n + sum(n-1)
}

console.log(sum(100000))

使用尾调用

"use strict";

function sum(n) {
    if (n === 0) { return 0}
    return n + sum(n-1)
}

console.log(sum(100000))

3.闭包
从理论上说:闭包指能够访问(即引用了)自由变量的函数,所以一切函数都是闭包。
从实践上说:能够访问已经被销毁的执行期上下文的活动对象的函数。或者直接高程上说的“有权访问另一个函数作用域中的变量的函数。”
自由变量是指在函数中使用的,但既不是此函数参数也不是此函数的局部变量的变量。

function f1() {
  var x = 1;
  function f2() {
    console.log(x)
  }
  return f2;
}

var f = fn();
f();

f2就是一个闭包。当return f2后,函数f1执行完毕,其上下文f1Context出栈(被销毁),但是由于f2中引用了f1Context.AO中的变量,所以引擎会把f1Context.AO继续留在内存中。这就是闭包访问已经被销毁的执行期上下文的活动对象的原因。

闭包在实际开发中,主要用来提供较好的封装,不污染环境。不然,就为了访问一个内部变量而已,直接把它赋值到全局最简单。比如防抖函数:

不使用闭包
let timer;
function debounced_handleClick() {
  clearTimeout(timer)
  timer = setTimeout(function() {
      console.log('clicked')
  }, 1000)
}
btn.addEventListener('click', handleClick)
使用闭包
function debounce(f) {
  let timer;
  return function() {
    clearTimeout(timer)
    timer = setTimeout(f, 1000)
  }
}
btn.addEventListener('click', debounce(handleClick))

参考
阮一峰 - 尾调用优化
Tail call optimization
How to use Tail Call Optimizations in JavaScript
冴羽 - JavaScript深入之闭包

相关文章

网友评论

      本文标题:7.尾调用优化和闭包

      本文链接:https://www.haomeiwen.com/subject/zhksqktx.html