一文弄懂 this 的前世今生

智数幽影
• 阅读 1165
JavaScript 的this关键字大家都不陌生,根据this所在的不同的运行环境,会产生一些容易让人困惑的表现和差异,在没有仔细了解this之前,我对它的理解非常的生硬和片面,希望大家阅读完这篇内容能够清楚的明白this的用途。

this 的由来

让我们看一下这个栗子:

var name = 'MyWindow'

function SayHi() {
  console.log('Hi, My name is ' + name + '.')
}

var MyJavaScript = {
  name: 'MyJavaScript',
  sayHi: SayHi,
}

SayHi() // Hi, My name is MyWindow.
MyJavaScript.sayHi() // Hi, My name is MyWindow.

可以看到,当我们想试图输出Hi, My name is MyJavaScript.的时候,还是输出了window。因为SayHi函数里输出的name变量值必定是沿着当前执行环境的作用域链查找的。

SayHi函数被调用时会创建出一个自己的执行环境,执行环境的三大元素

  1. 变量对象:定义在当前执行环境内可访问的所有变量和方法。
  2. 作用域链:本质是按照执行环境的嵌套顺序而形成的一组变量对象链。
作用域定义了在某个执行环境下可访问的所有变量和方法,作用域链定义了除当前作用域外还有权访问的父级作用域,按照被嵌套顺序及当前作用域形成一条作用域链。默认当我们访问一个变量或方法时,会按照作用域链由近及远查找,首先找到最近的作用域(即当前作用域)再向外层作用域查找。

作用域链图示
一文弄懂 this 的前世今生

按照作用域嵌套顺序,SayHi被嵌套在全局执行环境中,在SayHi函数内部查找name变量时,自然而然找到的就是外层全局作用域中的name变量,所以不论如何调用都会输出window

一文弄懂 this 的前世今生

那想一想,当我们调用MyJavaScript.sayHi()时要如何准确输出MyJavaScript对象的name属性呢?

一文弄懂 this 的前世今生

噔噔蹬蹬,this 机制就此诞生了!!(自带 bgm)

  1. this: 被用来指向函数真正的运行环境

让我们改写一下SayHi方法:

var name = 'MyWindow'

function SayHi() {
  // 此处发生变化
  console.log('Hi, My name is ' + this.name + '.')
}

var MyJavaScript = {
  name: 'MyJavaScript',
  sayHi: SayHi,
}

SayHi() // Hi, My name is MyWindow.
MyJavaScript.sayHi() // Hi, My name is MyJavaScript.

如上图,当我们将name修改为this.name后,sayHi函数当在作为MyJavaScript对象的某一属性被调用时,准确的输出了当前函数的调用者MyJavaScript对象的name属性值。

一文弄懂 this 的前世今生

由此我们可以得知,this对象的作用就是当函数在不同的执行环境下被调用,让我们能够得到真正调用函数的对象。与函数定义在哪里或者函数作用域无关,只关心函数的调用方式。

以上就是this对象的前世,下面会整理一些与this对象相关的必懂知识点,供大家查阅。

一些必懂的知识点

this 是什么

thisJavaScript关键字,在非严格模式下,它总是指向一个对象,而具体指向哪个对象是根据函数运行时所在的执行环境动态绑定的。

为什么需要 this

因为函数可以在不同的运行环境中执行,自身调用或作为方法调用,为了得到当前函数真正运行时的所在执行环境,即函数执行时真正的调用对象this机制就此诞生了。

this 有什么作用

(同上)指向函数真正的调用对象

this 的指向

一文弄懂 this 的前世今生

简单调用

全局环境下this默认指向全局对象,严格模式下则为undefined,浏览器中则为window对象。

e.g. func() => this默认指向全局对象 => window(浏览器下)。

一文弄懂 this 的前世今生

作为对象的方法调用

this指向由调用方式决定,在函数执行时确定。

e.g. obj.func() => this指向具体调用对象 => obj

一文弄懂 this 的前世今生

这里可以理解为obj.f1()是通过对象obj找到f1函数的,则它运行时所在环境就是obj环境,this指向obj

作为构造函数

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象,并作为构造函数的默认返回值。

this指向取决于函数返回的对象(默认无初始化值 => undefined),在类上下文(class关键字)中则是取决于类的构造函数返回对象(默认返回类中定义的所有属性和方法,不包括静态方法)。

函数上下文默认 this

无初始化值

一文弄懂 this 的前世今生

手动添加属性

一文弄懂 this 的前世今生

函数上下文手动返回

一文弄懂 this 的前世今生

类上下文默认 this

一文弄懂 this 的前世今生

类上下文构造函数手动返回

一文弄懂 this 的前世今生

是不是想回退了??快结束了啊,再坚持一下 👋

一文弄懂 this 的前世今生

箭头函数

我们知道普通函数的this指向在函数执行时确定,由函数的调用方式决定。

而在 ES6 的箭头函数中,this指向在函数定义时就确定了,永远指向该函数声明时所在的环境对象任何其他方式都无法修改this的指向。

一文弄懂 this 的前世今生

静态方法

因为this对象代表调用这个函数的对象,而静态方法是属于类的而不是调用对象,静态方法成功加载后,调用对象可能还不存在。因此在静态方法中是找不到this对象的值的。

一文弄懂 this 的前世今生

匿名函数

匿名函数中this的指向也是依旧由函数调用方式决定。

一文弄懂 this 的前世今生

foo作为objA的方法被调用,this指向objA

一文弄懂 this 的前世今生

foo被指向objA.foo所指向的匿名函数,当foo被简单调用时,this就指向了全局对象。

setTimeout 和 setInterval

当使用setTimeoutsetInterval时,回调函数中的this默认指向window对象,因为setTimeoutsetIntervalwindow对象提供的方法。

一文弄懂 this 的前世今生

可见,setTimeout中输出的是window对象上的count属性。

真的真的第 99 步了,坚持到底啊 👋

一文弄懂 this 的前世今生

改变 this 指向的几种方法和区别

call

一文弄懂 this 的前世今生

apply

一文弄懂 this 的前世今生

call 和 apply 的区别

call的参数是直接放进去的,第二第三...第n个参数全都用逗号分隔。

一文弄懂 this 的前世今生

apply的第二个参数接收的是一个数组,对应传递给被绑定的函数。

一文弄懂 this 的前世今生

bind

bind用法和call一致,但是bind返回的是被绑定的函数,需要做一次额外的手动调用。

一文弄懂 this 的前世今生

一文弄懂 this 的前世今生

到此本文就结束啦,感谢阅读,欢迎点赞 🇨🇳

一文弄懂 this 的前世今生

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
4个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(