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

2023-02-02 23:17:21 +08:00
 axo

需求: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')。

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

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

1367 次点击
所在节点    JavaScript
13 条回复
xy90321
2023-02-02 23:26:09 +08:00
另外搞一个队列,observer 的回调只负责往队列里面塞消息,另外开个处理去顺次消费队列里面的消息就可以了
ie88
2023-02-02 23:41:31 +08:00
this.run 和 this.callback 换成 arrow function 试试:() => {}
axo
2023-02-02 23:47:36 +08:00
@ie88 #2 哇,原来是这个原因,没注意 箭头函数没有自己的 this 但普通函数有 这点,感谢老哥呀 😘😁
ie88
2023-02-02 23:54:08 +08:00
@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
2023-02-03 00:02:49 +08:00
```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
2023-02-03 00:06:55 +08:00
确实还要把 this 。run=function(){ 换成 this 。run=()=>{
lisongeee
2023-02-03 00:07:57 +08:00
我使用 。而不是 . 是因为 V2EX 把上一条评论认为包含外链不让发送
axo
2023-02-03 00:17:58 +08:00
@lisongeee #5 试了下,完美!以前没用过 finally ,真是又简洁又解决了问题!非常感谢大佬!笔芯❤
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
2023-02-03 10:17:53 +08:00
歪个楼,话说各位 js 们写业务代码还会用到 call/bind/apply 吗?我个人观点:js 里的 context bind 跟 C 语言里的 goto 一样,需要用到的场景已经很少了。
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
2023-02-04 21:37:29 +08:00
@mistkafka 遍历 nodeList 时用 - -
justdoit123
2023-02-06 10:04:08 +08:00
@wdssmq 是哦! 不过也确实少用了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/912724

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX