关于【ajax和json】十问(读《红宝书》)
Sunny-lucking opened this issue · comments
第一问:原生js写一个简单的ajax请求
典型的xhr建立ajax的过程。(涵盖了ajax的大部分内容)
- new一个xhr对象。(XMLHttpRequest或者ActiveXObject)
- 调用xhr对象的open方法。
- send一些数据。
- 对服务器的响应过程进行监听,来知道服务器是否正确得做出了响应,接着就可以做一些事情。比如获取服务器响应的内容,在页面上进行呈现。
//1、创建一个ajax对象
var xhr = new XMLHttpRequest();
//3、绑定处理函数,我们写的代码,都在这里面
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
console.log(xhr.responseText)
} else {
console.log("fail")
}
}
}
//2、进行初始化
xhr.open('get','http://js.com/day6/ajax_get.php');
//4、发送请求
xhr.send(null);
注意点:
- 如果不需要通过请求头发送数据,必须传入null
- 为确保跨浏览器兼容性,建议
xhr.onreadystatechange
事件处理程序写在xhr.open
前面 - setRequestHeader必须写在
xhr.open
和xhr.send(null)
之间
第二问:readyState各阶段的含义
- 未初始化,但是已经创建了XHR实例
- 调用了open()函数
- 已经调用了send()函数,但还未收到服务器回应
- 正在接受服务器返回的数据
- 完成响应
第三问:怎么终止请求
再接收到响应之前还可以调用obort()方法来取消异步请求
xhr.abort()
调用这个方法后,XHR对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求之后,还应该对XHR对象进行解引用操作。由于内存原因,不建议重用XHR对象
第四问:如何利用xhr做请求的进度条
另一个革新是添加了progress事件,这个时间会在浏览器接受新数据期间周期性的触发,而onprogress事件处理程序会接收到一个event对象,其target属性是XHR对象,但包含着两个额外的属性:position和totalSize。其中position表示已经接受的字节数,totleSize表示根据Content-Length响应头部确定的预期字节数。
xhr.onprogress = function (event) {
var divStatus = document.getElementById("status");
divStatus.innerHTML = "Received" + event.position + "of" + event.totalSize + "bytes";
};
第五问:谈一下跨域
1、JSONP跨域:
jsonp的原理就是利用<script>
标签没有跨域限制,通过<script>
标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
2、跨域资源共享(CORS):
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
浏览器将CORS跨域请求分为简单请求和非简单请求。
只要同时满足一下两个条件,就属于简单请求
(1)使用下列方法之一:
- head
- get
- post
(2)请求的Heder是
- Accept
- Accept-Language
- Content-Language
- Content-Type: 只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain
不同时满足上面的两个条件,就属于非简单请求。浏览器对这两种的处理,是不一样的。
简单请求:
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
非简单请求:
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
预检请求:
预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。请求头信息里面,关键字段是Origin,表示请求来自哪个源。除了Origin字段,"预检"请求的头信息包括两个特殊字段。
3、nodejs中间服务器代理跨域
利用本地服务器(跟前端项目同协议,同域名,同端口)来代理转发,利用的是同源策略是只发生在浏览器,而两个服务端是不会出现跨域问题的。
webpack种配置跨域就是这个原理。
webpack.config.js部分配置:
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain2.com:8080', // 代理跨域目标接口
changeOrigin: true,
secure: false, // 当代理某些https服务报错时用
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}],
noInfo: true
}
}
4、使用图片ping跨域
图像Ping跨域请求技术是使用标签。一个网页可以从任何网页中加载图像,不用担心跨域不跨域。这也是在线广告跟踪浏览量的主要方式。也可以动态地创建图像,使用它们的 onload 和 onerror事件 处理程序来确定是否接收到了响应
动态创建图像经常用于图像Ping:图像Ping是与服务器进行简单、单向的跨域通信的一种方式。 请求的数据是通过査询字符串形式发送的,而响应可以是任意内容,但通常是像素图或204响应。通过图像Ping,浏览器得不到任何具体的数据,但通过侦听load和error事件,它能知道响应是什么时候接收到的
图像Ping有两个主要的缺点
,一是只能发送GET请求,二是无法访问服务器的响应文本。因此,图像Ping只能用于浏览器与服务器间的单向通信
第六问:JS跨域方案JSONP与CORS的各自优缺点以及应用场景?
两者优点与缺点大致互补,放在一块介绍:
JSONP的主要优势在于对浏览器的支持较好;虽然目前主流浏览器支持CORS,但IE10以下不支持CORS。
JSONP只能用于获取资源(即只读,类似于GET请求);CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被玩虐,但大部分情况下GET已经能满足需求了)
JSONP的错误处理机制并不完善,我们没办法进行错误处理;而CORS可以通过onerror事件监听错误,并且浏览器控制台会看到报错信息,利于排查。
JSONP只会发一次请求;而对于复杂请求,CORS会发两次请求。
始终觉得安全性这个东西是相对的,没有绝对的安全,也做不到绝对的安全。毕竟JSONP并不是跨域规范,它存在很明显的安全问题:callback参数注入和资源访问授权设置。CORS好歹也算是个跨域规范,在资源访问授权方面进行了限制(Access-Control-Allow-Origin),而且标准浏览器都做了安全限制,比如拒绝手动设置origin字段,相对来说是安全了一点。
但是回过头来看一下,就算是不安全的JSONP,我们依然可以在服务端端进行一些权限的限制,服务端和客户端也都依然可以做一些注入的安全处理,哪怕被攻克,它也只能读一些东西。就算是比较安全的CORS,同样可以在服务端设置出现漏洞或者不在浏览器的跨域限制环境下进行攻击,而且它不仅可以读,还可以写。
应用场景:
如果你需要兼容IE低版本浏览器,无疑,JSONP。
如果你需要对服务端资源进行谢操作,无疑,CORS。
其他情况的话,根据自己的对需求的分析和对两者的理解来吧。
第七问:我要是硬要用CORS方法解决跨域呢?有没办法兼容IE低版本
大部分浏览器都已经实现了CORS(跨域资源共享)的规范,IE低版本浏览器却无法支持这一规范。
在ie8,9中,有XDomainRequest对象可以实现跨域请求。可以和xhr一起使用。这个对象拥有onerror,onload,onprogress,ontimeout四个事件,abort,open,send三个方法,contentType, responseText,timeout三个属性。具体参见XDomainRequest对象。
XDomainRequest对象有很多限制,例如只支持get、post方法、不能自定义请求的header头、不能携带cookie、只支持text/plain类型的内容格式等等。具体参见XDomainRequest对象限制。
因此虽然XdomainRequest作为ie8、9中的一种跨域手段,但是适用的业务场景还是比较局限的。
第八问:json.stringify()与json.parse()的区别
json.stringfy()将对象、数组转换成字符串;json.parse()将字符串转成json对象。
1.parse 用于从一个字符串中解析出json 对象
var str='{"name":"TMD","sex":"男","age":"26"}';
console.log(JSON.parse(str));//{name: "TMD", sex: "男", age: "26"}
2.stringify用于从一个对象解析出字符串
var o={a:1,b:2,c:3};
console.log(JSON.stringify(o));//{"a":1,"b":2,"c":3}
第九问:json.stringify()用于实现深拷贝时有什么缺点呢?
弊端:
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
3.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
第十问:现在我要用json.stringify()用于实现深拷贝,而且对象里有undefined或者函数,Date,该怎么办呢?
查看文档,发现JSON.parse是可以传一个转换结果的函数的
JSON.parse()方法也可以接收一个函数参数,在每个键值对儿上调用,这个函数被称为还原函数(reviver)。该函数接收两个参数,一个键和一个值,返回一个值
如果还原函数返回undefined,则表示要从结果中删除相应的键;如果返回其他值,则将该值插入到结果中
var book = {
"title": "javascript",
"date": new Date(2016,9,1)
}
var jsonStr = JSON.stringify(book);
//'{"title":"javascript","date":"2016-09-30T16:00:00.000Z"}''
console.log(jsonStr)
var bookCopy = JSON.parse(jsonStr,function(key,value){
if(key == 'date'){
return new Date(value);
}
return value;
})
console.log(bookCopy.date.getFullYear());//2016