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

这篇具有很好参考价值的文章主要介绍了一文把 JavaScript 中的 this 聊得明明白白。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

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>

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


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());

一文把 JavaScript 中的 this 聊得明明白白
调用返回值是 person 整个对象。


2.5 构造函数中的 this 指向

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

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

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

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

虽然构造函数返回的默认值是 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 的原型中
一文把 JavaScript 中的 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()

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

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


2.7 派生类中的 this 指向

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

一文把 JavaScript 中的 this 聊得明明白白
派生类不能在调用 super() 之前返回,除非其构造函数返回的是一个对象,或者根本没有构造函数。

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

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


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


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

到了这里,关于一文把 JavaScript 中的 this 聊得明明白白的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Vue-router 3.x 参数传递看完让你明明白白!

    Vue- routrer,页面路由跳转时,携带参数传递 并拿取传递过来的参数,本文将详细讲解 Vue router传递参数的多种方式; 如果你还不了解 Vuerouter 基本使用及配置请通过下面传送门,了解vue router 路由的基本配置及使用 传送门去了解 Vue-router params参数传递也有两种方式:第一种就

    2024年02月06日
    浏览(39)
  • 吊打面试官系列之:什么是 认证、鉴权、授权、权限控制,这一篇必须安排的明明白白。

    小屌丝 :鱼哥,啥是认证,啥是鉴权? 小鱼 :嗯?? 做了这么多年码农,这个还不知道呢? 小屌丝 :唉~~ 没整明白啊,能不能给我讲一讲啊 小鱼 :好吧,正好今晚有时间,我就把 认证、鉴权、授权及权限控制这点事,都给你说一说。 小屌丝 :奈斯啊。 定义:

    2024年02月04日
    浏览(49)
  • Python 自动化测试框架环境怎么搭建?这篇文章给你讲的明明白白

    目录 Python 自动化测试框架环境搭建 第一步:安装 Python 第二步:安装 PyCharm 第三步:安装 Selenium WebDriver 第四步:安装浏览器驱动 第五步:创建测试用例 第六步:集成持续集成平台 总结 Python 是一种流行的编程语言,可以用于多种应用场景,包括自动化测试。本文将介绍如

    2023年04月12日
    浏览(46)
  • node使用node-xlsx实现excel的下载与导入,保证你看的明明白白

    需求简介 node-xlsx 的简单介绍 node-xlsx 构建 xlsx 文件[将数据转化为excel] 需要注意的2点 如何设置列宽呢? 03png 实现导出下载功能-node后端代码 实现导出下载功能-前端代码 关于axios.get() 置请求头responseType:\\\'blob\\\'不生效 mockjs会导致文件下载失败及原因 验证 mockjs 会导致下载失败

    2023年04月10日
    浏览(36)
  • 接口自动化测试要做什么?8个步骤讲的明明白白(小白也能看懂系列)

    先了解下接口测试流程: 1、需求分析 2、Api文档分析与评审 3、测试计划编写 4、用例设计与评审 5、环境搭建(工具) 6、执行用例 7、缺陷管理 8、测试报告 那\\\"接口自动化测试\\\"怎么弄?只需要在上篇文章的基础上再梳理下就可以。为更好理解\\\"接口自动化\\\"具体操作流程,最

    2024年03月09日
    浏览(53)
  • 【接口自动化测试】月薪12k必会技术,从0到1学习接口自动化测试,6个操作安排的明明白白

    导读:在所有的开发测试中,接口测试是必不可少的一项。有效且覆盖完整的接口测试,不仅能保障新功能的开发质量,还能让开发在修改功能逻辑的时候有回归的能力,同时也是能优雅地进行重构的前提。编写接口测试要遵守哪些原则?测试代码的结构应该是什么样的?接

    2024年02月01日
    浏览(55)
  • javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域。并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码,都存放在Window函数内(这是个假设),也就是说javascript中只有函数作用域(前面假设做前提下)

    2024年02月06日
    浏览(34)
  • 【JS】JavaScript中的this关键字

    目录 this是什么? this的指向 ①全局环境 ②构造函数 ③对象的方法 this的四类调用方式 ①作为对象方法调用 ②纯粹的函数调用 ③作为构造函数调用 ④使用apply、call、bind调用 举例说明 JavaScript  this  指的是它所属的对象。 它拥有不同的值,具体取决于它的使用位置:

    2024年02月14日
    浏览(46)
  • this 之谜揭底:从浅入深理解 JavaScript 中的 this 关键字(二)

    系列首发于公众号『前端进阶圈』 ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。 调用位置 在理解 this 的绑定过程之前,首先要理解 调用位置 : 调用位置就是函数在代码中被调用的位置(而不是声明的位置) 。 通常来说,寻找调用位置就是寻找

    2024年02月08日
    浏览(48)
  • 一文吃透JavaScript中的DOM知识及用法

       DOM : D ocument O bject M odel(文档对象模型),定义了用户操作文档对象的接口,可以说DOM是自HTML将网上相关文档连接起来后最伟大的创新。它使得用户对HTML有了空前的访问能力,并使开发者将HTML作为XML文档来处理。 本文知识导图如下:    DOM是网页的核心结构,无论是

    2023年04月08日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包