• 跨域的异步请求二


    说一下JSONP的原理,我们的跨域严重依赖这东西。其实这是一种脚本注入的行为,前后涉及两段脚本片断,并公开一些全局变量。为了放便讲解,我把例子先放出来:

    <!doctype html>
    <html>
      <head>
      <title>jsonp原理 by 司徒正美</title>
      <meta charset="utf-8"/>
      <meta content="IE=8" http-equiv="X-UA-Compatible"/>
      <meta name="keywords" content="jsonp原理 by 司徒正美" />
      <meta name="description" content="jsonp原理 by 司徒正美" />
      <script type="text/javascript" charset="utf-8">
        window.onload = function(){
          var script = document.createElement("script");
          window.jsonpCallBack = function(json){
            for(var item in json){
              alert(item)
            }
            if(window.JSON){
              alert(window.JSON.stringify(json))
            }
          }
          script.src = "http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=jsonpCallBack"
          var head = document.getElementsByTagName("head")[0];
          head.appendChild(script);
        }
      </script>
    
    </head>
    <body>
      <h1>JSONP原理by 司徒正美</h1>
    
    </body>
    </html>
    

    这个页面上有一个内部脚本,我称之为脚本片断1。它将在页面加载后,动态生成一个script标签,设置src并加入DOM树中。重点就是这个src的构成,它是一个普通url与一些查询参数组成的。这些查询参数视公司而定,但其中用一个肯定是用来标识回调函数的名字。我这里是jsonpCallBack,既然有函数名,必然要有函数本身。由于我们定义这函数时,不一定在全局作用域下,因此我为它加了个“window前缀”。有一个有趣的比喻说,全局作用域就像一个公共厕所,虽然你免不了要去它那里,但你也应该少去它那里。如果可能,我们在使用了它以后,也应该努力打扫一下它,避免全局变量过多,减少命名冲突的风险。不过,现在我们不打扫了,这还涉及一个问题。我们看后台是怎么处理的。

    既然是这script标签添加src属性,它理应是一个JS文件,要不会报错,在标准浏览器下我们可以使用onerror来监听它。但现在我们怎么看它也不是一个JS文件。这就全然靠目标服务器的造化了!当我们把这段奇怪的东西发送过去时,它应该会把这些参数与函数名进行分解,根据参数生成一个JSON,写入一个JS文件(动态生成的),然后取得函数名,由于这是一个全局函数,可以直接调用。嘛,反正此时是文本状态,在函数名加一对括号,把JSON放到中间便是。换言之,这个JS文件是这个样子:

    //动态加载的新JS文件
          var json = {"title":"Recent Uploads tagged cat",
            "link":"http://www.flickr.com/photos/tags/cat/",
            "description":"",
            "modified":"2010-05-25T17:21:19Z",
            "generator":"http://www.flickr.com/",
            "items":[/**很多很多的数据,这里略**/]}
          window.jsonpCallBack(json) 
    

    因此,一旦这JS文件被解析,就能执行回调函数与处理我们请求回来的数据(JSON)。好了,是时候打扫一下了。首先这个用于后台交互的script标签,在回调函数执行完没有用了,我们可以移除它。另,回调函数执行完了,这个函数也没有用了,也可以移除了。

            window.jsonpCallBack = function(json){
              for(var item in json){
                alert(item)
              }
              if(window.JSON){
                alert(window.JSON.stringify(json))
              }
    
              script.parentNode && script.parentNode.removeChild( script );
              //这是一种权宜之计,虽然值设为undefined了,但我们还能在for...in循环中遍历出jsonpCallBack
              window.jsonpCallBack = undefined;
              try {
                //彻底删除jsonpCallBack这个成员
                //只可惜IE的window是基于COM,没有delete这方法,因此失败!
                alert(window instanceof Object)
                delete window.jsonpCallBack
              } catch(e) {}
             
            }
    

    注释中也说了,真是请神容易送神难。为了我们需要改变策略,采用单足独立式结构,减少全局污染!换言之,这些回调函数的方法名必须成为某个对象的成员!为了,提高效率,我搞了对象数组(如果只用一个对象来装载它们,for...in循环恶心死了!)

    //小型JSONP类库 by 司徒正美
          var dom = {};//暴露类库唯一一个全局变量作为命名空间
          dom.jsonp = function(url,obj,method){
            var self = arguments.callee;
            if (self.callbacks.length) {//实现异步装载,异步回调
              setTimeout(function() {self(url,obj,method)}, 0);
              return;
            }
            var query =[];
            for(var i in obj){
              query.push(i +"="+obj[i])
            }
            var callback = "dom.jsonp.callback"
            url = url+"?"+ query.join("&")+"&"+method+"="+callback;
            var script = document.createElement("script");
            script.src = url;
            var head = document.getElementsByTagName("head")[0];
            var obj = {
              method:function(json){
                for(var item in json){
                  alert(item)
                }
                if(window.JSON){
                  alert(window.JSON.stringify(json))
                }
                script.parentNode && script.parentNode.removeChild( script );
              }
            }
            dom.jsonp.addCallback(obj)
            head.appendChild(script);
          }
          dom.jsonp.callbacks = []
          dom.jsonp.addCallback = function(obj){
            this.callbacks.push(obj)
          }
          dom.jsonp.callback = function(json){//统一处理回调函数
            var objs = this.callbacks;
            for (var i=0,el;el=objs[i++];) {
              el.method.call(el,json)
            }
            this.callbacks = [];
          }
    
          window.onload = function(){
            dom.jsonp("http://api.flickr.com/services/feeds/photos_public.gne",{
              tags: 'cat',
              tagmode: 'any',
              format: 'json' },"jsoncallback");
            
            dom.jsonp("http://api.cnet.com/restApi/v1.0/techProductSearch",{
              partTag: 'mtvo',
              iod: 'hlPrice',
              viewType: 'json',
              results: '100',
              query: 'ipod'},"callback");
            
            dom.jsonp("http://del.icio.us/feeds/json/fans/stomita",null,"callback");
          }
    

    //http://www.blogjava.net/emu/articles/129240.html var dom = {};//暴露类库唯一一个全局变量作为命名空间 //{url,obj,callbackName,callback} dom.jsonp = function(url,obj,method,callback,caching){ method = method || "callback"; var script = document.createElement('script'); var index = dom.jsonp.index++; var query = dom.toQueryString(obj); callback = callback || function(json){ for(var item in json){ alert(item) } if(window.JSON){ alert(window.JSON.stringify(json)) } } url = url+"?"+ query+"&"+method+"="+escape('dom.jsonp.callbacks._'+index); if (!caching) url += '&rand='+Math.random(); dom.jsonp.callbacks['_'+index] = function() { delete dom.jsonp.callbacks['_'+index]; if (arguments.length==1) { callback(arguments[0]); } else { var arry = []; for (var i=0; i
  • 相关阅读:
    课时15.DTD文档声明下(了解)
    Python-01 学习第一节
    常用数据库备份还原命令
    Oracle排除记录集
    存储过程分页语句
    TFS统计编码行数语句
    数据库所有表替换所有列的特定字符串
    MSSQL查询所有数据库表,指定数据库的字段、索引
    统计整个库所有表的记录数
    执​行​o​r​a​c​l​e​函​数​的​四​种​方​法
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1744045.html
Copyright © 2020-2023  润新知