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

文章目录

  • 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 的所有使用场景。所以本章节内容还是很值得大家阅读的。


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

84.python input输入函数知识拓展

文章目录 1. input函数知识回顾2. input常犯错误解析3. 用函数转换从终端输入的数据3.1 输入的数为整数&#xff0c;则用int转换为整数3.2 输入的数为浮点数&#xff0c;则用float转换为浮点数3.3 不考虑输入的数据类型&#xff0c;则用eval函数转换 4. 变量的多种赋值方式4.1 …

【软考中级】2022下半年软件设计师案例分析题级答案

试题一(共15分) 阅读下列说明和图&#xff0c;回答问题1至问题4&#xff0c;将解答填入答题纸的对应栏内。 【说明】 随着新能源车数量的迅猛增长&#xff0c;全国各地电动汽车配套充电桩急速增长&#xff0c;同时 也带来了充电桩计量准确性的问题。充电桩都需要配备相应的…

JAVA9新特性

JAVA9新特性 概述 ​ 经过4次推迟&#xff0c;历经曲折的Java9最终在2017年9月21日发布。因为里面加入的模块化系统&#xff0c;在最初设想的时候并没有想过那么复杂&#xff0c;花费的时间超出预估时间。距离java8大约三年时间。 ​ Java 9提供了超过150项新功能特性&#x…

三个令人惊艳超有用的 ChatGPT 项目,开源了!

自 3 月初 Open AI 开放 ChatGPT API 以来&#xff0c;GitHub 上诞生的开源项目数量之多&#xff0c;着实多得让我眼花缭乱、应接不暇。 今天&#xff0c;我将着重挑选几个对日常工作、生活、学习帮助较大的 ChatGPT 开源项目&#xff0c;跟大家分享下&#xff0c;希望对你有所…

使用eclipse创建一个图书管理系统(1)-----搭建架构

目录 思维导图&#xff1a; 图书管理系统的创建&#xff1a; 第一步&#xff1a;搭建框架-------使用者 第二步&#xff1a;搭建框架------被使用者 第三步&#xff1a;操作方法 第四步&#xff1a;main函数 思维导图&#xff1a; 前言&#xff1a; 昨天学了一下使用Java…

games103——作业2

实验二主要使用隐式积分法以及PBD法完成布料仿真 完整项目已上传至github。 文章目录 基于物理的方法弹簧系统单个弹簧多个弹簧弹簧网络结构化弹簧网络(Structured Spring Networks)非结构化弹簧网络(Unstructured Spring Networks)三角网格表示 代码 求解质量弹簧系统的显示积…

(别再手动点APP了)UiAutomator2自动化测试框架带你玩转APP操作

目录 前言 一、uiautomator/uiautomator2的前生今世 1.官方文档介绍 2.梳理一下脉络 3.三款框架对比 二、uiautomator2简介 1.项目组成 2.工作原理 三、环境搭建 1.安装uiautomator2 2.初始化设备 3.init时都干了啥&#xff1f; 四、基础操作 1.连接设备 2.命令…

Python——函数

概念 函数是一段具有特定功能&#xff0c;可重复使用的代码&#xff0c;python提供了很多内置函数&#xff0c;如&#xff1a;print()&#xff0c;input()&#xff0c;len()函数等&#xff0c;以及标准库函数&#xff0c;math库中的sqrt()函数等&#xff0c;除此之外用户还可以…

Hive3面试基础

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、基本知识Hive31.表的类型和表的存储格式a)b)c)创建表i&#xff09;ii&#xff09; 2.表 二、使用步骤1.引入库2.读入数据 总结 前言 面试准备之Hive 回顾…

设计模式之适配器模式

目录 1、什么是适配器模式 2、为什么用适配器模式 3、适配器模式的结构 4、类适配器模式代码实现 4.1 思想 4.2 代码实现 4.3 问题分析 5、对象适配器模式代码实现 5.1 思想 5.2 代码实现 6、适配器模式应用场景 1、什么是适配器模式 适配器模式&#xff08;Adapter…

19. Unity - 2D游戏开发小记02 --- 伪透视图、2D物体碰撞、瓦片地图碰撞、素材缩放平铺

1. 伪视图 在2D游戏开发当中,当角色移动时,会发生物体与物体之间的前后遮挡。2D视图中的前后关系是由 Y 轴决定,y 值越小物体越靠前。unity的渲染应开启根据 y 值的大小进行渲染才能保证正确的遮挡效果,在菜单栏Editor–>project setting --> Graphic中按照下图方式…

C++三大特性—继承“复杂的菱形继承及菱形虚拟继承”

C的一个大坑&#xff1a;菱形继承 希望这篇文章能让你理解什么是菱形继承&#xff0c;以及菱形继承的注意事项 单继承与多继承 单继承&#xff1a;一个子类只有一个直接父类时称这个继承关系为单继承 多继承&#xff1a;一个子类有两个或以上直接父类时称这个继承关系为多继承…

凌恩生物美文分享|HGTree v2.0:水平基因转移数据库

水平基因转移(HGT)是指遗传物在物种间从一个相邻生物体到另一个生物体横向传递&#xff0c;是原核生物遗传变异的重要过程。HGT是负责塑造原核生物基因组和在自然选择中生存的驱动力之一&#xff0c;对原核生物的进化有很大贡献&#xff0c;但它会使物种进化史复杂化&#xff0…

【Linux】进程信号保存

前言 上篇博客我们了解了进程信号的概念和信号如何产生。 本篇我们将学习进程信号如何保存。 文章目录 前言一. 阻塞信号二. 递达动作三. 信号集四. 信号集操作函数结束语 一. 阻塞信号 首先我们需要一些预备知识 实际执行信号的处理动作称为信号递达&#xff08;Delivery&am…

数字图像处理-绪论

数字图像处理-绪论 文章目录 前言一、闲谈二、什么是数字图像处理&#xff1f;2.1. 什么是数字图像&#xff1f;2.1.1. 可见光图像2.1.2. 不可见光图像 2.2. 什么是数字图像处理&#xff1f; 三、数字图像处理的前世今生3.1. 数字图像处理的前世3.2. 数字图像处理的今生 四、数…

【嵌入式系统】课程复习资料整理

【嵌入式系统】课程复习资料整理 一、绪论 1.定义 从技术的角度定义&#xff1a;以应用为中心、以计算机技术为基础、软件硬件可裁剪、对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。从系统的角度定义&#xff1a;嵌入式系统是设计完成复杂功能的硬件和软件&a…

使用crontab定时自动更新DDNS

需求说明&#xff1a; N1盒子的armbian系统配置好了 ipv6 的ddns&#xff0c;实现了域名访问本机&#xff0c;但是本地ipv6可能会发生变化&#xff0c;当发生变化后&#xff0c;需要手动执行指令&#xff0c;将新的ip与域名绑定&#xff0c;现在我们采用定时任务&#xff0c;每…

Nuvoton NK-980IOT开发板 u-boot 编译

前言 最近搭建了 Nuvoton NK-980IOT开发板 的开发编译环境&#xff0c;记录一下 u-boot 的 编译流程 Nuvoton NK-980IOT开发板 资源还是比较的丰富的&#xff0c;可以用于 嵌入式Linux 或者 RT-Thread 的学习开发 开发板上电比较的容易&#xff0c;两根 USB 线即可&#xff0…

计网笔记 01 概述 计算机网络体系结构、参考模型

文章目录 前言1、计网概述1.1 概念、组成、功能、分类1.1.1 概念1.1.2 计网组成1.1.2 计网分类 1.2 标准化工作及相关组织1.2.1 标准的分类 1.3 性能指标★★★1.3.1 速率相关性能指标1.3.2 时延相关指标 2、体系结构&参考模型★★★★★&#xff08;对应王道视频7-10p 相当…

Android Jetpack:利用Palette进行图片取色

与产品MM那些事 新来一个产品MM&#xff0c;因为比较平&#xff0c;我们就叫她A妹吧。A妹来第一天就指出&#xff1a;页面顶部的Banner广告位的背景是白色的&#xff0c;太单调啦&#xff0c;人家不喜欢啦&#xff0c;需要根据广告图片的内容自动切换背景颜色&#xff0c;颜色…