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

指向 MutationObserver 的 this 为什么会是 undefined 啊?

  •  
  •   axo · 2023-02-02 23:17:21 +08:00 · 1317 次点击
    这是一个创建于 420 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求:MutationObserver 的回调内部需要 await 读取环境变量,且原子化的运行(前一次变动的回调没执行完前,不会有新的回调同时运行)

    只是简单地在回调里面用 await ,声明前加上 async ,显然不管用。于是尝试了下面的写法:回调里先把观察器断掉,等处理完其他事,返回前再连上。虽然写法很丑陋,且会丢掉一些变动没处理,但不影响使用场景:

    let ManualHider = function(rules) {
    
        this.run = function() {
            this.list_observer = new MutationObserver(this.callback);
            this.list_observer.observe(document.querySelector(this.matching_unique_rule.root), {
                childList: true
            });
        }
    
        this.callback = async function() {
            this.list_observer.disconnect();   // 报错的行
            await new Promise(r => setTimeout(r, 3000));  // do sth asynchronously
            this.list_observer.observe(document.querySelector(this.matching_unique_rule.root), {
                childList: true
            });
        }
    };
    
    new ManualHider(rules).run();
    
    

    但是当第二次触发时,this 会变成 undefined ,提示 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'disconnect')。

    这是为什么呀?另外若这种写法实在不行,还有什么简单方法(太专业的搞不来…)能实现需求吗?谢谢

    (楼主文科生,偶尔代码仅自用,若说的不对,请大佬对业余者宽容谅解)

    13 条回复    2023-02-06 10:04:08 +08:00
    xy90321
        1
    xy90321  
       2023-02-02 23:26:09 +08:00 via iPhone   ❤️ 1
    另外搞一个队列,observer 的回调只负责往队列里面塞消息,另外开个处理去顺次消费队列里面的消息就可以了
    ie88
        2
    ie88  
       2023-02-02 23:41:31 +08:00   ❤️ 1
    this.run 和 this.callback 换成 arrow function 试试:() => {}
    axo
        3
    axo  
    OP
       2023-02-02 23:47:36 +08:00
    @ie88 #2 哇,原来是这个原因,没注意 箭头函数没有自己的 this 但普通函数有 这点,感谢老哥呀 😘😁
    ie88
        4
    ie88  
       2023-02-02 23:54:08 +08:00   ❤️ 2
    @axo 我从某本书摘几句话给你,有空看看 ES6/7/Next
    - Inside a `normal function`, `this` is a reference to the context object that the function is operating on
    - Inside an `arrow function`, `this` refrences the context in which the arrow function expression is defined.
    - when used inside global `normal functions`, `this` is equal to `window` in nonstrict mode and `undefined` in strict mode
    lisongeee
        5
    lisongeee  
       2023-02-03 00:02:49 +08:00   ❤️ 1
    ```js
    let ManualHider = function(rules) {

    this.run = function() {
    this.list_observer = new MutationObserver(this.callback);
    this.list_observer.observe(document.querySelector(this.matching_unique_rule.root), {
    childList: true
    });
    }

    this.task = Promise.resolve()
    this.callback = ()=> {
    this.task = this.task.finally(async()=>{
    await new Promise(r => setTimeout(r, 3000));
    // do sth asynchronously
    })
    }
    };

    new ManualHider(rules).run();
    ```
    lisongeee
        6
    lisongeee  
       2023-02-03 00:06:55 +08:00   ❤️ 1
    确实还要把 this 。run=function(){ 换成 this 。run=()=>{
    lisongeee
        7
    lisongeee  
       2023-02-03 00:07:57 +08:00   ❤️ 1
    我使用 。而不是 . 是因为 V2EX 把上一条评论认为包含外链不让发送
    axo
        8
    axo  
    OP
       2023-02-03 00:17:58 +08:00
    @lisongeee #5 试了下,完美!以前没用过 finally ,真是又简洁又解决了问题!非常感谢大佬!笔芯❤
    autoxbc
        9
    autoxbc  
       2023-02-03 00:47:04 +08:00
    只是为了让一串异步函数顺序执行,没必要把观察者接来接去,只要把 promise 接起来就够了,也就两行代码

    let promise = Promise.resolve();
    const run = asyncFunction => promise = promise.then( () => asyncFunction() );

    const getAsyncFunction = name => async () => {
    console.log(`${ name } begin`);
    await new Promise( resolve => setTimeout( resolve , 3000 ) );
    console.log(`${ name } end`);
    };

    run( getAsyncFunction('f1') );
    run( getAsyncFunction('f2') );
    run( getAsyncFunction('f3') );
    justdoit123
        10
    justdoit123  
       2023-02-03 10:17:53 +08:00
    歪个楼,话说各位 js 们写业务代码还会用到 call/bind/apply 吗?我个人观点:js 里的 context bind 跟 C 语言里的 goto 一样,需要用到的场景已经很少了。
    angrylid
        11
    angrylid  
       2023-02-03 10:53:49 +08:00
    习惯上会这样写吧:

    function ManualHider(rules) {
    this.rules = rules;
    }
    ManualHider.prototype.run = function() {
    this.list_observer = new MutationObserver(this.callback);
    this.list_observer.observe(document.querySelector(this.matching_unique_rule.root), {
    childList: true
    });
    }

    ManualHider.prototype.callback = async function() {
    this.list_observer.disconnect(); // 报错的行
    await new Promise(r => setTimeout(r, 3000)); // do sth asynchronously
    this.list_observer.observe(document.querySelector(this.matching_unique_rule.root), {
    childList: true
    });
    }
    new ManualHider({}).run();
    wdssmq
        12
    wdssmq  
       2023-02-04 21:37:29 +08:00   ❤️ 1
    @mistkafka 遍历 nodeList 时用 - -
    justdoit123
        13
    justdoit123  
       2023-02-06 10:04:08 +08:00
    @wdssmq 是哦! 不过也确实少用了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4610 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 49ms · UTC 09:54 · PVG 17:54 · LAX 02:54 · JFK 05:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.