JSONP
前言
在面试中经常会问到怎么解决跨域,面试题中经常会听到一个词 JSONP,利用 JSONP 跨域,闲暇之余就去看了看 JSONP 到底是什么东西,在此记录一下。
什么是 JSONP
JSONP 是解决跨域问题的一种方案,和 JSON 并没有关系,而是一种绕过跨域的技巧
实现原理
- 首先它的原理就是利用 script 标签不受同源策略的限制,将 src 指向服务端地址
- 将 js 方法名作为参数传递到服务端(www.xxx.com?jsonCallback=callbackFunction), 然后由服务端用这个方法名也就是callbackFunction来包裹数据(callbackFunction(data)),相当于在前端执行这个方法
- 由于 callbackFunction 在前端是没有这个方法的,所以要在 scirpt 的 src 访问这个 url 之前在 window 上注册这个方法,这样在服务端返回(callbackFunction(data))执行的时候就可以在 window 上定义的这个方法去接收数据
JSONP 实现
function jsonp(url, opts, fn) {
// 因为opts是可选参数,所以先判断opts是不是一个function
if ("function" == typeof opts) {
fn = opts;
opts = {};
}
if (!opts) opts = {};
// String 处理响应的全局回调函数的前缀 默认为__jp
var prefix = opts.prefix || "__jp",
count = 0;
// String 处理响应的全局回调函数的名称 如果没有传入name则默认 prefix + 递增计数器
var id = opts.name || prefix + count++;
// String 用于指定回调的查询字符串参数的名称
var param = opts.param || "callback";
// Number 响应超时,默认是60000
var timeout = null != opts.timeout ? opts.timeout : 60000;
var enc = encodeURIComponent;
var target = document.getElementsByTagName("script")[0] || document.head;
var script;
var timer;
// 超时时间
if (timeout) {
timer = setTimeout(function () {
cleanup();
// 如果超时抛出错误信息Timeout
if (fn) fn(new Error("Timeout"));
}, timeout);
}
// 清除定时器
function cleanup() {
if (script.parentNode) script.parentNode.removeChild(script);
window[id] = noop;
if (timer) clearTimeout(timer);
}
function cancel() {
if (window[id]) {
cleanup();
}
}
// 在window顶层对象增加一个属性方法,接收参数为响应返回的数据
window[id] = function (data) {
debug("jsonp got", data);
cleanup();
if (fn) fn(null, data);
};
// 处理请求地址
url += (~url.indexOf("?") ? "&" : "?") + param + "=" + enc(id);
url = url.replace("?&", "?");
script = document.createElement("script");
script.src = url;
target.parentNode.insertBefore(script, target);
return cancel;
}
ajax 使用 jsonp
以调用百度位置方法为例
$.ajax({
type: "get", // 设置请求类型
timeout: 5000, // 设置超时时间
url: "[请求url]",
data: {
ak: "[填写ak]",
output: "json",
coordtype: "bd09ll",
location: `${lat},${lng}`,
extensions_poi: 1,
},
dataType: "jsonp", // 指定类型为jsonp
success: function (data) {
console.log(data); // 接收数据
},
error: function (err) {
alert("获取定位失败");
console.error(err);
},
});