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


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

相关推荐
changuncle13 分钟前
Angular初学者入门第一课——搭建并改造项目(精品)
javascript·ecmascript·angular.js
拾光拾趣录26 分钟前
8道题穿透前端原理层
前端·面试
海天胜景42 分钟前
vue3 el-table 去除小数 并使用千分号
javascript·vue.js·elementui
cc蒲公英1 小时前
uniapp x swiper/image组件mode=“aspectFit“ 图片有的闪现后黑屏
java·前端·uni-app
前端小咸鱼一条1 小时前
React的介绍和特点
前端·react.js·前端框架
谢尔登1 小时前
【React】fiber 架构
前端·react.js·架构
哈哈哈哈哈哈哈哈8531 小时前
Vue3 的 setup 与 emit:深入理解 Composition API 的核心机制
前端
漫天星梦1 小时前
Vue2项目搭建(Layout布局、全局样式、VueX、Vue Router、axios封装)
前端·vue.js
ytttr8732 小时前
5G毫米波射频前端设计:从GaN功放到混合信号集成方案
前端·5g·生成对抗网络
水鳜鱼肥2 小时前
Github Spark 革新应用,重构未来
前端·人工智能