news 2026/3/3 10:41:54

04.深入闭包和js函数的this指向跟规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
04.深入闭包和js函数的this指向跟规则

闭包的内存泄漏测试

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><scriptsrc="./01_js闭包内存泄漏案例.js"></script></html>
functioncreateFnArray(){// 整数占据 4 个字节// arr 占据内存大小:1024 * 1024 * 4 = 4Mvararr=newArray(1024*1024).fill(1)returnfunction(){console.log(arr.length)}}vararrayFns=[]for(vari=0;i<100;i++){setTimeout(()=>{arrayFns.push(createFnArray())},i*100)}setTimeout(()=>{for(vari=0;i<50;i++){setTimeout(()=>{arrayFns.pop()},i*100)}},100*100)

打开浏览器的开发者工具,找到性能模块然后点击录制,我这里录制了40s,由于浏览器的一些干扰因素影响可能不太准确,可自行多录制几次,大致的内存变化就是1-10s 开始慢慢增加内存到 4M,然后 10-15s 过程中数组断开一半的引用,然后由 GC 算法统一回收,内存降到 2M。

闭包引用的自由变量销毁

functionfoo(){varname='why'varage=18// js引擎会将没有使用的变量销毁functionbar(){debuggerconsole.log(name)}returnbar}varfn=foo()fn()

打了 debugger 之后,我们可以在控制台输入 name,以及 age,可以看到 age 是访问报错的

为什么需要 this

在常见的编程语言中,几乎都有 this 这个关键字(Objective-C 中使用的是 self), 但是 JavaScript 中的 this 和常见的面向对象语言中的 this 不太一样

  • 常见面向对象的编程语言中,比如 Java、C++、Swift、Dart 等等一系列语言中,this 通常只会出现在类的方法中
  • 也就是你需要有一个类,类中的方法(特别是实例方法)中,this 代表的是当前调用的对象
  • 但是 JavaScript 中的 this 更加灵活,无论是它出现的位置还是它代表的含义

编写一个 obj 的对象,有 this 和没有 this 的区别

// 从某种角度来说,开发中如果没有 this,很多问题我们也是有解决方案// 但是没有 this,会让我们编写代码变得非常的不方便varobj={name:'why',eating:function(){console.log(this.name+'在吃东西')console.log(obj.name+'在吃东西')// obj 一改动,里面用到 obj 的都要改},running:function(){console.log(this.name+'在跑步')console.log(obj.name+'在跑步')},studying:function(){console.log(this.name+'在学习')console.log(obj.name+'在学习')}}

this 指向什么

在大多数情况下,this 都是出现在函数中

在全局作用域下 this 的指向:

  • 浏览器:window
  • Node 环境:{}

node 环境里面的全局作用域的 this 为什么是{}

在 Node.js 的“全局作用域”里看到的 this 并不是“真正的全局对象”,而是模块作用域里的 this。Node.js 在启动你的文件时,会把文件内容包进一个函数里执行,大致过程:module -> 加载 -> 编译 -> 放到一个函数 -> 执行这个函数.call({}),因此,在“文件顶层”打印 this 得到{},只是 Node 的模块封装机制带来的副作用,而不是浏览器里那种指向全局 window 的行为。

https://registry.npmmirror.com/binary.html?path=node/v14.9.0/

开发中直接在全局作用域下去使用 this,通常都是在函数中使用

  • 所有的函数在被调用时,都会创建一个执行上下文
  • 这个上文中记录着函数的调用栈、AO 对象等
  • this 也是其中的一条记录

this 到底指向什么

下面定义了一个函数,采用三种不同的方式对它进行调用,产生了三种不同的结果

// this 指向什么,跟函数所处的位置没有关系// 跟函数被调用的方式有关系functionfoo(){console.log(this)}// 1. 直接调用这个函数foo()// window// 2. 创建一个对象,对象中的函数指向 foovarobj={name:'why',foo:foo}obj.foo()// obj 对象// 3. 通过 apply 调用foo.apply('abc')// String {'abc'} 对象

这个案例给我们的启示:

  • 函数在调用的时,JavaScript 会默认给这个 this 绑定一个值
  • this 的绑定和定义的位置(编写的位置)没有关系
  • this 的绑定和调用方式以及调用的位置有关系
  • this 是在运行时被绑定的

this 到底是怎么样的绑定规则

绑定规则一:默认绑定

什么情况下使用默认绑定?独立函数调用。

独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用。

// 案例1functionfoo(){console.log(this)}foo()// 案例2functionfoo1(){console.log(this)}functionfoo2(){console.log(this)foo1()}functionfoo3(){console.log(this)foo2()}foo3()// 案例3varobj3={name:'why',foo:function(){console.log(this)}}varbar3=obj3.foobar3()// 案例4functionfoo4(){console.log(this)}varobj4={name:'obj4',foo:foo4}varbar4=obj4.foobar4()// 案例5functionfoo5(){functionbar(){console.log(this)}returnbar}varfn=foo5()fn()

绑定规则二:隐式绑定

通过某个对象进行调用的:也就是它的调用位置中,是通过某个对象发起的函数的调用。

隐式绑定有一个前提条件:

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
  • 正是通过这个引用,间接的将 this 绑定到了这个对象上
// 隐式绑定:object.fn()// object 对象会被 js 引擎绑定到 fn 函数中的 this 里面// 案例1functionfoo(){console.log(this)}varobj={name:'why',foo:foo}obj.foo()// 案例2varobj2={name:'why',eating:function(){console.log(this.name+'在吃东西')},running:function(){console.log(this.name+'在跑步')},studying:function(){console.log(this.name+'在学习')}}obj2.eating()obj2.running()obj2.studying()// 案例3varobj3={name:'obj3',foo:function(){console.log(this)}}varobj4={name:'obj4',bar:obj3.foo}obj4.bar()

绑定规则三:显示绑定

如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做?

  • JavaScript 所有的函数都可以使用 call 和 apply 方法(这个和 Prototype 有关)
  • 这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么?就是给 this 准备的
  • 在调用这个函数时,会将 this 绑定到这个传入的对象上

通过 call 或者 apply 绑定 this 对象,显示绑定后,this 就会明确的指向绑定的对象

functionfoo(){console.log(this)}// foo 直接调用和 call/apply 调用的不同在于 this 绑定的不同// foo 直接调用指向的是全局对象 windowfoo()varobj={name:'why'}// call/apply 是可以指定 this 的绑定对象foo.apply(obj)foo.call(obj)// call/apply 在传参上有所区别functionsum(num1,num2){console.log(num1+num2,this)}sum.apply(obj,[10,20])sum.call(obj,10,20)// call 和 apply 在执行函数时,是可以明确的绑定 this,这个绑定规则称之为显示绑定。

如果希望一个函数总是显示的绑定到一个对象上可以使用 bind

functionfoo(){console.log(this)}// foo.call('kaimo')// foo.call('kaimo')// foo.call('kaimo')// foo.call('kaimo')// foo.call('kaimo')// 默认绑定和显示绑定 bind 冲突:显示绑定优先级更高varnewFoo=foo.bind('kaimo')newFoo()newFoo()newFoo()newFoo()newFoo()

绑定规则四:new 绑定

JavaScript 中的函数可以当做一个类的构造函数来使用,也就是使用 new 关键字

使用 new 关键字来调用函数时,会执行如下的操作:

  • 1.创建一个全新的对象
  • 2.这个新对象会被执行 prototype 链接
  • 3.这个新对象会绑定到函数调用的 this 上(this 的绑定在这个步骤完成)
  • 4.如果函数没有返回其他对象,表达式会返回这个新对象
// 通过一个 new 关键字调用一个函数时(构造器),这个时候 this 是在调用这个构造器时创建出来的对象// this = 创建出来的对象// 这个绑定过程就是 new 绑定functionPerson(name,age){this.name=namethis.age=age console.log(this)}varp1=newPerson('why',18)console.log('p1---->',p1)varp2=newPerson('kaimo',313)console.log('p2---->',p2)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/28 0:58:57

超详细逻辑回归解说

逻辑回归是用来做“分类”的模型&#xff08;比如判断“是不是垃圾邮件”“病人有没有患病”“用户会不会点击广告”&#xff09;&#xff0c;而非回归。它的核心是&#xff1a;用“概率”的方式&#xff0c;把线性回归的输出&#xff08;连续值&#xff09;转化为“是/否”的分…

作者头像 李华
网站建设 2026/3/1 18:52:04

基于springboot的美食分享平台网站设计实现

技术背景SpringBoot作为Java生态中广泛使用的框架&#xff0c;简化了传统Spring应用的配置和部署流程。其自动配置、内嵌服务器和依赖管理特性&#xff0c;使得开发者能快速构建高可用的Web应用。在美食分享平台场景中&#xff0c;SpringBoot的高效开发模式适合处理用户生成内容…

作者头像 李华
网站建设 2026/3/1 18:16:36

BMI270,高性能高性价比的运动传感器, 现货库存

BMI270 是一款高性能、低功耗的 6 轴惯性测量单元 (IMU)&#xff0c;集成了高精度三轴加速度计和陀螺仪&#xff0c;并具备智能运动触发中断功能&#xff0c;为各种高性能应用提供了可靠且经济的解决方案。高性能特点 高精度传感器&#xff1a; BMI270 的加速度计和陀螺仪均具有…

作者头像 李华
网站建设 2026/2/24 5:32:34

信息安全的核心支柱:深入解析网络安全技术体系与实践

一、网络安全基础 OSI参考模型与TCP/IP开放模型对比 应用层 应用层 TehnetHTTPSMTPFTP DNSTFTPSSH 表示层 会话层 传输层 传输层 TCPUDP网络层 网络层 IP协议簇&#xff08;RIP、OSPF、SNMP、ICMP&#xff09; 数据链路层 物理和数据链路层 以太网 令牌…

作者头像 李华
网站建设 2026/2/21 13:56:29

在claude code中使用glm模型出现Unable to connect to Anthropic services的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

作者头像 李华
网站建设 2026/3/2 16:12:16

AI圈炸锅!GraphRAG让大模型不再“一本正经地胡说八道“,小白程序员也能上手的知识图谱增强技术!

前言 传统RAG系统通过检索——生成两阶段法有效缓解了大模型知识陈旧和幻觉问题&#xff0c;但是在处理复杂问题仍存在局限。依赖非结构化的文本向量的检索方式&#xff0c;很难捕捉到实体之间的深层关系&#xff0c;导致上下文不精确、信息碎片化&#xff0c;甚至有发模型幻觉…

作者头像 李华