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


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

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

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

相关文章

投递简历用什么邮箱最好用?

春招已经开始了&#xff0c;你却还没明白一个问题&#xff1a;为什么你的简历邮件&#xff0c;没有回音&#xff1f;但也许企业根本没有收到你的邮件。邮件&#xff0c;其实是求职中一门不可缺少的基础课。选什么邮箱&#xff1f;邮件如何命名&#xff1f;简历如何命名&#xf…

程序员如何写简历

【面试】程序员如何写简历 一份好的简历可以在整个申请面试以及面试过程中起到非常重要的作用。 一 简历的重要性 简历就像是我们的门面&#xff0c;是 HR 对我们求职者的第一映像&#xff0c;它在很大程度上决定了是否能够获得面试机会。 网申&#xff1a;简历必然经过 HR 的…

大厂程序员手把手教你如何写简历!!(附简历模板)

本文 https://github.com/youngyangyang04/leetcode-master 已经收录&#xff0c;里面还有leetcode刷题攻略、各个类型经典题目刷题顺序、思维导图&#xff0c;可以fork到自己仓库&#xff0c;有空看一看一定会有所收获&#xff0c;如果对你有帮助也给一个star支持一下吧&#…

程序员如何写一份更好的简历

简历中的常见错误 1. 信息过多&#xff0c;缺乏重点 信息过多的常见表现是十几行的技能列表&#xff0c; 我举一个血淋淋的例子&#xff1a; 20 行的技能列表&#xff0c;这位求职者开始就把自己了解的所有工具都列出来&#xff0c;希望能够突显自己的经验和学习能力&#xf…

邮箱投递简历,如何正确书写正文和主题?

前言 又快到了一年一度的春招和毕业季。身边还有许多准备从事IT行业的朋友没有找到工作&#xff0c;基兄特此整理了一份关于自己找工作的经验&#xff08;也参考了网上的部分数据和内容&#xff09;。 本文的内容主要分为邮箱投递简历&#xff0c;如何正确书写正文和主题&#…

【干货】如何打造HR无法拒绝的简历?测试开发大牛带手把手你写简历!

通过率90%&#xff0c;优秀的软件测试简历长什么样&#xff1f; 也许口才好的人会觉得简历不重要&#xff0c;能说就行了&#xff0c;那是因为你没有体会过石沉大海的感觉&#xff01; 很多人觉得疑惑&#xff0c;为什么我投了那么多简历&#xff0c;都没有接到面试通知&…

chatgpt赋能python:Python获取数据:更高效的数据获取与分析

Python获取数据&#xff1a;更高效的数据获取与分析 Python是一种高级的编程语言&#xff0c;常用于数据分析、机器学习、Web开发等领域。它的简单易学、高效便捷的特点使其成为了许多数据分析工作的首选语言。本文将介绍如何使用Python获取数据并进行分析。 为什么选择Pytho…

chatgpt赋能python:Python预处理:提高数据分析效率的重要一步

Python预处理&#xff1a;提高数据分析效率的重要一步 在数据分析的过程中&#xff0c;数据预处理是不可避免的一步&#xff0c;能否有效地进行数据预处理&#xff0c;对于后续的数据分析效率以及结果的准确性至关重要。Python作为一种先进的编程语言&#xff0c;不仅在数据分…

PHP使用CURL请求微信接口返回为空

先贴请求代码 /*** 发送post请求* param string $url 需要请求的地址* return json $output 返回的json字符串*/ function postUrl($url, $data){$data json_encode($data);$headerArray array("Content-type:application/json;charsetutf-8","Accept:appli…

关于PHP调用openAI chatGPT一些问题

我是直接使用gpt生成的curl代码区调用的 1&#xff1a;windows可能出现代理访问问题&#xff0c;报443端口问题 解决办法&#xff1a;开启代理后&#xff0c;需要到设置 确认这里打开状态 在curl请求里面加上对应的代码 curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1&qu…

【包装类】

1.包装类的分类 1&#xff09;针对八种基本数据类型相应的引用类型---包装类 2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法 3&#xff09; 基本数据类型 包装类 boolean Boolean char Character byte Byte short Short int Integer long Long …

包装类

一.包装类&#xff1a; 1. 包装类(Wrapper Class): Java语言是一个面向对象的语言&#xff0c;但是Java中的基本数据类型却是不面向对象的。基本类型的数据不具备"对象"的特性&#xff08;没有成员变量和成员方法可以调用&#xff09;&#xff0c;因此&#xff0c;ja…

包装类概述

Java中有8中基本数据类型&#xff0c;分别是&#xff1a; 包装类就是这8种数据类型所对应的引用数据类型&#xff0c;分别是&#xff1a; - 可能有同学会问&#xff1a;Java为什么要给基本数据类型提供对应的引用数据呢? - 第一&#xff0c;Java是面向对象的语言&#xff0c…

uni-app使用小图标的方式

1.首先我们需要去这里 随便点入一个进入第二步骤 2.把你需要的小图标加入到购物车 3.点击购物车把小图标【添加至项目】 4.最后下载到本地【解压放到项目中去】 5.如下&#xff1a; 6.在main.js里面引入 import ./style/iconfont.css 7.最后就是在页面中直接通过样式类名使…

教你如何用PS制作iOS端APP应用图标AppIcon.appiconset

开发iOS端APP要准备18个不同尺寸的logo&#xff0c;虽然现在有网站提供在线生成APP应用图标&#xff0c;但是这样可能存在泄露高清logo甚至被抢占的风险&#xff0c;下面我将教大家如何用Photoshop制作iOS端AppIcon.appiconset。 其实很简单&#xff0c;我们只要按照Xcode提示做…

【新拟态】左上角标签样式、ICON图标样式、模仿AppStore的应用图标

效果如图↑ <!doctype html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, user-scalableno, initial-scale1.0, maximum-scale1.0, minimum-scale1.0"…

老胡的周刊(第088期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 nginx-ui[2] Nginx 后台管理 UI&#xff1a;…

【总结】1739- 使用 Pake 一键将网页打包成桌面 App

通常&#xff0c;我们说的软件指的是一个可以独立运行的程序&#xff0c;那么我们前端工程师开发的网页也属于软件吗&#xff1f;从计算机的角度来说&#xff0c;网页也是一个软件&#xff0c;但它很少让人归为软件一类。作为一个前端 web 开发者&#xff0c;我一直被桌面 App …

资讯周报@2 (2023-03-19)

title: 资讯周报-2 date: 2023-03-19 author: “KazooTTT” tags: 周报资料收集chatgpt设计figmavercel 时间范围&#xff1a;2023-03-12-2023-03-19 来源&#xff1a;github、twitter、微博、b站 一、浏览器插件&油猴脚本 沉浸式双语网页翻译扩展 #翻译 #浏览器插件 …