基于原生JS实现仿JQ中的Ajax

本文探讨了AJAX的跨域问题,介绍了同源策略及其影响,并详细解析了jQuery中Ajax的JSONP用法。通过示例展示了如何用原生JavaScript实现JSONP,解释了为什么JSONP仅支持GET请求。最后提到了CORS头在跨域中的作用,以及解决跨域的其他策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  从AJAX诞生那天起,XMLHttprequest对象不能跨域请求的问题就一直存在,这似乎是一个很经典的问题了。这是由于同源策略所导致。这里引入下同源策略的百科:

  同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

 

  围绕Ajax的两大问题就是 一、Ajax以何种格式来交换数据;二、就是跨域问题。交换数据的格式就是json(JavaScript Object Notation) 一种轻量级的数据交换格式。所以这里主要来研究如何解决Ajax的跨域问题。

    首先看看jQuery中的Ajax的调用用法:

$.ajax({
	url : "",
	type : "get/post",
	dataType : "json/jsonp",
	data : {
		jian : ,
		zhi : ,
		dui : 
	},
	success : function(data){
		//请求成功后
	}
});

  在调用jQuery中的Ajax解决跨域问题时,只需要在dataType中选择是json还是jsonp即可。那么问题来了,jsonp是什么呢?

  贴一下百科:

  JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。

  首先,jsonp与json虽然只差一个"p"的区别,但是绝对不是一个类型的东西。json是数据格式,而jsonp是一种跨域请求方式,两者可谓是天差地别。

  根据上面百科的解释,我们可以了解到jsonp实现跨域的方法就是通过创建script标签。利用script标签的src属性来获取到“任意的JavaScript”。顺便一提,src 属性是用来规定外部脚本文件的 URL。话不多说,我们来实现一下:

<script>
    //成功后的回调函数
    var callBack = function(data){
        //请求成功后执行
    };
    //提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
    var url = "";
    // 创建script标签,设置其属性
    var script = document.createElement('script');
    script.setAttribute('src', url);
    // 把script标签加入head,此时调用开始
    document.getElementsByTagName('head')[0].appendChild(script); 
 </script>

   如此一来,我们就最简单的实现了jsonp跨域。整理一下思路:1、创建script标签;2、拼接script标签的src;3、将script标签插入到head标签中;4、回调函数

  可以看到,思路并不复杂。

  但是,在一开始引用的jQuery中的Ajax里,我们只需要在dataType那里设置是json还是jsonp就能实现,这是怎么做到的呢?

  先上代码,我自己封装的与jQuery中的Ajax近似用法的对象:

var my = {
	createXHR : function(){
		if(window.XMLHttpRequest){	
			//IE7+、Firefox、Opera、Chrome 和Safari
			return new XMLHttpRequest();	
		}else if(window.ActiveXObject){
			//IE6 及以下
			return new ActiveXObject('Microsoft.XMLHTTP');
		}else{
			throw new Error('浏览器不支持XHR对象!');
		}
	},

	ajax : function(obj){
		if(obj.dataType === 'json'){
			var xhr = this.createXHR();		//创建XHR
			//通过使用JS随机字符串解决IE浏览器第二次默认获取缓存的问题
			obj.url = obj.url + '?rand=' + Math.random();
			obj.data = this.formatParams(obj.data);	//通过params()将名值对转换成字符串
			
			if(obj.method === 'get'){
				obj.url += obj.url.indexOf('?') == -1 ? '?' + obj.data : '&' + obj.data; 
			}
			if(obj.async === true){   //true表示异步,false表示同步
				//使用异步调用的时候,需要触发readystatechange 事件
				xhr.onreadystatechange = function () {
					if (xhr.readyState == 4) {   //判断对象的状态是否交互完成
						callback();		 //回调
					}
				};
			}
			//在使用XHR对象时,必须先调用open()方法,
			//它接受三个参数:请求类型(get、post)、请求的URL和表示是否异步。
			xhr.open(obj.method, obj.url, obj.async);
			if (obj.method === 'post'){
				//post方式需要自己设置http的请求头,来模仿表单提交。
				//放在open方法之后,send方法之前。
				xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
				xhr.send(obj.data);		//post方式将数据放在send()方法里
			} else {
				xhr.send(null);			//get方式则填null
			}
			if(obj.async === false){  //同步
				callback();
			}

			var callback = function(){
				//判断http的交互是否成功,200表示成功
				if(xhr.status == 200){
					obj.success(xhr.responseText);			//回调传递参数
				} else {
					alert('获取数据错误!错误代号:' + xhr.status + ',错误信息:' + xhr.statusText);
				}	
			};
		} else if (obj.dataType === "jsonp"){
			var oHead = document.getElementsByTagName("head")[0];
			var oScript = document.createElement("script");
			var callbackName = 'callback' + new Date().getTime();
			var params = this.formatParams(obj.data) + '&callback=' + callbackName;		//按时间戳拼接字符串

			//拼接好src
			oScript.src = obj.url.split("?") + '?' + params;
			//插入script标签
			oHead.insertBefore(oScript, oHead.firstChild);
			//jsonp的回调函数
			window[callbackName] = function(json){
				var callback = obj.success;
				callback(json);
				oHead.removeChild(oScript);
			};
		}
	},

	formatParams : function(data){
		var arr = [];
		for(var i in data){
			//特殊字符传参产生的问题可以使用encodeURIComponent()进行编码处理
			arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
		}
		return arr.join('&');
	}
};

   调用时的用法:

my.ajax({
	method : 'get',
	url : '',
	data : {
		jian : '',
		zhi : '',
		dui : ''
	},
	dataType : 'json/jsonp',
	async : 'true/false',
	success : function(data){
		//.......
	}
});

  这里可以看到与jQuery的Ajax相差无几。详细的细节在注释里写的比较清楚,这里讲一下大体的思路。

  由于json与jsonp是完全不同的东西,所以在ajax方法里首先判断dataType是json还是jsonp。如果是json,则创建xhr对象,在上面代码中我单独把创建xhr写成了一个方法。由于不管是json还是jsonp都需要拼接url,所以把这个也单独写成了formatParams方法,方便调用。这里值得一提的是jsonp只支持get请求而不支持post请求。

  做一个小实验,如果在需要跨域的时候用了在dataType那里写了json会怎样:


  可以看到浏览器会报错,这里提一下报错的那个   “原因:CORS头缺少‘Access-Control-Allow-Origin’”,其意思是没有关闭同源策略,不允许跨域请求。所以是可以通过设置关掉同源策略,但是那样会很不安全。

 


  解决跨域的问题的方法还有很多,可以根据实际的需求来选择选择不同的策略。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值