this 的值到底是什么?一次说清楚

杭州饥人谷教育科技有限公司 CTO
1,071 人赞同了该文章

你可能遇到过这样的 JS 面试题:

var obj = {
foo: function(){
console.log(this)
}
} var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window

请解释最后两行函数的值为什么不一样。

-------

初学者关于 this 的理解一直很模糊。今天这篇文章就要一次讲清楚了。

而且这个解释,你在别的地方看不到。看懂这篇文章,所有关于 this 的面试题,都是小菜。

有用请点赞。

函数调用

首先需要从函数的调用开始讲起。

JS(ES5)里面有三种函数调用形式:

func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2) // 先不讲 apply

一般,初学者都知道前两种形式,而且认为前两种形式「优于」第三种形式。

从看到这篇文章起,你一定要记住,第三种调用形式,才是正常调用形式:

func.call(context, p1, p2)

其他两种都是语法糖,可以等价地变为 call 形式:

func(p1, p2) 等价于
func.call(undefined, p1, p2) obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)

请记下来。(我们称此代码为「转换代码」,方便下文引用)

至此我们的函数调用只有一种形式:

func.call(context, p1, p2)

这样,this 就好解释了

this,就是上面代码中的 context。就这么简单。

this 是你 call 一个函数时传的 context,由于你从来不用 call 形式的函数调用,所以你一直不知道。

先看 func(p1, p2) 中的 this 如何确定:

当你写下面代码时

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

用「转换代码」把它转化一下,得到

function func(){
console.log(this)
} func.call(undefined) // 可以简写为 func.call()

按理说打印出来的 this 应该就是 undefined 了吧,但是浏览器里有一条规则:

如果你传的 context 是 null 或 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

因此上面的打印结果是 window。

如果你希望这里的 this 不是 window,很简单:

func.call(obj) // 那么里面的 this 就是 obj 对象了

再看 obj.child.method(p1, p2) 的 this 如何确定

var obj = {
foo: function(){
console.log(this)
}
} obj.foo()

按照「转换代码」,我们将 obj.foo() 转换为

obj.foo.call(obj)

好了,this 就是 obj。搞定。

回到题目:

var obj = {
foo: function(){
console.log(this)
}
} var bar = obj.foo
obj.foo() // 转换为 obj.foo.call(obj),this 就是 obj
bar()
// 转换为 bar.call()
// 由于没有传 context
// 所以 this 就是 undefined
// 最后浏览器给你一个默认的 this —— window 对象

[ ] 语法

function fn (){ console.log(this) }
var arr = [fn, fn2]
arr[0]() // 这里面的 this 又是什么呢?

我们可以把 arr[0]( ) 想象为arr.0( ),虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2) 对应上了,于是就可以愉快的转换了:

        arr[0]()
假想为 arr.0()
然后转换为 arr.0.call(arr)
那么里面的 this 就是 arr 了 :)

箭头函数

我不明白为什么需要讨论箭头函数,实际上箭头函数里并没有 this,如果你在箭头函数里看到 this,你直接把它当作箭头函数外面的 this 即可。外面的 this 是什么,箭头函数里面的 this 就还是什么,因为箭头函数本身不支持 this。

有人说「箭头函数里面的 this 指向箭头函数外面的 this」,这很傻,因为箭头函数内外 this 就是同一个东西,并不存在什么指向不指向。

总结

  1. this 就是你 call 一个函数时,传入的第一个参数。(请务必背下来「this 就是 call 的第一个参数」)
  2. 如果你的函数调用形式不是 call 形式,请按照「转换代码」将其转换为 call 形式。

以后你遇到所有跟 this 有关的笔试题,都不会有疑问了。

完。

更多精彩教程,请看我的首页加 QQ 群。

P.S.

  1. 使用 new 时,情况又不一样,可以看另一篇文章《JS 的 new 到底是干什么的?》。
  2. 后续篇《你怎么还没搞懂 this? - 知乎专栏
  3. 有人说你怎么不讲 strict mode 呢,strict mode 也会影响 this 呀。我认为 strict mode 只是影响了 context 的默认值而已,你看懂此文稍微看看 strict mode 就懂了。我只讲最重要的内容。
  4. 有人问怎么不讲 bind 对 this 的影响。那是因为 bind 本质就是自动在你调用一个函数的时候,把 context 换掉而已,你看看 bind 的 polyfill 就知道,其核心依然是 apply(跟 call 差不多)。
  5. 有人问箭头函数,箭头函数其实跟 this 没关系,所以如果你在箭头函数里面看到 this ,就当作是它外面的函数的 this 即可。

this 的值到底是什么?一次说清楚的更多相关文章

  1. C++ 赋值构造函数的返回值到底有什么用?且返回值是否为引用类型有什么区别吗?

    首先定义类Person class Person{ public: string name; Person()=default; //默认构造函数 Person(string nam):name(na ...

  2. Java的Object.hashCode()的返回值到底是不是对象内存地址?

    关于这个问题,查阅了网上的资料,发现证明过程太繁琐,这里我用了反证法. java.lang.Object.hashCode()的返回值到底是不是对象内存地址? hashCode契约 说到这个问题,大家 ...

  3. JS中this的值到底为何?

    之前很久的时间,因为研究不深,对于this的值一直模模糊糊,不是很清楚,最近有空做了一些研究,终于彻底弄明白了this到底为何物. 首先, 先抛出一个定论:”在Javascript中,this关键字永 ...

  4. 彻底理解this 的值到底是什么?

    作者:方应杭 来源:知乎 你可能遇到过这样的 JS 面试题: var obj = { foo: function(){ console.log(this) } } var bar = obj.foo ...

  5. 转载自知乎大神---this 的值到底是什么?一次说清楚

    你可能遇到过这样的 JS 面试题: var obj = { foo: function(){ console.log(this) } } var bar = obj.foo obj.foo() // ...

  6. input框中的value值到底是什么

    value 属性为 input 元素设定值. 对于不同的输入类型,value 属性的用法也不同: type="button", "reset", "s ...

  7. GET和POST可传递的值到底有多大?

    前日,看到这个问题了. 没有深入了解.我的常识里面get最大传递的值为256b,post 是2M.这是很久以前不知在哪看到的.于是又百度一下.看到两篇文章装过来看看: 浅谈 HTTP中Get与Post ...

  8. this 的值到底是什么?

    你可能遇到过这样的 JS 面试题: var obj = { foo: function(){ console.log(this) } } var bar = obj.foo obj.foo() // ...

  9. cmp的值到底是0还是1还是-1的问题

    返回值不局限于这三个数返回负数,表示第一个参数小于第二个参数返回整数,表示第一个参数大于第二个参数返回0,表示他们相等

随机推荐

  1. 硬件知识整理part2--电阻在反馈网络中的应用

    学而不厌,诲人不倦,不知老之将至.--孔子 电阻作为电路中基本的元器件之一.在电路设计中,我们有时会使用欧姆定律来大致估计一下电阻值的大小,但是大多时候我们是不用去过多考虑电阻值的大小,像我这样大方的 ...

  2. .Net Core程序最终只产生1个exe

    1.用VS新建一个.Net Core 命令行程序,程序里面写什么都行,我的是这样的 2.打开程序包控制台,或者使用PowerShell等可以执行dotnet命令的工具,把你的.Net 程序发布(编译) ...

  3. Hadoop学习之路(8)Yarn资源调度系统详解

    文章目录 1.Yarn介绍 2.Yarn架构 2.1 .ResourceManager 2.2 .ApplicationMaster 2.3 .NodeManager 2.4 .Container 2 ...

  4. 剑指offer-面试题42-连续子数组的最大和-动态规划

    /*题目; 输入一个整形数组(可能有正数和负数),求数组中连续子数组(最少有一个元素)的最大和. 要求时间复杂度为O(n). 先输入数组的格式,再依次输入数组的值.*//*思路: f(i) = pDa ...

  5. 如何在IDEA中使用GitHub

    第一步:在GitHub网站中注册自己的账号 地址:https://github.com/ 第二步:下载Git客户端 地址:https://git-scm.com/ 第三步:在GitBash中配置用户名 ...

  6. kvm虚拟化安装及虚拟机创建

    1.开启虚拟化(VMware虚拟机为例),安装centos7操作系统. 2.查看CPU是否开启虚拟化 #zgrep  "vmx" /proc/cpuinfo 3.配置yum源,通过 ...

  7. gulp常用插件之gulp-rev-format使用

    更多gulp常用插件使用请访问:gulp常用插件汇总 gulp-rev-format这是一款提供静态资产的哈希格式选项(前缀,后缀,最后扩展名). 更多使用文档请点击访问gulp-rev-format ...

  8. PP: Soft-DTW: a differentiable loss function for time-series

    Problem: new loss Label: new loss; Abstract: A differentiable learning loss; Introduction: supervise ...

  9. Java基于SSM在线学习系统设计与实现

                 Spring+SpringMVC+MyBatis+Bootstrap+Vue开发在线学习系统 本课题的主要内容是开发基于Java EE的在线学习平台,使用MVC经典开发模式. ...

  10. javascripts的this

    前言 javascript 的 this 指向主要是依赖上下文对象决定,箭头函数例外. 默认绑定 在全局作用域下调用函数,函数的this默认指向window. 注意1:严格模式下,默认指向undefi ...