V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
waibunleung
V2EX  ›  JavaScript

萌新关于 js 箭头函数 this 的问题,头好晕

  •  
  •   waibunleung · 2019-02-28 16:53:19 +08:00 · 4844 次点击
    这是一个创建于 1855 天前的主题,其中的信息可能已经有所发展或是发生改变。

    萌新想问一个关于箭头函数 this 的问题,看了好多例子还是理解不了。

    代码:

    var obj = {
        a : 12,
        b : () => {console.log(this)},
        c : {
            d: () => {console.log(this)},
            f: function(){console.log(this)}
        },
        e : function(){console.log(this)}
    }
    
    obj.b();
    obj.c.d();
    obj.c.f();
    obj.e();
    

    问题:obj.c.f();处打印出来的为什么是 window 对象而不是 obj 对象 ?

    第 1 条附言  ·  2019-02-28 17:41:53 +08:00
    问题问错了,是 obj.c.d();处为什么是 window 而不是 obj,之所以觉得是 obj 是因为搜索引擎的时候告诉我 箭头函数是没有 this 的,箭头函数里面的 this 永远指向父级上下文的 this
    41 条回复    2019-03-01 18:31:50 +08:00
    stillsilly
        1
    stillsilly  
       2019-02-28 16:57:55 +08:00
    obj.c.f() 打印的是 c
    wxsm
        2
    wxsm  
       2019-02-28 16:58:31 +08:00
    建议你 Google 一下箭头函数与普通函数的区别。
    stillsilly
        3
    stillsilly  
       2019-02-28 16:58:47 +08:00
    是 obj.c
    不是你说的 window,也不是 obj
    Exia
        4
    Exia  
       2019-02-28 16:59:55 +08:00
    obj.c.f() 应该是 c 吧
    dmjob2015222
        5
    dmjob2015222  
       2019-02-28 17:00:34 +08:00
    貌似是打印 obj.c 吧,箭头就是一个语法,看看介绍就明白了
    roscoecheung1993
        6
    roscoecheung1993  
       2019-02-28 17:02:15 +08:00
    obj.c.f() 打印的是 obj.c 啊...

    var obj = {
    a : 12,
    b : () => {console.log(this)},
    c : {
    d: () => {console.log(this)},
    f: function(){console.log(this); return this;}
    },
    e : function(){console.log(this)}
    }

    obj.b();
    obj.c.d();
    console.log(obj.c.f() === obj.c);
    obj.e();
    roscoecheung1993
        7
    roscoecheung1993  
       2019-02-28 17:05:05 +08:00
    把这坨代码粘贴到 babel 的 playground 里面,你就懂了
    Mexion
        8
    Mexion  
       2019-02-28 17:07:03 +08:00
    楼上正解,c 是普通函数,谁调用它 this 就是谁,所以打印的是 c
    而箭头函数中的 this 始终指向自身外的第一个 this,也就是始终等于调用它的函数的 this
    Mexion
        9
    Mexion  
       2019-02-28 17:08:16 +08:00
    @Mexion 打错了,f 是普通函数,谁调用它 this 就是谁,所以打印的是 c
    myl0204
        10
    myl0204  
       2019-02-28 17:10:22 +08:00
    问题:obj.c.d();处打印出来的为什么是 window 对象而不是 obj 对象 ?
    binux
        11
    binux  
       2019-02-28 17:14:37 +08:00 via Android
    你想问的是 obj.c.d() 吧
    so1n
        12
    so1n  
       2019-02-28 17:18:27 +08:00
    rabbbit
        13
    rabbbit  
       2019-02-28 17:18:43 +08:00
    @myl0204
    1 this 是 function 的关键字,而箭头函数没有这个关键字
    2 关键字其实本质上就是函数作用域里的一个变量

    因为箭头函数里没有 this, 所以只能向上一层作用域里去找这个变量, 也就是 windows 了
    Chingim
        14
    Chingim  
       2019-02-28 17:28:10 +08:00
    @myl0204 因为 d() 函数的 this 指向它外层的 this

    问题变成了 c 的 this 是什么?

    如果 c 是一个函数, 那调用 obj.c() , c 里的 this 肯定是 obj.
    但是 c 不是一个函数, 它没有 this, obj 也没有 this

    所以 d() 的外层的 this 就是 window 对象
    waibunleung
        15
    waibunleung  
    OP
       2019-02-28 17:39:06 +08:00
    @wxsm 这种东西我没 google 过我是不会上来问的,建议你下次不要提这种建议
    waibunleung
        16
    waibunleung  
    OP
       2019-02-28 17:39:42 +08:00
    @binux 对的对的
    dmjob2015222
        17
    dmjob2015222  
       2019-02-28 18:00:15 +08:00
    @waibunleung 你这 google 能力不行啊,这种问题 google 不出来???
    almost00
        18
    almost00  
       2019-02-28 18:45:59 +08:00
    // 你可以拷贝代码 到 jsbin 点 com 运行下试试
    // 你不知道 this 是因为 你不用 call
    // this 是 fn.call(this) 的 第一个参数
    // 这是历史原因,js 作者创造 js 的一个需求就是 要求它像 java
    // 所以你会看到很多 java 的面向对象写法,但是如果你是一个 js 程序员 就不会那样用
    // 比如 js 你声明数组 var arr = [ ] java 就会这样 var arr = new Array( )
    // 比如 var o = {} java 就要 var o = new Object()

    // 默认当前环境是浏览器且没用 严格模式
    function a(){console.log(this);}
    a(); // window
    // a() 等价于 a.call(undefined) 所以是 window
    a.call(1); // 1
    var o = {};
    a.call(o); // o

    var o2 = {};
    o2.a = a;
    o2.a(); // o2

    console.log('----------华丽的分割线----------')
    var b = ()=>{console.log(this);}
    b();
    b.call(1);
    // ES6 的箭头函数干掉了 this 和 arguments 参数
    // 所以就算你强行指定 this 也会指向外层的 this
    a4854857
        19
    a4854857  
       2019-02-28 19:05:33 +08:00
    this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数根本没有自己的 this,导致内部的 this 就是外层代码块的 this。正是因为它没有 this,所以也就不能用作构造函数。

    所以,箭头函数转成 ES5 的代码如下。

    // ES6
    function foo() {
    setTimeout(() => {
    console.log('id:', this.id);
    }, 100);
    }

    // ES5
    function foo() {
    var _this = this;

    setTimeout(function () {
    console.log('id:', _this.id);
    }, 100);
    }

    上面代码中,转换后的 ES5 版本清楚地说明了,箭头函数里面根本没有自己的 this,而是引用外层的 this。

    http://es6.ruanyifeng.com/#docs/function#%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0
    DOLLOR
        20
    DOLLOR  
       2019-02-28 19:15:14 +08:00 via iPhone
    箭头函数并没有 this,如果你在箭头函数里写 this,实际上这个 this 属于它外层的普通函数,而不属于这个箭头函数。
    SilentDepth
        21
    SilentDepth  
       2019-02-28 19:48:41 +08:00
    Lexical this,是说这段代码的在文法(源码)所在位置是什么 this 就是什么 this。它不是没有 this,只是无法手动 bind。
    loy6491
        22
    loy6491  
       2019-02-28 20:09:21 +08:00 via iPhone   ❤️ 1
    父级指的是父级函数,不是父级对象。d 那个函数的外层没有其他函数了,直接到顶层就完事了
    waibunleung
        23
    waibunleung  
    OP
       2019-02-28 21:03:13 +08:00
    @dmjob2015222 你来你来,我发现来这里的有一部分回复不将焦点集中在问题上面,而是关注别的问题,解释一下又能怎样,我也不至于是伸手党啊,每个回复我都有好好研究,我连 MDN 都翻了,你跟我说这些对我完全没有意义,以前这种都是不回的,可是见多了真的忍不住....就像是我语气重了一点,依然跟你说多多担待一样,其实也没差~
    auroraccc
        24
    auroraccc  
       2019-02-28 21:17:29 +08:00
    你要理解对象本身是没有 this 的,箭头函数不会创建自己的 this, 它只会从自己的作用域链的上一层继承 this。那执行 obj.c.d()的时候,由于 c 和 obj 都没有 this,再沿着作用域链往上,console 出来的 this 就是 windows 了
    waibunleung
        25
    waibunleung  
    OP
       2019-02-28 21:17:54 +08:00
    @loy6491 听你这么说,我将代码转成 es5 试了一下,这样是能解释通的,暂时结案吧。欢迎反驳这个回复😯~
    waibunleung
        26
    waibunleung  
    OP
       2019-02-28 21:19:12 +08:00
    @auroraccc 你的解释跟 @loy6491 差不多,我大概理解了,感谢
    auroraccc
        27
    auroraccc  
       2019-02-28 21:21:47 +08:00
    打错,window
    waibunleung
        28
    waibunleung  
    OP
       2019-02-28 21:23:25 +08:00
    @auroraccc 就喜欢你这种严谨的小机灵鬼
    autoxbc
        29
    autoxbc  
       2019-02-28 21:46:26 +08:00
    注意箭头函数中 this 的上溯,不是沿着作用域链,不是函数调用链,不是成员访问链,是执行上下文
    waibunleung
        30
    waibunleung  
    OP
       2019-02-28 21:52:50 +08:00
    @autoxbc 我要怎么理解你这句话呢?能否详细一些...谢谢你的提点
    waibunleung
        31
    waibunleung  
    OP
       2019-02-28 21:55:27 +08:00
    @autoxbc 而且我翻 MDN 文档,里面有一句话:“箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。"
    那这不是沿着作用域链来上溯吗....?
    rabbbit
        32
    rabbbit  
       2019-02-28 22:13:05 +08:00
    暂时不要去抠作用域 执行上下文这些本来就容易混淆的东西,知道 this 在不同场合指向哪就行
    每代 Es 标准都在变,不一定过一段时间又加了啥新东西

    例如 Es3 的 scope chain(作用域),到 Es5 里就没这个东西了,取而代之的是 Declarative Environment Record(声明式环境记录)
    http://ecma-international.org/ecma-262/5.1/#sec-D

    还有什么 AO/VO ,es5 里根本就,没这些东西
    autoxbc
        33
    autoxbc  
       2019-02-28 23:05:04 +08:00
    @waibunleung #31 抱歉,是我想当然了,是作用域
    xieranmaya
        34
    xieranmaya  
       2019-02-28 23:08:49 +08:00
    看箭头函数里的 this 时,你就当在看 if 语句里的 this,这么说懂了吧
    posebear1990
        35
    posebear1990  
       2019-03-01 03:37:10 +08:00 via iPhone
    简单的说就是函数中 this 的值,如果显式绑定了的话(比如用 bind/call,或者箭头函数),就是绑定的值。否则没有显式绑定,那 this 等于调用对象。
    rodjl
        36
    rodjl  
       2019-03-01 08:45:37 +08:00 via iPhone
    https://github.com/mqyqingfeng/Blog/issues/7 this 这里面解释得挺好的
    a4854857
        37
    a4854857  
       2019-03-01 10:23:04 +08:00
    在查找楼主的问题过程中查漏补缺了不少东西...感谢楼主...
    amumu666
        38
    amumu666  
       2019-03-01 10:34:44 +08:00
    两句话,函数 this 指向运行时的作用域,箭头函数 this 指向定义时的作用域。
    wxsm
        39
    wxsm  
       2019-03-01 11:41:26 +08:00
    @waibunleung 绝大多数人在提问之前不会搜索,但是搜索确实可以解决这种问题。我相信我的回答在大部分时候是能提供帮助的。当然,这些题外话就没必要深入讨论了。
    waibunleung
        40
    waibunleung  
    OP
       2019-03-01 18:30:38 +08:00
    @autoxbc 我在网上找到了很多想当然的例子和文章,真心希望大家可以严谨求证,不然就真的很悲哀
    waibunleung
        41
    waibunleung  
    OP
       2019-03-01 18:31:50 +08:00
    @wxsm 你说的对,然而你的答案呢?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1157 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 18:30 · PVG 02:30 · LAX 11:30 · JFK 14:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.