面试题:js中this的指向问题

在JavaScript中,this的指向一直是面试的热点问题,这篇文章就带大家彻底搞懂this的指向问题,面试遇到this问题不再害怕。

1.this的定义

在绝大多数情况下,函数的调用方式决定了this的值,this不能在执行期间被赋值,并且在每次函数调用的时候,this的值也可能不同。此外,在严格模式和非严格模式之间this的值也会有差别。

2.为什么使用this

在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见的面向对象语言中的this不太一样:

  • 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。
  • 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。
  • 但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义。

使用this有什么意义呢?下面的代码中,我们通过对象字面量创建出来一个对象,当我们调用对象的方法时,希望将对象的名称一起进行打印。

如果没有this,那么我们的代码会是下面的写法:

  • 在方法中,为了能够获取到name名称,必须通过obj的引用(变量名称)来获取。
  • 但是这样做有一个很大的弊端:如果我将obj的名称换成了info,那么所有的方法中的obj都需要换成info。
js 复制代码
var obj = {  
  name: "word",  
  running: function() {  
    console.log(obj.name + " playing");  
  },  
  eating: function() {  
    console.log(obj.name + " drinking");  
  },  
  studying: function() {  
    console.log(obj.name + " studying");  
  }  
}

事实上,上面的代码,在实际开发中,我们都会使用this来进行优化:

  • 当我们通过obj去调用playing、drinking、studying这些方法时,this就是指向的obj对象
js 复制代码
var obj = {  
  name: "word",  
  running: function() {  
    console.log(this.name + " playing");  
  },  
  eating: function() {  
    console.log(this.name + " drinking");  
  },  
  studying: function() {  
    console.log(this.name + " studying");  
  }  
}

所以我们会发现,在某些函数或者方法的编写中,this可以让我们更加便捷的方式来引用对象,在进行一些API设计时,代码更加的简洁和易于复用。

当然,上面只是应用this的一个场景而已,开发中使用到this的场景到处都是,这也是为什么它不容易理解的原因。

3.this指向什么

我们先说一个最简单的,this在全局作用域下指向什么?

  • 这个问题非常容易回答,在浏览器中测试就是指向window
  • 所以,在全局作用域下,我们可以认为this就是指向的window
js 复制代码
console.log(this); // window  
  
var name = "word";  
console.log(this.name); // word  
console.log(window.name); // word

但是,开发中很少直接在全局作用域下去使用this,通常都是在函数中使用

所有的函数在被调用时,都会创建一个执行上下文:

  • 这个上下文中记录着函数的调用栈、函数的调用方式、传入的参数信息等;
  • this也是其中的一个属性;

我们先来看一个让人困惑的问题:

  • 定义一个函数,我们采用三种不同的方式对它进行调用,它产生了三种不同的结果
js 复制代码
// 定义一个函数  
function foo() {  
  console.log(this);  
}  
  
// 1.调用方式一: 直接调用  
foo(); // window  
  
// 2.调用方式二: 将foo放到一个对象中,再调用  
var obj = {  
  name: "word",  
  foo: foo  
}  
obj.foo() // obj对象  
  
// 3.调用方式三: 通过call/apply调用  
foo.call("abc"); // String {"abc"}对象

上面的案例可以给我们什么样的启示呢?

  • 1.函数在调用时,JavaScript会默认给this绑定一个值;
  • 2.this的绑定和定义的位置(编写的位置)没有关系;
  • 3.this的绑定和调用方式以及调用的位置有关系;
  • 4.this是在运行时被绑定的;

那么this到底是怎么样的绑定规则呢?让我们一起探索一下吧

4.this绑定规则

我们现在已经知道this无非就是在函数调用时被绑定的一个对象,我们就需要知道它在不同的场景下的绑定规则即可。

4.1默认绑定规则

当函数独立调用时,不带任何修饰符,this会指向全局对象(在浏览器中为window对象,而在Node.js环境中为global对象)。

js 复制代码
function foo() {
  console.log(this);
}

foo();  // 在浏览器中输出window对象,在Node.js环境中输出global对象

4.2 隐式绑定规则

当函数作为对象的方法调用时,this会指向调用该方法的对象。

js 复制代码
var obj = {
  name: 'John',
  greet: function() {
    console.log('Hello, ' + this.name);
  }
};

obj.greet();  // 输出:Hello, John

4.3 显式绑定规则

通过函数的callapplybind方法,我们可以显式地指定函数执行时的this值。

js 复制代码
function greet() {
  console.log('Hello, ' + this.name);
}

var obj1 = { name: 'John' };
var obj2 = { name: 'Jane' };

greet.call(obj1);  // 输出:Hello, John
greet.apply(obj2);  // 输出:Hello, Jane

var boundGreet = greet.bind(obj1);
boundGreet();  // 输出:Hello, John

4.4 new绑定规则

当使用new关键字调用构造函数时,this会指向新创建的对象。

js 复制代码
function Person(name) {
  this.name = name;
}

var john = new Person('John');
console.log(john.name);  // 输出:John

4.5 箭头函数中的this

箭头函数与普通函数的行为不同,它没有自己的this值,而是继承外层作用域的this值。

js 复制代码
var obj = {
  name: 'John',
  greet: function() {
    setTimeout(() => {
      console.log('Hello, ' + this.name);
    }, 1000);
  }
};

obj.greet();  // 输出:Hello, John(箭头函数继承了obj对象的this值)

5.使用bind、call和apply改变this指向

通过使用bindcallapply,我们可以显式地改变函数执行时的this指向。

6.注意事项和常见问题

  • 在箭头函数中,不能使用bindcallapply来改变this的指向。
  • 在回调函数中,this的指向可能会发生变化,常见的解决方法是使用箭头函数或将this存储在变量中。
  • 在使用setTimeout、setInterval等定时器函数时,this的指向可能会出现问题,可以通过将this存储在变量中或使用箭头函数来解决。

总结

本文详细解析了JavaScript中this的指向问题。通过理解默认绑定、隐式绑定、显式绑定、new绑定以及箭头函数的行为,我们可以更好地掌握this关键字的用法和规则。同时,我们还介绍了如何使用bindcallapply来改变函数执行时的this指向,以及一些注意事项和常见问题。通过深入理解和应用this关键字,您将能够更加灵活地编写JavaScript代码,并避免this指向问题带来的困扰。

相关推荐
星辰引路-Lefan1 天前
[特殊字符] 开源一款基于 PaddleOCR 的纯离线 OCR 识别插件 | 支持身份证、银行卡、驾驶证识别
前端·开源·ocr
Cache技术分享1 天前
285. Java Stream API - 通过 Supplier 创建 Stream
前端·后端
阿基米东1 天前
从嵌入式到前端的探索之旅,分享 5 个开源 Web 小工具
前端·javascript·github
clove1 天前
js中的数据类型,类型判断,类型转换,一篇文章全面罗列解析
javascript
hxjhnct1 天前
响应式布局有哪些?
前端·html·css3
LYFlied1 天前
【每日算法】LeetCode215. 数组中的第K个最大元素
前端·算法
怎么就重名了1 天前
Kivy的KV语言总结
前端·javascript·html
jqq6661 天前
解析ElementPlus打包源码(四、打包主题)
前端·javascript·vue.js
代码猎人1 天前
类数组对象是什么,如何转化为数组
前端
duanyuehuan1 天前
js 解构赋值
开发语言·前端·javascript