面试中如何实现一个高质量的JSONP

4,675 阅读2分钟

最近面试中问到候选人 JSONP ,发现大多数候选人 JSONP 原理都可以回答正确,但是如果让写代码实现一个 JSONP 函数,有很多人都写不出来,或者是考虑不全面,写出来的代码没法使用。

接下来我们一起来看,如何实现一个高质量的 JSONP 。

首先先来说一下 JSONP 的原理:

JSONP 原理

全称 JSON with Padding,是解决跨域问题的一种方案。

由于同源策略的限制,浏览器只允许请求当前源(域名、协议、端口)的资源,而 HTML 的 script 元素是一个例外。利用 script 元素的这个开放策略,网页可以得到从其它来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。

具体的实现上有几个关键点:

1、服务端返回的数据不是 JSON,而是 JavaScript,也就是说 contentType 为 application/javascript ,内容格式为callbackFunction(JOSN)

2、callbackFunction 需要注册在 window 对象上,因为 script 加载后的执行作用域是window作用域。

3、需要考虑同时多个 JSONP 请求的情况,callbackFunction 挂在 window 上的属性名需要唯一。

4、请求结束需要移除本次请求产生的 script 标签和window上的回调函数。

5、最好支持 Promise 。

代码实现

函数定义:

function jsonp ({url, data, callback}) {
    
}

url 是请求地址,data(Object类型) 是请求参数,callback(Function类型) 是回调函数。

使用方法:

jsonp({
  url: 'url',
  data: {  
    key1: 'value1'  
  },  
  callback (data) {  
    // data 是服务端返回的数据  
  }  
})

常规实现

先写一个JSON转Query参数的Function

function objectToQuery(obj) {
    const arr = [];
    for ( var i in obj) {
      arr.push(encodeURIComponent(i)+ '=' +encodeURIComponent(obj[i]));
    }
    return arr.join('&');
}
function jsonp ({url, data, callback}) {
    const container = document.getElementsByTagName('head')[0];
    const fnName = `jsonp_${new Date().getTime()}`;
    const script = document.createElement('script');
    script.src = `${url}?${objectToQuery(data)}&callback=${fnName}`;
    script.type = 'text/javascript';
    container.appendChild(script);

    window[fnName] = function (res) {   
        callback && callback(res);
        // 很多候选人漏掉clean这块
        container.removeChild(script);
        delete window[fnName];
    }

    script.onerror = function() { // 异常处理,也是很多人漏掉的部分
        window[fnName] = function() {
        callback && callback(
          'something error hanppend!'
        )
        container.removeChild(script);
        delete window[fnName];
      }
    }
    
}

Promise实现

在常规实现的基础上改造。

function jsonp ({url, data, callback}) {
    const container = document.getElementsByTagName('head')[0];
    const fnName = `jsonp_${new Date().getTime()}`;
    const script = document.createElement('script');
    script.src = `${url}?${objectToQuery(data)}&callback=${fnName}`;
    script.type = 'text/javascript';
    container.appendChild(script);
    
    return new Promise((resolve, reject) => {
    
        window[fnName] = function (res) {   
            // 很多候选人漏掉clean这块
            container.removeChild(script);
            delete window[fnName];
            resolve(res);
        }
    
        script.onerror = function() { // 异常处理,也是很多人漏掉的部分
            container.removeChild(script);
            delete window[fnName];
            reject('something error hanppend!');
          }
        }
  })
    
}

希望大家都能找到一个好工作!

需要内推的可以加我微信: