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

关于 JS 作用域与 this 的一点疑问

  •  
  •   gam2046 · 2018-11-30 13:56:18 +08:00 · 3184 次点击
    这是一个创建于 2188 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本人,后端开发人员,对 JS 了解不多。不知道标题是否准确。

    出于自用目的,正在写一个 userscript。编写过程中遇到了一点障碍,奈何不知道应该如何表述,所以没法搜。

    样例代码

    class Foo {
        constructor() {
            this.field = new Map()
        }
    
        requestOnLoad(details) {
            /*
             * 此方法由于产生于回调
             * 而 JavaScript 中函数本身也属于对象
             * 因此
             * 此时的 this 代表的是 GM_xmlhttpRequest 方法所产生的回调函数
             * 并不指向与 Foo 对象的实例
             * 
             * 但是,我希望在此方法内访问 Foo 对象的实例,并设置 field 属性
             */
            this.field.set('aa', 'bb') // 如何能够让这句话符合预期呢?
        }
    
        invoke() {
            GM_xmlhttpRequest({
                url: requestUrl,
                method: "GET",
                onload: this.requestOnLoad,
            });
        }
    }
    
    new Foo().invoke()
    

    因此当样例代码执行时,this 指向的对象与预期不符,导致我没法调用 field.set 方法。同时,奈何我搜了一堆 ES6 相关资料。

    也没见到有相关的解释。

    请教各位前端大佬,对于样例代码中,requestOnLoad 方法内,应该如何正确的访问到 Foo 对象的实例呢?

    10 条回复    2018-11-30 22:18:11 +08:00
    wxsm
        1
    wxsm  
       2018-11-30 14:02:07 +08:00
    使用箭头函数。

    requestOnLoad: (details) => {
    //...
    }
    imyxz
        2
    imyxz  
       2018-11-30 14:04:57 +08:00 via Android
    应该是这样吧(new Food()).invoke()
    wildnode
        3
    wildnode  
       2018-11-30 14:12:11 +08:00
    箭头函数可解
    sker101
        4
    sker101  
       2018-11-30 14:12:13 +08:00 via iPhone   ❤️ 1
    onload 的函数试试.bind ( this )
    gam2046
        5
    gam2046  
    OP
       2018-11-30 14:12:33 +08:00
    @wxsm 感谢。你是指这样调用嘛?
    GM_xmlhttpRequest({
    url: requestUrl,
    method: "GET",
    onload: (details)=>{ /*Do something*/},
    onerror: this.requestOnError,
    onabort: this.requestOnAbort,
    ontimeout: this.requestOnTimeout
    });

    但是有一点很尴尬的事情,按照我既往的经验,我设计的是,后面还有一个 class 会继承( extends )这个 Foo,因此 requestOnLoad 实际做的事情,并不在 Foo 里面定义。这样话,我就没法这么写了。是否还有其他变通方法呢?
    xqin
        6
    xqin  
       2018-11-30 14:17:11 +08:00   ❤️ 1
    GM_xmlhttpRequest({
    url: requestUrl,
    method: "GET",
    onload: this.requestOnLoad.bind(this),
    });

    @gam2046

    onload: this.requestOnLoad.bind(this),
    gam2046
        7
    gam2046  
    OP
       2018-11-30 14:18:39 +08:00
    感谢 @sker101 提供的方法。该方法可以解决我遇到的问题,顺便我去搜了下相关资料

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

    解决方案:

    GM_xmlhttpRequest({
    url: requestUrl,
    method: "GET",
    onload: this.requestOnLoad.bind(this) // 此时在回调方法中 this 对象即为 bind 方法中传递的对象
    });
    DrugsZ
        8
    DrugsZ  
       2018-11-30 14:58:55 +08:00   ❤️ 1
    constructor() {
    this.field = new Map()
    this.requestOnLoad = this.requestOnLoad.bind(this)
    }
    这样好一点吧,
    SoloCompany
        9
    SoloCompany  
       2018-11-30 21:36:05 +08:00
    https://github.com/tc39/proposal-bind-operator

    虽然有 babel 的实现, 却仍然是 stage 0 提案

    onload: ::this.requestOnLoad
    TwoDays91
        10
    TwoDays91  
       2018-11-30 22:18:11 +08:00 via iPhone   ❤️ 1
    除了 bind 方法 onload: () => {this.requestonload()}也可以
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5451 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 01:23 · PVG 09:23 · LAX 17:23 · JFK 20:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.