原文作者:SegmentFault ——写bug

原文链接:https://segmentfault.com/a/1190000015438195

this的指向已经是一个老生常谈的问题,每逢面试都要去复习复习,近来巩固js的基础,决心彻底掌握这个知识点,一劳永逸。说明一下,为了不影响大家的思考过程,下面的代码都不会去注释答案,想知道答案,只需要去控制台执行一下。

四类场景逐一击破

首先,分析this的指向共有四种类型,在分析之前,我们首先带好两个锦囊:
1.函数被调用时(即运行时)才会确定该函数内this的指向。因为在函数中this与arguments是两个特殊的变量,在函数被调用时才会取得它们,而且搜索这两个变量时只会在活动对象范围里面去搜。(有关活动对象与变量对象的知识,请移步到js 中的活动对象 与 变量对象 什么区别?
2.要确定函数中this的指向,必须先找到该函数被调用的位置。

认准第一种“test()”形式

var a = 1
function test () {
console.log(this.a)
}
test()

直接不带任何引用形式去调用函数,则this会指向全局对象,因为没有其他影响去改变this,this默认就是指向全局对象(浏览器是window,Node中是global)的。这个结论是在非严格模式的情况下,严格模式下这个this其实是undefined的。

认准第二种“xxx.test()”形式

var a = 1
function test () {
console.log(this.a)
}
var obj = {
a: 2,
test
}
obj.test()

这种形式对比起第一种,很明显test()已经是名花有主的了!看清楚,是谁呼唤的test()?没错,就是obj,所以this的指向就不言而喻了。一句话,谁去调用这个函数的,这个函数中的this就绑定到谁身上。

var a = 1
function test () {
console.log(this.a)
}
var obj = {
a: 2,
test
}
var obj0 = {
a: 3,
obj
}
obj0.obj.test()

即使是这种串串烧的形式,结果也是一样的,test()中的this只对直属上司(直接调用者obj)负责。再来看一个综合点的例子:

var a = 1
function test () {
console.log(this.a)
}
var obj = {
a: 2,
test
}
var testCopy = obj.test
testCopy()

嗯,聪明的你一定想到,换了个名字就能骗到我了!?虽然经过了一波改名换姓,但本质上还不是obj.test()嘛!结果一定和上面一样!唔,请F12在控制台试试,竟然……其实这里并不需要去思考什么,按照我们的套路,我们就认函数调时的样子,有没有看到最后调用的时候跟第一种情况一毛一样?我再介绍一个场景大家一定不会觉得陌生:

var a = 1
function test () {
console.log(this.a)
}
var obj = {
a: 2,
test
}
setTimeout(obj.test)

你可以意淫一下setTimeout的本质,是不是相当于有一个setTimeout函数,接收两个参数:

function setTimeout (fn, time) {
// 这里干了一大波不可描述的事情,最后会去调一下你传进来的回调函数
fn()
}

看到怎样调用你传进来的函数了吗!?再想想我们第一种形式的标题认准第一种“test()”形式

认准第三种“test.call(xxx) / test.apply(xxx) / test.bind()”形式

看了上面两种形式之后,你可能会想,我非常讨厌上面那些矫情的扭扭捏捏的九曲十八弯的调用方式,让人毫无安全感,我要我自己指定this的指向,我要胜天半子!没问题,我的代码我做主:

var a = 1
function test () {
console.log(this.a)
}
var obj = {
a: 2,
test
}
var testCopy = obj.test
testCopy.call(obj)

可以看到,我们通过call(apply跟call的区别只是传参,作用是一样的,bind有点区别,bind能让我们的函数延迟执行,apply与call调用就执行,所以bind这样的形式我们也称为函数柯里化,这些就不是我们这里要说的啦)来调用testCopy,并且传入了你想要this指向的上下文,那么this就会乖乖按照你的指示行事啦。看到这里,我们也可以想象第一、二种形式其实可以转化成call/apply的形式,有一篇比较棒的文章描述了这样的思考过程,大家也可以看看this 的值到底是什么?一次说清楚

认准第四种“new test()”形式

终于到了最后一种形式了,这种形式比较好认,因为有标志性的new:

var a = 1
function test (a) {
this.a = a
}
var b = new test(2)
console.log(b.a)

new这个操作符其实是new了一个新对象出来,而被new的test我们称为构造函数,我们可以在这个构造函数里定义一下将要到来的新对象的一些属性。那么在构造函数里,我们怎样去描述这个还未出生的新对象呢?没错,就是用this。所以构造函数里的this指的就是将要被new出来的新对象。

One more thing

感谢大家看到这里,但还要说最后一种形式。等等,不是说好的只有四种形式吗!稍安勿躁,正常套路下确实只有上面四种,但是有个东西别忘了,就是大家最喜欢的箭头函数。

var a = 1
var test = () => {
console.log(this.a)
}
var obj = {
a: 2,
test
}
obj.test()

来,往上翻一下我们的第一个锦囊,“函数被调用时(即运行时)才会确定该函数内this的指向。”现在函数这两个字要加个词修饰一下,变成普通函数(非箭头函数)才能区别于箭头函数。箭头函数中的this在函数定义的时候就已经确定,它this指向的是它的外层作用域this的指向。

最后

我们最后还要说:“到此为止,真的没有了。”
希望看完这篇文章之后,再有人问this指向的问题,你可以嘴角微微上扬,冷笑一声:“不要再问我this的指向问题了。”
扬长而去。

JavaScript this 的指向问题的更多相关文章

  1. 图解javascript中this指向

    JavaScript 是一种脚本语言,支持函数式编程.闭包.基于原型的继承等高级功能.JavaScript一开始看起来感觉会很容易入门,但是随着使用的深入,你会发JavaScript其实很难掌握,有些 ...

  2. javascript的this指向

    JavaScript 是一种脚本语言,支持函数式编程.闭包.基于原型的继承等高级功能.JavaScript一开始看起来感觉会很容易入门,但是随着使用的深入,你会发现JavaScript其实很难掌握,有 ...

  3. JavaScript对象的指向问题

    JavaScript对象的指向问题 标签(空格分隔): JavaScript 对象 在接触了JavaScript之后,我们常听到一句话就是一切皆对象,意思是说除了object以外,JavaScript ...

  4. JavaScript对象this指向(普通键this指向 非指向函数的键)

    1.结论 JavaScript对象普通键(非指向函数的键)this指向是window. 2.示例 <!DOCTYPE html> <html lang="zh"& ...

  5. 图解javascript的this指向

    图解javascript的this指向 作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... 以下就只有两张图,请放心食用!! #简版th ...

  6. javascript this关键字指向详解

    在之前写代码的经历中,常常试过写着写着this就莫名其妙的不知道指向到哪里去了.今天看了曾探的javascript设计模式,里面特别谈到了this在不同情况下指代的对象,非常有意思. this指代的情 ...

  7. javascript this指针指向?

    前言 理解javascript的指针就需要先了解js的执行环境和作用域!执行环境的定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境都有一个与之关联的变量对象,环境中定义的所有的变 ...

  8. javascript中this指向问题

    本文参考http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html this是JavaScript的一个关 ...

  9. 从零开始学习前端JAVASCRIPT — JavaScript中this指向的四种情况

    JavaScript中this的四种情况(非严格模式) 1.当this所在函数是事件处理函数时,this指向事件源.2.当this所在函数是构造函数时,this指向new出来的对象.3.this所在函 ...

  10. JavaScript 的 this 指向问题深度解析

    与我们常见的很多语言不同,JavaScript 函数中的 this 指向并不是在函数定义的时候确定的,而是在调用的时候确定的.换句话说,函数的调用方式决定了 this 指向. JavaScript 中 ...

随机推荐

  1. os删除文件或者文件夹

    最近在使用os.remove删除目录时报错了,这里记录下os操作文件或者目录的常用方法 递归删除文件或文件夹 import shutil shutil.rmtree() 文件的重命名 os.renam ...

  2. [转帖]Red Hat K8s 关键人物 Grant Shipley 跳槽到 VMware

    Red Hat K8s 关键人物 Grant Shipley 跳槽到 VMware   https://news.cnblogs.com/n/641944/ 这四小时的工作效率 太无敌了.. 投递人  ...

  3. Mybatis应用入门

    mybatis简介 Mybatis是在jdbc的基础之上封装而成的持久层框架. Mybatis是一个ORM框架.ORM(object relational mapping):对象关系型映射 搭建myb ...

  4. 【MIT 6.824 】分布式系统 课程笔记(二)Lecture 03 : GFS

    Lecture 03 : GFS 一.一致性 1, 弱一致性 可能会读到旧数据 2, 强一致性 读到的数据都是最新的 3, 一致性比较 强一致性对于app的写方便, 但是性能差 弱一致性有良好的性能, ...

  5. WEB学习路线2019完整版(附视频教程+网盘下载地址)

    WEB学习路线2019完整版(附视频教程+网盘下载地址).适合初学者的最新WEB前端学习路线汇总! 在当下来说web前端开发工程师可谓是高福利.高薪水的职业了.所以现在学习web前端开发的技术人员也是 ...

  6. 【SCALA】3、模拟电路

    Simulation package demo17 abstract class Simulation { type Action = () => Unit case class WorkIte ...

  7. io.ByteIO和open操作二进制流的区别(转)

    转自Stack Overflow:https://stackoverflow.com/questions/42800250/difference-between-open-and-io-bytesio ...

  8. raspbian buster阿里镜像

    配置以前,谨记教诲,先去看一下是否有对应版本的源,如下所示,阿里这更新速度...确实挺赶潮流的 最后就是正常更新即可,这样就快多了,要不然用国外的源,还不知道要等到猴年马月的. sudo apt-ge ...

  9. Java 之 字节输入流 [InputStream]

    一.字节输入流 java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中. 它定义了字节输入流的基本共性功能方法. 共性方法: public void c ...

  10. Android状态栏和导航栏

    1.隐藏状态栏或导航栏 View decordView = getWindow().getDecorView(); /*SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI ...