【JavaScript】聊聊js中关于this的指向

javascript,js,this · 浏览次数 : 92

小编点评

本文主要讨论了JavaScript中this指向的问题,包括this的基本概念、在不同场景下的指向以及箭头函数和call、apply、bind的区别。 首先,本文介绍了JavaScript中this的概念,指出this的指向取决于函数的调用方式,而不是固定在某个特定的对象上。接着,通过例子展示了在不使用new关键字和使用new关键字的情况下,this的指向会有所不同。 然后,文章详细讨论了箭头函数的特点,包括其简洁的语法、捕获定义时所在上下文的this值、没有arguments对象以及不能用作构造器等。此外,还通过例子展示了箭头函数的this指向是如何被其调用方式影响的。 最后,本文比较了call、apply和bind这三种改变函数this指向的方法,包括它们的用法和区别。通过例子演示了如何使用这些方法来改变函数的this指向,并强调了它们之间的区别。 总的来说,本文通过对JavaScript中this指向问题的深入分析,帮助读者更好地理解this在不同场景下的行为,以及箭头函数和call、apply、bind的使用技巧。

正文

前言

最近在看回JavaScript的面试题,this 指向问题是入坑前端必须了解的知识点,现在迎来了ES6+的时代,因为箭头函数的出现,所以感觉有必要对 this 问题梳理一下,所以刚好总结一下JavaScript中this指向的问题。

什么是JavaScript

在了解this指向的问题前,首先得了解一下什么是JavaScript。

JavaScript(简称“JS”)是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。JavaScript基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式、支持函数式编程、闭包、基于原型的继承等高级功能。

什么是this

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

在方法中,this 表示该方法所属的对象。

如果单独使用,this 表示全局对象。

在函数中,this 表示全局对象。

在函数中,在严格模式下,this 是未定义的(undefined)。

在事件中,this 表示接收事件的元素。

类似 call() 和 apply() 方法可以改变 this 的指向 ,引用到任何对象。

所以this的指向完全取决于函数的调用方式。

this的指向

接下来我将在(非严格模式下)通过下面例图与例子来了解this的指向。

1.不使用new关键字,使用dot调用。

var obj = {
    name: 'bug',
    obj2: {
           name: 'bug2',
           fn: function () {
                console.log(this.name); //bug2
           }
     }
}
//此处通过obj.obj2.fn(),调用了obj中的obj2中的fn函数,此时fn函数中this的指向为dot (.) 前面的对象,即为obj2,obj2中的name即是bug2。
obj.obj2.fn();

2.使用new关键字调用。

function fn() {
  this.x = 1;
}

//此处通过new关键字生成了一个实例对象,此时的this指向了该实例对象fn
var obj = new fn();

//此时的obj的结构为{x:1},所以obj.x=1
obj.x // 1

讲到new关键字就刚好衍生出看另外一个关键点,如果我用new去创建一个实例对象,这个时候实例对象有返回值呢?

通常情况下是不应该有显式的返回值的。

但是如果当return返回的是一个对象,那么将返回该对象。

但是如果当return返回非对象类型(比如数字、字符串等),那么就不会影响到new关键字对对象的创建。

以下就用几个例子来验证一下:

①return 空对象

function fn() 
{ 
   this.name= 'bug'; 
   //此处return回了一个对象
   return {}; 
}
var obj = new fn(); 
//此时因为return回的是一个对象,所以此时的obj的结构是返回的空对象{},所以obj.name才会是undefined
console.log(obj.name); //undefined

②return一个非空对象

function fn() 
{ 
   this.name= 'bug'; 
   //此处return回了一个对象
   return {name:'bug2'}; 
}
var obj = new fn(); 
//此时因为return回的是一个非空对象,所以此时的obj的结构是返回的非空对象{name:'bug2'},所以obj.name是bug2
console.log(obj.name); //bug2

③返回数字

function fn() 
{ 
   this.name= 'bug'; 
   //此处return回了一个数字
   return 11; 
}
var obj = new fn(); 
//此时因为return回的是一个数字,所以此时返回的实例对象不受影响,结构是{name:'bug'},所以obj.name是bug
console.log(obj.name); //bug

④返回字符串

function fn() 
{ 
   this.name= 'bug'; 
   //此处return回了一个字符串
   return 'xxxxx'; 
}
var obj = new fn(); 
//此时因为return回的是一个字符串,所以此时返回的实例对象不受影响,结构是{name:'bug'},所以obj.name是bug
console.log(obj.name); //bug

既然现在进入了Es6+的时代了,就不得不讲一讲箭头函数的this指向了

1.什么是箭头函数

箭头函数是ECMAScript 6中新增的一种函数定义方式,也被称为Lambda函数。 它使用箭头(=>)符号来替代传统的function关键字,从而更简洁地定义函数,使代码更加简洁易读。

箭头函数有以下特点:

①语法简洁:箭头函数表达式的语法比普通函数更简洁,使用箭头(=>)符号来定义函数,可以省略一些不必要的语法元素,如function关键字、大括号和参数列表周围的括号(如果只有一个参数)。

②this绑定:箭头函数不绑定自己的this,它会捕获定义时所在上下文的this值,这使得在回调函数或嵌套函数中使用箭头函数时,this的指向更加明确和可预测。

③没有arguments对象:箭头函数没有自己的arguments对象,这意味着它们无法访问到传统函数的特殊属性arguments。

④不能用作构造器:箭头函数不能作为构造器使用,即它们不能用作类的实例化。

2.箭头函数的this指向

因为箭头函数不绑定自己的this,它会捕获定义时所在上下文的this值。所以简单的说就是箭头函数没有属于自己的this。

一下用个例子来简单了解。

①正常function函数

const obj={
      mythis: function(){
           console.log(this) //指向了上一级对象obj
      }
}
obj.mythis() //返回了obj对象

②箭头函数

const obj={
     mythis: ()=>{
          console.log(this) //因为箭头函数没有自己的this,所以指向的是window
     }
}
obj.mythis() //返回了window

哦这里还有一个坑,就是前面说的,this指向完全取决于函数的调用方式。

你再看看这道题最终返回的是什么?

const obj={
    mythis: function(){
       console.log(this)
    }
}
var a =obj.mythis
a()
点击查看答案与解析
//是不是有小伙伴认为这里使用的是function,所以返回的还是mythis的上一级对象obj ???
//不不不,这时候返回的是window!因为this指向完全取决于函数的调用方式
//上述例子①为何返回的是obj是因为它是直接obj.mythis()去调用,this指向是mythis的上一级对象
//但是本例子是通过减mythis直接赋值给a,此时,a 成为一个普通的函数引用,它只是 obj.mythis 的一个复制,并没有 obj 对象的上下文信息
//所以,当 a 作为一个普通函数调用时(不作为对象的方法调用),在非严格模式下,JavaScript 中的 this 默认指向全局对象 window
const obj={
    mythis: function(){
       console.log(this)
    }
}
var a =obj.mythis
a() //window

当然,this的指向除了调用的方式不同而不同的同时,也可以通过其它方式强制改变this的指向!那就是使用call、apply、bind。

什么是call、apply、bind,区别是什么?

1.什么是call?

call方法可以接受两个参数,第一个参数就是this的指向,指向xxx,第二个参数为一个参数列表。当第一个参数为null或者undefined时,this默认指向window。

function fn(...args) {
    console.log(this, args);
}
let obj = {
    name: "bug"
}

//将fn的this指向obj,并传入参数列表 1,2
fn.call(obj, 1, 2); //{name:'bug'} , [1,2]

//次数fn中的this指向为window
fn(1, 2) //window , [1,2]

//当第一个参数为null时,this指向为window
fn.call(null,[1,2]);//window , [1,2]

//当第一个参数为undefined时,this指向为window
fn.call(undefined,[1,2]);//window , [1,2]

2.什么是apply?

apply方法可以接受两个参数,第一个参数就是this的指向,指向xxx,第二个参数为一个参数数组。当第一个参数为null或者undefined时,this默认指向window。

function fn(...args) {
    console.log(this, args);
}
let obj = {
    name: "bug"
}

//将fn的this指向obj,并传入参数数组 [[1,2]]
fn.apply(obj, [1,2]); //{name:'bug'} , [[1,2]]

//次数fn中的this指向为window
fn([1,2]) //window , [[1,2]]

//当第一个参数为null时,this指向为window
fn.apply(null,[1,2]);//window ,  [[1,2]]

//当第一个参数为undefined时,this指向为window
fn.apply(undefined,[1,2]);//window , [[1,2]]

3.什么是bind?

bind方法跟call、apply十分相似,第一个参数也是this的指向,第二个参数传的也是一个参数列表,但是!这个参数列表可以分多次传入!并且改变完this的指向并不会立刻执行,而是返回一个已经永久改变this指向的函数

function fn(...args) {
     console.log(this, args);
}
let obj = {
     name: "bug"
}
const bindFn = fn.bind(obj); //this变为obj,且不会立马执行
bindFn(1, 2) //得通过调用才会执行,并传入参数列表1,2,最终this指向obj {name:'bug'}
fn(1, 2) //this执行window

4.call、apply、bind的区别是什么?

①三者都可以改变函数的 this 对象指向

②三者第一个参数都是 this 要指向的对象,如果如果没有这个参数或参数为 undefined 或 null,则默认指向全局 window

③三者都可以传参,但是 apply 是数组,而 call 是参数列表,且 apply 和 call 是一次性传入参数,而 bind 可以分为多次传入bind 是返回绑定this之后的函数,apply、call 则是立即执行

总结

简单来说,this的指向不是固定不变的,它会随着执行环境的改变而改变,具体怎么改变完全取决于函数的调用方式。

箭头函数没有属于自己的this,作为方法的箭头函数this的指向是当前的上下文。

我是刚毕业一年多的小菜鸟,上述为个人学习整理内容,水平有限,如有错误之处,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!谢谢~๑•́₃•̀๑ [鲜花][鲜花][鲜花]

与【JavaScript】聊聊js中关于this的指向相似的内容:

【JavaScript】聊聊js中关于this的指向

最近在看回JavaScript的面试题,this 指向问题是入坑前端必须了解的知识点,现在迎来了ES6+的时代,因为箭头函数的出现,所以感觉有必要对 this 问题梳理一下,所以刚好总结一下JavaScript中this指向的问题。

【JavaScript】聊一聊js中的浅拷贝与深拷贝与手写实现

什么是深拷贝与浅拷贝?深拷贝与浅拷贝是js中处理对象或数据复制操作的两种方式。‌在聊深浅拷贝之前咱得了解一下js中的两种数据类型:

构建 JavaScript ChatGPT 插件

> 聊天插件系统是一种令人兴奋的新方式,可以扩展ChatGPT的功能,纳入您自己的业务数据,并为客户与您的业务互动增加另一个渠道。在这篇文章中,我将解释什么是聊天插件,它们能做什么,以及你如何用JavaScript建立你自己的聊天插件。 这篇文章(或OpenAI所称的"训练数据")提供了一个快速入门

高级前端开发需要知道的 25 个 JavaScript 单行代码

1. 不使用临时变量来交换变量的值 2. 对象解构,让数据访问更便捷 3. 浅克隆对象 4. 合并对象 5. 清理数组 6. 将 NodeList 转换为数组 7. 检查数组是否满足指定条件 8. 将文本复制到剪贴板 9. 删除数组重复项 10. 取两个数组的交集 11. 求数组元素的总和 12. ...

神奇的JavaScript弱等价类型转换

JavaScript语言特性 - 类型转换 JavaScript这门语言的类型系统从来没有它表面看起来的那样和善,虽然比起Java、C#等一众强类型语言,它的弱类型使用起来似乎是如此便利,但正因为它极高的自由度,所以才会衍生出令人摸不着头脑的荒诞行为。 举个例子,虽然我们都知道一个包含内容的字符串会

彻底搞懂JavaScript原型和原型链

基于原型编程 在面向对象的编程语言中,类和对象的关系是铸模和铸件的关系,对象总是从类创建而来,比如Java中,必须先创建类再基于类实例化对象。 而在基于原型编程的思想中,类并不是必须的,对象都是通过克隆另外一个对象而来,这个被克隆的对象就是原型对象。 基于原型编程的语言通常遵循下面的规则: 所有的数

如何在低代码平台中引用 JavaScript ?

引言 在当今快速发展的数字化时代,企业对业务应用的需求日益复杂且多元。低代码开发平台作为一个创新的解决方案,以直观易用的设计理念,打破了传统的编程壁垒,让非技术人员也能轻松构建功能完备的Web应用程序,无需深入编码。这一特性极大地简化了应用开发流程,加速了业务需求转化为实际应用的速度,为企业带来了前

引爆你的网页乐趣!前端十个令人捧腹的JavaScript整蛊代码。

愚人节整蛊代码。想要在网页上增添一抹幽默与惊喜吗?或是想给你的朋友一个意想不到的“小惊喜”?那么,这十款简单而有趣的JavaScript前端整蛊代码绝对能满足你的需求!每一个代码都能让你的网页瞬间变得生动有趣。

JavaScript 如何实现一个响应式系统

JavaScript 如何实现一个响应式系统 第一阶段目标 数据变化重新运行依赖数据的过程 第一阶段问题 如何知道数据发生了变化 如何知道哪些过程依赖了哪些数据 第一阶段问题的解决方案 我们可用参考现有的响应式系统(vue) vue2 是通过 Object.defineProperty实现数据变化的

第119篇: JavaScript 类

好家伙,我们先来复习一下 关于Java,类的三大特征: 1、封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。 2、继承,继承性更符合认知规律,使程序更易于理解,同时节省不必要的重复代码。 3、多态,体现为覆盖和重载,Js没有重载,有