【已知】

不知道大家有木有了解过jQuery1.0到2.0时候针对jsonp那一块的修改。v1.0的时候还在使用iframe作为请求数据的临时暂居地。以便让过往数据有据可查。保证了jsonp请求的时候即便用了同样的全局callback 也只至于先返回的数据丢失,造成数据污染的问题。

但是自从jq2.0之后,就不再采用iframe来记录jsonp获得的数据了。可是依然,不得不支持同一个callback名。

那么,这样,问题就来了...

【问题】

jsonp的原理其实就是我们把请求地址当作一个js地址以script tag 的方式插入到页面,把服务端返回的数据当作script 脚本来运行以获得所需的数据(通常是第一个参数)。既然是以script脚本的方式载入,自然就绕过的跨域的问题。这自然需要服务端做一点配合。把我们约定传的callback参数以函数的形式返回,方便我们执行,获得数据。

那,如果我们有多个jsonp的请求,而且非要,死活要指定同样的callback名呢,肿么办?

<script src="omg.php?callback=myFunc&param=1"></script>
<script src="omg.php?callback=myFunc&param=2"></script>
<script src="omg.php?callback=myFunc&param=3"></script>

因为请求耗时的不确定性,无法确定每个回调执行的顺序,比如,一个简单的input suggest, 假设我们每次用户keyup都去请求一次数据,然后每次请求回调里面重新render我的们suggest list。实际上,这些请求是有“顺序”可言的,也就是说:我们原意是,后来的请求的回调也应该后执行才对, 这样才能保证 我输入 abc ,最后得到的suggest list 不是 a 的, 或者 ab的。(这是一个问题,但不是jsonp本身应该讨论的问题)

当然,你可以创建一个hash维护数据,当成一个自定义的cache来用,用一个唯一的且可以标识这个请求的key就行。每次要render的时候校验一次当前数据是否是你想要的请求得来的数据即可。

可是....

这里要讨论的并不是这个问题。

a) 如果是一个同名的全局callback,单单从这个函数来看的话,我们其实是不知道它被重写了多少次?结果和请求是否能对应起来?尤其是当其中有某些脚本发生错误的时候(当然,如果非要去hack,也并非不可)

b)不管在哪个浏览器里,一个脚本在执行之前,是无法对其干预的(removeNode之类的不算),只有当脚本loaded并且执行后,或者发生错误之后,才能对其进行操作。

c)正因为在execution 之前你不能对其进行操作,所以无法得知,这些脚本会以什么样的顺序进行loading和executing。

【通常】

为了避免所谓的callback污染:

a)用计数器对请求结果进行连接控制,jq v2.0+ 类似的方式

b)每次使用完返回数据后都重置,下一次使用前都检查下,防止二次污染使用。

对jsonp做一个简单通用的requestCallback, onload和onerror都能调

// some generic code
// common to all requestsvar lastValue;function genericCallback( value ) {lastValue = [ value ];
}// then the request specific closurefunction request( options ) {window[ options.callback ] = genericCallback;function requestCallback() {var tmp = lastValue;lastValue = undefined;if ( ! tmp ) {options.error();} else {options.success( tmp[ 0 ] );}}// create the script tag// "attach" requestCallback to the script tag// put the script tag in the DOM}..........
scriptTag.onload = scriptTag.onerror = requestCallback;

Opera并没提供不标准的onerror的钩子。但是无妨,onload亦可。

【IE下onreadystatechange 怪癖】

scriptTag.onreadystatechange = function() {if ( /loaded|complete/.test( scriptTag.readyState ) ) {requestCallback();}
};

ie下,onreadystatechange 的调用 并不是紧紧跟在脚本执行或者失败之后, 而是有一点点延迟,尤其是当有这个请求有缓存的时候。而有可能这一点点延迟的时候就有另一个脚本回调进来了,那就悲剧了。

如果这样:

<div id="divId" /><scriptid="scriptId"for="divId"event="onclick"src="script.js">
</script>

奇淫技巧,尝试load脚本,如果成功,ie下会触发divId 的一个onclick,而且不管load是否成功,都会触发script的onreaderstatechange的loaded readyState,而且最重要的是这个发生在脚本load完成,excute之前。

进一步,div的onclick可以被触发,同样的方式对于script呢?

<scriptid="scriptId"for="scriptId"event="onclick"src="script.js">
</script>

答案是肯定的,所以我们也不用额外的创建一个dom了。

所以可以用 script的event和for属性 来fix ie的onreaderstatechange的毛病了:

scriptTag.event = "onclick";
scriptTag.id = scriptTag.htmlFor = generateNewId();scriptTag.onreadystatechange = function() {if ( /loaded|complete/.test( scriptTag.readyState ) ) {try {scriptTag.onclick();} catch( e ) {}requestCallback();}
};

ie>=9 或许并不需要这个hack,但是无影响。

【所以】

// 代码其中一段... var head = document.getElementsByTag('head')[0],uniqid = 0,lastValue;function generalCallback(data) {lastValue = data}function urlappend(url, s) {return url + (/\?/.test(url) ? '&' : '?') + s}function handleJsonp(o, fn, err, url) {var reqId = uniqid++,cbkey = o.jsonpCallback || 'callback', // the 'callback' keycbval = o.jsonpCallbackName || ('__myrequest__' + reqId), // the 'callback' valuecbreg = new RegExp('(' + cbkey + ')=(.+)(&|$)'),match = url.match(cbreg),script = doc.createElement('script'),loaded = 0;if (match) {if (match[2] === '?') {url = url.replace(cbreg, '$1=' + cbval + '$3') // wildcard callback func name} else {cbval = match[2] // provided callback func name}} else {url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em}win[cbval] = generalCallback;script.type = 'text/javascript';script.src = url;script.async = true;if (typeof script.onreadystatechange !== 'undefined') {// need this for IE due to out-of-order onreadystatechange(), binding script// execution to an event listener gives us control over when the script// is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.htmlscript.event = 'onclick';script.htmlFor = script.id = '_myrequest_' + reqId;}script.onload = script.onreadystatechange = function () {if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) {return false}script.onload = script.onreadystatechange = null;script.onclick && script.onclick();// Call the user callback with the last value stored and clean up values and scripts.o.success && o.success(lastValue);lastValue = undefined;head.removeChild(script);loaded = 1;}// Add the script to the DOM headhead.appendChild(script);}

  

更多阅读:http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html  

  

  

  

【备忘】指定为同名callback的jsonp IE下script loaded状态标记相关推荐

  1. CSP浏览器安全策略备忘

    挺久之前过了一遍CSP的安全策略,很多人把它喻为XSS攻击的终结者,因为这种策略不再像传统只靠各种正则和特征匹配来识别跨站攻击Payload,而是直接从协议层把一些存在安全隐患的用法默认给干掉了,把同 ...

  2. Spring Spring MVC Hibernate 整合备忘

    以下为此三种框架整合配置的详细备注,以及部分问题备忘 项目结构和配置文件可访问 Github 查看 1. pom.xml 尽量使用 Maven 管理项目依赖以减少包引入时的麻烦,以及避免跨开发工具问题 ...

  3. PowerBuilder/PB常用备忘

    目录 1. PB使用Microsoft.XMLHttp组件的属性与方法 1-1 使用步骤 1-2 XMLHTTP方法: 1-3 XMLHTTP属性: 1-4 示例: 2. Pb中Window添加鼠标滚 ...

  4. EOS源码备忘-Push Transaction机制

    这里我们讨论EOS Push Transaction 的逻辑,这块EOS与Eosforce实现有一些区别,我们会着重点出. 关于wasm相关的内容我们会有一片专门的文档分析. 我们这里通常将Trans ...

  5. (网页的缓存控制)HTML配置no-cache(备忘) “Cache-control”常见的取值

    HTML配置no-cache(备忘) No-cache配置 html表头如下 <meta http-equiv="Content-Type" content="te ...

  6. jQuery学习笔记--JqGrid相关操作 方法列表 备忘 重点讲解(超重要)

    JqGrid相关操作备忘 方法列表 特别推荐:怎样获取某一方某一列的值: [html] view plaincopy var rowdata=jQuery("#list").jqG ...

  7. Webstorm常用快捷键备忘(Webstorm入门指南)

    WebStorm 是jetbrains公司旗下一款JavaScript 开发工具.被广大中国JS开发者誉为"Web前端开发神器"."最强大的HTML5编辑器". ...

  8. mysql一些操作个人备忘(持续更新)

    安装mysql数据库目录 1./usr/local/mysql/bin/mysql_install_db --user=mysql --datadir=/home/mysql/var ##指定安装后的 ...

  9. vue 插件 滑块验证_VUE接入腾讯验证码功能(滑块验证)备忘

    最近在用VUE做个简单的用户系统,登录注册需要验证码,想找个那种拖动的,找geetest居然已经不面向小客户了(或者说只有收费套餐). 腾讯防水墙的验证码免费使用,有2000/小时的免费额度,对于小网 ...

最新文章

  1. 3dmax Vray建筑可视化入门学习教程
  2. 非常精美的全能视频转换器 注册版
  3. Byte和bit的区别?
  4. zoj1081判断点是否在多边形内
  5. 获取Ip所在城市名与详细
  6. Codeforces Round #674 (Div. 3) F. Number of Subsequences 简单计数dp
  7. 前端 == Ajax
  8. mysql主从和dump_MySQL主从同步--原理及实现(一)
  9. Java 匿名内部类解析
  10. 【转】ASP.NET ViewState详解
  11. TP-LINK 无线路由器桥接步骤
  12. Linux下C语言编程入门-8关于计时器
  13. C程序设计--结构体+单向链表
  14. PEANUT西门子CNC OPC UA连接说明
  15. UVC系列2-探索Android UVC协议
  16. mysql数据库库推荐书籍
  17. Java中的拦截器和过滤器有什么区别
  18. 朱军清华大学计算机系是哪里人,朱军(清华大学计算机系教授)_百度百科
  19. 第六章 传统金融行业的区块链战略
  20. 第一章 由内而外全面造就自己

热门文章

  1. Nature:“解构”母爱
  2. 人工智能在能源行业的5个应用
  3. 毕业三年薪水翻三倍!?你想要吗?
  4. ​10.24,华为鲲鹏要为程序员发福利!
  5. ​如何设计一个安全可靠的 API 接口?
  6. jvm误区--动态对象年龄判定
  7. 唯一聚集索引上的唯一和非唯一非聚集索引
  8. 执行startx后Ubuntupassword正确进不去的问题
  9. 对第三方 SDK 依赖冲突,重新打个包试试
  10. 从请求管道深入剖析HttpModule的实现机制,有图有真相