【干货】Javascript千面之变幻莫测的this指向

九路 等级 686 0 0

相信很多前端人对“this”的指向是很懵逼的,因为this的指向总是变幻莫测,在不同的调用环境中,它的指向总是各不相同。

在面试中,this也是经常考的必考题之一,很多前端老鸟经常会在this这里掉坑。

接下来,看笔者来一层一层的揭开this指向的面纱。

1.事件调用环境中的this指向

<div class="box1"></div>
<div class="box2"></div>

<script>
  const box1 = document.querySelector('.box1')
  const box2 = document.querySelector('.box2')

  function event() {
    console.log(this)
  }

  box1.onclick = event  ------>  <div class="box1"></div>
  box2.onclick = event  ------>  <div class="box2"></div>
  event()   ------>  window对象

</script>

从上述实例中可以看出:

box1点击后,event方法中this指向div.class.box1

box2点击后,event方法中this指向div.class.box2

event()直接调用,this指向window对象,严格模式下指向的是undefined

总结:1.当事件被动调用时,谁去触发,this就指向谁; 2.当时间主动执行时(事件后面加()), this指向的就是window,严格模式下指向的是undefined

2.全局环境下的this指向

前端全局环境分两部分,浏览器环境,node环境

浏览器全局环境

<script>
  var aaaa = 100000
  function aaatestEvent() {
    console.log(this)
  }
  // console.log(this) ------> window对象
  // aaatestEvent() === window.aaatestEvent() ------> window对象
  // aaa = window.aaa
</script>

【干货】Javascript千面之变幻莫测的this指向

我相信有很多小伙伴应该试过在script书写的时候直接打印出this的指向,看看this指向的是个什么东东?没错,指向的是window全局对象;我们经常定义的常量和变量也存放在window对象中

node环境

console.log(this)  -----> {}
console.log(this === module.exports)  -----> true

小伙伴从上述实例中可以看出,在node环境中,this指向的是暴露出来的对象

总结:全局环境中,浏览器环境指向的是window对象,node环境指向的是暴露出来的object对象

3.函数内部环境中的this指向

在实际开发中,调用函数方法时,为了节约对内存的消耗,函数方法经常被存放于一个object对象中去,有利于方法的管理性和可读性

实例1:

<**script**\>
  **var** _obj_ \= {
    **num**: 100,
    fn: **function** () {
      _console_.log(**this**)
    }
  }
  _// 调用
_ _obj.fn() ----> this指向obj_ </**script**\>

obj.fn()运行后,this指向的obj,可以发现,函数方法存在对象中的时候,函数内部的this指向的调用它的对象

实例2:

<**script**\>
  **var** _obj_ \= {
    **num**: 100,
    fn: **function** () {
      _console_.log(**this**)
    }
  }
  _// 调用
_ _obj.fn() ----> this指向obj_ _window.obj.fn() ---> this指向obj_ </**script**\>

var obj1 = { num: 100, aaa: { fn: function () { console.log(this) } } } // 调用 window.obj1.aaa.fn() ---> this指向aaa


通过实例1中的测试,我们稍微总结了下this指向的是调用它的对象;

我们在上面浏览器全局环境中讲过,定义常量和变量会被存放在window对象中,通过window对象输出,我们也能发现obj在window中,可以尝试去执行

window.obj.fn(),我们发现this还是指向obj啊?笔者刚刚不是说this指向的是调用它的对象吗?现在是window在调用obj的fn方法啊?怎么回事?

window.obj1.aaa.fn(),我们发现this指向了aaa啊?

解析:

    实例1:_obj.fn()_    fn被obj调用,this指向obj

    实例2:_window.obj.fn()_ fn被window调用,this指向obj

                  _window.obj1.aaa.fn()_ fn被window调用,this指向aaa

> **此时,我们总结:**
> 
> **实例1:【this指向调用它的对象】**
> 
> **实例2:【函数被多层对象包含,函数被最外层对象调用时,this指向的是它的上一级的对象】**
> 
> **有很多面试过程中,经常在这个地方会被问到,注意别掉坑哦?**

有些考官特别可恶,喜欢在这个地方设置障碍故意绕懵面试者,请看下面的测试实例

<script> var obj = { num: 100, aaa: { fn: function () { console.log(this) } } };

var abc = obj.aaa.fn; window.obj.aaa.fn() ------> 指向aaa abc() ------>指向window abc()等同于window.abc(),此处abc省略了window </script>


解析上面实例

_window_._obj_.**aaa**.fn()  就是上面总结的实例2,this指向的是它的上一级的对象

此时假如window._obj_.**aaa**.fn被定义为到了一个abc上,调用abc()就相当于调用window.abc(), 实例1的总结是这样的:this指向调用它的对象,this的输出执行是abc方法被执行了,谁调用了?没错window对象调用了,所以this指向的就是window对象,此处非常的绕,请小伙伴们仔细思考哦!

4.构造函数环境中的this指向
----------------

哈哈哈,凡事都是有例外的,当我们在写构造函数或者在写面向对象的逻辑中的时候,this的指向情况又不一样了,请看实例:

#### 不含返回值的构造函数

<script> function fn() { this.num = 10 console.log(this) }

var obj = new fn(); console.log(obj) /* _ _* new在此时的作用 * * 1. 调用fn这个函数方法 * 2. 自动创建一个object对象 * 3. 把创建出来的对象与this进行绑定 * 4。如果构造函数没有返回值,隐式返回this对象 * */ </script>


![](https://img-hello-world.oss-cn-beijing.aliyuncs.com/imgs/e860461a251327b63ad9fc3a778be306.png)

上面的例子可以看出

this返回的东西和fn new出来的实例返回的东西是一样的,这个就是new的魅力

    哈哈哈,扯远了,主要是照顾一下刚入前端坑的小伙伴们,解释下new所做的事情

 _1. 调用fn这个函数方法_

 _2. 自动创建一个object对象_

 _3. 把创建出来的对象与this进行绑定_

 _4. 如果构造函数没有返回值,隐式返回this对象_

> **总结:构造函数中,this指向的是新创建出来的并且如果又属性值,进行绑定属性的新建对象**

ok, ok,anyway!我们再来个复杂的,同时也是更有意思的实例

<script> function fn() { this.num = 10 console.log(this) }

// 此处的num与fn中的num不是同一个东西哦 _ _fn.num = 20; // 每个方法都有自己的原型链,在原型链上定义一个num _ _fn.prototype.num = 30; // 在原型链上定义一个method的方法 _ _fn.prototype.method = function () { console.log(this); }

var prototype = fn.prototype; 就相当于var prototype = {} var method = prototype.method // 直接new fn _ new _fn() new fn().method() prototype.method() method()

</script>


![](https://img-hello-world.oss-cn-beijing.aliyuncs.com/imgs/50377d84db7b9fcb495da6c0c5d80f8e.png)

实例解析:

    1.**new** _fn_()  刚刚说了,this指向的是新创建出来的并且如果又属性值,进行绑定属性的新建对象 ,所以,此时this输出的值是{num: 10}

    2.**new** _fn_().method(), new的作用说了,先调用方法,然后创建一个对象,然后进行绑定this,最后输出;  
 **new** _fn_().method()就相当于{}.method(), 空对象{}绑定this下面的num = 10,所以此时this输出的值是{num: 10}

    3.函数方法都有原型链,原型链就是一个实例化了空对象{}进行扩展,并绑定原型链上的属性(可以理解为new了一个原型链)

 _prototype_.method()执行中,**var** _prototype_ \= _fn_.**prototype** **就相当于** **var** _prototype_ \= _{},原型链上含有很多私有方法和定义属性等等,_

 _比如我们定义的__fn_.**prototype**.**num** \= 30; _fn_.**prototype**.method \= **function** () { _console_.log(**this**); }

    所以此时this输出的值是{num: 30,......}

    4._method_()就相当于window._method(),很显然,全局环境中的this指向的是window, 全局window对象没有定义num,所以如果输出this.num就是undefined_

_这个实例很大程度依赖小伙伴们对js基础知识掌握的考验_

> **总结:在没有返回值的构造函数中,this指向的是new实例化创建出来的对象,对象中与函数this进行绑定(原型链相同原理,参考解析3)**

#### 有return返回值的构造函数

看实例

<script> function fn() { this.num = 10; return '' * _/\ _ * '' 空字符串 ---> 10 * [] 数组 --->undefined * {} 对象 ---> 10 * 123 数字 ---> 10 * function(){} 方法 ---> undefined * null ------> 10 * */ }

var obj = new fn() console.log(obj.num); </script>


从上述实例中,我们可以

> **总结:构造函数有返回值,当返回值是对象(type of进行类型查看)的时候,this指向的是实例化创建出来的对象(obj)**
> 
>  **当返回值不是对象的时候,保持原本规则不变,**
> 
>  **此处null是一个特例,返回null时,this指向的是实例化创建出来的对象(obj)**

5.箭头函数环境中的this指向 
-----------------

经常有前端刚入门的小伙伴,this指向发生偏移的时候,有些同伴就说,赶紧用ES6的箭头函数啊?这样this的指向就不会变了,至于为什么不会变,自己都说不出来的情况

接下来,让我们剖析一下箭头箭头函数

<script> var box1 = document.querySelector('.box1'); var box2 = document.querySelector('.box2');

box1.onclick = move1; box2.onclick = move2;

function move1() { setTimeout(function () { console.log(this); }, 1000) }

function move2() { setTimeout(() => { console.log(this); }, 1000) } </script>


![](https://img-hello-world.oss-cn-beijing.aliyuncs.com/imgs/583faf821bf2db83d1c6554d3580d4e8.png)

解析:为何在setTimeout中使用function(){}和箭头函数() => {}this的指向不一样呢?

          延迟调用的函数在setTimeout中是以入参的形式调用的

        1.  其实如果传入的入参提到外面进行定义,不就是如下面一样嘛,这是不难理解,fn()就是相当于window.fn(),所以this指向window对象

function fn() { console.log(this) }

function move1() { setTimeout(fn, 1000) }


    2.入参以箭头函数形式进行程序执行,this输出为调用的节点了,是不是如下面一样了

![](https://img-hello-world.oss-cn-beijing.aliyuncs.com/imgs/47400a2a1f489d2104cfad3030ac5ac4.png)![](https://img-hello-world.oss-cn-beijing.aliyuncs.com/imgs/84e39406822339687574f070578e545c.png)

<script> var obj = { fn: () => { console.log(this) } } // obj是不能形成独立作用域的 obj.fn() ------> this指向的是window对象 </script>

```

总结:箭头函数中的this指向官方文档指的是上下文环境中的this;

我们理解为:箭头函数本身是没有this和argument的,在箭头函数中调用this实际上就是在调用定义在上一层作用域的this指向;这里强调一下,指的是上一层作用域,因为对象是不能形成独立的作用域的。

this的指向常用的几种情况都在这里了,小伙伴们请认真阅读,仔细思考才能不被绕晕哦,待看后期广大借阅者理解程度,如果理解的不是很透彻,笔者可以考虑录个视频也无不可哦…………^ - ^

6.修改this指向

关于修改this的指向的方法,可以参考一下我的另一篇博文:Javascript千面之call、apply、bind区别和使用

收藏
评论区

相关推荐

一、手写源码之 Promise
版本一,构造函数 javascript function MyPromise(fn () {}) { // const this {} this.state 'pending' this.value undefined const resolve (value) { if (this.state
【干货】Javascript千面之变幻莫测的this指向
相信很多前端人对“this”的指向是很懵逼的,因为this的指向总是变幻莫测,在不同的调用环境中,它的指向总是各不相同。 在面试中,this也是经常考的必考题之一,很多前端老鸟经常会在this这里掉坑。 接下来,看笔者来一层一层的揭开this指向的面纱。 1.事件调用环境中的this指向 <div class"b
JS弹出对话框的三种方式
JS弹出对话框的三种方式 ------------ 我们用到了alert()方法、prompt()方法、prompt()方法,都是在网页有一个弹出框,那么就让我们探究一下他们之间的区别: 一、第一种:alert()方法 <html> <head> <title>编写html页面</title>
AJAX与Django
AJAX ---- #### 什么是AJAX? AJAX不是JavaScript的规范,它的缩写:Asynchronous JavaScript and XML,意思就是用JavaScript执行异步网络请求。提交任务之后,不原地等待,直接执行下一行代码,任务的返回通过回调机制。 局部刷新,不整体刷新,而是界面莫个地方局部刷新 #### AJAX原理
ES2020 中 Javascript 10 个你应该知道的新功能
好消息 - ES2020 新功能已经落地!这就意味着,现在对 ES2020 中 Javascript 的新增和改进要有一个完整的了解。让我们来看看都有哪些改变。 **1、BigInt** ------------ BigInt,Javascript 中最期待的新功能终于落地。它允许开发者在 JS 中使用更大的整数进行数据处理。 之前,Javas
GitHub标星13.1k,JavaScript基础知识必知(一)!前端入门必看!
JavaScript背景 ------------ Web前端有三层: * HTML:从语义的角度,描述页面**结构** * CSS:从审美的角度,描述**样式**(美化页面) * JavaScript:从交互的角度,描述**行为**(实现业务逻辑和页面控制) ### 发展历史 JavaScript诞生于**
JavaScript 内存详解 & 分析指南
![](https://oscimg.oschina.net/oscnet/74587263-715c-49e9-abc1-c4a7d37b2cef.gif "引导关注") 前言 == JavaScript 诞生于 1995 年,最初被设计用于网页内的表单验证。 这些年来 JavaScript 成长飞速,生态圈日益壮大,成为了最受程序员欢迎的开发语言之
JavaScript 的面向切面编程
我们都知道面向对象编程,或者至少听说过 JavaScript 领域的函数式编程,但是,你听说过面向切面编程吗? 我知道,它听起来像是《魔法战队》中某一集出现的东西。然而,AOP 是实际存在的。此外,虽然我们现在没有使用它,但它却可以被应用于我们日常会见到的一些用例中。 它最大的优势在于,你可以毫不费力的将 AOP 与 FP 或 OOP 结合使用,就像 J
JavaScript 非常重要的几个概念
JavaScript是一门比较复杂的语言。如果你是一名JavaScript开发人员,不管处于什么样的水平,都有必要了解JavaScript的基本概念。小编最近的工作涉及到JavaScript,于是本文就介绍了几个非常重要的 JavaScript 概念,但绝对不是说JavaScript 开发人员只需要知道这些就可以了。 01-变量赋值(值与引用) Java
JavaScript中的“ new”关键字是什么?
### 问题: _The `new` keyword in JavaScript can be quite confusing when it is first encountered, as people tend to think that JavaScript is not an object-oriented programming languag
JavaScript的入门简介
#### 什么是 JavaScript JavaScript,我们一般简称为 JS,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。 JavaScript 现在已经被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并支持面向对象、命令式和声明式风格。 HTML、CSS、JavaScript三者不同的功能:
Node.js 简单学习
明白 JavaScript 语言,你就会用 Node.js 了。最常见的运行 JavaScript 语言的地方就是用户的浏览器,几乎所有的浏览器上都有个 JavaScript 引擎,这个引擎负责运行在页面中嵌入的 JavaScript 代码。代码是在用户的浏览器上运行的,用户那头叫前端(Frontend),服务器这头叫后端(Backend)。Node.js
Node.js简介及如何学习Node.js
本文介绍Node.js的诞生史以及如何学习Node.js。 Node.js简史 --------- 从Node.js的命名上可以看到,Node.js的官方开发语言是JavaScript。之所以选择使用JavaScript,显然与JavaScript的开发人员多有关。总所周知,JavaScript是伴随着互联网的发展而火爆起来的,JavaScript也是前
Python 与 Javascript 之比较
最近由于工作的需要开始开发一些Python的东西,由于之前一直在使用Javascript,所以会不自觉的使用一些Javascript的概念,语法什么的,经常掉到坑里。我觉得对于从Javascript转到Python,有必要总结一下它们之间的差异。 ### **基本概念** [Python](https://www.oschina.net/action/G
TypeScript 教程
TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发。\[1\] TypeScript扩展了JavaScript的句法,所以任何现有的JavaScript程序可以不