【前端必修】闭包、`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 一下午。


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

相关推荐
放下华子我只抽RuiKe56 分钟前
React 从入门到生产(四):自定义 Hook
前端·javascript·人工智能·深度学习·react.js·自然语言处理·前端框架
XinZong43 分钟前
OpenClaw 实现双重心跳(Heartbeat)+ clawreach虾聊项目实现
javascript
IT_陈寒2 小时前
Redis缓存击穿把我整不会了,原来还有这手操作
前端·人工智能·后端
idcu2 小时前
深入 Lyt.js 组件系统:L2 渲染引擎层的核心
前端·typescript
这是程序猿2 小时前
Spring Boot自动配置详解
java·大数据·前端
文心快码BaiduComate2 小时前
干货|Comate Harness Engineering工程实践指南
前端·后端·程序员
还有多久拿退休金2 小时前
一张栈的图,治好你面试答不出 script 阻塞的病
前端·javascript
光辉GuangHui3 小时前
Agent Skill 也需要测试:如何搭建 Skill 评估框架
前端·后端·llm
To_OC3 小时前
我终于搞懂 Claude Code 核心逻辑!90%的人都用错了模式
前端·ai编程
蓝宝石的傻话3 小时前
Headless浏览器的隐形陷阱:为什么你的AI自动化工具抓不到页面早期错误?
前端