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


前言

相信很多朋友和我一样,总是搞不清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这个面试必考点呢!

猜你喜欢

转载自blog.csdn.net/m0_57524265/article/details/131624968