跨域Ajax的原生JavaScript实现及原理

服务端 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. 随机函数名 如果和之前创建的 随机函数名 相同,需要重新创建,具体的代码怎么实现?
  2. 每次执行完函数后,如何清除当前函数?
//问题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的答案:
//还没想出来
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值