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. 洛谷P1063能量项链(区间dp)

    题目描述: 给定一串序列x[],其中的每一个Xi看作看作一颗珠子,每个珠子包含两个参数,head和tail,前一颗的tail值是后一个的head值,珠子呈现环形(是一条项链),所以最后一颗的tail是 ...

  2. 新年上新!极光认证 Web SDK 首版上线

    新年伊始,极光开发者服务也抢先为各位开发者朋友带来了"新年大礼包",几款明星产品都悉数有不少更新: 极光认证 Web SDK 版本上线 相信不少小伙伴早已熟知极光认证这款产品,3秒 ...

  3. Mac上的屏幕截图不起作用该如何修复?

    屏幕截图是Mac提供的内置功能,很少有它不起作用.但是由于某些意外的设置或硬件问题,Mac上的屏幕截图有时无法正常工作,这里提供的是Mac上的屏幕截图不起作用该如何修复? 1.在Mac上启用屏幕快照快 ...

  4. linux版本的jdk1.8+hadoop2.9.2下载地址

    hadoop: 链接:https://pan.baidu.com/s/14AhhPYP8933tn-EfSX-i8Q 提取码:e90m jdk1.8: 链接:https://pan.baidu.com ...

  5. Uva10791 唯一分解定理模板

    唯一分解定理: Uva10791 题意: 输入整数n,要求至少两个正整数,使得他们的最小公倍数为n,且这些整数的和最小 解法: 首先假设我们知道了一系列数字a1,a2,a3……an,他们的LCM是n, ...

  6. CF718C Sasha and Array [线段树+矩阵]

    我们考虑线性代数上面的矩阵知识 啊呸,是基础数学 斐波那契的矩阵就不讲了 定义矩阵 \(f_x\) 是第 \(x\) 项的斐波那契矩阵 因为 \(f_i * f_j = f_{i+j}\) 然后又因为 ...

  7. SQL Server database – Error 3743

    Database mirroring must be removed before you drop SQL Server database – Error 3743 If you try to dr ...

  8. java - GC垃圾收集器详解(三)

    以前收集器的特点 年轻代和老年代是各自独立且连续的内存块 年轻代收集必须使用单个eden+S0+S1进行复制算法 老年代收集扫描整个老年代区域 都是以尽可能少而快速地执行GC为设计原则 G1是什么 G ...

  9. 【Unity|C#】基础篇(13)——特性(Attribute)

    [学习资料] <C#图解教程>(第24章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu. ...

  10. C++-蓝桥杯-入门训练

    Fibonacci数列,快速幂 #include <cstdio> ][];}; ,MOD=; Matrix A,B,O,I; Matrix Mul(Matrix A,Matrix B){ ...