关于 this 指向的问题你都清楚吗

JavaScript 中的this关键字在开发中的角色是非常灵活的,基本上可以指代一切调用者,也正因为如此,成为了 JS 相关的面试中的重要考查点。对它的原理和使用的梳理还是很有必要的。

一句话总结

虽然this使用灵活多变,但是我们依然可以找出一定的规律进行概括。一句话总结,那就是 this指向的是函数运行时的所在的环境对象。也有其他说取决于调用函数时的执行上下文,或者取决于函数调用位置的,虽然没错但个人觉得并不及这一句精炼。有了抽象概括,接着再来实际的场景中看看这一结论是如何得以体现的。

全局环境下的函数调用

通常来说,全局环境一般指浏览器和node环境,而关于this的考察一般是在浏览器环境中, 对于如下代码:

var a = 1;
function fr() {
  console.info("this.a", this.a);
  console.info("this", this);
}
fr();

在浏览器的环境中的输出是:

1
Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

可见,直接函数调用的时候,this指向环境对象(浏览器也就是window)。不过,严格模式下上面代码的thisundefined,this.a会直接报错。

如果是在Node环境中呢?

undefined
Object [global] {...

Node环境下,的this指向的是全局global, a变量并没有写到全局,所以this.aundefined

因实际中关于考察 this 的考察大多指浏览器环境,后面对Node环境只作简单描述,不过多展开。

函数作为对象的方法调用

函数经常被挂载到一个对象上面,通过对象属性的方式进行调用。

const obj = {
  name: "te",
  say: function () {
    console.info("Hi !");
    console.info("this=", this);
  },
};
obj.say();

// Hi !
// this= { name: 'te', say: [Function: say] }

可见,函数作为对象的一个属性来调用的时候,this是指向这个上级对象的。此时函数运行所在的环境对象也是这个上级obj对象。

我们对上面的obj对象不做修改,对调用方式做简单修改:

const s = obj.say;
s();

此时的输出是:

Hi !
VM872:5 this= Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}

可以看到函数say中的this指向已经发生了变化,不再指向obj对象,而是window这个全局对象。是不是看起来有一点不可思议?

这是因为obj.say函数被赋值给s之后,对s的调用其实是在全局环境中的,所以函数内部的this又成了全局的window

构造函数中的 this

看到这里估计会有声音说,构造函数中的this不是最简单的吗,指向当前返回的新对象,但真的是这样吗?

关于new操作符创建新的对象的过程可以简单描述为:

  1. 创建新的空对象

  2. 为该对象添加属性和方法,并使构造函数的this指向新对象。

  3. 返回该新对象

在实际使用new创建对象的时候,一般不会在构造函数中手动指定返回值,因为new是会返回一个符合预期的对象的。不过如果手动给构造函数添加了return,那么还是要注意区分的:

  • 显式return一个非对象的值,得到的是创建出的对象实例。

function ori() {
  this.name = "A";
  return;
}
const obj = new ori();
console.info("", obj);
//  ori { name: 'A' }

并没有返回值(相当于undefined),得到的是对象实例。这里return数值类型和字符串的话,构造函数的结果都是对象实例。

  • return一个对象,得到的就是该对象,相应的this也指向这个对象。

function ori() {
  this.name = "A";
  const inner = {
    name: "B",
  };
  return inner;
}
const obj = new ori();
console.info("", obj);
// { name: 'B' }

可以看到,这时的返回的对象已经是inner对象。所以,构造函数中的返回对象的this也是要视具体的return来决定的。

bind/call/apply 方法

三个方法都是可以改变对应函数的this指向的,bind方法通过该函数调用并且返回一个新的函数,新的函数已经修改了this指向,只有调用这个新的函数,函数体才会被执行。而call/apply方法是会直接绑定this并进行调用的,调用的时候,函数内部的this指向call/apply的第一个参数,两者的区分主要在于传参的格式上,其他深入点可以自行补充。

三者可以这样转换:

const obj = {};
fun.call(obj, "arg1","arg2",...);

相当于:

const obj = {};
fun.call(obj, ["arg1","arg2",...]);

bind则需要手动调用一次:

const obj = {};
fun.bind(target, "arg1","arg2",...)();

关于修改原函数中的this绑定,可以参考如下:

const personA = {
  name: "A",
  say: function () {
    console.info("my name is " + this.name);
  },
};
const personB = { name: "B" };
personA.say.call(personB);

// my name is B

最初是personA在执行自身的say方法,怎么就输出B了呢,因为call方法将personA.say函数中的this绑定到了personB,故输出的时候,this.name也就是personBname

箭头函数的 this

箭头函数作为ES6的特殊化函数,this的绑定比较特殊,不适用于上面根据运行时环境决定,只能通过定义的作用域链,也就是定义的外层的环境来决定。更加浅显一点来说,箭头函数的this指向父作用域,否则就是全局对象,call/apply/bind方法都是不能改变箭头函数的this指向的。

const name = "xx";
const personA = {
  name: "A",
  say: () => {
    console.info("my name is " + this);
  },
};
personA.say();

// my name is [object Window]

可以看到,父级对象personA的环境就是全局,say中的this也就是全局window

const name = "xx";
const personA = {
  name: "A",
  say: function () {
    let name = "In";
    return () => {
      console.info("my name is " + this.name);
    };
  },
};
personA.say()();

// my name is A

箭头函数的父级是say对应的匿名函数,所在的环境是在对象personA中,故personA.say()得到的箭头函数中的this就是指向personA的,所以最终的this.name就是A

现在可以看看call方法对箭头函数this的影响:

const personA = {
  name: "A",
  say: function () {
    return () => {
      console.info("my name is " + this.name);
    };
  },
};
const personB = { name: "B" };
personA.say.call(personB)();
personA.say().call(personB);

// my name is B
// my name is A

先对personA.say调用callsay对应的匿名函数的this指向被改变为personBsay对应的匿名函数的this取决于父级匿名函数this,调用时this也指向personB,故得到的this.nameB

而执行personA.say()之后,已经得到了箭头函数,此时再去调用call方法的话,是无法改变箭头函数中的this指向的,故this.name依然还是A


以上便是对JavaScript中this相关的总结,觉得不错的给点个赞~

-End-



最近热文:一周内B站疯转5.6W次,好东西呀!TikTok软件,请务必谨慎使用!美国如果把根域名服务器封了,中国会从网络上消失?
超全递归技巧整理,这次一起拿下递归
LeetCode1-20题汇总,速度收藏!
限时加入!程序员读者微信群,先到先得!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java,关键字this用于引用当前对象。它可以用来访问对象的成员变量和方法,以及调用构造函数。 下面是关键字this的几个常见用法: 1. 引用当前对象的成员变量 当对象的成员变量与方法参数同名时,我们可以使用关键字this来引用当前对象的成员变量。这种情况通常发生在有参构造函数,用于将参数值赋给对象的成员变量。 ``` public class MyClass { private int x; public MyClass(int x) { this.x = x; // 使用this引用当前对象的成员变量x } } ``` 2. 调用当前对象的方法 我们可以使用关键字this来调用当前对象的其他方法。这种情况通常发生在某个方法需要调用该对象的其他方法时。 ``` public class MyClass { public void method1() { // 执行一些操作 } public void method2() { this.method1(); // 调用当前对象的method1方法 } } ``` 3. 调用当前对象的构造函数 我们可以使用关键字this来调用当前对象的构造函数。这种情况通常发生在一个有多个构造函数时,其一个构造函数需要调用另一个构造函数完成一些初始化操作。 ``` public class MyClass { private int x; public MyClass() { this(0); // 调用另一个构造函数完成初始化 } public MyClass(int x) { this.x = x; } } ``` 在上面的示例,无参构造函数调用了有参构造函数完成了初始化操作。 总之,关键字this是用来引用当前对象的,它可以用于访问对象的成员变量和方法,以及调用构造函数。 ### 回答2: this是一个指向当前对象的关键字。在的方法使用this可以引用当前调用该方法的对象。通过this关键字,我们可以访问和操作当前对象的成员变量和成员方法。this可以用来传递参数值,调用构造方法,以及解决局部变量和成员变量之间的命名冲突。 使用this关键字可以提高代码的可读性和可维护性。它可以明确地指示当前对象,并与其他对象进行区分。这在当一个存在多个同名的变量时特别有用。 例如,在一个有一个成员变量name和一个方法setName,我们可以通过this.name引用成员变量name,通过this.setName()调用方法setName。这样,在方法内部和外部都能清楚地表明我们正在操作的是成员变量而不是局部变量。 另外,this还可以在一个构造方法内部调用其他构造方法,以实现构造方法之间的调用关系。这在一个有多个构造方法的情况下非常有用。通过使用this关键字,我们可以避免在不同的构造方法重复设置相同的初始化代码,提高代码的复用性和简洁性。 总之,this关键字是用来指向当前对象的,可以用来引用和操作当前对象的成员变量和成员方法。它在解决命名冲突、提高代码可读性和实现构造方法之间的调用关系等方面有着重要的作用。 ### 回答3: 关键字"this"用于指代当前对象,在面向对象编程具有以下作用: 1. 引用当前对象:使用关键字"this"可以在的方法引用该方法所属的对象。例如,在一个的方法使用"this"可以获取或修改该对象的属性和调用其它方法。这种方式可以避免命名冲突,并且提高代码的可读性和易维护性。 2. 调用当前对象的构造方法:在的构造方法,使用"this"可以调用该的其它构造方法,用于初始化对象。这在构造方法的重载或相互调用时特别有用。 3. 作为方法返回值:当一个的方法需要返回当前对象时,可以使用"this"关键字来返回当前对象的引用。 4. 进行对象的比较:使用"this"可以比较两个对象是否相等。在重写equals()方法时,通常会使用"this"关键字进行引用比较,以确定两个对象是否相同。 总之,关键字"this"可以帮助编程者更方便地操作当前对象,使代码更加清晰和灵活。它在面向对象编程起到非常重要的作用,能够提高代码的可读性、可维护性,并且简化对象之间的交互逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值