安全基础 --- js的闭包和this属性
js闭包
简介
一个函数和对其周围状态(lexical exviroment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)
在js中,通俗来讲,闭包就是能够读取外层函数内部变量的函数
(1)变量作用域:
两种:
- 全局作用域
- 局部作用域
[1] 函数内部可以读取全局变量
var code = 200;function f1() {console.log(code);
}
f1(); //200
[2] 函数外部无法读取函数内部的局部变量
function f1() {var code = 200;
}
console.log(code); // 无法读取,在外部未被定义
(2)读取函数内部的局部变量
[1] 在函数内部再定义一个函数
function f1(){var code = 200;function f2() {console.log(code);}
}
函数 f1 内部函数 f2 可读取 f1 中所有的局部变量。因此,若想在外部访问函数 f1 中的局部变量 code,可通过函数 f2 间接访问
[2] 为外部程序提供访问函数局部变量的入口
function f1() {var code = 200;function f2() {console.log(code);}return f2;
}
f1()(); // 200
(3)闭包概念
函数 f2 就是闭包,作用就是将函数内部与外部进行了连接
- 闭包访问的变量,是每次运行上层函数时重新创建的,是相互独立的
function f1() {var obj = {};function f2() {return obj;}return f2;
}let result1 = f1();
let result2 = f1();
console.log(result1() === result2()); // false
- 不同的闭包,可共享上层函数中的局部变量
function f() {var num = 0;function f1() {console.log(++num);}function f2() {console.log(++num);}return {f1,f2};
}var result1 = f();
result1.f1(); // 1
result1.f2(); // 2// 闭包f1和闭包f2共享上层函数中的局部变量num
例:旅行者走路的问题
function factory() {var start = 0;function walk(step) {var new_total = start + step;start = new_total;return start;}return walk;
}var res = factory();
console.info(res(1));
console.info(res(2));
console.info(res(3));// start 将记录上一次的值

PS:
- 闭包使得函数中变量保存在内存中,内存消耗大,不可滥用,否则会造成网页的性能问题,IE中可能导致内存泄露。解决办法:退出函数前,不使用的局部变量全部删除
- 闭包会在父函数外部,改变父函数内部变量的值。若把父函数当做对象(object)使用,把闭包当做它的公用方法(Public Method),把内部变量当做它的私有属性(private value),这时不可随意改变父函数内部变量的值
this关键字
(1)关键点:
- this始终指向调用该函数的对象;
- 没有指明调用的对象,则顺着作用域链向上查找,最顶层为global(window)对象;
- 箭头函数中的this是定义函数时绑定的,与执行上下文有关;
- 简单对象(非函数,非类)没有执行上下文;
- 类中的this,始终指向该实例对象;
- 箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象
(2)四类调用方式
[1] 作为对象方法使用
function f(){console.log(this.code);
}
var obj = {code:200,f:f
};
obj.f(); // 200
[2] 纯粹的函数调用
function f() {console.log(this.code);
}
// 此处,通过var(函数作用域)声明的变量会绑定在windows上,
// 如果使用let(块作用域)声明变量code,则不会绑定在windows上,
// 因此下面的两次函数调用f(),显示为undefined
// let code = 200;var code = 200;
f();// 200
code = 404;
f();// 404
复杂:
function doF(fn) {this.code = 404;fn();
}function f() {console.log(this.code);
}let obj = {code:200,f:f
};
var code = 500;
doF(obj.f); // 404
结果解析 --- 该例中,为分析出 this 的指向,应找到关键点:哪个对象调用了函数 f()。obj.f 作为doF()的入参,将函数 f 传给了doF,而 doF 是由 window 调用的,所以函数doF中的 this 指向 window ,继而函数 f 中的 this 指向window
但最终执行是 doF,所以 this 指向 doF,结果为404
[3] 作为[构造函数]调用
code = 404;
function A() {this.code = 200;this.callA = function() {console.log(this.code);}
}
var a = new A();
a.callA(); // 200,callA在new A返回的对象里
[4] 使用apply、call、bind调用
<1> apply
作用:与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别为,它接受一个数组作为函数执行时的参数
var code = 404;
let obj = {code:200,f:function() {console.log(this.code);}
};
obj.f(); // 200,作为对象的方法调用
obj.f.apply(); // 404,参数为空时,默认使用全局对象global,在此处为对象window
obj.f.apply(obj); // 200,this指向参数中设置的对象
<2> call
函数实例的call方法,作用:可以指定函数内部的this指向(即函数执行时所在的域),然后在所指定的作用域中,调用该函数
function f() {console.log(this.code);
}var obj = {code:200
};f.call(obj); // 200
<3> bind
bind()方法作用:用于将函数体内的this绑定到某个对象,然后返回一个新函数
// bind返回一个新的函数
function f(b) {console.log(this.a,b);return this.a+b;
}
var obj = {a:2
};
var newF = f.bind(obj);
var result = newF(3); // 2 3
console.log(result); // 5
(3)箭头函数中的this
[1] 概念
箭头函数中的this是定义函数时绑定的,而不是在执行函数时绑定。若箭头函数在简单对象中,由于简单对象没有执行上下文,所以this指向上层的执行上下文;若箭头函数在函数、类等有执行上下文的环境中,则this指向当前函数、类
[2] 箭头函数在普通对象中
var code = 404;
let obj = {code:200,getCode:() => {console.log(this.code);}
};
obj.getCode(); // 404
结果解析:
在箭头函数中,this 的值是在定义函数时确定的,而不是在运行时确定的。函数 getCode 是在对象 obj 定义时创建的,而不是在调用obj.getCode()的时候
箭头函数中 this 指向的是外层语法作用域的 this 值,而不是指向调用他的对象。在全局作用域中,this 指向的是全局对象(在浏览器环境中通常是window对象)。所以的那个箭头函数中使用 this.code 时,实际上引用全局作用域的code变量,值为404
[3] 箭头函数在函数中
var code = 404;
function F() {this.code = 200;let getCode = () => {console.log(this.code);};getCode();
}
var f = new F(); // 200
var f = F(); // 构造函数没有new调用,成为了一个普通函数
console.log(f);
console.log(code); // 200
[4] 箭头函数在类中
var code = 404;
class Status {constructor(code){this.code = 200;}getCode = () => {console.log(this.code);};
}
let status = new Status(200);
status.getCode(); // 200
PS:不管是箭头函数还是普通函数,只要在类中,this就指向实例对象
实例解析
(1)例1:
var code = 404;let status = {code:200,getCode:function(){return function(){return this.code;};}
};console.log(status.getCode()()); // 404
执行status.getCode()时,返回函数,status.getCode()()表示当前返回的函数,其调用者为全局变量window,所以this.code为绑定在window中的code,值为404
(2)例2:
var code = 404;
let status = {code:200,getCode:function(){let that = this;return function(){return that.code;};}
};console.log(status.getCode()()); // 200
执行status.getCode()时,this指向status,并通过局部变量that保存this的值,最后返回值为函数。status.getCode()()表示执行返回的函数,其that指向的status,所以返回值为200
(3)例3:复杂
function f() {// 宏任务setTimeout(() => {console.log(">>>" + this); // >>>[object object],语句5this.code = 401;},0)// 同步console.log(this.code);
}let obj = {// ">>>" + thiscode:200,foo:f
};var code = 500;// 1.箭头函数 this 指向问题
// 2.字符串 + this [object object]
obj.foo(); // 200 语句1console.log("---" + obj.code); // 200 语句3// 宏任务
setTimeout(() =>{console.log("---" + obj.code);},0); // 401 语句4

obj.foo(); --- (语句1):调用obj对象的foo方法
输出:200
解释:在 foo 方法内部的console.log(this.code) 打印出 obj 对象的 code 属性,其值为200console.log("---" + obj.code); --- (语句3):打印obj对象的code属性
输出:---200
解释:全局作用域中,code被赋值为500,这里的obj.code指向的是obj对象的code属性,其值仍然是200
setTimeout(() => console.log("---" + obj.code);},0); --- (语句4):设置一个0延时的定时器,其中的箭头函数在调用
输出:---401
解释:在调用obj.foo()的过程中,foo方法中的setTimeout在当前宏任务结束后执行。由于是箭头函数,this的值保持与父作用域一致(也就是obj对象),所以在箭头函数内部,this.code的值被设为401
setTimeout(() => {console.log(">>>" + this); this.code = 401;},0) --- (语句5):设置一个0延时的定时器,其中的箭头函数在调用
输出:>>>[object object]
解释:在调用obj.foo()的过程中,foo方法中的setTimeout在当前宏任务结束后执行。箭头函数的this始终指向被他创建时的外部作用域,所以this指向了obj对象,在控制台中打印this时会将其转换为字符串。所以输出为 >>>[object object]
setTimeout()
函数setTimeout用于创建一个定时器,同一个对象,各个定时器用一个编号池,不同的对象是用独立的编号池,同一个对象上的多个定时器有不同的定时器编号;所以,setTimeout到了执行时间点时,其内部的this指向定时器所绑定的对象。
分析 --- :函数setTimeout中传入的函数句柄,由于js是单线程执行,即使延时为0,仍需等到本次执行的所有同步代码执行完毕,才能执行。在两次执行obj.foo()的过程中,其内部的setTimeout的入参函数(语句5)都未执行。知道执行语句4,当前同步代码执行完毕,语句5执行(执行2次,因为语句1和2分别执行1次),obj上绑定的code被执行为401。最终语句4的入参函数执行,输出obj.code的值为401。
(4)扩展
function doFoo(fn){this.code = 404;fn();
}function f() {setTimeout(() => {console.log(">>>" + this); // >>>[object window],语句3this.code = 401; // 语句4},0)console.log(this.code);
}let obj = {code:200,foo:f
};var code = 500;doFoo(obj.foo); // 语句1
setTimeout(() => {console.log(obj.code)},0); // 200,语句5
setTimeout(() => {console.log(window.code)},0); // 401,语句6
结果:obj.foo为函数句柄,作为入参传入函数doFoo,doFoo的调用方为全局变量window,所以,语句2中doFoo对象的code是404,3、4中的this均指向window
相关文章:
安全基础 --- js的闭包和this属性
js闭包 简介 一个函数和对其周围状态(lexical exviroment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure) 在js中,通俗来讲,…...
【C语言每日一题】08. 字符三角形
题目来源:http://noi.openjudge.cn/ch0101/08 08 字符三角形 总时间限制: 1000ms 内存限制: 65536kB 问题描述 给定一个字符,用它构造一个底边长5个字符,高3个字符的等腰字符三角形。 输入 输入只有一行, 包含一个字符。 输出…...
如何打war包,并用war包更新服务器版本
1.打包,我用的maven打包 先执行clean将已经生成的包清除掉 清除完,点package进行打包 控制台输出success,证明打包成功了 文件名.war的后缀就是生成的war包 2.将war包上传致服务器 一般会在war包加上日期版本上传至服务器 解压上传的war…...
uniApp webview 中调用底座蓝牙打印功能异常
背景: 使用uniApp, 安卓底座 webView 方式开发; 调用方式采用H5 向 底座发送消息, 底座判断消息类型, 然后连接打印机进行打印; 内容通过指令集方式传递给打印机; 过程当中发现部分标签可以正常打印, 但又有部分不行,打印机没反应, 也没有报错; 原因分析: 对比标签内容…...
Mac下安装Jmeter及其配置
一、安装JDK环境 安装方式:mac下配置JDK环境_只看不学的博客-CSDN博客 如果已安装JDK环境即可忽略该步骤,检查方式,在终端输入java -version,如果出现了java版本,即代表已经配置过JDK环境了,如下图所示: …...
js+html实现打字游戏v1
实现逻辑:设置定时器每秒刷新一次,定时器刷新多少次执行一次生成单词操作来决定单词的生成速度,例如初始单词生成速度为1,那么定时器刷新5次才生成一次单词,每个单词用span来装,每组10个单词放到div里。监听…...
Java on VS Code 8月更新|反编译器用户体验优化、新 Maven 项目工作流、代码高亮稳定性提升
作者:Nick Zhu 排版:Alan Wang 大家好,欢迎来到 Visual Studio Code for Java 的 8 月更新!在这篇博客中,我们将为您提供有关反编译器支持的更多改进。此外,我们将展示如何创建没有原型的 Maven 项目以及一…...
划分Vlan时需要注意的问题
网络部分2019年才开始学习的,在学习过程中配置了整个公司的网络,心里才有了一点把握,算是掌握了最基本的。 不会的就上网学,反正网络上什么知识都有,只要有需求就对照着学,很长时间没有学习网络了ÿ…...
【广州华锐互动】利用AR远程指导系统进行机械故障排查,实现远程虚拟信息互动
随着工业自动化和智能化的不断发展,机械故障诊断已经成为了工业生产中的重要环节。为了提高故障诊断的准确性和效率,近年来,AR(增强现实)远程协助技术逐渐应用于机械故障诊断领域。本文将探讨AR远程协助技术在机械故障…...
Spring工具类--CollectionUtils的使用
原文网址:Spring工具类--CollectionUtils的使用_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Spring的CollectionUtils的使用。 CollectionUtils工具类的作用:操作Collection,比如:List、Set。 判断 方法作用static boolean is…...
Node.js 应用的御用品: Node.js 错误处理系统
开发中,有些开发者会积极寻求处理错误,力求减少开发时间,但也有些人完全忽略了错误的存在。正确处理错误不仅意味着能够轻松发现和纠正错误,而且还意味着能够为大型应用程序开发出稳健的代码库。 特别是对于 Node.js 开发人员&am…...
K210-CanMV IDE开发软件
K210-CanMV IDE开发软件 界面功能简介连接设备临时运行开机运行程序 界面功能简介 区域①菜单栏:操作文件,使用工具等。 区域②快捷按钮:区域①中的文件和编辑中部分功能的快捷方式。 区域③连接设备:连接设备和程序控制按钮。 …...
0301yarnmapredude入门-hadoop-大数据学习
文章目录 1 MapReduce概述2 YARN2.1 yarn概述2.2 yarn与MapReduce关系2.3 yarn架构2.4 辅助角色 3 MapReduce & YARN部署3.1 集群规划3.2 配置文件3.3 分发配置文件 4 体验4.1 集群启动命令介绍4.2 提交MapReduce任务到YARN执行 结语 1 MapReduce概述 分布式计算是一种计算…...
大数据课程K15——Spark的TF-IDF计算Term权重
文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的TF-IDF算法概念; ⚪ 了解Spark的TF-IDF算法定义; ⚪ 了解Spark的TF-IDF算法案例; 一、TF-IDF算法概述 TF-IDF(term frequency–inverse document frequency)是一种用于信…...
【C语言】字符函数,字符串函数,内存函数
大家好!今天我们来学习C语言中的字符函数,字符串函数和内存函数。 目录 1. 字符函数 1.1 字符分类函数 1.2 字符转换函数 1.2.1 tolower(将大写字母转化为小写字母) 1.2.2 toupper(将小写字母转化为大写字母&…...
Spring MVC:域对象共享数据
Spring MVC 前言域对象共享数据使用 ModelAndView 向 request 域对象中共享数据使用 Map 、Model 或 ModelMap 向 request 域对象中共享数据使用 SesionAttributes 注解向 session 域对象中共享数据使用 Servlet API 向 application 域对象中共享数据 附 前言 在上一章中&…...
Vue框架--Vue中的计算属性
下面,我们来实现一个这样的需求。 实现输入框1和输入框2中文字内容的拼接。...
面试题-React(八):React如何实现插槽?
一、React插槽的概念 插槽是一种让组件变得更加灵活和可复用的技术。它允许我们在组件内部预留一些位置,然后在组件使用时填充这些位置,实现外部内容的嵌套。 二、实现React插槽的方法 在React中,实现插槽可以通过两种方式:pro…...
【前端demo】动态赋值CSS
文章目录 效果过程html实现oninput与onchange事件统一配置CSS 代码HTMLCSSJS 其他demo 效果 动态显示CSS样式,由:root统一配置。 效果预览:https://codepen.io/karshey/pen/BavLrwy 参考: Dynamic CSS Variables(codepen.io) 漫谈document…...
BlockUI专栏目录
文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C-CSDN博客 简介: BlockUI是一个设计NX对话框的工具,是官方推荐使用的对话框制作方法,能够与NX自身风格相统一,并且在实际…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
