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

image
最近面试中问到候选人 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 。

代码实现

函数定义:

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

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

使用方法:

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

常规实现

先写一个JSON转Query参数的Function

JAVASCRIPT
function objectToQuery(obj) { const arr = []; for ( var i in o) { arr.push(encodeURIComponent(i)+ '=' +encodeURIComponent(o[i])); } return arr.join('&'); }
JAVASCRIPT
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实现

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

JAVASCRIPT
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!'); } } }) }

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

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

【END】
阅读 22 | 发布于 2020-05-18
暂无评论