封装 JSONP

随机生成函数名是为了确保唯一性。因为 JSONP 的回调实际上是创建了一个临时的函数供加载的 js 调用。如果回调函数不是随机的,那在连续 JSONP 请求时,后发出的请求回调函数会覆盖掉之前的,返回的数据会出问题。

1
2
3
4
5
6
7
8
9
10
function getJSONP(url, callback) {
if (!url) {
return;
}
// 声明数组来随机生成函数名
var a = ["a", "b", "c", "d", "e", "f", "g", "h", "i"];
var r1 = Math.floor(Math.random() * a.length);
var r2 = Math.floor(Math.random() * a.length);
var r3 = Math.floor(Math.random() * a.length);
var name = "getJSONP" + a[r1] + a[r2] + a[r3]; //name上加的前缀可以任意取名

回调函数名传给服务器之后,这个回调函数名是要作为 getJSONP 函数的一个属性的,所以 cbname=‘getJSONP.’+name; 回调函数名前面拼接的一定得是‘getJSONP.’和 getJSONP 函数同命,表示获取 getJSONP 函数的属性。

1
2
3
4
5
6
7
8
9
var cbname = "getJSONP." + name;
// 判断url地址是否含有?
if (url.indexOf("?") === -1) {
url += "?jsonp=" + cbname;
} else {
url += "&jsonp=" + cbname;
}
// 动态创建script标签
var script = document.createElement("script");

核心,定义被脚本执行的回调函数
getJSONP 函数的精华在于定义被脚本执行的回调函数,定义完之后要被销毁。
函数内不要进行 if 判断,而是用 try catch 来尝试执行。
每跨域一次,都要生成一个回调函数,跨域多了生成的回调函数也多,会污染环境,所以在跨域执行完之后要把回调函数删掉。所以用 try catch 会更适合。

1
2
3
4
5
6
7
8
9
10
11
12
getJSONP[name] = function () {
try {
callback && callback(data); //两个必须都为真,才为真,若是第一个为假的话,第二个就不执行了,直接返回。
} catch (e) {
//
} finally { // finally 语句在 try 和 catch 之后无论有无异常都会执行。这里不管函数有没有执行成功,都要删除回调函数和script标签,避免污染环境。
// 注意: catch 和 finally 语句在语法上都是可选的,但你在使用 try 语句时必须至少使用一个。
//最后删除该函数及script标签
delete getJSONP[name];
script.parentNode.removeChild(script);
}
}
1
2
3
4
// 定义script的src
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}

最后跨域调用尝试

1
2
3
getJSONP("http://class.imooc.com/api/jsonp", function (data) {
console.log(data);
});