Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/437.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 为什么postMessage()只提供上一个DOM的信息?_Javascript_Closures_Postmessage - Fatal编程技术网

Javascript 为什么postMessage()只提供上一个DOM的信息?

Javascript 为什么postMessage()只提供上一个DOM的信息?,javascript,closures,postmessage,Javascript,Closures,Postmessage,浏览器:谷歌浏览器(Win 10x64) 这是我第一次使用javascript的postMessageAPI,因此我不知道它的所有细微差别 我试图循环一组DOM元素,然后在不同的选项卡中打开一组链接。打开这些选项卡后,在各自的DOM中找到某些元素,并通过主窗口中的postMessage()显示它们 问题是,即使我打开了几个选项卡,我也只能从最后一个选项卡获取信息 这是我的密码: 在主窗口上准备postMessage()侦听器: window.addEventListener('message',

浏览器:谷歌浏览器(Win 10x64)

这是我第一次使用javascript的
postMessage
API,因此我不知道它的所有细微差别

我试图循环一组DOM元素,然后在不同的选项卡中打开一组链接。打开这些选项卡后,在各自的DOM中找到某些元素,并通过主窗口中的
postMessage()
显示它们

问题是,即使我打开了几个选项卡,我也只能从最后一个选项卡获取信息

这是我的密码:

  • 在主窗口上准备
    postMessage()
    侦听器:

    window.addEventListener('message',(event)=>{console.log(event.data);},false)

  • 创建我的选项卡数组并获取我的DOM元素:

    alert_list=document.querySelectorAll(“tr.expand.alerts table tablerow”)
    
    
    var newTabs=[]

  • 循环DOM元素,打开选项卡,添加JS代码,用所需数据调用主窗口。这是主要工作发生的地方:

    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);
        };
    }
    
    } );

  • 现在,我不断从上次打开多次的选项卡获取信息。确切地说是打开的选项卡数。这让我相信问题在于javascript对
    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
      函数参数的环境记录中保存的标识符的绑定

    @Lynx242那么我如何从最后一个选项卡获取信息?如果我无法获取信息,我也应该无法从最后一个选项卡获取信息,对吗?@Lynx242在chrome上尝试一下,你可以从打开的选项卡读取数据。@Lynx242这基本上就是我正在做的。不,我不明白。请注意,我提供的是setTimeout立即使用回调函数,并且该函数包含对实际窗口元素的闭包。
    ((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);
            };
        }
    });