Javascript 为什么postMessage()只提供上一个DOM的信息?
浏览器:谷歌浏览器(Win 10x64) 这是我第一次使用javascript的Javascript 为什么postMessage()只提供上一个DOM的信息?,javascript,closures,postmessage,Javascript,Closures,Postmessage,浏览器:谷歌浏览器(Win 10x64) 这是我第一次使用javascript的postMessageAPI,因此我不知道它的所有细微差别 我试图循环一组DOM元素,然后在不同的选项卡中打开一组链接。打开这些选项卡后,在各自的DOM中找到某些元素,并通过主窗口中的postMessage()显示它们 问题是,即使我打开了几个选项卡,我也只能从最后一个选项卡获取信息 这是我的密码: 在主窗口上准备postMessage()侦听器: window.addEventListener('message',
postMessage
API,因此我不知道它的所有细微差别
我试图循环一组DOM元素,然后在不同的选项卡中打开一组链接。打开这些选项卡后,在各自的DOM中找到某些元素,并通过主窗口中的postMessage()
显示它们
问题是,即使我打开了几个选项卡,我也只能从最后一个选项卡获取信息
这是我的密码:
postMessage()
侦听器:
window.addEventListener('message',(event)=>{console.log(event.data);},false)代码>
alert_list=document.querySelectorAll(“tr.expand.alerts table tablerow”)代码>
var newTabs=[]代码>
alert_list.forEach((currentValue, currentIndex) => {
alert_status = currentValue.childNodes[13].innerText;
if(alert_status == "Enabled") {
console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
newTabs.push(window.open(currentValue.childNodes[5].children[0].href, "_blank"));
newTabs[newTabs.length - 1].onload = function() {
setTimeout(((tab_element) => {return () => {
window.parent.postMessage(tab_element.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")[0].innerText);
}})(newTabs[newTabs.length - 1]), 120*1000);
};
}
}
);setTimeout
回调进行了延迟绑定,因此我将其更改为以下内容:
这实际上执行了我想要的实际DOM元素的闭包。但它仍然不起作用
我做错了什么
如果需要任何其他信息,请告诉我
谢谢。setTimeout不是同步调用的,而是在选项卡的“加载”事件触发后调用的,此时,
forEach
循环已完成运行,并离开newTabs.length-1
引用最后一个选项卡
一种解决方案是使用变量来代替重复使用length-1
。未经测试,但大致如下:
alert_list.forEach((currentValue, currentIndex) => {
alert_status = currentValue.childNodes[13].innerText;
if(alert_status == "Enabled") {
// console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
let tabWin = window.open(currentValue.childNodes[5].children[0].href, "_blank")
newTabs.push( tabWin);
tabWin.onload = function(event) {
setTimeout( () => {
window.parent.postMessage(
tabWin.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")
[0].innerText
);
}, 120*1000);
};
}
});
以下是缩进的原始代码,以更清楚地显示延迟评估发生的位置(添加了
alert\u status
的声明):
alert_list.forEach((currentValue, currentIndex) => {
let alert_status = currentValue.childNodes[13].innerText;
if(alert_status == "Enabled") {
console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
newTabs.push(window.open(currentValue.childNodes[5].children[0].href, "_blank"));
newTabs[newTabs.length - 1].onload = function() {
setTimeout(
(
(tab_element) => {
return () => {
window.parent.postMessage(tab_element.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")[0].innerText);
}
}
)(newTabs[newTabs.length - 1])
, 120*1000);
};
}
});
匿名onload函数在被添加时被编译成函数对象,但直到加载事件触发后才执行。当它执行时,它会为tab_元素
创建一个“闭包”,从newTabs[newTabs.length-1]
中获取它的值,后者现在是最后一个tab元素
解决方案是删除导致问题的技巧代码
闭包在JavaScript中是如何工作的,这是一个关于闭包本身的主题。不过,为了解释这一答案,本文将做一个介绍:
- 调用函数时,使用记录(JavaScript术语中的对象)保存函数中使用的变量和函数的值。实际上,变量标识符现在绑定到这样的“环境记录”。在ES3和早期版本中,在块级别(大括号中的语句)不需要额外的记录,或对于(let identifier…循环的
特例,因为
,let
和const
声明尚未引入。ES3中的单个环境记录称为“激活对象”类
- 通常,当函数返回时,为单个调用创建的环境记录可能会被垃圾收集,因为它们不再能够通过代码访问,这是JavaScript内存垃圾收集(MGC)的当前标准
- 如果嵌套函数和变量值在函数退出后可以在代码中达到,则它们不符合从内存中删除的条件。这种情况通常被描述为函数和变量被保存在“闭包”中
- 示例解决方案中的
的值保存在不同的环境记录中,每个调用winVar
对其函数参数进行修改-在连续调用中生成的不同值不会相互覆盖forEach
- onload和
回调函数是单独闭包中的不同函数对象。setTimeout
回调函数可以访问其父函数setTimeout
函数的环境记录,该函数可以访问其父选项卡打开函数的环境记录onload
- 计时器回调中的
引用最终解析为在调用时打开选项卡窗口的tabWin
函数参数的环境记录中保存的标识符的绑定forEach
((tab_元素)=>{return()=>{window.parent.postMessage(tab_元素.document.querySelectorAll(“h1.search name.section title.search title searchname”)[0].innerText);}(newTabs[newTabs.length-1])
,这将立即执行,并且不会等待onload
,此函数返回的函数将在超时/onload之后执行。您同意我的分析吗?我是否在某个地方出错?不,我不同意-用于设置的匿名函数中没有任何内容。onload
将在“load”之前执行事件激发,只有到那时才计算最终的newTabs[newTabs.length-1]
。我将展开答案进行解释。如果我错了,请纠正我,但您的解决方案之所以有效,是因为let
允许您在自己的词法范围内声明变量,而不是像
alert_list.forEach((currentValue, currentIndex) => {
let alert_status = currentValue.childNodes[13].innerText;
if(alert_status == "Enabled") {
console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
newTabs.push(window.open(currentValue.childNodes[5].children[0].href, "_blank"));
newTabs[newTabs.length - 1].onload = function() {
setTimeout(
(
(tab_element) => {
return () => {
window.parent.postMessage(tab_element.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")[0].innerText);
}
}
)(newTabs[newTabs.length - 1])
, 120*1000);
};
}
});