服务端 api.php
// 服务端动态拼接一段js脚本,让脚本在页面中执行
$id = $_GET['id'];
//取得页面get请求中的参数id的值
//定义一个数组,该数据最后一个元素从发起请求的页面传递过来
$arr = array(1, 2, 3, 4, 5, $id);
header('Content-Type: text/javascript');
//这里如果echo一个js变量,会污染页面的全局使用域
//所以前后端开发人员 约定一个全局函数onLoadData
//虽然函数也会污染,不过只要大写都遵守这个约定,之后避开这个函数名,还是很容易的
//函数的好处是,如果以后有更多的变量,也在这个约定函数里面操作就行了
echo 'onLoadData('.json_encode($arr).');';
客户端html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp 函数的封装</title>
</head>
<body>
<script>
//jsonp封装成和jQuery差不多
// $.get('http://api.douban.com/api.php', {id: 23, name: 'LebronJames'}, function() {
// console.log(data);
// })
//jsonp 函数封装思路
//1 自动添加一个全局函数
//2 自动添加一个script标签
function jsonp(url, param, callback) {
//1. 创建回调
window.onLoadData = callback;
//2. 创建script标签
var script = document.createElement('script');
var queryString = '?';
for (var key in params) {
queryString += key + '=' params[key] + '&';
//循环最后,queryString的尾巴多了一个&
// queryString => ?id=23&name=LebronJames&
}
script.src = url + queryString;
//追加到
document.body.appendChild(script);
}
//调用jsonp
jsonp(
'http://api.douban.com/api.php',
{id: 23, name: 'LebronJames'},
function() {
console.log(data);
}
)
</script>
</body>
</html>
这时控制台得到
[1, 2, 3, 4, 5, 23]
思考一下: 如果html页面中 script 里面的 jsonp 方法被调用两次,会是什么结果
//其它代码不变,这里只写调用的时候
jsonp(
'http://api.douban.com/api.php',
{id: 23, name: 'LebronJames'},
function() {
console.log('LebronJames',data);
}
)
jsonp(
'http://api.douban.com/api.php',
{id: 24, name: 'KobeBryant'},
function() {
console.log('KobeBryant',data);
}
)
再看看最终的打印结果
KobeBryant [1, 2, 3, 4, 5, 23]
KobeBryant [1, 2, 3, 4, 5, 24]
虽然数组的结果不一样,但打印的第一个参数都是 KobeBryant,证明两次都是执行了第二个次调用传递进去的那个回调函数 为什么呢????
原因就是它:
window.onLoadData = callback;
我们在调用 jsonp(url, param, callback)
的时候,传递的 callback 被复制给了全局方法 onLoadData
,第二次调用jsonp的callback函数覆盖了第一次调用时,传递的callback。又由于jsonp是异步方法,两次调用并不会立即得到结果,所以最终异步完成,执行第一次调用的callback时,callback已经变成了第二次调用时的函数代码
如何解决呢?答案是创建动态函数名
只要将window.onLoadData ;
的 onLoadData 写成动态的,此时每次调用,都会创建一个全局新的全局方法!
但是服务端仍然调用的是onLoadData方法,怎么办?将每次创建的方法名,在此次请求中,作为参数传递到服务端,然后服务端每次都返回这个新方法的调用
修改后的html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp 函数的封装</title>
</head>
<body>
<script>
function jsonp(url, param, callback) {
//1. 创建回调 这里现在需要动态全局方法
var callbackName = 'dynamics_func_name_' + Math.random().toString().replace('.','');
window[callbackName]= callback;
//2. 创建script标签
var script = document.createElement('script');
var queryString = '?';
for (var key in params) {
queryString += key + '=' params[key] + '&';
}
script.src = url + queryString + 'callback=' + callbackName;
//追加到
document.body.appendChild(script);
}
jsonp(
'http://api.douban.com/api.php',
{id: 23, name: 'LebronJames'},
function() {
console.log('LebronJames',data);
}
)
jsonp(
'http://api.douban.com/api.php',
{id: 24, name: 'KobeBryant'},
function() {
console.log('KobeBryant',data);
}
)
</script>
</body>
</html>
修改后的服务端 api.php
$id = $_GET['id'];
//取得页面get请求中的参数id的值
$callback = $_GET['callback '];
//取得页面get请求中的参数callback 的值
//定义一个数组,该数据最后一个元素从发起请求的页面传递过来
$arr = array(1, 2, 3, 4, 5, $id);
header('Content-Type: text/javascript');
echo $callback.'('.json_encode($arr).');';
仍然有两个问题
- 随机函数名 如果和之前创建的 随机函数名 相同,需要重新创建,具体的代码怎么实现?
- 每次执行完函数后,如何清除当前函数?
//问题1的答案:
if(callbackName != window[callbackName]) {
window[callbackName] = callback;
}
else {
while(callbackName == window[callbackName]) {
//在while里面动态定义全局callbackName,是为了防止Math.random()取到相同的随机值,虽然这种几率很小很小
callbackName = 'dynamics_func_name_' + Math.random().toString().replace('.','');
}
window[callbackName] = callback;
}
//问题2的答案:
//还没想出来