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


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

相关推荐
一斤代码1 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子1 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年1 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子2 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina2 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路3 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_3 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
伍哥的传说3 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409193 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app
我在北京coding3 小时前
element el-table渲染二维对象数组
前端·javascript·vue.js