JS中的this指向问题
JS中的this指向问题
2023 / 05 / 29 By:Awayer
前言:
在JS中this指向问题异常重要,因为在开发中要经常用到this,所以我将在本篇文章中进行讲解
在讲解之前,要声明两件非常重要的事情:
1.this在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了
2.this指向的永远是一个对象
1.构造函数中的this
function Fn(name,age){
this.name = name;
this.age = age;
console.log(this); // {name:'Awayer',age:19}
}
let f1 = new Fn('Awayer',19);
构造函数,在new出一个实例对象f1的时候,this指向new出的这个对象。按照前言所说,第一点不应该是谁调用函数指向谁吗?还有上述代码中构造函数到底是谁调用的?这两个问题我将放在new的文章中。
关于new这个关键字在执行的时候发生了什么我同样会在另一篇文章中进行讲解。
2.普通函数中的this
构造函数和普通函数在JS中其实是有一点区别的,构造函数约定好了要名字首字母大写。如果直接调用函数而不是来new对象的话,结果会有一些不一样:
function fn(name,age){
this.name = name;
this.age = age;
console.log(this); // Window:{...}
}
fn();
按照前言说的,fn函数是由全局调用的,所以this的指向指向Window(浏览器中),并且Window是一个对象。
3.函数作为对象的一个属性
let obj = {
name:'Awayer',
age:19,
show:function() {
console.log(this);
console.log(this.age);
}
}
obj.show(); // {name: 'Awayer', age: 19, show: [Function: show]} , 19
上述例子中show属性是一个匿名函数,函数中输出this。show属性(函数)的调用者是一个函数,所以他的this指向调用者obj,自然也能访问到obj上的属性。
**注意:**如果这个函数不是通过obj.xxx的方式来调用的呢?就像下面这个例子,结果又是不同:
let obj = {
name:'Awayer',
age:19,
show:function() {
console.log(this);
console.log(this.age);
}
}
let fn = obj.show;
fn(); // Window undefined
上述代码中,将obj的show属性(函数)赋值给了一个变量fn,然后再全局中调用,这时this就指向了调用者Window,同时window中没有age这个变量,自然输出了undefined。
是不是差不多明白了,来一道题试试:
let A = {
name:'Awayer',
f:function(){
console.log(this.name);
}
}
let B = {
name:'Kater'
}
B.f = A.f;
A.f(); // ??
B.f(); // ??
是不是非常简单,答案是:Awayer ,Kater
4.定时器中的this
有的时候,那两句话也不是完全正确的,就比如下面这段代码:
let obj = {
f:function() {
console.log(this);
}
}
setTimeout(obj.f,1000);
我们发现他是指向Window的,和我们之前的规律好像不太一样,因为定时器这个函数就是被定义在window下的。同理,setInterval的this指向也是window:
let obj = {
f:function() {
console.log(this);
}
}
setInterval(obj.f,1000);
所以开发中有的时候我们需要改变定时器中this的指向,这个问题我将在以后的文章中讲解。
5.硬绑定
通过call(..)和apply(..),bind(...)方法,可以绑定this的指向至某个对象身上,例如:
let obj1 = {
name:'Awayer',
age:19,
say:function(){
console.log(this);
}
}
let obj2 = {
name:'Kater',
age:20,
}
obj1.say.call(obj2);
上述代码最后行原本是obj1.say方法,this指向应该是obj1,然后通过call(绑定的对象名)来将this指向改到了obj2.同理apply和bind也能实现:
let obj1 = {
name:'Awayer',
age:19,
say:function(){
console.log(this);
}
}
let obj2 = {
name:'Kater',
age:20,
}
let obj3 = {
name:'CubeBamboo',
age:114,
}
obj1.say.apply(obj2); // { name: 'Kater', age: 20}
obj1.say.bind(obj3)(); // { name: 'CubeBamboo', age: 114 }
除了bind方法要多加一个(),这3个方法的用法是一样的,只是传参的方式不一样,区别可以具体参考这篇文章:JavaScript 中 call()、apply()、bind() 的用法 | 菜鸟教程 (runoob.com)
6.箭头函数的this
箭头函数的this指向不遵循上述规律,而是定义该函数时所在的作用域指向的对象,而不是调用时所在的作用域指向的对象,这和上面的结论相反,如下:
普通函数:
var name = 'window'; // window.name = 'window'
var A = {
name: 'A',
sayHello: function(){
console.log(this.name)
}
}
A.sayHello(); // A
箭头函数:
var name = 'window';
var A = {
name: 'A',
sayHello: () => {
console.log(this.name)
}
}
A.sayHello(); // window
我一开始看这段代码的时候也是很迷惑,难道不是箭头函数的外层吗?外层难道不就是A吗?
戳辣!如果了解作用域的人的可能就能发现问题了。上述代码中只有两个执行上下文,一个是全局的,一个是sayHello函数的上下文。根据我上面那句粗体的话,"定义该函数时所在的作用域指向的对象",定义此箭头函数所在的作用域为全局作用域,所以this指向全局。自然输出window。
那么如何将this的指向固定到A呢?我们可以再给箭头函数外层再套一层函数,如下:
let name = 'window';
let A = {
name: 'A',
sayHello:function() {
return ()=>{
console.log(this.name);
}
}
}
A.sayHello()(); // A
本篇到此,完。
转载自:https://juejin.cn/post/7241790368949583909