【前端必修】闭包、`this`、`箭头函数`、`bind`、节流,一篇文章全懂!

大家好,我是前端学习小菜鸟 今天咱们聊点痛点:
为什么 this 总丢?
箭头函数凭啥能保住 this
bindthat = this 真的不是土办法?
节流函数里 that 又是怎么混进闭包的?

别急,一起带着问题,撸个示例,一把梳理明白!


1. 谁偷走了 this

先看下面这段:

js 复制代码
const person = {
  name: 'Allen',
  sayHello: function() {
    setTimeout(function() {
      console.log(`${this.name} says hello!`);
    }, 1000)
  }
}

person.sayHello(); // ??? 

你以为 1 秒后能打印 "Allen says hello!" ,结果呢?

输出是 " says hello!" 或报 undefinedthis 丢了。


为啥丢?

  • setTimeout 的回调函数,是普通函数调用 ,所以 this 默认是 window(非严格模式)。
  • this.name 就找不到 person.name 了,GG。

解法 1:保存 that = this

js 复制代码
const person = {
  name: 'Allen',
  sayHello: function() {
    let that = this;
    setTimeout(function() {
      console.log(`${that.name} says hello!`);
    }, 1000)
  }
}

that 把外层的 this(就是 person)保存下来,闭包里能用,老土但好用!


解法 2:用 bind 改变 this

js 复制代码
const person = {
  name: 'Allen',
  sayHello: function() {
    setTimeout(function() {
      console.log(`${this.name} says hello!`);
    }.bind(this), 1000)
  }
}

bind(this) 返回一个新的函数,this 永远固定成 person,优雅且无副作用。


解法 3:用箭头函数

js 复制代码
const person = {
  name: 'Allen',
  sayHello: function() {
    setTimeout(() => {
      console.log(`${this.name} says hello!`);
    }, 1000)
  }
}

箭头函数没有自己的 this

它的 this 自动取外层 sayHellothis,就是 person,完美继承。


总结:this 保不住?要么 that,要么 bind,要么 =>


2. throttle 节流背后,其实也是闭包+that

很多同学学节流、去抄百度出来的版本,只会用 throttle,却没想过:为啥 throttle 函数里总要写个 that = this

看下面这段节流函数

ini 复制代码
function throttle(fn, delay) {
  let last, deferTimer;

  return function(args) {
    let that = this; // 保存外层 this!
    let now = +new Date();

    if (last && now < last + delay) {
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function() {
        last = now;
        fn.call(that, args); // 用 call 保住 this
      }, delay);
    } else {
      last = now;
      fn.call(that, args);
    }
  }
}

这里面发生了啥?

1️⃣ throttle 返回一个新函数,叫包装函数 ,这就是闭包

2️⃣ that = this 把外层(事件监听器触发时)的 this 保存下来。

3️⃣ 真正执行 fn 时,用 fn.call(that, args) 确保 this 没跑掉。


如果你把 that 去掉,直接写:

js 复制代码
fn(args)

那里面 this 就是 window(或 undefined),又炸了。


为啥需要 this

想想:

jsx 复制代码
inputC.addEventListener('keyup', function(e) {
  throttleAjax(e.target.value);
});

这个匿名函数里的 thisinputC(事件触发元素),所以包装后的 throttle 执行 ajax 时,也要保证 ajax 里的 this 正确传递。


3. 节流应用:输入防抖和节流

有了节流,输入框也不怕狂打字了

jsx 复制代码
let throttleAjax = throttle(ajax, 200);

inputC.addEventListener('keyup', function(e) {
  throttleAjax(e.target.value);
});


4. 所有这些,背后都是闭包!

  • throttle 用闭包保存 lastdeferTimer
  • that = this 利用闭包保存外层上下文。
  • 箭头函数也是闭包 + 继承上下文。
  • bind 其实是生成一个新的闭包,预先把 this 固定住。

一句话总结

闭包保护变量,thatbind 固定 this,箭头函数天然"偷懒"省事!


彩蛋:立即执行函数(IIFE)也是闭包!

再看闭包经典套路:

js 复制代码
const Counter = (function() {
  let count = 0;

  return function() {
    return {
      increment: () => ++count,
      getCount: () => count
    }
  }
})();

每次 Counter(),就开一套闭包,count 外面拿不到,但接口能访问,私有变量 + 公有方法,经典组合拳。


🎉 全文小结

技术 用法 干嘛用
闭包 保存变量 countlastdeferTimer
that = this 保存上下文 this 保证回调里 this 不丢
bind 固定 this 返回新函数,this 永远不跑
箭头函数 没有 this 自动用外层的 this
IIFE 私有作用域 立即执行,封装变量

写给自己也写给未来的同事

这套小技巧面试也考、项目也用,别背死了,搞懂原理,下次再遇到 this 丢了,淡定写个箭头函数,没法用就 bind,再不行就 that = this,保证老板再也看不到你 Debug 一下午。


我是小阳,感谢点赞收藏转发,咱们下篇见!

相关推荐
你的人类朋友37 分钟前
🫏光速入门cURL
前端·后端·程序员
兴趣使然_1 小时前
【笔记】使用 html 创建网址快捷方式
笔记·html·js
01传说1 小时前
vue3 配置安装 pnpm 报错 已解决
java·前端·vue.js·前端框架·npm·node.js
小李飞飞砖1 小时前
React Native 组件间通信方式详解
javascript·react native·react.js
小李飞飞砖1 小时前
React Native 状态管理方案全面对比
javascript·react native·react.js
烛阴2 小时前
Python装饰器解除:如何让被装饰的函数重获自由?
前端·python
千鼎数字孪生-可视化2 小时前
Web技术栈重塑HMI开发:HTML5+WebGL的轻量化实践路径
前端·html5·webgl
凌辰揽月2 小时前
7月10号总结 (1)
前端·css·css3
天天扭码3 小时前
很全面的前端面试——CSS篇(上)
前端·css·面试
EndingCoder3 小时前
搜索算法在前端的实践
前端·算法·性能优化·状态模式·搜索算法