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

news/2024/11/22 6:55:25/

文章目录

  • 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.ppmy.cn/news/75984.html

相关文章

Flink第五章:处理函数

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作. Flink第三章:基本操作(二) Flink第四章:水位线和窗口 Flink第五章:处理函数 文章目录 系列文章目录前言一、基本处理函数(ProcessFunction)二、按键分区处理函数&#xff08;KeyedProcessFunction&#xff09;1.处理…

一、尚医通登录需求

文章目录 一、登录需求1、登录效果2、登录需求 二、登录1&#xff0c;搭建service-user模块1.1 搭建service-user模块1.2 修改配置1.3 启动类1.4 配置网关 2、添加用户基础类2.1 添加model2.2 添加Mapper2.3 添加service接口及实现类2.4 添加controller 3、登录api接口3.1 添加…

linux系统升级/更新OpenSSL版本操作流程记录

问题描述&#xff1a;有时 OpenSSL 版本过老升级&#xff0c;或者需要更新 OpenSSL 版本 1. 登录 linux 系统后输入 openssl version 查看现在使用的版本 我的输入后版本信息为&#xff1a;OpenSSL 1.1.1g FIPS 21 Apr 2020 &#xff0c;可以看到是一年前更新版本&#xff0c;…

深入浅出 SQL Server CDC 数据同步

简介 SQL Server 是一款老牌关系型数据库,自 1988 年由 Microsoft、Sybase 和 Ashton-Tate 三家公司共同推出&#xff0c;不断迭代更新至今&#xff0c;拥有相当广泛的用户群体。 如今&#xff0c;我们提到 SQL Server 通常指 Microsoft SQL Server 2000 之后的版本。 SQL S…

网络安全里主要的岗位有哪些?小白如何快速入门学习黑客?

入门Web安全、安卓安全、二进制安全、工控安全还是智能硬件安全等等&#xff0c;每个不同的领域要掌握的技能也不同。 当然入门Web安全相对难度较低&#xff0c;也是很多人的首选。主要还是看自己的兴趣方向吧。 本文就以下几个问题来说明网络安全大致学习过程&#x1f447; 网…

什么是jquery jq的基本使用

JQuery的概述 jQuery是一个快速的&#xff0c;简洁的javaScript库&#xff0c;使用户能更方便地处理HTML documents、events、实现动画效果&#xff0c;并且方便地为网站提供AJAX交互。 jQuery能够使用户的html页保持代码和html内容分离&#xff0c;也就是说&#xff0c…

Java高并发核心编程—CAS与JUC原子类

注&#xff1a;本笔记是阅读《Java高并发核心编程卷2》整理的笔记&#xff01; CAS原理 JUC原子类一Atomic 基本原子类 数组原子类 引用原子类 字段更新原子类 AtomicInteger 线程安全原理 引用类型原子类 属性更新原子类 ABA问题 提升高并发场景下CAS提作的性能 以空间换时间:…

【饿了么UI】elementUI密码框图标实现睁眼和闭眼效果(阿里巴巴iconfront图标库vue项目本地引用)

elementUI中输入框的密码框属性&#xff0c; 默认是一个始终睁眼的图标&#xff0c;测试今天提bug要有闭眼效果&#xff08;无大语&#xff09;… 因为elementUI中的icon没有闭眼的&#xff0c;所以还要去iconfront下载引入 效果图&#xff1a; 点击后 一、下载图标 http…