【问题标题】:Javascript TypeError variable is undefinedJavascript TypeError 变量未定义
【发布时间】:2012-12-01 11:01:15
【问题描述】:

我正在“学习 PHP、MySQL 和 JavaScript -O'Reilly”中学习一些非常基本的 AJAX 编程。有几个非常基本的 Java 程序使用 AJAX 获取一些文本或页面。我正在使用 W3Schools.com 站点来尝试了解有关使用 AJAX 获取 XML 格式文档的更多信息。我有两个与我正在学习的内容相关的问题。

当我使用来自http://www.w3schools.com/dom/dom_loadxmldoc.asp 的代码时,我可以返回一个带有以下 sn-p 的 XML 文档:

function loadXMLDoc(dname)
{
if (window.XMLHttpRequest)
  {
  xhttp=new XMLHttpRequest();
  }
else
  {
  xhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xhttp.open("GET",dname,false);
xhttp.send();
return xhttp.responseXML;
}

//called this way
xmlDoc=loadXMLDoc("books.xml");

但是 async 必须设置为 false 才能工作。如果我将 async 设置为 true,它会返回 null。所以我的第一个问题是为什么 async 必须为 false 才能返回 XML 文档?

我的第二个问题,当我尝试将书中的一些代码和网站上的一些代码结合起来时,sn-p 如下:

<body>API Call output
<div id='output'>will be here</div>
<script>
out = ""
xmlDoc = getResponse()
users = xmlDoc.getElementsByTagName('username')
for (count = 0; count < users.length ; count++)
{
    out += "Username: " + users[count].childNodes[0].nodeValue + '<br />'
}
document.getElementById('output').innerHTML = out

function ajaxRequest()
{
    try //good browser
    {
        var request = new XMLHttpRequest()
    }
    catch(e1)
    {
        try // IE6+
        {
            request = new ActiveXObject("Msxml2.XMLHTTP")
        }
        catch(e2)
        {
            request = false
        }
    }//end catch(e1)
    return request
}

function getResponse()
{
    params = "apikey=test"
    request = new ajaxRequest()
    request.open("POST", "processapi.php", true)
    request.setRequestHeader("Content-type",
        "application/x-www-form-urlencoded")
    request.setRequestHeader("Content-length", params.length)
    request.setRequestHeader("Connection", "close")
    request.onreadystatechange = function()
    {
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                if (this.responseXML != null)
                {
                    return this.responseXML
                }
                else alert("Ajax error: No data received")
            }
            else alert( "Ajax error: " + this.statusText)
        }
    }

    request.send(params)
}
</script>
</body>

我收到“TypeError: xmlDoc is undefined”的错误。在异步设置为 true 或 false 的函数 getResponse() 中它不起作用,我得到 xmlDoc is undefined 错误。在将 xmlDoc 设置为 loadXMLDoc() 的返回之前,在第一个示例(来自 w3schools.com)的其他地方没有定义 xmlDoc,并且效果很好。 ajaxRequest() 不在书中,getResponse() 中的大部分内容都在书中,但我把它放在了 getResponse() 函数中。当从这个脚本调用时,processapi.php 只是回显一个 XML 文档。

为什么 async 在第一个示例中不起作用,为什么 xmlDoc 在第二个示例中未定义?任何帮助是极大的赞赏。

【问题讨论】:

    标签: javascript ajax


    【解决方案1】:

    这两个问题的答案在于异步的本质。 asynctrue,这将返回 null

    xhttp.open("GET",dname,false);
    xhttp.send();
    return xhttp.responseXML;
    

    ...因为在return 语句发生时HTTP 请求还没有完成,所以xhttp.responseXML 仍然有它的默认值(null)。 HTTP 请求稍后在该代码流之外完成(异步 - 字面意思,不是同步)。

    您的getResponse 函数根本不会返回值。您分配给onreadystatechange 的匿名回调函数会返回一个值,但这绝不会影响getResponse 的返回值。

    客户端 Web 编程的一个关键方面是接受环境的异步、事件驱动性质。几乎没有在 XHR 请求上将 async 设置为 false 的用例。相反,习惯于对事物使用回调。例如,您的 getResponse 函数可能如下所示:

    // ***CHANGE*** -----v--- Accept a callback
    function getResponse(callback)
    {
        params = "apikey=test"
        request = new ajaxRequest()
        request.open("POST", "processapi.php", true)
        request.setRequestHeader("Content-type",
            "application/x-www-form-urlencoded")
        request.setRequestHeader("Content-length", params.length)
        request.setRequestHeader("Connection", "close")
        request.onreadystatechange = function()
        {
            if (this.readyState == 4)
            {
                if (this.status == 200)
                {
                    if (this.responseXML != null)
                    {
                        // ***CHANGE*** Trigger the callback with the response
                        callback(this.responseXML);
                    }
                    else alert("Ajax error: No data received")
                }
                else alert( "Ajax error: " + this.statusText)
            }
        }
    
        request.send(params)
    }
    

    然后,而不是这个:

    out = ""
    xmlDoc = getResponse()
    users = xmlDoc.getElementsByTagName('username')
    for (count = 0; count < users.length ; count++)
    {
        out += "Username: " + users[count].childNodes[0].nodeValue + '<br />'
    }
    document.getElementById('output').innerHTML = out
    

    你会这样做:

    getResponse(function(xmlDoc) {
        out = ""
        users = xmlDoc.getElementsByTagName('username')
        for (count = 0; count < users.length ; count++)
        {
            out += "Username: " + users[count].childNodes[0].nodeValue + '<br />'
        }
        document.getElementById('output').innerHTML = out
    });
    

    请注意我是如何将所有依赖于响应的代码移动到一个函数中,然后将该函数传递给getResponsegetResponse 在有数据时调用它。请求/响应、事件驱动、异步,无论你想怎么称呼它,这是尽早加入的关键。 :-)


    旁注:您的代码正在成为The Horror of Implicit Globals 的牺牲品。强烈建议声明所有变量,并将所有代码包装在一个作用域函数中以避免创建全局符号(浏览器上的全局命名空间已经人满为患)。

    【讨论】:

    • 谢谢。我使用了 T.J 的代码,并且正在通过它来了解发生了什么。看来我今晚将阅读有关回调和隐式全局变量的内容。谢谢!
    【解决方案2】:

    getResponse 正在执行异步 XHR 请求,因此函数将在结果准备好之前返回。您需要改用回调方法,

    更改函数声明以接受回调参数:

    function getResponse(callback) {
    

    然后在函数体中修改这部分

    if (this.responseXML != null)
    {
        return this.responseXML; // WRONG
    }
    

    以便它在收到 XML 时调用回调

    if (this.responseXML != null)
    {
        callback(this.responseXML); // GOOD
    }
    

    然后你会像这样调用getResponse

    getResponse(function(xmlDoc){
       // do something with xmlDoc
    })
    

    【讨论】:

      猜你喜欢
      • 2014-11-07
      • 2021-03-17
      • 2015-07-25
      • 2022-06-16
      • 2021-08-05
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多