一文把 JavaScript 中的 this 聊得明明白白

news2024/11/24 10:15:03

文章目录

  • 1.this 是什么?
  • 2.this的指向
    • 2.1 全局上下文的 this 指向
    • 2.2 函数(普通函数)上下文中的 this 指向
    • 2.3 事件处理程序中的 this 指向
    • 2.4 以对象的方式调用时 this 的指向
    • 2.5 构造函数中的 this 指向
    • 2.6 在 `类`上下文中 this 的指向。
    • 2.7 `派生类`中的 this 指向
    • 2.8 原型链 中的 this 指向
    • 2.9 箭头函数的 this 指向
      • 2.9.1定义在全局 作用域中
      • 2.9.2定义在对象中
      • 2.9.3定义在普通函数中
  • 3. 修改 this 的指向
    • call、apply 和 bind 的区别?
  • 总结


在这里插入图片描述

1.this 是什么?

this 是一个代表指向性的关键词,他所指向的是当前整个代码执行上下文中它所属的对象

this会拥有不同的值,具体取决于它所使用的位置:

  • 在方法中,this 指的是所有者对象。
  • 单独的情况下,this 指的是全局对象。
  • 在函数中,this 指的是全局对象。
  • 在函数中,严格模式下,this 是 undefined
  • 在事件中,this 指的是接收事件的元素

相比于其他语言,在 JavaScript 中 函数的 this 关键字的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。

在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES6 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。


2.this的指向

当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。

2.1 全局上下文的 this 指向

全局上下文环境中(在任何函数体外部),直接单独执行,不论开不开启严格模式this的指向都是 全局 window 对象。

<script>
   "use strict";   //开启严格模式
    console.log(this);    //window 对象
</script>

2.2 函数(普通函数)上下文中的 this 指向

在函数内部,this的值取决于函数被调用的方式。

如下的代码在未开启严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向也是全局对象 window

<script>
    function fn() {
        console.log(this);   //全局window 对象
    }
    fn()
</script>

即使函数经过 深层次嵌套,this 依然指向 全局 window 对象

<script>
    function fn() {
        console.log(this);   //全局window 对象
        function user() {
            console.log(this);  //全局window 对象
            function type() {
                console.log(this);   //全局window 对象
            }
            type()
        }
        user()
    }
    fn()
</script>

然而,在开启严格模式下,如果进入执行环境时没有设置 this 的值,并且 JavaScript 严格模式下不允许默认绑定。因此在函数中使用时,this 是未定义的,指向 undefined

<script>
    "use strict";   //开启严格模式
    function fn() {
        console.log(this);   //  undefined
    }
    fn()
</script>

2.3 事件处理程序中的 this 指向

在 HTML 事件处理程序中,this 指向的是接收此事件的 HTML Dom 元素

<button onclick="fn(this)">
    按钮点击
</button>

<script>
    function fn(val) {
        console.log(val);     // <button οnclick="fn(this)">按钮点击</button>
    }
</script>

在这里插入图片描述


2.4 以对象的方式调用时 this 的指向

如下案例中,person 属于 fullName 函数的拥有者,所以当函数作为对象里的方法被调用时,this 指向为调用该函数的对象

let person = {
  firstName: "Bill",
  lastName : "Gates",
  id     : 678,
  fullName : function() {
      console.log(this);
      return this
  }
};
console.log(person.fullName());

在这里插入图片描述
调用返回值是 person 整个对象。


2.5 构造函数中的 this 指向

在这里插入图片描述

当一个函数用作构造函数时(使用new关键字),它的 this 被绑定到正在构造的新对象

function Person(name) {
    this.name = name;
    console.log(this);	// {name:"实例参数"}
}
new Person("实例参数")

在这里插入图片描述
注意

虽然构造函数返回的默认值是 this 所指的那个对象,但它仍可以手动返回其它的对象(如果返回值不是一个对象,则返回 this 对象)。

如例:

function Person(name) {
    this.name = name;
    return { a: 666 }
}
let type = new Person("实例参数")
console.log(type.a);  //666

如上例,在调用构造函数的过程中,手动的设置了返回对象,与this绑定的默认对象被丢弃了。(这基本上使得语句“this.name = name;” 成了“僵尸”代码,但实际上这并不是真正的“僵尸”,这条语句实际还是执行了,但是对于外部没有任何影响,因此完全可以忽略它)。


2.6 在 上下文中 this 的指向。

this 在 类 中的表现与在函数中类似,因为类本质上也是函数(构造函数),但也有一些区别和注意事项。

在类的构造函数中,this 是一个常规对象。类中所有非静态的方法都会被添加到 this 的原型中
在这里插入图片描述

class Example {
    constructor() {
        const proto = Object.getPrototypeOf(this);
        console.log(proto);
        console.log(Object.getOwnPropertyNames(proto));  // ['constructor', 'first', 'second']
    }
    first() { }
    second() { }
    static third() { }
}
new Example()

在这里插入图片描述
注明

静态方法不是 this 的属性,它们只是类自身的属性。


2.7 派生类中的 this 指向

不像基类的构造函数,派生类的构造函数没有初始的 this 绑定。在构造函数中调用 super() 会生成一个 this 绑定。

在这里插入图片描述
派生类不能在调用 super() 之前返回,除非其构造函数返回的是一个对象,或者根本没有构造函数。

class Base { }   //Base 为基类:
class Bad extends Base {
    constructor() { 
        console.log(this);    //此处输出会报错
        super()
        console.log(this);    // 此处输出不会报错
    }
}
new Bad(); 

在这里插入图片描述


2.8 原型链 中的 this 指向

对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法就在这个对象上一样。

let a = {
    f: function () {
        console.log(this);   //{a: 1, b: 4}
        return this.a + this.b;
    }
};
let body = Object.create(a);
body.a = 1;
body.b = 4;
console.log(body.f()); // 5

概述
上例中,对象 body 没有属于它自己的 f 属性,它的 f 属性继承自它的原型。虽然最终是在 a 中找到 f 属性的,这并没有关系;查找过程首先从 body .f 的引用开始,所以函数中的 this 指向 body 。也就是说,因为 f 是作为 body 的方法调用的,所以它的this指向了 body。概括说就是:在原型链中,函数都会把 this 绑定到设置或获取属性的对象身上。这是 JavaScript 的原型继承中的一个有趣的特性。


2.9 箭头函数的 this 指向

与常规函数相比,箭头函数对 this 的处理也有所不同,简而言之,使用箭头函数没有对 this 的绑定。在常规函数中,关键字 this 表示调用该函数的对象,可以是窗口、文档、按钮或其他任何东西。对于箭头函数,this 关键字始终表示定义箭头函数的对象

简而言之:

  • 箭头函数的 this 指向取决于当前箭头函数声明的环境(执行上下文)。
  • 执行上下文又分为:全局执行上下文、函数级别上下文、eval 执行上下文
  • 因为箭头函数没有自己的 arguments 和 this,箭头函数需要获取函数定义时所在的 context 的 this,箭头函数定义在哪个作用域中,它的 this 就继承当前作用域 this 的指向。

2.9.1定义在全局 作用域中

<script>
    let typefn = () => { console.log(this) }    // 指向 全局window 
</script>

2.9.2定义在对象中

由于箭头函数没有自己的 this,所以 它的 this 使用的是 父级作用域的 this。

var name = "关羽";
let obj = {
    user: "马超",
    nest: {
        talk: () => {
            console.log(this.name);	// 指向了 window,所以打印 关羽
        }
    }
}
obj.nest.talk();
   //  {} 不会生成作用域,即箭头函数声明在全局作用域中,this 指向全局
	//   通过 var 声明的变量会被认作是 window 的属性 即 var name = "关羽"; window.name == "关羽"

注意
如上案例中,由于 name 是通过 var 关键字声明的变量,属于ES5 的方法,具有变量提升的特性,会默认成为 window 对象的属性 而箭头函数中的 this 又直接指向全局 window 对象,所以,就可以直接在 this 身上 拿到 name的 值,但是如果:name 是通过 ES6的 let关键字 声明的 ,由于 let 不会变量提升,不会绑定到 window 对象身上,所以 在箭头函数中,输出 this.name 会找不到键值对。

2.9.3定义在普通函数中

如上 同理 箭头函数没有自己的 this,所以 它的 this 使用的是 父级作用域的 this。

function fn(){
  let num = ()=>{
    console.log(this);	// window,箭头函数没有自己的 this,它的 this 使用 fn 的 this
  }
  num();
}
fn();

3. 修改 this 的指向

JS 提供了3个方法 可以用来重定向 this 的指向

  • call()
  • apply()
  • bind()
let obj = { a: 1, b: 2 }
function foo() {
    console.log(this);   //默认指向全局 window 对象
}
foo.call(obj); 	//{a: 1, b: 2}    //改变指向,使其foo 的this 指向 obj
foo.apply(obj);	 //{a: 1, b: 2}   //改变指向,使其foo 的this 指向 obj
foo.bind(obj)();  //{a: 1, b: 2}   //改变指向,使其foo 的this 指向 obj

当有参数传递时候:

let obj = { a: 1, b: 2 }
function foo(val, num) {
    console.log(val,num);
}
 foo.call(obj, 3, 4);
foo.apply(obj, [5, 6]);  //第二个参数是一个数组,数组里面的元素会被展开传入foo,作为foo的参数
 foo.bind(obj)(7);

call、apply 和 bind 的区别?

相同点:

都是用来改变函数中this的指向的。

不同点:
(执行方式不同)

callapply 都能立即执行函数并且改变this的指向;

bind 则是将函数返回,供后续调用。bind不改变原函数的this指向,而是创建了一个新的函数。调用方式也不同,需要使用函数名加()来调用,如:func.bind(obj)(arg1, arg2)。

(参数传递方式不同)
callapply 传递参数的方式不同,call接受一个参数列表,如:func.call(obj, arg1, arg2);apply则接受一个数组数组作为参数,如:func.apply(obj, [arg1, arg2])。

bind 参数传递 和 call 是一样的

在函数中的形参,会把相应的参数,进行展开接收。


总结

以上就是本章节,给大家带来的 JavaScript 中 this 关键字的 作用以及使用方法,this 这个知识点,还是比较重要的,本章的知识点内容,略微有点多,但是知识点,博主整理的几乎涵盖了 this 的所有使用场景。所以本章节内容还是很值得大家阅读的。


🚵‍♂️ 博主座右铭:向阳而生,我还在路上!
——————————————————————————————
🚴博主想说:将持续性为社区输出自己的资源,同时也见证自己的进步!
——————————————————————————————
🤼‍♂️ 如果都看到这了,博主希望留下你的足迹!【📂收藏!👍点赞!✍️评论!】
——————————————————————————————

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源企业资源规划ERPNext的安装

往常节假日&#xff0c;都是呆在家里看别人堵&#xff0c;这回老苏也出门凑了个热闹&#xff0c;28号早上 7 点半出的门 2 点半往回走的 一天啥也没干&#xff0c;就开了 7 个小时的车去舅舅家蹭了顿饭。还别说&#xff0c;那个田园鸡味道是真不错。 车很久没开了&#xff0c;…

(详解)vue中实现 ‘换肤 / 主题切换’ 功能的三种方式

目录 一、背景 二、实现思路 方法1&#xff1a;定义全局的CSS变量 方法2&#xff1a;切换已定义好的css文件 方法3&#xff1a;切换顶级CSS类名 (需使用css处理器,如sass、less等) 一、背景 在我们开发中我们会遇到像是需要切换程序风格、主题切换啦这种应用场景。 二、实现…

JavaScript通过函数异常处理来输入圆的半径,输出圆的面积的代码

以下为实现通过函数异常处理来输入圆的半径&#xff0c;输出圆的面积的代码和运行截图 目录 前言 一、通过函数异常处理来输入圆的半径&#xff0c;输出圆的面积 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以…

【RPA开发】pymongo 使用教程

实际开发时抓取到的诸多数据如何保存是一个关键问题&#xff0c;MongoDB 相比传统关系型数据库&#xff08;比如mysql&#xff09;来说灵活度更高&#xff0c;爬虫时字段格式及数量很可能会随着需求或实际数据的变动而改变&#xff0c;因此 MongoDB 作为数据库来说最合适不过了…

2023年华中杯选题人数公布

2023年华中杯选题人数公布 经过一晚上代码的编写&#xff0c;论文的写作&#xff0c;C题完整版论文已经发布&#xff0c; 注&#xff1a;蓝色字体为说明备注解释字体&#xff0c;不能出现在大家的论文里。黑色字体为论文部分&#xff0c;大家可以根据红色字体的注记进行摘抄。…

【STM32】知识补充 分频技术深度解析: 原理、应用与实现方法

【STM32】知识补充 分频技术深度解析: 原理、应用与实现方法 概述分频概念分频原理技术器分频器触发器分频器模数计数器分频器 分频应用微控制器时钟分频通信系统中的频率合成数字电路设计中的计时与同步 分频实现方法硬件分频器软件分频器 案例总结 概述 分频 (Frequency Div…

c#笔记-创建一个项目

创建一个项目 创建控制台程序 在你安装完成Visual Studio后打开它&#xff0c;你会的到一个启动窗口 点击创建新项目&#xff0c;选择右上角c#的没有Framework的控制台应用。 项目名称&#xff0c;位置自己随意。 目标框架选择NET7.0。 项目创建完成后应该你的界面应该类似…

nvdec与vaapi与vdpau的关系

nvdec/vaapi/vdpau的关系 NVDEC&#xff1a;nvidia video decoder。 英伟达官网中有关video codec SDK的介绍&#xff0c;其中有头文件、开发API文档等。 Nvidia Video SDK中提供了NVDEC、NVENC&#xff0c;其中NVDEC以前也叫做NVCUVID&#xff08;不知道NVDEC的开发API中很…

【9.HTML入门知识-其他知识补充】

其他知识补充 1.使用Web字体和图标1.1 web fonts兼容性写法1.2 字体图标1.2.1 字体图标的使用 2.精灵图 CSS Sprite2.1 精灵图的使用 3.置鼠标指针&#xff08;光标&#xff09;样式cusor4.居中方案4.1 使用绝对定位元素居中 1.使用Web字体和图标 1.1 web fonts兼容性写法 1.2 …

软件开发团队的护网低成本应对方案

主题&#xff1a; 1、攻击方技术手段说明&#xff0c;结合攻击队手段重点关注的防御点介绍&#xff1b; 2、防守方&#xff08;软件开发团队&#xff09;的低成本应对思路&#xff1b;系统是如何被攻破的 攻防演练&#xff08;APT&#xff09;攻击路径 未知攻&#xff0c;焉知…

IGH EtherCAT主站应用层代码开发:控制驱动电机

1、安装IGH EtherCAT主站 Ubuntu18.04环境下安装igH EtherCAT Master 2、查询从站配置信息 连接从站通过网线连接主站与从站 启动主站打开终端,输入: sudo /etc/init.d/ethercat star 显示Starting EtherCAT master 1.5.2 done则说明成功。 查询从站列表终端输入: eth…

【C++】 list-map 链表与映射表的简单使用

目录 list 链表 定义链表&#xff0c;并在首、尾添加、删除元素 迭代器遍历链表 任意位置插入或删除 获取首尾节点中元素的值 使用增强的范围for循环进行遍历链表 其他常见的函数 map 映射表 定义map 添加 使用函数插入元素 迭代器遍历map 修改 删除 使用增强的范…

PMP项目管理-[第七章]成本管理

成本管理知识体系&#xff1a; 规划成本管理&#xff1a; 估算成本&#xff1a; 制定预算&#xff1a; 控制成本&#xff1a; 7.1 挣值管理新兴实践 ps&#xff1a;了解即可&#xff0c;考试考不到 7.2 规划成本管理 定义&#xff1a;确定如何估算、预算、管理、监督和控制项目…

直接插入排序

一、概念及其介绍 插入排序(InsertionSort)&#xff0c;一般也被称为直接插入排序。 对于少量元素的排序&#xff0c;它是一个有效的算法。插入排序是一种最简单的排序方法&#xff0c;它的基本思想是将一个记录插入到已经排好序的有序表中&#xff0c;从而一个新的、记录数增…

你是否曾经为自己写的代码而感到懊恼?那如何才能写出高质量代码呢?

这里写目录标题 一、 前言二、高质量代码的特征三、编程实践技巧1. 遵循编码规范2. 使用有意义的变量名和函数名3. 减少代码重复4. 使用注释5. 编写单元测试6. 使用设计模式7. 使用版本控制工具8. 保持代码简洁9. 优化代码性能10. 学习和借鉴他人的代码总结 一、 前言 写出高质…

Python入门教程+项目实战-11.5节: 程序实战-选择排序算法

目录 11.5.1 排序算法简介 11.5.2 选择排序算法 11.5.3 系统学习python 11.5.1 排序算法简介 所谓排序&#xff0c;是指将数据集合中的元素按从小到大的顺序进行排列&#xff0c;或按从大到小的顺序进行排列。前者称为升序排序&#xff0c;后者称为降序排序。在数据结构与算…

ChatGPT会颠覆SEO内容创作吗

近几年 AI 的发展日新月异。除了搜索算法本身大规模应用人工智能&#xff0c;我也一直关注着 AI 用于写作的进展。 上篇关于 Google 有用内容更新的帖子还在说&#xff0c;高质量内容创作是 SEO 最难的事之一&#xff0c;对某些网站来说&#xff0c;如果能有工具帮助&#xff…

python毕业设计之django+vue.js幼儿园网站系统

开发语言&#xff1a;Python 框架&#xff1a;django Python版本&#xff1a;python3.7.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm 采用了Windows10操作系统平台&#xff0c;使用HTMLCSSJS前端模板django作为后台监控&#xff0…

大数据Doris(九):Apache Doris 简单使用

文章目录 Apache Doris 简单使用 一、用户创建 二、创建数据库 三、账户赋权 四、创建数据表 1、创建数据表 2、准备数据 3、导入数据 五、查询数据表 Apache Doris 简单使用 下面按照官网给出的示例简单操作Doris&#xff0c;首先创建用户、创建数据库、赋权、创建表…

第二弹进阶吴恩达 ChatGPT Prompt 技巧

第一弹笔记在这里&#xff1a; 总结吴恩达 ChatGPT Prompt 免费课程 今天分享第二弹&#xff0c;进阶篇。 第一点&#xff0c;任务序列化。 通常看完一篇长文&#xff0c;脑子里往往充满无数疑问。急切想知道所有答案&#xff0c;必须列一个问题清单。对话式问法&#xff0c;对…