安全基础 --- 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:

  1. 闭包使得函数中变量保存在内存中,内存消耗大,不可滥用,否则会造成网页的性能问题,IE中可能导致内存泄露。解决办法:退出函数前,不使用的局部变量全部删除
  2. 闭包会在父函数外部,改变父函数内部变量的值。若把父函数当做对象(object)使用,把闭包当做它的公用方法(Public Method),把内部变量当做它的私有属性(private value),这时不可随意改变父函数内部变量的值

this关键字

(1)关键点:

  1. this始终指向调用该函数的对象;
  2. 没有指明调用的对象,则顺着作用域链向上查找,最顶层为global(window)对象
  3. 箭头函数中的this是定义函数时绑定的,与执行上下文有关;
  4. 简单对象(非函数,非类)没有执行上下文;
  5. 类中的this,始终指向该实例对象;
  6. 箭头函数体内的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 属性,其值为200

console.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/120423.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app 之 安装uView,安装scss/sass编译

uni-app 之 安装uView&#xff0c;安装scss/sass编译 image.png image.png image.png 点击HBuilder X 顶部&#xff0c;工具&#xff0c;插件安装&#xff0c;安装新插件 image.png image.png 安装成功&#xff01; 注意&#xff0c;一定要先登录才可以安装 image.png 1. 引…

【数据分析】用Python秒懂概率分布!(附完整Python代码实现)

本文涉及的概念分布包括&#xff1a; 随机变量(Random Variable) 密度函数(Density Functions) 伯努利分布(Bernoulli Distribution) 二项式分布(Binomial Distribution) 均匀分布(Uniform Distribution) 泊松分布(Poisson Distribution) 正态分布(Normal Distribution) …

ChatGPT AIGC 完成动态堆积面积图实例

先使用ChatGPT AIGC描述一下堆积面积图的功能与作用。 接下来一起看一下ChatGPT做出的动态可视化效果图: 这样的动态图案例代码使用ChatGPT AIGC完成。 将完整代码复制如下: <!DOCTYPE html> <html> <head><meta charset="utf-8"><tit…

阿里云大数据实战记录9:MaxCompute RAM 用户与授权

文章目录 问题来源&#xff1a;maxcompute 管理员无法访问敏感列&#xff1f;主线问题&#xff1a;如何提高用户等级衍生问题1&#xff1a;怎么知道自己的等级和表单的等级衍生问题2&#xff1a;为什么 dataworks 空间管理员也没有设置等级的权限&#xff1f;衍生问题3&#xf…

模板和STL简介

模板和STL简介 一、泛型编程1、通用交换函数的实现&#xff08;1&#xff09;代码&#xff08;2&#xff09;总结 2、泛型编程的概念3、模板的概念 二、函数模板1、概念2、格式3、代码4、原理 三、函数模板实例化1、概念2、隐式实例化&#xff08;1&#xff09;概念&#xff08…

[unity]三角形顶点顺序

序 详见官方文档&#xff1a;Unity - Manual: Mesh data (unity3d.com) Topology&#xff1a;拓扑结构 翻译&#xff1a; 拓扑描述网格具有的面类型。 网格的拓扑定义了索引缓冲区的结构&#xff0c;索引缓冲区又描述了顶点位置如何组合成面。每种类型的拓扑都使用索引数组中…

医院安全(不良)事件上报系统源码 不良事件报告平台源码 前后端分离,支持二开

医院安全&#xff08;不良&#xff09;事件上报系统源码 系统定义&#xff1a; 规范医院安全&#xff08;不良&#xff09;事件的主动报告&#xff0c;增强风险防范意识&#xff0c;及时发现医院不良事件和安全隐患&#xff0c;将获取的医院安全信息进行分析反馈&#xff0c;…

Nacos 开源版的使用测评

文章目录 一、Nacos的使用二、Nacos和Eureka在性能、功能、控制台体验、上下游生态和社区体验的对比&#xff1a;三、记使使用Nacos中容易犯的错误四、对Nacos开源提出的一些需求 一、Nacos的使用 这里配置mysql的连接方式&#xff0c;spring.datasource.platformmysql是老版本…

Web前端开发概述

Web&#xff08;World Wide Web&#xff0c;全球广域网&#xff09;是指一种基于互联网的信息系统&#xff0c;通过超文本链接将全球各地的文档、图像、视频等资源相互关联起来&#xff0c;并通过Web浏览器进行交互浏览和访问。Web的发展使得人们可以方便地获取和共享各种类型的…

规避【虚拟专线技术】使用风险实现业务系统安全

本文为作者学习文章&#xff0c;按作者习惯写成&#xff0c;如有错误或需要追加内容请留言&#xff08;不喜勿喷&#xff09; 本文为追加文章&#xff0c;后期慢慢追加 一、技战法描述 VPN是利用Internet等公共网络基础设施&#xff0c;通过隧道加密通信技 术&#xff0c;为用…

js+vue,前端关于页面滚动让头部菜单淡入淡出实现原理

今天遇到个需求&#xff1a;我这里借用小米商城的详情页做个比喻吧。 刚开始其商品详情页是这样的&#xff1a; 当滚动到一定高度时&#xff0c;是这样的&#xff1a; 可以看到当滚动到轮播图底下的时候&#xff0c;详情页的菜单完全显现出来。 以下上代码&#xff1a; HTML…

大数据组件-Flink环境搭建

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

电脑硬盘数据恢复一般需要收费多少钱

随着电子信息时代的发展&#xff0c;个人和企业对电脑硬盘中存储的数据越发重视。然而&#xff0c;由于各种原因&#xff0c;硬盘数据丢失的情况屡见不鲜。如果您正陷入这样的困境&#xff0c;您可能会好奇恢复失去的数据需要花费多少钱。本文将为您介绍电脑硬盘数据恢复的一般…

【内网穿透】使用Nodejs搭建简单的HTTP服务器 ,并实现公网远程访问

目录 前言 1.安装Node.js环境 2.创建node.js服务 3. 访问node.js 服务 4.内网穿透 4.1 安装配置cpolar内网穿透 4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation…

hadoop3.3.1单机版环境搭建详细流程记录

1、在centos7中创建必要的目录&#xff1b; 2、上传JDK安装包到tools目录&#xff1b; 3、解压JDK到/opt/server/目录&#xff1b; tar -zxvf jdk-8u221-linux-x64.tar.gz -C /opt/server/ 4、“vim&#xff1a;未找到命令”的解决办法&#xff1b; 安装vim即可&#xff1b; …

Vue-router路由

配置路由 相当于SpringMVC的Controller 路径然后&#xff0c;跳转到对应的组件 一键生成前端项目文档

手写Mybatis:第7章-SQL执行器的定义和实现

文章目录 一、目标&#xff1a;SQL执行的定义和实现二、设计&#xff1a;SQL执行的定义和实现三、实现&#xff1a;SQL执行的定义和实现3.1 工程结构3.2 SQL执行实现的关系图3.3 执行器的定义和实现3.3.1 Executor 接口3.3.2 BaseExecutor 抽象基类3.3.3 SimpleExecutor 简单执…

机器学习---预剪枝、后剪枝(REP、CCP、PEP、)

1. 为什么要进行剪枝 横轴表示在决策树创建过程中树的结点总数&#xff0c;纵轴表示决策树的预测精度。 实线显示的是决策树 在训练集上的精度&#xff0c;虚线显示的则是在⼀个独⽴的测试集上测量出来的精度。 随着树的增⻓&#xff0c;在 训练样集上的精度是单调上升的&…

【前端demo】动态赋值CSS

文章目录 效果过程html实现oninput与onchange事件统一配置CSS 代码HTMLCSSJS 其他demo 效果 动态显示CSS样式&#xff0c;由:root统一配置。 效果预览&#xff1a;https://codepen.io/karshey/pen/BavLrwy 参考&#xff1a; Dynamic CSS Variables(codepen.io) 漫谈document…

Vue 2 nextTick方法|异步更新|事件循环

1 nextTick的用处 vm.$netTick的作用是将回调延迟到下次DOM更新周期之后执行。 它接受一个回调函数作为参数。 其实&#xff0c;在我们更新数据状态后&#xff0c;是不会立马渲染的&#xff0c;你不能即刻获取到新的DOM&#xff1a; <!DOCTYPE html> <html><…