JavaScript中的this
在实际应用中,了解
this的行为是非常重要的,特别是在编写库或框架时,或者当你需要在回调函数中访问特定的上下文时,通常推荐使用箭头函数或者其他方法来确保this的正确指向。
在ES6中,this 的值取决于它是如何被调用的。
this 不是一个函数或对象,而是一个特殊的关键字,其值在函数被调用时确定。
以下是在不同场景中 this 的值的概述:
01 全局作用域中的this
在JavaScript中,全局作用域指的是在代码的任何位置都可以访问的、变量和函数的范围。
具体可以这么理解:当你在脚本的顶层(不在任何函数或代码块内部)声明一个变量或函数时,它就在全局作用域中。
在全局作用域中,this 指向全局对象。
在浏览器环境中,全局对象是 window;
在 Node.js 环境中,全局对象是 global。
下面是一个在浏览器环境中演示这一点的简单例子:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>"use strict"// 在全局作用域中console.log(this === window); // 应该输出 true,表示 this 指向 window 对象console.log(this); // 输出window对象// 定义一个全局变量var globalVar = "I am a global variable";// 使用 this 访问全局变量console.log(this.globalVar); // 输出 "I am a global variable"// 使用 window 访问全局变量console.log(window.globalVar); // 输出 "I am a global variable"// 定义一个函数,并在全局作用域中调用它function testFunction() {console.log(this); // 在浏览器环境中,输出 window 对象,"use strict"下输出undefined}testFunction();</script>
</body>
</html>
在这个例子中,this 在全局作用域中指向 window 对象。我们通过比较 this 和 window 来验证这一点,并且使用 this 和 window 来访问一个全局变量 globalVar,以证明它们都可以用来访问全局作用域中的变量。
如果你在 Node.js 环境中运行代码,全局对象将是 global,你可以类似地测试:
// 在 Node.js 的全局作用域中
console.log(this === global); // 应该输出 true,表示 this 指向 global 对象// 定义一个全局变量
global.globalVar = "I am a global variable";// 使用 this 访问全局变量
console.log(this.globalVar); // 输出 "I am a global variable"// 使用 global 访问全局变量
console.log(global.globalVar); // 输出 "I am a global variable"// 定义一个函数,并在全局作用域中调用它
function testFunction() {console.log(this); // 在 Node.js 环境中,输出 global 对象
}testFunction();
在这个 Node.js 例子中,this 在全局作用域中指向 global 对象,并且我们同样使用 this 和 global 来访问一个全局变量 globalVar。
02 函数调用中的this
当一个函数不是作为对象的方法调用时(也就是说,它是独立调用的,或者作为回调函数等被调用),this 的值在非严格模式下默认为全局对象(在浏览器中通常是 window),而在严格模式下它是 undefined。
下面我将给出两个示例程序来演示这个行为。
首先是非严格模式下的示例:
// 非严格模式
function exampleFunction() {console.log(this); // 在非严格模式下,this 指向 window 对象console.log(this.globalVar); // => I am a global variable
}exampleFunction();// 你可以通过检查 window 对象来验证这一点
function setGlobalVar() {window.globalVar = "I am a global variable";
}
setGlobalVar();
exampleFunction(); // 输出包含 globalVar 的 window 对象
在这个例子中,exampleFunction 不是作为任何对象下的方法调用的,因此在非严格模式下,this 指向 window 对象。
我们在 setGlobalVar 函数中设置了一个全局变量 globalVar,然后在 exampleFunction 中通过 this 访问它,证明了 this 确实指向 window。
03 对象方法中的this
当一个函数作为对象的方法被调用时,this 关键字指向调用该方法的对象。下面是一个简单的示例来展示这个行为:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>03-对象方法中的this</title>
</head>
<body><script>// 定义一个对象,它有一个方法叫做 greetconst person = {name: "Alice",getName: function () {console.log(this.name);}};// 调用 person 对象的 getName方法person.getName(); // 输出 "Alice"// getName中this指向person对象,因为getName是作为 person 的方法被调用的</script>
</body>
</html>
在这个例子中,getName 函数是 person 对象的一个方法。当我们通过 person.getName() 调用这个方法时,this 关键字在函数内部指向了 person 对象。因此,this.name 访问的是 person 对象的 name 属性,并输出了相应的信息。
04构造函数中的this
当一个函数被用作构造函数,并使用 new 关键字调用时,this 关键字会指向新创建的对象实例。下面是一个简单的例子来说明这个行为:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>04-构造函数中的this</title>
</head>
<body><script>// 定义一个构造函数 function Person(name, age) {this.name = name;this.age = age;// 可以添加一个方法来访问实例属性 this.greet = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);};}// 使用 new 关键字来调用构造函数,创建一个新的对象实例 const alice = new Person('Alice', 25);alice.greet(); // 输出 "Hello, my name is Alice and I'm 25 years old." // 验证 this 指向的是 alice 对象 console.log(alice.name); // 输出 "Alice" console.log(alice.age); // 输出 25 // 创建一个新的对象实例 const bob = new Person('Bob', 30);bob.greet(); // 输出 "Hello, my name is Bob and I'm 30 years old." // 验证 this 指向的是 bob 对象 console.log(bob.name); // 输出 "Bob" console.log(bob.age); // 输出 30</script>
</body>
</html>
每次使用 new 关键字调用 Person 构造函数时,都会创建一个新的对象实例,并且 this 都会指向那个新对象。这就是为什么 alice 和 bob 有各自独立的 name 和 age 属性,以及它们各自的 greet 方法。
在这个例子中,Person 函数被用作构造函数,因为我们使用 new 关键字来调用它。当构造函数被调用时,JavaScript 会创建一个新的空对象,并将这个新对象的内部链接([[Prototype]])设置为构造函数的 prototype 对象。然后,构造函数中的代码执行,其中 this 关键字引用新创建的对象。
因此,当我们设置 this.name 和 this.age 时,我们实际上是在新创建的对象上设置属性。同样,this.greet 方法也是添加到新对象上的一个方法。
05箭头函数中的this
箭头函数在 JavaScript 中是一个非常重要的特性,它提供了一种更简洁的函数书写方式,并且它不绑定自己的 this,而是继承自包围它的函数或全局作用域的 this。
这意味着在箭头函数内部,this 的值是在定义箭头函数时确定的,而不是在调用时确定的。
下面是一个简单的示例来说明这个行为:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>05-箭头函数中的this</title>
</head>
<body><script>function OuterFunction() {this.value = 42;// 这是一个普通函数 function innerFunction() {console.log(this.value); // 这里的 this 指向 OuterFunction 的实例 }// 这是一个箭头函数 const arrowFunction = () => {console.log(this.value); // 这里的 this 继承自 OuterFunction 的实例 };// 调用普通函数和箭头函数 innerFunction(); // 输出 42 arrowFunction(); // 输出 42 }const obj = new OuterFunction();// 当我们在外部调用 innerFunction 时,this 指向全局对象(在浏览器中通常是 window) // 因此,以下代码会抛出一个错误,因为 this.value 是 undefined obj.innerFunction(); // Uncaught TypeError: Cannot read property 'value' of undefined // 但是,箭头函数不会改变它的 this 上下文 // 因此,即使我们在外部调用 arrowFunction,它仍然可以访问 OuterFunction 的 this 上下文 obj.arrowFunction(); // 输出 42</script>
</body>
</html>
在OuterFunction 中定义了一个普通函数 innerFunction 和一个箭头函数 arrowFunction。
当 OuterFunction 被调用时,它创建了一个新的对象实例,并且 this 在 innerFunction 和 arrowFunction 中都指向这个新创建的对象。
当我们直接调用 obj.innerFunction() 时,this 指向了全局对象(在浏览器中是 window),因此 this.value 是 undefined,导致抛出一个错误。
然而,当我们调用 obj.arrowFunction() 时,即使我们是在外部调用的,arrowFunction 内部的 this 仍然指向 OuterFunction 的实例,因此可以正确地访问 value 属性。这是因为箭头函数不绑定自己的 this,而是从定义它的上下文中继承 this。
TODO 嵌套函数
全局函数中的嵌套函数
对象方法中的嵌套函数
总结
| 场景 | this 的值 | 描述 |
|---|---|---|
| 全局作用域 | window (浏览器) 或 global (Node.js) | 在全局作用域中,this 指向全局对象。 |
| 函数调用 | undefined (严格模式) 或 window (非严格模式) | 当一个函数不是作为对象的方法调用时,this 的值在非严格模式下是 window,在严格模式下是 undefined。 |
| 对象方法 | 调用该方法的对象 | 当一个函数作为对象的方法被调用时,this 指向调用该方法的对象。 |
| 构造函数 | 新创建的对象 | 当一个函数作为构造函数使用 new 关键字调用时,this 指向新创建的对象。 |
| 箭头函数 | 定义时的上下文 | 箭头函数不绑定自己的 this,它继承自包围它的函数或全局作用域的 this。 |
| 事件处理器 | 调用事件处理器的对象 | 在DOM事件处理器中,this 通常指向触发事件的元素。 |
| 定时器函数 | undefined (严格模式) 或 window (非严格模式) | 在 setTimeout 或 setInterval 的回调函数中,this 的值在非严格模式下是 window,在严格模式下是 undefined。 |
| Call, Apply, Bind | 指定的对象 | 使用 call、apply 或 bind 方法可以显式地设置 this 的值。 |
请注意,箭头函数的行为略有不同,因为它们不绑定自己的 this。相反,它们从定义它们的上下文继承 this。
希望这个表格能帮助你理解ES6中 this 的不同行为!
在JavaScript中,特别是在ES6及其之后的版本中,回调函数中使用普通函数和箭头函数时,this 的值可能会有所不同。这主要取决于回调函数的调用方式和上下文。以下是普通函数和箭头函数在回调中作为方法使用时 this 的区别:
普通函数
当回调函数是一个普通函数时,this 的值通常取决于该函数如何被调用。如果该函数是作为对象的方法被调用,那么 this 将指向调用该方法的对象。如果该函数是作为回调函数被调用,并且没有使用 call、apply 或 bind 方法来显式地设置 this 的值,那么 this 可能会指向全局对象(在浏览器中是 window),或者在没有严格模式的情况下是 undefined。
例如:
function Example() {this.value = 5;setTimeout(function() {console.log(this.value); // this 指向全局对象或undefined(严格模式)}, 1000);
}const example = new Example(); // 输出可能是 undefined,取决于环境
箭头函数
箭头函数不绑定自己的 this,它继承自包围它的函数或全局作用域的 this。这意味着在箭头函数内部,this 的值将始终与包围它的外部函数的 this 保持一致。
因此,当回调函数是箭头函数时,this 将保持外部函数的 this 值,即使回调函数以不同的方式被调用。
例如:
function Example() {this.value = 5;setTimeout(() => {console.log(this.value); // this 继承自Example函数的this,所以输出5}, 1000);
}const example = new Example(); // 输出 5
在这个例子中,即使 setTimeout 是一个全局函数,并且通常会导致回调函数中的 this 指向全局对象,但由于使用了箭头函数,this 仍然保持了 Example 函数的上下文。
总结:在回调函数中,普通函数的 this 值可能会根据回调函数的调用方式而改变,而箭头函数的 this 值则始终与其外部函数的 this 保持一致。因此,在需要确保 this 的值始终为特定上下文时,使用箭头函数通常是更安全的选择。
this不是常量, 它在程序中的不同地方会求值为不同的值。
this是面向对象编程中使用的关键字。在方法体中,this求值为调用方法的对象。
this 在 标准函数 和 箭头函数 中有不同的行为。
1、标准函数中的 this
window.color = 'red';
// 全局上下文中调用函数时,this 指向 windows
function sayColor() { console.log(this.color);
}
sayColor(); // 'red' let o = { color: 'blue'
};
o.sayColor = sayColor;
o.sayColor(); // 'blue'
2、嵌套函数
嵌套函数普通函数内部的this是全局对象,嵌套箭头函数的this是外层对象的this
注意:vue中相反:vue中嵌套普通函数内部的this是vue,而嵌套箭头函数的this是全局对象
let o = {m:function(){let self = this;this === o;console.log('o.m() this:',this);f(); function f(){this === o;self ===o;console.log('o.m.f() this:',this);}const f2 = ()=>{console.log('o.m.f2() this:',this);}f2();const f3 = (function(){console.log('o.m.f3() this:',this);}).bind(this);f3();}
}
o.m();
console.log(o);
2、箭头函数中的this
window.color = 'red';
let sayColor = () => console.log(this.color);
sayColor(); // 'red' let o = { color: 'blue'
};
o.sayColor = sayColor;
o.sayColor(); // 'red'
相关文章:
JavaScript中的this
在实际应用中,了解 this 的行为是非常重要的,特别是在编写库或框架时,或者当你需要在回调函数中访问特定的上下文时,通常推荐使用箭头函数或者其他方法来确保 this 的正确指向。 在ES6中,this 的值取决于它是如何被调用…...
宝塔php站点设置伪静态规则 访问 a.com 时候跳转到 a.com/b.html
要在宝塔 PHP 站点中设置伪静态规则,实现访问a.com时跳转到a.com/b.html,可以按照以下步骤进行操作: 打开宝塔面板并登录到你的服务器管理界面。进入网站设置页面,找到你要设置伪静态规则的 PHP 站点。在站点设置中,找…...
git介绍4.2
git(版本控制工具) 一、git 介绍 1、git是目前世界上最先进的分布式版本控制系统,可以有效,高速的处理从小到大的项目版本管理。 2、git是linux torvalds 为了帮助管理linux内核开发二开发的一个开放源码的版本控制软件。 3、git作用:更好…...
【深入了解设计模式】组合设计模式
组合设计模式 组合模式是一种结构型设计模式,它允许你将对象组合成树状结构来表现“整体-部分”关系。组合模式使得客户端可以统一对待单个对象和组合对象,从而使得代码更加灵活和易于扩展。 概述 对于这个图片肯定会非常熟悉,上图我们可…...
4.Java---方法+重载
方法 方法的调用是需要开辟内存的,方法调用结束内存就被销毁了. 下面将介绍一个经典的错误标准的0分的示意! 我们日常中写交换两个数字的代码的时候都会用如下的方法进行描述: 你是不是觉得自己写的特别对!终于可以独立写一个小小的函数了? 下面运行一下看看结果 哦莫!怎么…...
蓝桥杯Java B组历年真题(2013年-2021年)
一、2013年真题 1、世纪末的星期 使用日期类判断就行,这里使用LocalDate,也可以使用Calendar类 答案 2099 使用LocalDate import java.time.LocalDate; import java.time.format.DateTimeFormatter; // 1:无需package // 2: 类名必须Main, 不可修改p…...
C++笔记(五)--- 虚函数(virtual)
目录 虚函数介绍 虚函数、覆盖和重载区别 虚函数介绍 C的虚函数是多态性的表现 1.构造函数不能为虚函数2.子类继承时虚函数仍为虚函数3.虚函数类外实现时,不需要加virtual4.有虚函数的类,析构函数一定要写成虚函数(否则可能会造成内存泄漏&…...
编写加密程序,加密规则为:将所有字母转化为该字母后的第三个字母,即A->D、B->E
编写加密程序,加密规则为:将所有字母转化为该字母后的第三个字母,即A->D、B->E、C->F、…、Y->B、Z->C。小写字母同上,其他字符不做转化。输入任意字符串,输出加密后的结果。 例如:输入&qu…...
【笔记】:更方便的将一个List中的数据传入另一个List中,避免多重循环
这里是 simpleInfoList 集合,记为集合A(传值对象) List<CourseSimpleInfoDTO> simpleInfoList courseClient.getSimpleInfoList(courseIds);if(simpleInfoListnull){throw new BizIllegalException("当前课程不存在!");}这…...
Cisco Secure ACS 5.8.0.32 安装 + Crack 教程
Cisco Secure ACS 5.8.0.32 安装 Crack 教程 前言系统环境开始安装 开始破解导入授权文件 前言 在ESXi 6.7 上经历过无数次的安装尝试 测试了各种兼容版本都没有安装成功,记最后一次安装成功的过程. 系统环境 服务器 : Dell R720xd CPU : E5-2620 v2 系统 : ESXi 6.7…...
项目准备March
Nginx主要用来作为Http服务器,要实现Tomcat的负载均衡,就可以通过Nginx来实现。 正向代理代理的是客户端,反向代理代理的是服务端。SpringBoot采用约定优于配置的思想,简化Spring项目的配置开发。 前端请求其实并未直接发送到后…...
集智书童 | YOLO+混合注意力机制 | YOLOv5再加4.3%才可以做对手,Transformer混合设计依旧可以卷
本文来源公众号“集智书童”,侵权删,干货满满。YOLOv5重出江湖! 原文链接:https://mp.weixin.qq.com/s/vb7HsA0fKDgRc3uC8Z-2yw 在工业生产过程中,由于低效率、不统一的评估、高成本以及缺乏实时数据,传统…...
Codeforces Round 894 (Div. 3)----->C. Flower City Fence
题目总思路: 要判断是否对称,只需要判断两个放法得到的图形是否相同(竖着放,横着放),这两个放法有个很重要的特性:就是数组中大于1的个数,就是横着放时,第一竖排的高度。…...
CryoEM - CryoAI: Amortized Inference of Poses 工程源码复现
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/136384544 Paper: CryoAI: Amortized Inference of Poses for Ab Initio Reconstruction of 3D Molecular Volumes from Real Cryo-EM Images CryoAI: 基于摊…...
项目预备知识
导入两个头文件 #include <graphics.h> // 引入 EasyX 的图形库头文件 #include <conio.h> // 引入 conio.h 以使用 getch() 窗口创建函数:小黑屏 initgraph(640, 480, SHOWCONSOLE); closegraph(); //关闭一个窗口 设置背景颜色:这…...
redis实战笔记汇总
文章目录 1 NoSQL入门概述1.1 能干嘛?1.2 传统RDBMS VS NOSQL1.3 NoSQL数据库的四大分类1.4 分布式数据库CAP原理 BASE原则1.5 分布式集群简介1.6 淘宝商品信息的存储方案 2 Redis入门概述2.1 是什么?2.2 能干嘛?2.3 怎么玩?核心…...
elment-ui table表格排序后 清除排序箭头/恢复默认排序 的高亮样式
问题描述: 1.默认排序是按照名称升序排列(图一) 2.在选择了筛选项以及其他排序方式之后,箭头高亮是这样的(图二) 3.当我点击清空按钮后,类型清空了,并且传给后端的排序方式是名称/升…...
MySQL数据库基本操作(二)
查询语句 1. 排序查询* 语法:order by 子句* order by 排序字段1 排序方式1 , 排序字段2 排序方式2... * 排序方式:* ASC:升序,默认的。* DESC:降序。 * 注意:* 如果有多个排序条件&#…...
Unity(第十部)时间函数和文件函数
时间函数 using System.Collections; using System.Collections.Generic; using UnityEngine;public class game : MonoBehaviour {// Start is called before the first frame updatefloat timer 0;void Start(){//游戏开始到现在所花的时间Debug.Log(Time.time);//时间缩放值…...
【Java学习笔记】
常见算法 查找算法 1.基本查找 public class BasicSearchDemo01 {public static void main(String[] args) {//基本查找//核心://从0索引开始挨个往后查找//需求:定一个方法利用基本查找,查询某个元素是否存在//数据如下:{131&…...
高光谱成像基础(十一)异常检测算法 RX 与 KRX合
一、简化查询 1. 先看一下查询的例子 /// /// 账户获取服务 /// /// /// public class AccountGetService(AccountTable table, IShadowBuilder builder) {private readonly SqlSource _source new(builder.DataSource);private readonly IParamQuery _accountQuery build…...
Kairoa v1.1.18 版本:AI聊天功能协议支持升级,助力开发者高效开发
AI聊天功能协议支持再升级Kairoa作为一款专为开发者打造的跨平台桌面工具箱,其v1.1.18版本在AI聊天功能上进行了重要完善。此前,AI聊天模块仅支持OpenAI格式的接口,而此次更新新增了Anthropic Messages API和Google Gemini原生协议的支持。这…...
ISE仿真流程详解:从Verilog代码到Isim仿真的完整指南
1. ISE仿真流程概述 第一次接触FPGA开发的朋友们,常常会被复杂的工具链和仿真流程搞得晕头转向。今天我就以最常用的ISE设计套件为例,手把手带大家走完从Verilog代码编写到Isim仿真的完整流程。这个教程特别适合刚入门FPGA的开发者,我会尽量避…...
OpenClaw 在国内的热度逐渐降温了
👉 这是一个或许对你有用的社群🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 《项目实战(视频)》:从书中学,往事上…...
手把手教你用QT设计FPGA图像接收器:从UDP数据包到实时显示窗口的完整流程
QTUDP实现FPGA图像接收器的工程实践指南 在工业视觉检测、医疗影像传输等实时性要求较高的场景中,FPGA与PC端的高效图像传输一直是开发者面临的挑战。本文将分享一个基于QT框架的UDP图像接收器完整实现方案,重点解决网络流媒体处理中的三个核心问题&…...
ESLint 9.0+ 配置实战:从零到一构建现代前端代码规范
1. 为什么你需要ESLint 9.0的扁平化配置 最近接手了一个Vue 3 TypeScript的新项目,当我像往常一样准备配置ESLint时,发现官方文档已经全面转向了全新的扁平化配置方式。作为一个从ESLint 6.x时代就开始使用它的老用户,我必须承认这次改动确实…...
告别黑屏!用Arduino和TFT_eSPI库给你的项目加块彩色LCD屏(ESP32/ESP8266保姆级教程)
从零玩转ESP32彩色LCD屏:TFT_eSPI库实战指南 1. 硬件准备与连接 当你第一次拿到那块小巧的彩色LCD屏时,可能会被背面密密麻麻的引脚吓到。别担心,大多数常见的1.8寸到3.5寸LCD屏(如ILI9341、ST7789驱动)其实只需要连接…...
Rust的#[repr(transparent)]:单字段包装器的ABI保证
Rust的#[repr(transparent)]:单字段包装器的ABI保证 在系统级编程中,类型安全与内存布局的精确控制至关重要。Rust通过#[repr(transparent)]属性提供了一种独特的能力,允许开发者创建单字段包装器类型,同时保证其ABI(…...
C++逆向解析通达信shm.tnf文件:从模糊格式到精准读取股票数据的实战
1. 初识通达信shm.tnf文件 第一次接触通达信的shm.tnf文件是在开发一个股票数据分析工具的时候。当时我需要获取沪市所有股票的代码和名称信息,但发现通达信并没有提供官方的文件格式说明。这个文件就像是一个黑盒子,里面装满了股票数据,却没…...
CentOS 7 等保测评踩坑记:手把手教你用脚本升级OpenSSH到9.6p1(附完整回滚方案)
CentOS 7 等保合规实战:OpenSSH 9.6p1 升级全流程与风险控制手册 当企业服务器面临等保测评时,OpenSSH 版本漏洞往往是高频整改项。去年某金融客户就因 SSH 弱版本导致测评扣分,最终通过系统化升级方案在复测中获得满分。本文将分享从沙箱测试…...
