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

请教一个关于 this 的问题

  •  
  •   fourstring 2019-01-30 18:34:13 +08:00 5355 次点击
    这是一个创建于 2454 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下:

    var name="Window"; var object={ name:"object", getName:function (){ var a= function(){ return this.name; }; console.log(a()); return a; } } object.getName()(); 

    运行结果:

    Window Window 

    我的问题在于console.log(a());这一行。return a后,是在全局作用域中执行的返回的匿名函数,所以this的值是window。但是我在getName函数中直接执行a()的话,根据this的定义“ this 指向函数运行时的执行环境对象”,而每个函数执行时都会创建一个自己的执行环境,那么a()执行时this的值应该是getName函数的环境对象,这样 a 函数内对this.name的引用就应该得到undefined。请问我的理解有什么问题呢?谢谢:)

    40 条回复    2019-03-01 09:03:15 +08:00
    4ark
        1
    4ark  
       2019-01-30 18:50:23 +08:00 via Android   1
    t/529532 看看吧,另外贴代码的时候能不能带有格式啊,看着真糟心!
    4ark
        2
    4ark  
       2019-01-30 18:54:27 +08:00 via Android   1
    就你这段代码而言,a 并不属于任何对象,也没有通过 call 等方法调用,所有这样的函数 this 默认指向 Window,如果是严格默认下,就是 undefinde。
    4ark
        3
    4ark  
       2019-01-30 18:56:13 +08:00 via Android
    收回我说的第一句话,才发现我的文章代码也没有格式,为什么 V2EX 会这样??
    Trim21
        4
    Trim21  
       2019-01-30 19:08:06 +08:00 via Android
    @4ark 其实我这边手机看着两个帖子里的代码都有格式…
    4ark
       
    4ark  
       2019-01-30 19:09:32 +08:00 via Android
    @Trim21 因为我用的是 v2er 的客户端,才发现这个问题,现在已卸载。
    4ark
        6
    4ark  
       2019-01-30 19:12:19 +08:00 via Android
    @Trim21 想问一下您手机端用的啥客户端?
    Trim21
        7
    Trim21  
       2019-01-30 19:13:33 +08:00 via Android
    @4ark chrome
    4ark
        8
    4ark  
       2019-01-30 19:14:31 +08:00 via Android
    @Trim21 使用起来体验没有客户端好,虽然不会有显示问题
    Nitroethane
        9
    Nitroethane  
       2019-01-30 19:15:59 +08:00 via Android
    @4ark 最新的 beta 版已经实现了代码格式化功能:
    https://imgur.com/a/0uF0P3M
    4ark
        10
    4ark  
       2019-01-30 19:20:31 +08:00 via Android
    @Nitroethane 求下载链接,感激不尽!
    4ark
        11
    4ark  
       2019-01-30 19:28:01 +08:00 via Android
    @Nitroethane 已在 play 商店搜到
    rabbbit
        12
    rabbbit  
       2019-01-30 19:41:38 +08:00   3
    this 是什么取决于如何调用
    本文中 a 函数的调用者是 a() 而非外层的 object.getName()

    当 a 函数被作为变量调用时, 会将内部方 GetBase(ref). ImplicitThisValue 返回的值作为 thisArg(通常是 undefined)
    http://ecma-international.org/ecma-262/5.1/#sec-11.2.3

    在进入函数时
    1 如果是严格模式,把 this 指向 thisArg
    2 如果 thisArg 为 undefined,则将 this 指向 window
    http://ecma-international.org/ecma-262/5.1/#sec-10.4.3

    总之一句话,作为变量调用的函数里边的 this 都指向 window
    OSF2E
        13
    OSF2E  
       2019-01-30 19:54:13 +08:00
    《 Javascript 高级程序设计(第三版)》( P73 ) 4.2 执行环境与作用域
    rabbbit
        14
    rabbbit  
       2019-01-30 19:55:32 +08:00   1
    另外补充下
    ImplicitThisValue 通常返回 undefined, with 除外
    fourstring
        15
    fourstring  
    OP
       2019-01-30 19:59:42 +08:00
    @OSF2E #13 谢谢,我看的就是这本书。我之所以会认为 a()的结果是"object",就是因为这本书 5.5.4 里说 this “引用的是函数执行的环境对象”,然后 4.2 节说“每个函数都有自己的执行环境”,我看了下面别的 v 友回复以后,觉得应该是我对“执行环境”理解有误,但是这本书里确实没有明确讲解函数的执行环境究竟是什么(也就是不是词法环境)。
    fleam
        16
    fleam  
       2019-01-30 20:01:49 +08:00 via Android
    我总结的就是相关性就近原则,菜鸟飞过。。。
    X37B
        17
    X37B  
       2019-01-30 20:38:06 +08:00   1
    this(4 个规则):函数位置上的调用形式
    默认绑定
    独立调用 fn()
    this -->window "use strict" this --> undefined

    隐式绑定
    对象.对象.fn()
    this -->最近的调用者
    隐式丢失?
    使用隐式调用的形式进行传参 赋值;使用其他形式进行调用 this 的指向发生改变
    解决隐式丢失?
    硬绑定 bind 返回一个新的函数

    显示绑定
    apply
    call
    立即调用
    this --> 绑定的对象

    构造绑定
    new fn()
    this --> 构造出来的实例对象

    当一行代码出现多个绑定,this 绑定优先级:new --> 显示 --> 隐式 --> 默认
    shintendo
        18
    shintendo  
       2019-01-30 20:42:26 +08:00   1
    其实很简单,你的 this 在哪个函数里面?在 a 里面,那么 this 的值只取决于 a 被谁调用,跟其它什么 getName 没有何关系。
    而所谓的 a 被谁调用,指的是“调用者.a()”这种调用,不是说 a()写在 getName 里面就是被 getName 调用了这种意思。
    fourstring
        19
    fourstring  
    OP
       2019-01-30 20:58:04 +08:00
    @X37B #17 请问知道这样的知识什么地方或者书能看到呢
    secondwtq
        20
    secondwtq  
       2019-01-30 21:17:41 +08:00   1
    @fourstring 犀牛书应该是这样理解语言设计者的意图的:词法作用域和 this 都属于函数运行环境 /上下文,现在的实践中,一般词法作用域是写代码时固定的,this 允许用户在运行时动态指定上下文,并且这东西和函数调用语法关联,因此也是一种“环境”

    实际上现在的 OO 语言对于 this 的实现基本就是函数参数,也就是非要说的话所有参数都是“环境”的一部分 ... 不过 OO 的语义上 this 确实有环境的意思

    词法作用域环境也可以动态绑定,用 eval 或者 with 就行,只不过现在没人用了
    OSF2E
        21
    OSF2E  
       2019-01-30 21:19:07 +08:00
    @fourstring

    首先,这部分内容要结合第 7 章的内容来理解。

    另外,这一节有一个关键概念是执行环境的变量对象( variable object ),每一个函数的执行环境( execution context )中所定义的变量和函数都保存在这个对象中。而 web 浏览器中最外层的执行环境(的变量对象)被认为是 window 对象,因此所有全局变量和函数都是作为 window 对象的属性和方法创建的。

    因此,你的代码中的匿名函数 a 在定义之初便自动“挂靠”到全局执行环境中的(解析器就是这么解析的,没有为什么),即该匿名函数是 window 对象的方法,所以 this 自然指向 window。

    可以使用 bind 方法改变该匿名函数“挂靠”的变量对象,代码如下:

    var a = function(){}.bind(this);
    rabbbit
        22
    rabbbit  
       2019-01-30 22:02:26 +08:00
    @OSF2E

    能深入讲讲"匿名函数 a 在定义之初便自动“挂靠”到全局执行环境中的"这句吗?
    没看懂意思,有哪本书提到过吗?
    rabbbit
        23
    rabbbit  
       2019-01-30 22:09:09 +08:00   1
    如果指的是第 7 章 182 页 "匿名函数的执行环境具有全局性" 这句话的话

    这句话是翻译错误,原文是

    ```
    Anonymous functions are not bound to an object in this context, meaning the this object points to window unless executing in strict mode (where this is undefined).
    ```
    翻译:在这个上下文(执行环境)中匿名函数并没有绑定到任何一个对象中,意味着 this 指向 window (除非这个上下文(执行环境)是在严格模式下执行的,而严格模式下该 this 指向 undefined )
    https://www.zhihu.com/question/21958425/answer/278063919
    palmers
        24
    palmers  
       2019-01-30 22:35:42 +08:00
    this 是动态的指向当前对象 并不会主动的缓存 所以 是 window 在 getName 函数中将 this 缓存起来就会得到你预料的结果了
    autoxbc
        25
    autoxbc  
       2019-01-30 23:12:17 +08:00   1
    个人觉得我的理解最接近本质

    记住唯一一条规则:this 是隐参数,在成员访问过程中,如果属性是函数,且不是箭头函数,宿主作为隐参数传递给属性

    这里的关键是成员访问,这是个在宿主对象上查找属性的过程,语句反应了这个过程,隐参数就会传递

    ---------------------
    console.log( a() )
    这里 a 是函数,但是查找 a 的过程不是成员访问,所以没有隐参数

    ---------------------
    既然 this 是隐参数,bind call apply 就是隐参数显式传递,这个是不是非常自然
    OSF2E
        26
    OSF2E  
       2019-01-31 00:47:57 +08:00
    @rabbbit @fourstring

    这句是纯个人理解,没有原文可以引用,我自己也觉得表达的不够准确。
    mamahaha
        27
    mamahaha  
       2019-01-31 01:08:20 +08:00   1
    这种问题你今天想明白了也许第二天又糊涂了,所以没必要浪费时间钻研,把它看成黑箱就得了
    yxcoder
        28
    yxcoder  
       2019-01-31 09:13:22 +08:00
    举个栗子:

    let obj = {
    getThis(){
    console.log(this);
    let func = function(){
    console.log(this);
    }
    func();
    }
    }
    obj.getThis();

    两个 console 分别会输出什么?
    yxcoder
        29
    yxcoder  
       2019-01-31 09:14:33 +08:00
    @yxcoder 这代码格式。。。哈哈
    X37B
        30
    X37B  
       2019-01-31 09:38:44 +08:00   1
    @fourstring https://github.com/getify/You-Dont-Know-JS
    <你不知道的 js>,国内有译本,分上中下,不过中册翻译的不好
    libook
        31
    libook  
       2019-01-31 10:52:34 +08:00   1
    “每个函数执行时都会创建一个自己的执行环境”的前提是使用 new 指令来执行函数。

    function a(){this.n=1;console.log(this.n);}

    你直接执行 a()的时候,此时没有一个新的对象创建,this 默认指向全局作用域,就像你直接 var x=1 然后发现 x 变成了 global ( window )的属性,是一样的。
    你执行 new a()的时候,依照 new 指令的原理,会创建一个新对象(类似于调用 Object.create(a.prototype)),然后再让 this 指向这个创建出来的对象,此时这个对象就是你所说的“自己的执行环境”。

    为了方便理解,我上面说得比较浅显,具体你可以去网上搜一下 new 指令的功能,这个是原型和原型链的思想。
    no1xsyzy
        32
    no1xsyzy  
       2019-01-31 11:43:34 +08:00
    this 是栈帧上的动态作用域!

    @libook 你可能没明白执行环境是什么意思?另一个名字叫帧( Frame )。
    或者是 (eval exp env) 的那个 env。
    其实说了这么多,其实就是说
    func(...params) := (apply func params (new-env))
    obj.func(...params) := (apply func params (assoc (('this obj)) (new-env)))
    func.call(obj) := (apply func '() (assoc (('this obj)) (new-env)))
    ian511
        33
    ian511  
       2019-01-31 12:02:19 +08:00
    哈哈看到标题就知道在问 js
    wly19960911
        34
    wly19960911  
       2019-01-31 13:45:28 +08:00
    /t/519845

    自己看吧
    libook
        35
    libook  
       2019-01-31 16:37:29 +08:00
    @no1xsyzy

    其实我是没听说过“执行环境”这个术语,也不知道应该对应哪个英文名称,所以为了帮助楼主解决问题,只能根据楼主的描述进行推测:

    楼主的“那么 a()执行时 this 的值应该是 getName 函数的环境对象”,显然楼主是期望 a 内部的 this 指向的是“ getName 函数的环境对象”即 object 对象,符合楼主预期的结果,输出应该是'object'字符串才对。
    所以我的判断是楼主说的“执行环境”就是指的是 this 指向的对象,@fourstring 楼主可以自己解释一下这个“执行环境”究竟是想说的什么。

    然后 Stack frame 应该指的是 JS 的 Call Stack 里的原理,我承认这方面我确实不懂,不过一方面我觉得楼主也未必能理解,另一方面楼主的问题在 ES 语法规范上应该就能解决,不至于挖掘到 JS 解释器的实现方式。
    no1xsyzy
        36
    no1xsyzy  
       2019-02-01 09:52:30 +08:00
    @libook 实际上这块的名称英文似乎也很混乱,因为似乎每个语言都会新加一个名称,甚至本身用多个名称…… 也可能是侧重方面的区别。
    大概 JS 里叫 Scope ?
    libook
        37
    libook  
       2019-02-01 10:46:18 +08:00
    @no1xsyzy 参考 MDN 英文的说法,https://developer.mozilla.org/en-US/docs/Glossary/Scope Scope 可能对应的是我们平时说的“作用域”。楼主的问题是 this 的问题,那么 this 是遵循原型链原理的,而原型链和作用域链貌似是 JS 里的两套独立体系。
    no1xsyzy
        38
    no1xsyzy  
       2019-02-01 11:27:13 +08:00
    @libook 一个变量就算是关键字也是有作用域的,而 this 是动态作用域(由调用方决定绑定到什么)而不是词法作用域(闭包)。你知道箭头函数不影响 this 吗?这实际上就是作用域的区别。
    所以这就是 Javascript 中 this 为什么是个大问题,包括 #33 说的一看就知道是在说 JS。
    就是因为 JS 大量使用词法作用域以及广泛使用闭包但却唯有 this 采用动态作用域。更糟糕的是采用的是隐式动态作用域。
    Python 的 self (约定上)采用显式动态作用域(作为参数传递)。
    C++ 根本没有词法作用域,似乎 lambda 也是手动传入环境的。
    billyangg
        39
    billyangg  
       2019-03-01 08:57:46 +08:00
    在 node 里为啥是 undefined ?
    billyangg
        40
    billyangg  
       2019-03-01 09:03:15 +08:00
    我觉得这个写法跟下面这种差不多:给 a 赋值 this 的指向就很明显了:

    ```js
    var name="Window";
    var object={
    name:"object",
    getName:function (){
    var a= function(){
    return this.name;
    };
    console.log(a());
    return a;
    }
    }
    var a = object.getName();
    a()
    ```

    ![TIM 截图 20190301090238.png]( https://i.loli.net/2019/03/01/5c7884cacf4c3.png)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2293 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 15:55 PVG 23:55 LAX 08:55 JFK 11:55
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86