【问题标题】:Ajax Callback function's locationAjax 回调函数的位置
【发布时间】:2016-06-09 15:36:04
【问题描述】:

我对位于 $(document).ready 的回调函数有疑问。回调函数以前不起作用。当我把它放在$(document).ready 之外时,代码已经开始完美运行。我不明白为什么。位置重要吗?

这行得通:

$(document).ready(function() {
  $("#button1").click(function() {
    $.ajax({
      url: "http://www.example.com/data.php",
      type: "get",
      dataType: "jsonp",
      jsonpCallback: "read",
    });
  });
});
var read = function(data) {
  console.log(data);
}

这不起作用。

$(document).ready(function() {
  $("#button1").click(function() {
    $.ajax({
      url: "http://www.example.com/data.php",
      type: "get",
      dataType: "jsonp",
      jsonpCallback: "read",
    });
  });
  var read = function(data) {
    console.log(data);
  }
});

UPDATE1:抱歉,链接并没有什么不同。我忘了换第二个。读取函数的位置只有一个区别。

【问题讨论】:

标签: javascript jquery ajax


【解决方案1】:

您将 JsonP 回调名称作为这样的字符串传递的原因是因为 JQuery 需要将其添加到您的 URL 中,例如 ?callback=read。一个 JsonP 请求只是一个由 JQuery 在后台创建并添加到页面<script src="http://www.csstr.com/data.json?callback=read"></script><script> 标签。一旦 JQuery 将该脚本标记添加到您的页面,浏览器就会将其视为正在加载要执行的普通 JavaScript 文档。由于请求的?callback=read 部分,远程服务器知道响应可执行的JavaScript 而不仅仅是原始数据。该可执行 JavaScript 只是对具有您提供的名称的函数的函数调用,在本例中为 read 函数。因为返回的脚本是在全局范围内执行的,所以read函数也需要存在于全局范围内。浏览器中的全局作用域是window 对象,所以基本上read 函数需要存在于window 对象上,以便执行的脚本找到该函数。

$(document).ready(function() {
  $("#ara-button").click(function() {
    $.ajax({
      url: "http://www.csstr.com/data.json",
      type: "get",
      dataType: "jsonp",
      jsonpCallback: "read",
    });
  });
  window.read = function(data) {
    console.log(data);
  }
});

它在您的第一个示例中的 ready 函数之外工作,因为在根级别定义的任何内容都是全局范围的。

Codpen 演示:http://codepen.io/anon/pen/qNbRQw


如果您想进一步了解 JsonP 的工作原理,请继续阅读。

如果您仍然感到困惑,那可能是因为您不是 100% 熟悉 JsonP 的实际工作原理。 JsonP 是绕过Same-origin Policy 的一种方法。同源策略的简短版本是,浏览器不会让您读取从请求返回的域而不是请求源自的域的响应,除非该其他域上的服务器表示可以。

大多数浏览器都实施了同源策略,以帮助保护用户免受恶意脚本的侵害。例如,如果您在一个浏览器选项卡中通过您的银行网站进行身份验证,然后在另一个选项卡中访问了恶意网站,则该恶意网站可以在浏览器中没有同源限制的情况下向您的银行网站发出 ajax 请求。该请求将携带您的浏览器为该域存储的任何 cookie,并且您的 cookie 将显示您已通过身份验证,从而使攻击脚本可以访问您银行帐户中经过身份验证的数据。同源策略可防止恶意网站看到该请求的响应数据。

一开始,客户端和服务器并没有正式的方式来选择加入跨域共享。当时它只是被浏览器直接阻止了。为了解决这个问题,发明了 JsonP。同源策略仅隐藏来自 ajax 请求的响应,但您可能已经注意到浏览器完全可以通过 <script> 标签从其他网站加载脚本。 script 标签只是对 javascript 文档执行一个普通的旧 GET 请求,然后开始在您的页面上执行该脚本。 JsonP 利用了同源限制不适用于<script> 标签这一事实。

请注意,如果您在浏览器中直接访问 http://www.csstr.com/data.json,您将看到您所追求的数据。但是,请尝试添加以下查询字符串。

http://www.csstr.com/data.json?callback=read

注意到有什么不同吗?不仅仅是返回您想要的数据,而是将您的数据传递到名为read 的函数中。这就是你知道服务器理解 JsonP hack 的方式。它知道将您想要的数据包装在函数调用中,以便 JQuery 可以在客户端上执行 JsonP hack,它通过创建 <script> 标记并将其嵌入到您的网页中来执行此操作。该脚本标记指向 URL,但还将该查询字符串添加到它的末尾。结果是一个脚本标记,它从该 URL 加载脚本并执行它。该脚本正在加载到您页面的全局范围内,因此当调用 read 函数时,它希望该函数也存在于全局范围内。

今天,围绕同源策略的官方方法是通过跨域资源共享策略(又名 CORS)。 JsonP 基本上完成了与正确的 CORS 请求相同的事情。服务器必须通过知道如何将响应格式化为可执行 JavaScript 来选择加入,而客户端必须知道不要执行正常的 ajax 请求,而是动态创建脚本标记并将其嵌入页面正文中。然而,JsonP 仍然是一种 hack,并且随着 hack 的出现,它也有自己的缺点。 JsonP 真的很难调试,因为处理错误几乎是不可能的。如果请求失败,则没有简单的方法来捕获错误,因为请求是通过<script> 标记发出的。 <script> 标签也无法控制请求的格式;它只能发出普通的 GET 请求。 JsonP 最大的缺点是需要创建一个全局函数来用作数据的回调。没有人喜欢污染全局命名空间,但 JsonP 需要这样做。

正确的 CORS 请求不需要客户端的额外工作。浏览器知道如何询问服务器是否允许读取数据。服务器只需要以正确的 CORS 标头进行响应,表示可以,然后浏览器将解除同源限制并允许您在该域中正常使用 ajax。但有时您尝试访问的资源只知道 JsonP 而不会返回正确的 CORS 标头,因此您必须回退它。

关于 CORS 的更多信息,我不久前写了一个非常详细的 blog post,它应该真的可以帮助你理解它。如果您控制正在返回您所追求的数据的服务器,那么您应该考虑让它返回正确的 CORS 标头并忘记 JsonP。但是当您不控制服务器并且无法将其配置为返回正确的标头时,这是完全可以理解的。有时 JsonP 是获得所需内容的唯一方法,即使它确实让您在编写代码时有点吐槽。

希望能帮助你解决一些问题:)

【讨论】:

  • 完美解释!!谢谢@Chev
  • 没问题 :) 不要忘记点击接受按钮来验证我是一个人:P
  • :'(现在我永远不会有我需要快乐的自信。
【解决方案2】:

$(document).ready(function() { 表示:在页面完成加载时执行此操作。 在第二个示例中,您仅在页面加载完成后才定义 read 函数。

在工作示例中,您首先定义 read 函数并说,“一旦加载页面,执行 ajax 调用,然后调用 read 函数”

编辑:另外,您可以阅读@IGeoorge 答案以获得更详细的解释。

【讨论】:

  • 这实际上不是在定义函数和 ajax 调用完成之间存在竞争条件的情况。因为它是一个 JsonP 请求,所以回调必须存在于全局范围 (window) 上,否则执行的 JsonP 脚本将找不到该函数。
【解决方案3】:

在ajax前添加read方法

$(document).ready(function() {
  var read = function(data) {
    console.log(data);
  }
  $("#ara-button").click(function() {
    $.ajax({
      url: "http://www.csstr.com/data.json",
      type: "get",
      dataType: "jsonp",
      jsonpCallback: "read",
    });
  });

});

【讨论】:

  • 这仍然行不通。 JsonP 请求需要回调函数存在于全局范围内。
【解决方案4】:

您的函数被定义到 Jquery Scope 中,所以在执行 ajax 的那一刻,找不到“读取”函数的定义。

这就是第一个示例有效的原因。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2019-12-05
    相关资源
    最近更新 更多