原生JS实现JSONP

JSONP

  之前写JS哈希算法的时候,就说要把原生JSONP的代码贴进来,正好前几天阿里三面在线编码,考到了这个东西,不小心写成了异步加载JS并执行回调的逻辑了。现在把修正后的版本贴进来。

  Talk is cheap, so I will show you the code.😆
  
  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 题目:原生代码实现jsonp函数,
// 带超时、 兼容性好: `jsonp(url,params,{timeout,success,error,prefix})`
function jsonp(url, params, options) {
// 该方法递归地将p拷贝进c中
var extend = function(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object' && !!p[i]) {
c[i] = (p[i].constructor === Array) ? [] : {};
extend(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
};
// 该方法将对象转换成URL查询参数
var encodeObjectToQueryString = function(param, key) {
var paramStr = '';
var separator = '&';
var mappingOperator = '=';
if (param instanceof String || typeof(param) == 'string' || param instanceof Number || typeof(param) == 'number' || param instanceof Boolean || typeof(param) == 'boolean') {
paramStr += separator + key + mappingOperator + encodeURIComponent(param);
} else {
for (var i in param) {
var value = param[i];
var k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i);
paramStr += separator + encodeObjectToQueryString(value, k);
}
}
return paramStr.substr(1);
};
// 定义参数默认值
var defaultOptions = {
timeout: 0,
success: function() {},
error: function() {},
prefix: 'callback'
};
// 调用方法拷贝默认值
options = extend(options, defaultOptions);
// 定义定时器timer
var timer = null;
// 定义callback函数名,这里加了随机数后缀防止重复
var callbackName = 'jsonpCallback_' + Math.floor(Math.random() * 2147483648).toString(36);
// 实例化 script DOM对象
var cdjs = document.createElement('script');
cdjs.type = 'text/javascript';
cdjs.src = url + '?' + encodeObjectToQueryString(params) + '&' + options.prefix + '=' + callbackName;
// 创建jsonp失败回调
if (options.timeout !== 0) {
// 设置定时器
timer = setTimeout(function() {
// 调用失败回调
options.error && options.error(url,params,options);
// 一些清理工作
document.getElementsByTagName('head')[0].removeChild(cdjs);
window[callbackName] = null;
}, options.timeout);
}
// 创建jsonp成功回调
window[callbackName] = function(resp) {
// 停止定时器
clearTimeout(timer);
// 调用成功回调
options.success && options.success(resp);
// 一些清理工作
document.getElementsByTagName('head')[0].removeChild(cdjs);
window[callbackName] = null;
};
// 追加元素,触发请求
document.getElementsByTagName('head')[0].appendChild(cdjs);
}

下面是一个测试用例,用了百度的老搜索接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jsonp('http://suggestion.baidu.com/su', {
wd: 'Alibaba',
someObject: { key: 'value', subObject: { arrayInObj: [1, 2] } },
someBoolean: true,
someArray: [1, 2, 3]
}, {
prefix: 'cb',
timeout: 2000,
success: function(resp) {
console.log(resp);
},
error: function() {
console.log('JSONP timeout.');
console.log(arguments);
}
});