面试题: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指向问题带来的困扰。

相关推荐
桃园码工9 分钟前
15_HTML5 表单属性 --[HTML5 API 学习之旅]
前端·html5·表单属性
百万蹄蹄向前冲1 小时前
2024不一样的VUE3期末考查
前端·javascript·程序员
轻口味1 小时前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami1 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
wakangda2 小时前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡2 小时前
lodash常用函数
前端·javascript
emoji1111112 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼2 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_748250032 小时前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
vue3 如何使用 mounted
前端·javascript·vue.js