【面试必考点】这一次带你彻底学会this的指向问题

news2025/1/6 0:19:26

文章目录

  • 前言
  • 一、this的指向问题
    • 1.1 全局中的this
    • 1.2 普通函数中的this
    • 1.3 定时器中的this
    • 1.4 事件处理函数中的this
    • 1.5 构造函数中的this
    • 1.6 构造函数静态方法中的this
    • 1.7 箭头函数中的this
  • 二、修改函数中的this指向
    • 2.1 call
    • 2.2 apply
    • 2.3 bind
  • 三、 this指向练习
    • 3.1 某小游戏公司笔试题
    • 3.2 大厂笔试题
  • 总结


前言

相信很多朋友和我一样,总是搞不清this的指向关系,由于没有硬性要求,所以又总是不想学习,经过两次面试后,笔者发现这个考点真是面试官常考的点大家静下心来一起学习这篇this的指向吧!


文章主体内容分为两块:this的指向以及如何改变this的指向

一、this的指向问题

1.1 全局中的this

全局的this指向window

    // 1. 全局中的this
    console.log(this) // window

1.2 普通函数中的this

普通函数中的this指向调用者

	function fn() {
	      console.log(this) // window
	   }
	 fn()  // window.fn(),window调用了fn函数,所以fn中的this表示window

这里的fn(),调用者实际上是window,只不过是window.fn(),window省略了

    let obj = {
        name : '前端百草阁',
        fn: function(){
            console.log(this.name)
        }
    }
    obj.fn() // 输出结果: 前端百草阁

这里的fn调用者是obj这个对象,所以this.name等价于obj.name

1.3 定时器中的this

定时器中的this指向全局对象window

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

1.4 事件处理函数中的this

事件处理函数中的this指向事件源

   document.body.addEventListener('click', function () {
      console.log(this) // body
    }) 

当触发点击事件时,打印 body,此例中事件源为body,所以this指向事件源(body)

1.5 构造函数中的this

构造函数中的this指向实例化对象

	function Dog(age) {
	      this.age = age
	      this.say = function () {
	        console.log(this)
	        console.log(this.age)
	      }
	   }
	   
	   //利用构造函数创建实例化对象dog
	    let dog = new Dog()
	    dog.say()  // 输出结果: 
	    		   //			Dog {age: 3, say: ƒ}
	               //           3

这里的dog为实例化对象,所以构造函数中的this等价于dog这个实例化对象
有的同学可能会有点迷糊,这里大家切记this指向是在运行时确定的,而不是在定义时确定的
代码运行时实例化出了一个dog对象,这时this指向这个实例化对象,并不是说在定义构造函数时就确定this了

值得一提的是,若你在构造函数的原型对象上再添加一个方法,this依然指向实例化对象

	function Dog(age) {
	      this.age = age
	      this.say = function () {
	        console.log(this)
	        console.log(this.age)
	      }
	   }
	Dog.prototype.eat = function () {
      console.log(this)
    }
	   //利用构造函数创建实例化对象dog
	    let dog = new Dog()
	    dog.say()  // 输出结果: 
	    		   //			Dog {age: 3, say: ƒ}
	               //           3
	    dog.eat()  // 输出结果: Dog {age: 3, say: ƒ}

1.6 构造函数静态方法中的this

构造函数的静态方法中,this表示构造函数

	//	 构造函数Pig
		function Pig() {
	 
	  	 }
	    // 给构造函数,直接添加的方法,叫做静态方法
	    Pig.eat = function () {
	      console.log(this)
	    }
	    // 调用的时候,只能使用构造函数调用
	    Pig.eat() // 输出结果: ƒ Pig() { }

1.7 箭头函数中的this

箭头函数没有自己的this值,会继承外部作用域的this

  var age = 10
    let obj = {
      age: 20,
      say: () => {
        console.log(this.age) // 10
      }
 }

obj.say()

这里大家觉得this.age是10 还是 20呢?
这里的箭头函数没有this,但是它会继承外部作用域的this,这里箭头函数作用域外,就是全局作用域window了,可能会有很多人觉得为什么不是obj的局部作用域呢? 因为!!对象的大括号不能当做一个作用域
所以这里的this.age 等价于 window.age 即为 10
再来一个例子

 var age = 10
 let obj = {
      age: 20,
      eat: function () {
        let fn = () => {
          console.log(this.age) // 箭头函数中没有this,所以这里的this指向eat方法中的this,即obj
        }
        fn()
      }
   }

obj.eat()

这里箭头函数中的this指向eat方法中的this,eat中的this又指向obj对象,所以这里的输出结果为20
在这里插入图片描述

二、修改函数中的this指向

接下来介绍的这三种方法的调用者都必须是函数

2.1 call

call的语法:函数.call(新的this,3,4),其中3,4代表传递的参数,34方便理解

let obj = { age: 20 }
 
function fn(x, y) {
    console.log(this)
    console.log(x + y)
  }
fn(34) // 正常调用函数,函数中的this指向 window
fn.call(obj, 3, 4)
    // 总结:
    // 1. 函数.call() 表示调用函数,原函数fn得以调用了
    // 2. 修改了原函数中的this,改成call方法的第一个参数
    // 3. 如果原函数有形参,可以通过call方法为原函数传递实参

fn.call(obj,3,4) ,call函数一调用,就把原先fn里的this(指向window)改成了一个新的this(指向obj),3,4分别代表x,y要传递的参数
在这里插入图片描述

2.2 apply

apply的语法:函数.apply(新的this, [3, 4]),其中3,4代表传递的参数,34方便理解

let obj = { age: 20 }
 
function fn(x, y) {
    console.log(this)
    console.log(x + y)
}
fn(34)
fn.apply(obj, [3, 4])
    // 总结:
    // 1. 函数.apply() 表示调用函数,原函数fn得以调用
    // 2. 修改了原函数中的this,改成 apply 方法的第一个参数
    // 3. 如果原函数有形参,可以通过 apply 方法为原函数传递实参,但是必须使用数组格式(这也是与call方法的区别)

在这里插入图片描述

2.3 bind

bind的用法就比较不一样了,我们先看看bind函数的特点
1. 函数.bind() 表示创建了一个新的函数,并且不会调用任何函数
2. 修改了新函数中的this,改成 bind 方法的第一个参数了
3. 如果原函数有形参,可以通过 bind 方法为新函数传递实参

所以接下来直接给大家演示bind的使用

let obj = { age: 20 }
function fn(x, y) {
    console.log(this)
    console.log(x + y)
}
let a = fn.bind(obj, 3, 4)
a()

这里因为fn.bind()不会调用任何函数,所以要自己调用一遍
也可以这么写

let obj = { age: 20 }
function fn(x, y) {
    console.log(this)
    console.log(x + y)
}
fn.bind(obj, 3, 4)()

在这里插入图片描述

三、 this指向练习

3.1 某小游戏公司笔试题

请大家先想想答案是多少,再看讲解

        let obj = {
            stringName : "我是abc",
            getName(){
                return function(){
                    return this.stringName
                }
            }
        }
        console.log(obj.getName()()); // undefined

找普通函数的this指向一定要知道调用者是谁,这道笔试题很多人会被误导,以为调用者全都是obj,其实不然
我换种写法,大家就一目了然了

        let obj = {
            stringName : "我是abc",
            getName(){
                return function(){
                    console.log(obj.getName);
                    return this.stringName
                }
            }
        }
        let fn = obj.getName()
        console.log(fn()) // undefined

这两种方式是一模一样的,这样大家是不是一眼就辨别出了呢?调用对象的方法的返回值,this指向的是全局对象window! 。这是因为返回的函数是作为全局函数被调用的,而不是作为 obj的方法被调用的
接下来,我们再看看如何才能读取到obj中的srtingName呢?

        let obj = {
            stringName : "我是abc",
            getName(){
                let that = this
                return function(){
                    return that.stringName
                }
            }
        }
        console.log(obj.getName()());

这里和上面不一样的点:这里利用一个变量that,存储了getName中的this,getName的调用者又是obj,相当于他利用一个that存储了一个this并且指向obj,所以运行结果是:
在这里插入图片描述

3.2 大厂笔试题

手写实现call函数

    Function.prototype.myCall = function(context, ...args) {
        // 判断是否传入了context,如果没有则默认为全局对象
        context = context || window;

        // 将当前函数设置为context的一个属性,以便调用时可以通过context调用
        context.fn = this;

        // 调用函数并传入参数
        const result = context.fn(...args);

        // 删除context的fn属性
        delete context.fn;

        // 返回函数的执行结果
        return result;
};
    function greeting(name) {
    console.log(`Hello, ${this.name + name}!`);
}
    let a = {
        name: "前端"
    }
    greeting.myCall(a,'百草阁');
};

在这里插入图片描述
实现要点:1.函数要调用 2.要改变this指向 3.要传参
如何实现改变this的指向呢? 大家注意context.fn = this这一行 ,this指向的其实就是greeting这个函数,相当于原本是window.greeting()调用,现在把函数作为context的一个属性调用,把this指向了这个context,改变了原greeting函数的调用方式,从而改变了this的指向

总结

本文重点讲解了,各种this的使用场景如何改变函数中this的指向以及this的练习题
其实很多时候我们都会在各种场景下碰到各类this的问题,但是我们都选择了得过且过,不想花时间去了解,何不在这一次和笔者一起全面的学习this这个面试必考点呢!

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

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

相关文章

【软件分析/静态分析】chapter6 课程08 指针分析(Pointer Analysis)

🔗 课程链接:李樾老师和谭天老师的: 南京大学《软件分析》课程08(Pointer Analysis)_哔哩哔哩_bilibili 目录 第六章 指针分析(Pointer Analysis) 6.1 为什么需要指针分析 6.2 指针分析的基本…

AMAT 工业输入输出模块0100-77037

W;① ⑧ 0 ③ 0 ① 7 7 7 ⑤ 9 AMAT 工业输入输出模块0100-77037 0100-76124 0100-71313 0100-71311 0100-71309 0100-71278 0100-71267 0100-71229 0100-71224 0100-20100 IGBT 和 IGCT 是四层器件,乍一看并没有什么不同。但是,当您“ 深入了解…

Spring Boot原理分析(三):IoC容器的继承层次

文章目录 一、Spring Ioc容器的继承层次1.BeanFactory2.ListableBeanFactory3.HierarchicalBeanFactory4.ApplicationContext 二、常用的ApplicationContext的实现类1.ClassPathXmlApplicationContext(基于XML配置)2.AnnotationConfigApplicationContext…

[Android]使用jni实现高斯模糊

1.高斯模糊的原理: 根据周边的像素值来确定自己的像素值,平均值,最大值,最小值,正太分布值 2.均值模糊blur 函数声明: CV_EXPORTS_W void blur( InputArray src, OutputArray dst,Size ksize, Point anc…

python绘制分组条形图

文章目录 数据导入多组条形图堆叠条形图 数据导入 我们经常会遇到对比多个统计量随时间变化的图像,比如想知道中国、美国以及欧盟最近几年GDP变化,如下表所示,单位是万亿美元。 中国美国欧盟201813.8920.5315.98201914.2821.3815.69202014.…

转换或是克隆的虚拟机无法联网,网络服务无法启动

新转换的虚拟机,无法联网,启动网络服务,报错: systemctl start network.service job for network.service failed because the control process exited with error code. 查看网络服务状态,systemctl status network…

SpringMVC 中的控制器如何处理文件上传

SpringMVC 中的控制器如何处理文件上传 Spring MVC 是一个基于 Java 的 Web 框架,它是 Spring 框架的一部分,提供了一系列的组件和工具,帮助开发人员构建 Web 应用程序。其中,控制器是 Spring MVC 中的核心组件之一,它…

SpringMVC 中的控制器如何返回 JSON 数据

SpringMVC 中的控制器如何返回 JSON 数据 SpringMVC 是一个基于 Spring 框架的 Web 框架,它提供了一种方便的方式来处理 HTTP 请求和响应。在 SpringMVC 中,控制器是用来处理请求的组件,它们负责接收请求、处理请求并返回响应。在本文中&…

三大城市分会场精彩呈现—2023架构·可持续未来峰会圆满收官!

2023年6月30日,由The Open Group主办的2023架构可持续未来峰会三大城市分会场成功举办,也代表着本次The Open Group半年度架构峰会圆满收官! 本次大会以“可持续未来”为主题,采用“13”,即北京主会场上海/成都/深圳三…

svg修改图标颜色

对于svg图标,想通过hover或者active 添加颜色,没有办法修改,解决办法: 1. 修改svg图片源 最开始的svg图标: 修改这个fill"currentColor" 要是要修改线条颜色就修改stroke属性: fill属性设置对象…

青岛大学_王卓老师【数据结构与算法】Week05_02_栈的定义和特点_学习笔记

本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享, 另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权,请留言作删文处理。 课程视频链接: 数据结构与算法基础…

这所985太好考了,专硕06方向仅刷一人,其余过线全收!

一、学校及专业介绍 东北大学(Northeastern University)位于辽宁省沈阳市,是中华人民共和国教育部直属全国重点大学。它是国家“双一流”建设高校、国家“211工程”和“985工程”重点建设高校,全国首批博士、硕士学位授予单位。 …

10.12UEC++/结构体和枚举

1.结构体: 定义: 类中创建对象; UE4中使用: 也可以定义一个结构体类型的变量: 结构体成员想要在细节面板中调整时,也需要每个都加上UPropPerty指令宏才可以: 此时该变量没有break功能&#xff0…

Seal AppManager v0.2 发布:进一步简化应用部署体验

经过近3个月的研发,Seal AppManager v0.2 已正式发布。 Seal AppManager 是一款基于平台工程理念的应用统一部署管理平台,于今年4月首次推出。在上一版本中,我们已经释出集成 ChatGPT 简化服务模板代码生成、云成本可视化、动态环境管理等功…

用html+javascript打造公文一键排版系统2:显示源码/显示预览、清除格式

我们从实现最简单、最基本的功能开始。 一、显示源码/显示预览 我们通过btnShowSrc来实现示源码/显示预览功能&#xff0c;根据btnShowSrc.value来判断<iframe>的显示的内容并切换。 <iframe>默认为显示预览&#xff0c;所以btnShowSrc.value值为“显示源码”&a…

Flink DataStream之Connect合并流

新建类 package test01;import org.apache.flink.configuration.Configuration; import org.apache.flink.streaming.api.datastream.ConnectedStreams; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.datastre…

flutter开发实战-指纹、面容ID验证插件实现

flutter开发实战-指纹、面容ID验证插件实现 在iOS开发中&#xff0c;经常出现需要指纹、面容ID验证的功能。 指纹、面容ID是一种基于用生物识别技术&#xff0c;通过扫描用户的面部特征来验证用户身份。 一、效果图 二、iOS指纹、面容ID验证 在iOS中实现指纹、面容ID验证功能…

一同感受C++模版的所带来的魅力

文章目录 一、泛型编程思想二、函数模版1、函数模板概念2、函数模板格式3、函数模板的原理4、函数模板的实例化5、模板参数的匹配原则 三、类模版1、类模板的定义格式2、类模板的实例化 四、总结与提炼 一、泛型编程思想 首先我们来看一下下面这三个函数&#xff0c;如果学习过…

磁盘与文件系统管理

磁盘结构及分区表示 硬盘 Hard Disk Drive &#xff0c;简称 HDD 是计算机常用的存储设备之一。 1 磁盘基础 1.1 硬盘的结构 1.1.1 数据结构 ①扇区&#xff1a;磁盘上的每个磁道被等分为若干个弧段,这些弧段便是硬盘的扇区(Sector)。硬盘的第一个扇区&#xff0c;叫做引导…

【mysql】-【锁】

文章目录 概述MySQL并发事务访问相同记录读-读情况写-写情况读-写或写-读情况并发问题的解决方案 锁的不同角度分类 概述 事务的隔离性由这章讲述的锁来实现。 MySQL并发事务访问相同记录 并发事务访问相同记录的情况大致可以划分为3种: 读-读情况 读-读情况&#xff0c;…