关于JS中this对象指向问题总结

news2024/10/3 4:36:39

一、前言

关于JS中this对象指向问题,相信做过项目的小伙伴多多少少都会遇到过,明明感觉代码写的没问题,可是运行的时候,就会报错,比如报错 xxx is not a function。
我最近也遇到了,百度学习了不少前辈对于this对象指向问题的解析,于是总结了这篇文章。

二、多种情况下使用this,指向有所不同

先简略概括下,this在英文中的意思是,“这,这个”的意思,在编程中我们通常把this成为当前对象。在这篇文章中,我们从始至终都要记得一句话:this永远指向,调用它的对象,默认指向window/全局对象。

如果有多层嵌套对象调用的话,this指向最后一次调用这个方法的对象。

1)全局作用域中的this

在全局作用域中,this指向 window/全局对象

 console.log(this) // window对象
 console.log(this === window) //true

在这里插入图片描述

2)函数调用中的this

当一个函数不是一个对象的属性时,直接作为函数来调用;
函数不是箭头函数时,this指向window/全局对象;
函数是箭头函数时,绑定的是父作用域的this指向。

function func(){
  console.log(this) ; //this指向window对象
}
func();

注意定时器内部的this永远指向window,比如setTimeout,setInterval

setTimeout(function(){
   console.log(this); //window对象
 },1000)

3)对象中的this

如果一个函数作为一个对象的方法来调用时,this 指向这个对象;(比如vue中的methods对象里面定义的函数方法)
箭头函数除外,因为它会捕获其所在上下文的this,所以可能会指向window/全局对象。

 let obj = {
      func: function () {
        console.log(this)
      }
    }
 obj.func()

在这段代码中,我们看到了 obj.func(),this 处在 func 函数的内部,那到底是谁调用的 func() 哪?显而易见是 obj,因为 this 永远指向,调用它的对象,所以最后的打印结果应该是 obj。

4)使用 new 实例化对象,构造函数中的this

构造函数中的this指向实例出来的对象。

function Person () {
  console.log(this)
  this.name = '铁锤妹妹'
}
var obj = new Person() // 得到一个实例化对象,继承了Person函数的属性
console.log(obj)

打印结果:就是Person

在这里插入图片描述

5)apply 、call 和 bind 调用中的this

apply和call 改变的是函数运行时的this指向,bind返回一个this绑定了传入对象的新函数。
这个函数的this指向使用new时会被改变。

注意:箭头函数中的this不能通过apply 、call 和 bind 改变,因为箭头函数中的this指向在定义时已经确认了,之后不会被改变

const obj = { name: '铁锤妹妹', age: 18 }
function Person () {
  console.log(this.name)
}
Person.apply(obj) //铁锤妹妹
Person.call(obj) //铁锤妹妹
Person.bind(obj)() //铁锤妹妹

6)事件中的this

在事件处理函数中,this指向触发事件的目标对象<div></div>

document.querySelector('div').onclick = function () {
  console.log(this) //<div></div>
}

总结:

1. 全局作用域中的this指向window
2. 普通函数this指向window,箭头函数指向它的上下文this
3. 对象中方法的this指向该方法所属的对象
4. 构造函数中的this指向实例出来的对象
5. 事件当中的this指向当前绑定的元素
6. 定时器中的this指向window
7. apply 、call 和 bind 调用中的this指向它想要指向的this

三、改变this指向的方法

  • 使用 ES6 的箭头函数
  • 在函数内部使用 _this = this
  • new 实例化一个对象
  • 使用 apply、call、bind

1)箭头函数不绑定this,会捕获其所在上下文的this,作为自己的this

  • 这句话需要注意的是,箭头函数的外层如果有普通函数,那么箭头函数的this就是这个外层普通函数的this(它会继承自己作用域上一层的this);箭头函数的外层如果没有普通函数,那么箭头函数的this就是window/全局对象。
  • 箭头函数中的this指向在定义时已经确认了,之后不会被改变

下面这个例子是箭头函数外层有普通函数。

 var name = 'windowsName'
 var a = {
      name: '铁锤妹妹',
      func1: function () {
        console.log(this.name)
      },
      func2: function () {
        setTimeout(() => {
          this.func1()
        }, 100)
      }
    }
    a.func2()  //铁锤妹妹

如果不使用箭头函数,运行会报错,原因是使用普通函数时,调用的setTimeout的对象是Window,而Window中没有定义func1函数。

 var name = 'windowsName'
 var a = {
      name: '铁锤妹妹',
      func1: function () {
        console.log(this.name)
      },
      func2: function () {
        setTimeout(function () {
          this.func1()
        }, 100)
      }
    }
    a.func2()

报错信息
在这里插入图片描述

2)如果不想使用箭头函数,也可以在函数内部使用var _this = this

 var name = 'windowsName'
 var a = {
      name: '铁锤妹妹',
      func1: function () {
        console.log(this.name)
      },
      func2: function () {
        var _this = this
        setTimeout(function () {
          _this.func1()
        }, 100)
      }
    }
    a.func2()  //铁锤妹妹

在 func2 中,首先设置 var _this = this,这里的this是调用 func2 的对象a,因为a是最后一次调用这个方法的对象;
为了防止在 func2 中的 setTimeout 被 window 调用而导致 setTimeout 中的 this 指向变为 window对象。我们将this(指向变量a)赋值给一个变量 _this,这样在 func2 中我们使用 _this 就是指向对象 a 了。

3)如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象

new操作符的执行过程:

  1. 首先创建了一个空的对象(创建一个新的存储空间)
  2. 设置原型,将对象的原型设置为函数的 prototype 对象
  3. 让函数的this指向这个新空对象,执行构造函数的代码(为这个新对象添加属性和方法)
  4. 返回新对象(所以构造函数不需要return)

因此,使用new 实例化对象,构造函数中的this指向实例化对象。

注意:上面说的箭头函数除外,因为箭头函数是匿名函数,不能作为构造函数,所以不能使用new命令,否则抛出错误。

//箭头函数使用new实例化报错代码
let func1 = () => {}
let func2 = new func1()
console.log(func2) // func1 is not a constructor

4)使用apply、call和bind指定 调用函数 的this指向

    var year = 2023
    function getDate (month, day) {
      return this.year + '-' + month + '-' + day
    }

    let obj = { year: 1998 }
    getDate.call(null, 3, 8) // 2023-3-8
    getDate.call(obj, 3, 8) // 1998-3-8
    getDate.apply(obj, [6, 8]) // 1998-6-8
    getDate.bind(obj)(3, 8) // 1998-3-8

使用 apply() 方法

apply接受两个参数,第一个是this的指向,第二个是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中);使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。

  • 语法规则 :
    函数名称.apply(obj,[arg1,arg2…,argN])
  • 参数说明:
    obj :this要指向的对象
    [arg1,arg2…argN] : 参数列表,要求格式为数组

使用 call() 方法

call 方法的第一个参数也是this的指向,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,表示指向window。和apply一样,call也只是临时改变一次this指向,并立即执行。

  • 语法规则 :
    函数名称.call(obj,arg1,arg2…argN)
  • 参数说明:
    obj :this要指向的对象
    arg1,arg2…argN : 参数列表,参数与参数之间使用一个逗号隔开

注意:call和apply的作用一致,区别仅仅在函数实参参数传递的方式上;

使用 bind() 方法

bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数,调用新函数的时候才会执行目标函数

三、关于setTimeout() 定时器的“this”指向问题

由setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的 this 关键字会指向 window (或全局) 对象,这和所期望的this的值是不一样的。

var name = 'windowsName'
    var a = {
      name: '铁锤妹妹',
      func1: function () {
        setTimeout(function () {
          console.log(this.name, 'name')
        }, 100)
      }
    }
    a.func1()
    //因为setTimeout,所以this指向window
    //打印结果:windowsName

四、总结本文箭头函数需要注意的地方

1. 函数是箭头函数的话,指向它的上下文对象的yhis
2. 箭头函数中的this不能通过apply 、call 和 bind 改变,因为箭头函数中的this指向在定义时已经确认了,之后不会被改变
3. 箭头函数使用new实例化对象时会报错

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

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

相关文章

Codeforces Round 703 (Div. 2)(A~D)

A. Shifting Stacks给出一个数组&#xff0c;每次可以将一个位置-1&#xff0c;右侧相邻位置1&#xff0c;判断是否可以经过若干次操作后使得数列严格递增。思路&#xff1a;对于每个位置&#xff0c;前缀和必须都大于该位置应该有的最少数字&#xff0c;即第一个位置最少是0&a…

Vue 2 组件发布到 npm

本教程使用官网教程中指示的 Rollup 作为打包工具&#xff0c;并尽量遵循官网教程的指引进行实践&#xff1b;组件项目的初始化创建方式亦是使用官网提倡的 Vue CLI 工具简便生成。另外组件打包发布到 npm 还可以使用 webpack 作为打包工具&#xff0c;但不在本文讨论范围。 前…

YOLOv5s网络模型讲解(一看就会)

文章目录前言1、YOLOv5s-6.0组成2、YOLOv5s网络介绍2.1、参数解析2.2、YOLOv5s.yaml2.3、YOLOv5s网络结构图3、附件3.1、yolov5s.yaml 解析表3.2、 yolov5l.yaml 解析表总结前言 最近在重构YOLOv5代码&#xff0c;本章主要介绍YOLOv5s的网络结构 1、YOLOv5s-6.0组成 我们熟知YO…

openFoam中cellZone的使用及编程

简介 通常在流体计算中需要对某个特定区域进行处理&#xff08;比如添加源项,可参考这篇文章OpenFOAM编程&#xff1a;VOF法与多孔介质模型相结合)&#xff0c;这是就需要用到cellZone. 通常有两种产生cellZone的方式&#xff1a; &#xff08;1&#xff09;从其他划分网格的…

一文带你看透通用文字识别 OCR

什么是 OCR&#xff1f; OCR技术指的是 Optical Character Recognition 或光学文字识别技术&#xff0c;即从图像中识别文字&#xff0c;并将其转换为电子文本或机器可读格式。它可以被广泛应用于图像处理&#xff0c;文字处理&#xff0c;自然语言处理&#xff0c;计算机视觉…

Kubernetes学习(五)持久化存储

Volume 卷 容器中的文件在磁盘上是临时存放的&#xff0c;这给容器中运行的特殊应用带来了一些问题。首先&#xff0c;当容器崩溃时&#xff0c;kubectl将重新启动容器&#xff0c;容器中的文件将会丢失--应为容器会以干净的状态重建。其次&#xff0c;当在一个Pod中运行多个容…

【算法题目】【Python】彻底刷遍DFS/BFS的算法题目

文章目录参考资料树的前序、中序、后序遍历树的层次遍历回溯与剪枝组合组合总和 III电话号码的字母组合组合总和组合总和 II参考资料 参考这里面的一些讲解&#xff1a; https://github.com/youngyangyang04/leetcode-master。 树的前序、中序、后序遍历 看完 树的种类 之后…

网络 | UDP与TCP协议讲解 | TCP可靠性是怎样实现的?

文章目录前置知识查看网络状态的工具查看进程idUDP协议协议格式UDP只有接收缓冲区基于UDP的应用层协议TCP协议流的理解协议格式确认应答机制缓冲区序号的作用流量控制超时重传机制6位标志位紧急数据的处理三次握手listen的第二个参数全连接和半连接队列都维护了什么信息&#x…

史上最全若依管理系统修改页面标题和logo

整理若依框架去除 若依标题、logo及其他内容。一&#xff1a;网页上的logo进入ruoyi-ui --> public --> favicon.ico&#xff0c;把这个图片换成你自己的logo二&#xff1a;页面中的logo进入ruoyi-ui --> src --> assets --> logo --> logo.png&#xff0c;把…

Git版本控制工具(详解)

Git版本控制工具 Git常见命令速查表 集中式版本控制 cvs和svn都是属于集中式版本控制系统 他们的主要特点是单一的集中管理服务器 保存所有文件的修订版本协同开发人员通过客户端连接到这台服务器 取出最新的文件或者提交更新 优点每个人都可以在一定程度上看到项目中的其他…

动态规划——子序列、编辑距离、回文子串

目录 子序列问题 300.最长递增子序列 674.最长连续递增序列 718.最长重复子数组 1143.最长公共子序列 1035.不相交的线 53.最大子序和 编辑距离 392.判断子序列 115.不同的子序列 583.两个字符串的删除操作 72.编辑距离 回文子串 647.回文子串 516.最长回文子序列…

使用sapply函数改写for循环并绘制迟滞温度与污染物效应图

For循环应该是我们在R语言使用得最普遍的循环了&#xff0c;优势就是简单、易上手&#xff0c;缺点就是慢&#xff0c;特别对于跑数据量比较大的数据。Apply家族函数使用C来编写&#xff0c;运行得非常快&#xff0c;非常适合代替for循环。今天介绍一下sapply函数改写for循环并…

abp.net 5.0 部署IIS10

今天遇到了abp.net 5.0部署iis10被卡住的问题&#xff0c;网上找了一些资料&#xff0c;都不是我要的&#xff0c;最后我总结一下我用的是 5.0的版本&#xff0c;所以我需要给服务器安装 iis5.0的相关运行环境 1&#xff1a;https://dotnet.microsoft.com/zh-cn/download/dotne…

html--学习

javascrapt交互&#xff0c;网页控制JavaScript&#xff1a;改变 HTML 图像本例会动态地改变 HTML <image> 的来源&#xff08;src&#xff09;&#xff1a;点亮灯泡<script>function changeImage() {elementdocument.getElementById(myimage) #内存变量&#xff0…

Linux---基本指令

专栏&#xff1a;Linux 个人主页&#xff1a;HaiFan. 基本指令ls 指令pwd命令cd 指令touch指令mkdir指令&#xff08;重要&#xff09;rmdir指令 && rm 指令&#xff08;重要&#xff09;man指令&#xff08;重要&#xff09;cp指令&#xff08;重要&#xff09;mv指令…

win10 C++调用conda的python

普通 比如说是conda的DL环境&#xff0c;路径是D:\Miniconda3\envs\DL VC目录->包含目录里加入D:\Miniconda3\envs\DL\include VC目录->库目录里加入D:\Miniconda3\envs\DL\libs 链接器->输入->附加依赖项里加入D:\Miniconda3\envs\DL\libs\python37.lib 在l…

“ 寻友之旅 “ 的三种解决办法

题目来源于&#xff1a;稀土掘金 " 寻友之旅 " 的三种解决办法&#xff01; 本文将分别讲解如何使用BFS、双向BFS以及 Dijsktra堆优化的方法来解决此题~ 一起来看看吧&#xff01; 附Java题解代码&#xff01; 文章目录" 寻友之旅 " 的三种解决办法&#…

如何将两个或多个PDF文件合并成一个?这3个方法可以看看

在工作中&#xff0c;有时候我们需要把两个或多个PDF文件合并成一个&#xff0c;这样一来&#xff0c;可以方便阅读、修改&#xff0c;还能快速打印文件。 下面分享3个工具&#xff0c;看看如何将两个或多个PDF文件合并成一个文件。 方法一&#xff1a;使用美图工具 如果PDF文…

【Spring AOP】如何统一“拦截器校验、数据格式返回、异常返回”处理?

目录 一、Spring 拦截器 1.1、背景 1.2、实现步骤 1.3、拦截原理 二、 统一url前缀路径 2.1、方法一&#xff1a;在系统的配置文件中设置 2.2、方法二&#xff1a;在 application.properies 中配置 三、统一异常处理 四、统一返回数据返回格式处理 4.1、背景 4.2、…

PTA:L1-025 正整数A+B、L1-026 I Love GPLT、L1-027 出租(C++)

目录 L1-025 正整数AB 问题描述&#xff1a; 实现代码&#xff1a; L1-026 I Love GPLT 问题描述&#xff1a; 实现代码&#xff1a; L1-027 出租 问题描述&#xff1a; 实现代码&#xff1a; 原理思路&#xff1a; 出租那道题有点意思哈 L1-025 正整数AB 问题描述…