事件流
事件流
事件流介绍
事件流的概念:事件流描述的是从页面中接收事件的顺序。事件发生后会在目标节点和 根节点之间按照特定的顺序传播、路径经过的节点都会接收到事件
例子:页面上有一个table 表格,分别在table
表格、tbody
表格体、tr
行、td
单元格上绑定了click
事件, 假如我在td
上执行了单击的操作,那么将会产生什么样的事件流呢?
第一种事件传递顺序是:先触发最外层的table元素,然后向内传播,依次触发tbody
、tr
与td
元素。
捕获型事件流
第二种事件传递顺序是:先触发最内层的td
元素,然后向外传播,依次触发tr、tbody与table
元素。
冒泡型事件流
一个完整的事件流实际包含了3个阶段:事件捕获阶段 > 事件目标阶段 >事件冒泡阶段
事件捕获阶段
主要表现是不具体的节点先接收事件,然后逐级向下传播,最具体的节点最后接收到事件。
事件目标阶段
表示事件刚好传到到用户产生行为元素上,可能是事件捕获的最后一阶段,也可能是事件冒泡的第一个阶段。
事件冒泡阶段
主要表现是最具体的元素先接收事件,然后逐级向上传播,不具体的节点最后接收事件。
例子:使用addEventListener()
函数绑定的事件在默认情况下,即第三个参数默认为false
时,按照冒泡型事件流处理。
<table border="1">
<tbody>
<tr>
<td>这是td元素</td>
</tr>
</tbody>
</table>
<script>
var table = document.querySelector('table');
var tbody = document.querySelector('tbody');
var tr = document.querySelector('tr');
var td = document.querySelector('td');
//捕获
table.addEventListener('click', function () {
console.log('table监听到了!')
}, true)
tbody.addEventListener('click', function () {
console.log('tbody监听到了!')
}, true)
tr.addEventListener('click', function () {
console.log('tr监听到了!')
}, true)
td.addEventListener('click', function () {
console.log('td监听到了!')
}, true)
</script>
不同的捕获和冒泡
把table
和tr
设置为事件捕获类型,把tbody
和td
设置为冒泡类型
<table border="1">
<tbody>
<tr>
<td>这是td元素</td>
</tr>
</tbody>
</table>
<script>
var table = document.querySelector('table');
var tbody = document.querySelector('tbody');
var tr = document.querySelector('tr');
var td = document.querySelector('td');
table.addEventListener('click', function () {
console.log('table触发');
}, false)
tbody.addEventListener('click', function () {
console.log('tbody触发');
}, true)
tr.addEventListener('click', function () {
console.log('tr触发');
}, false)
td.addEventListener('click', function () {
console.log('td触发');
}, true)
</script>
//触发顺序:tbody>td>tr>table
事件处理程序
事件处理程序
根据W3C DOM
标准,事件处理程序分为DOM0、DOM2、DOM3
这3种级别的事件处理程序
DOM0级事件处理程序
DOM0
级事件处理程序是将一个函数赋值给一个事件处理属性。
<table border="1">
<tbody>
<tr>
<td>这是td元素</td>
</tr>
</tbody>
</table>
<script>
var table = document.querySelector('table');
table.onclick = function () {
console.log(1);
}
table.onclick = function () {
console.log(2);
}
</script>
//2
优点:简单易用可跨浏览器。
缺点:只能绑定一种事件。
DOM2级事件处理程序
DOM2
级事件处理程序,不同的浏览器厂商指定了不同的实现方式,主要分为IE浏览器和非IE浏览 器。
-
在
IE10
浏览器及以下版本中,只支持事件冒泡阶段,在
IE11
中同时支持事件捕获阶段与事件冒泡阶段,在
IE10
及以下版本中,可以通过attachEvent()
函数添加事件处理程序,通过detachEvent()
函 数删除事件处理程序。
element.attachEvent("on"+eventName,handler);
element.detachEvent("on"+eventName,handler);
- 在
IE11
及其他非IE浏览器中,同时支持事件捕获和事件冒泡两个阶段,可以通过addEventListener()
函数添加事件处理程序,通过removeEventListener()
函数删除事件处理程序。
addEventListener(eventName,handel,useCapture);
removeEventListener(eventName,handel,useCapture);
相同点是不管IE浏览器还是非IE浏览器都支持对同一个事件绑定多个处理函数。
不同点是使用attachEvent()
函数为同一个事件添加多个事件处理函数时,会按照添加的相反顺序执行。
例子
<button id="btn">点击</button>
<script>
var btn = document.getElementById("btn");
btn.attachEvent("onclick", function () {
console.log(1);
})
btn.attachEvent("onclick", function () {
console.log(2);
})
</script>
//2 1
在IE浏览器下使用attachEvent()
函数添加的事件处理程序会在全局作用域中运行,因此this指向全局作用域window。
在非IE浏览器下,使用addEventListener()
函数添加的事件处理程序在指定的元素内 部执行,因此this指向绑定元素。
兼容性处理
var EventUtil = {
addEventHandler: function (element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeEventHandler: function (element, type, handler) {
if (element.addEventListener) {
element.removeEventListener(type, handler);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
}
var btn = document.getElementById("btn");
EventUtil.addEventHandler(btn, 'click', foo);
function foo() {
console.log(11);
}
EventUtil
是与事件相关的所有兼容性处理方案的工具类,后续还有很多处理函数都会依次添加到 EventUtil
类中。
DOM3级事件处理程序
与DOM2
区别在于DOM3
级事件处理程序允许自定义事件,自定义事件由createEvent()
函数创建,返回的对象 有一个initCustomEvent()
函数,通过传递对应的参数可以自定义事件。
函数可以接收以下4个参数:
- type:字符串,触发的事件类型。
- bubble:布尔值,表示事件是否可以冒泡。
- cancelable:布尔值,表示事件是否可以取消。
- detail:对象,任意值,保存在event对象的detail属性中。
创建完成的自定义事件,可以通过dispatchEvent()
函数去手动触发,触发自定义事件的元素需要和绑定自定义事件的元素为同一个元素。
场景:在页面初始化时创建一个自定义事件myEvent
,页面上有个div
监听这个自定义事件myEvent
,同时有一个button
按钮绑定了单击事件,当我们单击button
按钮时,触发自定义事 件,由div
监听到,然后做对应的处理。
实现步骤:
-
创建自定义事件。
通过立即执行函数创建一个自定义事件(支持冒泡,而且会携带参数
detailData
)在创建自定义事件之前,需要判断浏览器是否支持
DOM3
级事件处理程序(,可以判断下面代码的返回 值来确认,如果返回值为"true",则表示浏览器支持,如果返回值为"false",则表示浏览器不支持)document.implementation.hasFeature('CustomEvents','3.0');
var customEvent; //创建自定义事件 (function () { if (document.implementation.hasFeature('CustomEvents', '3.0')) { var detailData = { name: 'cao teacher' }; customEvent = document.createEvent('customEvent'); customEvent.initCustomEvent('myEvent', true, false, detailData); } })();
-
监听自定义事件。
通过
addEnentListener()
函数监听自定义的myEvent
事件。//获取元素 var div = document.querySelector('#watchDiv'); //监听myEvent事件 div.addEventListener('myEvent', function (e) { console.log('div监听到自定义事件的执行,携带参数为:', e.detail); });
-
触发自定义事件
将触发自定义事件的入口放在button上,当单击button时会通过
dispatchEvent()
函数触发myEvent
事件。//获取元素 var btn = document.querySelector('#btn'); //绑定click事件,触发自定义事件 btn.addEventListener('click',function(){ div.dispatchEvent(customEvent); });
完整代码:
<div id="watchDiv">监听自定义事件的div元素</div> <button id="btn">单击触发自定义事件</button> <script> //获取元素 var div = document.querySelector('#watchDiv'); //获取元素 var btn = document.querySelector('#btn'); var customEvent; //创建自定义事件 (function () { if (document.implementation.hasFeature('CustomEvents', '3.0')) { var detailData = { name: 'cao teacher' }; customEvent = document.createEvent('customEvent'); customEvent.initCustomEvent('myEvent', true, false, detailData); //监听myEvent事件 div.addEventListener('myEvent', function (e) { console.log('div监听到自定义事件的执行,携带参数为:', e.detail); }); //绑定click事件,触发自定义事件 btn.addEventListener('click', function () { div.dispatchEvent(customEvent); }); } })(); </script> //表明自定义事件支持事件冒泡机制,可以在初始化自定义事件的initCustomEvent()函数中通过第二个参数来设置事件是否可以冒泡
显示结果:
在document中增加了对自定义的myEvent事件的监听
document.addEventListener('myEvent', function () { console.log('document监听到自定义事件的执行 '); })
通过结果可以看出由于自定义myEvent事件是支持事件冒泡的,所以div和document都会监听到 myEvent事件的执行,输出对应的结果。
当我们将myEvent事件设置为不支持事件冒泡时:
var detailData = { name: 'cao teacher' }; customEvent = document.createEvent('customEvent'); customEvent.initCustomEvent('myEvent', false, false, detailData);
打印结果为:
从结果可以看出,document上监听的myEvent
事件并未触发,事件冒泡被阻止了。
Event对象
事件在浏览器中是以Event对象的形式存在的,每触发一个事件,就会产生一个Event对象,该对象包含所有与事件相关的信息,包括事件的元素,事件的类型及其他与特定事件相关的信息。
获取Event对象
方式:
- 在事件处理程序中,Event对象会作为参数传入,参数名为event。
- 在事件处理程序中,通过window.event属性获取Event对象。
使用上述两种方式获取event对象并输出:
<button id="button">btn</button>
<script>
var EventUtil = {
//获取事件对象
getEvent: function (event) {
return event || window.event;
}
}
var btn = document.getElementById("button");
btn.addEventListener('click', function (event) {
//方法1:event作为参数传入
console.log(event);
//方法2:通过window.event获取
var windowEvent = window.event;
console.log(windowEvent);
//判断两种方法的event是不是一样的
console.log(event == windowEvent);
})
</script>
打印结果:
获取事件的目标元素
- 在IE浏览器中,
event
对象使用srcElement
属性来表示事件的目标元素, - 而在非IE浏览器中,
event
对象使用target
属性来表示事件的目标元素, - 为了提供与IE浏览器下
event
对象相同的特性,某些非IE浏 览器也支持srcElement
。
在同一个事件处理程序中使用上述两种属性来获取事件的目标元素。
<button id="button">btn</button>
<script>
var EventUtil = {
//获取事件对象
getEvent: function (event) {
return event || window.event;
},
//获取事件目标元素
getTarget:function(event){
return event.target || event.srcElement;
}
}
var btn = document.getElementById("button");
btn.addEventListener('click', function (event) {
//获取event对象
var event = EventUtil.getEvent(event);
//使用两种属性获取事件的目标元素
var NoIETarget = event.target;
var IETarget = event.srcElement;
console.log(NoIETarget);
console.log(NoIETarget);
})
</script>
打印结果:
从结果可以看出,Chrome浏览器和Safari浏览器同事支持两种属性来获取事件目标元素,而 Firefox浏览器只支持event.target
属性来获取。
阻止事件冒泡
事件冒泡对于DOM操作有很大的帮助,在后面事件委托中可以体现出来,但是有时候我们并不想要事件进行冒泡
场景:
有一个学生基础信息的容器ul,每个li元素都表示一个学生的基本信息,单机li元素会改变li背景颜色 及表示选中的标识,在每个li元素内部会有一个表示删除的button按钮,单机button按钮则会提示是否 删除,单机确定则会删除元素。
操作:
<ul>
<li>
<p>姓名:lisa</p>
<p>学号:20213001</p>
<button class="btn btn-default" id="btn">删除</button>
</li>
</ul>
<script type="text/javascript">
var li = document.querySelector('li');
var btn = document.querySelector('#btn');
li.addEventListener('click', function (event) {
//真实操作使用console.log()代替
console.log('点击了li的响应处理');
li.style.background = "pink";
if (window.confirm('你确定要删除吗?')) {
//alert("确定");
return true;
} else {
//alert("取消");
return false;
}
})
btn.addEventListener('click', function (event) {
console.log('点击了btn的响应处理');
})
</script>
打印结果:
增加阻止事件冒泡之后
btn.addEventListener('click', function (event) {
//阻止事件冒泡
event.stopPropagation();
console.log('点击了btn的响应处理');
})
打印结果:
在event对象中还存在一个stopImmediatePropagation()
函数,也是用来阻止事件冒泡的
他们两的区别:
主要区别在于同一事件绑定多个事件处理程序的情况下:
stopPropagation()
函数仅会阻止事件冒泡,其他事件处理程序仍然可以调用。stopImmediatePropagation()
函数不仅会阻止事件冒泡,也会阻止其他事件处理程序的调用。
对button
按钮的click
事件增加三个事件处理程序,在第二个事件处理程序中 使用stopPropagation()
函数阻止事件冒泡
<ul>
<li>
<p>姓名:lisa</p>
<p>学号:20213001</p>
<button class="btn btn-default" id="btn">删除</button>
</li>
</ul>
<script type="text/javascript">
var EventUtil = {
//获取事件对象
getEvent: function (event) {
return event || window.event;
}
}
var li = document.querySelector('li');
var btn = document.querySelector('#btn');
li.addEventListener('click', function (event) {
//真实操作使用console.log()代替
console.log('点击了li的响应处理');
});
btn.addEventListener('click', function (event) {
console.log('点击了btn的响应处理1');
});
//第二个事件处理程序
btn.addEventListener('click', function (event) {
var event = EventUtil.getEvent(event);
//1. 阻止事件冒泡
//event.stopPropagation();
//2. 阻止事件冒泡
//event.stopImmediatePropagation();
console.log('点击了btn的响应处理2');
});
//第三个事件处理程序
btn.addEventListener('click', function (event) {
var event = EventUtil.getEvent(event);
console.log('点击了btn的响应处理3');
});
</script>
第一种阻止事件冒泡:
第二种阻止事件冒泡:
阻止默认行为
阻止默认行为
HTML中有一些标签具有默认行为,比如这三个:
- a标签,在单击后默认行为会跳转至href属性指定的链接中
- 复选框checkbox,在单机后默认行为是选中的效果
- 输入框text,在获取焦点后,键盘输入的值会对应展示到text输入框中
阻止上列标签的默认行为:
- a标签,加入a标签上显示的文案不符合预期,我们在单击a标签时将不会跳转至对应的链接中去。
- 复选框checkbox,加入已选中的复选框在单击的时候不会被取消,依然是选中的状态。
- 输入框text,假如我们限制用户输入的值只能是数字和大小写字符,其他值不能输入。
通过event.preventDefault()
函数去阻止元素的默认行为
场景:
限制用户输入的值只能是数字和大小写字母,其他的值则不能输入,如输入其他值则给出提 示信息,提示信息在两秒后消失。
数字和字母的编码Unicode编码范围:
-
数字的Unicode 48-57
-
大写字符A-Z的Unicode 65-90
-
小写字符a-z的Unicode 97-122
考虑到获取键的兼容性问题,所以兼容性处理:
var charcode = event.keyCode || event.which || event.charCode;
操作:
<input type="text" id="text">
<div id="tip">
</div>
<script type="text/javascript">
var text = document.querySelector("#text");
var tip = document.querySelector("#tip");
text.addEventListener('keypress', function (event) {
var charCode = event.keyCode || event.which || event.charCode;
//满足输入数字
var numberFlag = charCode <= 57 && charCode >= 48;
//满足输入大小写
var lowerFlag = charCode <= 90 && charCode >= 65;
//满足输入小写字母
var supperflap = charCode <= 122 && charCode >= 97;
if (!numberFlag && !lowerFlag && !supperflap) {
//阻止默认行为不允许输入
event.preventDefault();
tip.innerText = '只允许输入数字和大小写字母';
}
//设置定时器,清空提示语
setTimeout(function () {
tip.innerText = '';
},10000);
});
</script>
打印结果:
事件委托
事件委托
事件委托是利用事件冒泡原理,管理某一类型的所有事件,利用父元素来代表子元素的某一类型事 件的处理方式。
已有元素的事件绑定
场景:
加入页面上有一个ul标签,里面包含1000个li子元素,我们需要在单击每个li时输出li文本的 内容。遇到这样的场景时,很多人第一个想法就是给每个li绑定事件,然后输出li标签的内容。
操作:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
<script type="text/javascript">
//1.获取所有的li标签
var children = document.querySelectorAll('li');
//2.遍历添加onclick事件处理程序
for (var i = 0; i < children.length; i++) {
children[i].addEventListener('click', function () {
console.log(this.innerText);
});
}
</script>
打印结果:
分析:
- 事件处理程序过多导致页面交互事件过长。
- 事件处理程序过多导致内存占用过多。
事件委托机制的主要思想是
将事件绑定到父元素上,然后利用事件冒泡原理,当事件进入冒泡阶段 时,通过绑定在父元素上的事件对象判断当前事件流正在进行的元素,如果和期望的元素相同,则执行相应的事件代码。
//获取ul的父元素
var oUl = document.querySelector('ul');
oUl.addEventListener('click', function (event) {
//获取事件对象
var event = EventUtil.getEvent(event);
//获取target
var target = EventUtil.getTarget(event);
// console.log(target);
// console.log(target.nodeName);
//判断当前事件流所处的元素
if (target.nodeName.toLowerCase() === 'li') {
//6.目标元素相匹配,做对应的处理
console.log(target.innerText);
}
})
打印结果:
针对不同的元素所做的处理不一样,也可以用事件处理吗?
场景:
在页面上,有4个button按钮,分别表示增加,删除,修改,查询这四个功能,每个按钮绑定相同 的click事件处理程序,但是具体行为不同,在这个4个按钮触发后,分别输出新增,删除,修改,查询。
操作:
<div id="box">
<input type="button" id="add" value="新增">
<input type="button" id="remove" value="删除">
<input type="button" id="update" value="修改">
<input type="button" id="search" value="查询">
</div>
<script type="text/javascript">
var EventUtil = {
//获取事件对象
getEvent: function (event) {
return event || window.event;
},
getTarget: function (event) {
return event.target || event.srcElement;
}
}
//1.获取父元素
var parent = document.querySelector('#box');
//2.父元素绑定事件
parent.addEventListener('click', function (event) {
//3.获取事件对象
var event = EventUtil.getEvent(event);
//4.获取目标元素
var target = EventUtil.getTarget(event);
//判断ID属性输出对应文字
switch (target.id) {
case 'add':
console.log('新增');
break;
case 'remove':
console.log('删除');
break;
case 'update':
console.log('修改');
break;
case 'search':
console.log('查询');
break;
}
})
</script>
打印结果:
新创建元素的事件绑定
场景:
加入页面上有一个ul标签,里面包含9个li子标签,我们需要在单击每个li的时候,输出li中的 文本内容,在页面上有一个button按钮,单击button按钮会创建一个新的li元素,单击新创建的li元素, 输出它的文本内容。
手动绑定方法:
<ul>
<li>文本1</li>
<li>文本2</li>
<li>文本3</li>
<li>文本4</li>
<li>文本5</li>
<li>文本6</li>
<li>文本7</li>
<li>文本8</li>
<li>文本9</li>
</ul>
<button id="add">新增</button>
<script type="text/javascript">
//获取所有的li标签
var children = document.querySelectorAll('li');
//遍历添加onclick事件
for (var i = 0; i < children.length; i++) {
children[i].addEventListener('click', function () {
console.log(this.innerText);
})
}
var ul = document.querySelector('ul');
var add = document.querySelector('#add');
add.addEventListener('click', function () {
//创建新的li元素
var newLi = document.createElement('li');
var newText = document.createTextNode('文本10');
newLi.appendChild(newText);
ul.appendChild(newLi);
})
</script>
打印结果:
点击新增按钮的时候,会发现页面上新增了一个内容为"文本10"的li元素,点击其他li元素可以输出文本,但是点击li10不会出现文本10
原因:通过querySelectorAll()
函数获取到的li元素虽然会实时感知到数量的变化,但是并不会实 时增加对事件的绑定,如果需要新元素也具有相同的事件,则需要手动调用事件绑定的代码。
解决方案:
<ul>
<li>文本1</li>
<li>文本2</li>
<li>文本3</li>
<li>文本4</li>
<li>文本5</li>
<li>文本6</li>
<li>文本7</li>
<li>文本8</li>
<li>文本9</li>
</ul>
<button id="add">新增</button>
<script type="text/javascript">
var ul = document.querySelector('ul');
//获取所有的li标签
var children = document.querySelectorAll('li');
//遍历添加click事件处理程序
function bindEvent() {
for (var i = 0; i < children.length; i++) {
children[i].addEventListener('click', function () {
console.log(this.innerText);
})
}
}
add.addEventListener('click', function () {
//创建新的li元素
var newLi = document.createElement('li');
var newText = document.createTextNode('文本10');
newLi.appendChild(newText);
ul.appendChild(newLi);
//重新添加事件处理程序
bindEvent();
})
</script>
每次新增一个元素都要重新遍历添加事件,这样就会造成资源浪费
事件委托方法
使用事件委托机制,我们可以更加方便的实现创建元素的事件绑定,由于事件委托机制是利用的事 件冒泡机制,即使在元素自身没有绑定事件的情况下,事件仍然后冒泡到父元素中,因此对于新增的元 素,只要实例事件流就可以触发其事件。
代码:
<ul>
<li>文本1</li>
<li>文本2</li>
<li>文本3</li>
<li>文本4</li>
<li>文本5</li>
<li>文本6</li>
<li>文本7</li>
<li>文本8</li>
<li>文本9</li>
</ul>
<button id="add">新增</button>
<script type="text/javascript">
//1.获取父元素
var parent = document.querySelector('ul');
//2.父元素绑定事件
parent.addEventListener('click', function () {
//3.获取事件对象
var event = EventUtil.getEvent(event);
//4.获取目标元素
var target = EventUtil.getTarget(event);
//5.判断当前事件流所处的元素
if (target.nodeName.toLowerCase() === 'li') {
//6.与目标元素相同,做对应的处理
console.log(target.innerText);
}
})
var add = document.querySelector('#add');
add.addEventListener('click', function () {
//创建新的li元素
var newLi = document.createElement('li');
var newText = document.createTextNode('文本10');
newLi.appendChild(newText);
parent.appendChild(newLi);
})
</script>
打印结果:
浏览器的重排和重绘
浏览器的重排
浏览器渲染页面默认采用的是流式布局模型。
浏览器渲染页面是基于流式布局的,对某一个DOM节点信息进行修改时,就需要对该DOM结构进行重新计算,该DOM结构的修改会决定周边DOM结构的更改范围,主要分为全局范围和局部范围。
全局范围就是从页面的根节点HTML标签开始,对整个渲染树进行重新计算
局部范围只会对渲染树的某部分进行重新计算
重排的过程就发生在DOM节点信息修改的时候,重排实际是根据渲染树中每个渲染对象的信息, 计算出各自渲染对象的信息,例如DOM元素的位置,尺寸,大小等,然后将其安置在界面中正确的位 置。
重排是一种明显的改变页面布局的操作,几种引起页面重排的操作:
- 页面首次渲染。
- 浏览器窗口大小发生改变。
- 元素尺寸或者位置发生改变。
- 元素内容发生变化。
- 元素字体发生变化。
- 添加或者删除可见的DOM元素
- 获取特定的属性。例如offsetTOP offsetLeft等等 scrollTop,scrollLeft
浏览器的重绘
重绘只是改变元素在页面中的展示样式,而不会引起元素在文档流中位置的改变,例如更改了元素的字体颜色,背景色,透明度等,浏览器都会将这些新样式赋予元素并重新绘制。
引发重绘的操作:
- color
- background
- box-shadow
- border-radius
重排一定会引起重绘的操作,但是重绘却不一定会引起重排的操作。
性能优化
浏览器的重排和重绘是比较消耗性能的操作,所以我们应该尽量减少重排与重绘的操作,这样也是 网站性能优化的一种方式。
将多次改变样式的属性操作合并为一次
将需要多次重排的元素设置为绝对定位
需要进行重排的元素都是出于正常文档流中的,如果他不在正常的文档流中,那么它的变化就不会 影响到其他元素的变化。这样就不会引起重排的操作。
在内存中多次操作节点,完成后在添加至文档树中
通过变量遍历操作的节点,遍历存在内存中,完成后添加至文档树中。
将要进行复杂处理的元素处理为display属性为none处理完成后在进行显示
因为display为none的属性不会出现在渲染树中。
将频繁获取会引起重排的属性缓存到变量
将一些offsetLeft或者offsetRight等属性存在变量里面。
尽量减少使用table布局
table中的任何一个元素改变,都会引起重排。
使用事件委托事件处理程序
使用事件委托事件处理程序
Ajax的基本执行原理和执行过程
Ajax的基本原理:
通过XMLHttpRequest
对象向服务器发送异步请求,获取服务器返回的数据后, 利用DOM的操作来更新页面。(交换数据)
其中最核心的部分就是XMLHttpRequest
对象,它是一个JavaScript对象,支持异步请求,可以及时 向服务器发送请求和处理响应,并且不阻塞用户,达到不刷新页面的效果。
XMLHttpRequest对象
XMLHttpRequest
对象从创建到销毁存在一个完整的生命周期,在生命周期的每个阶段会调用 XMLHttpRequest
对象的不同函数,在函数中需要通过XMLHttpRequest
对象的特定属性来判断函数执行情况。
XMLHttpRequest对象的函数
abort()
函数:如果请求已经发送,则停止当前请求。getAllResponseHeaders()
函数:获取所有HTTP请求的响应头部,作为键值返回,如果没有收到响 应,则返回null。getResponseHeader("key")
函数:获取指定key的HTTP响应头,如果没有收到响应或者响应中不 存在key对象的报头,则返回null。open("method","URL",[asyncFlag],["userName"],["password"])
函数: 建立对服务器的调用。 method参数表示请求方式,可以为GET,POST或者PUT。 URL参数表示请求的路径,可以使相对路径,也可以时绝对路径。 后面3个是可选参数,分别表示是否异步,用户名,密码,其中asyncFlag = true
表示异步,asyncFlag = false
表示同步,默认值为true
。send(content)
函数:向服务器发送请求。setRequestHeader('key','value')
函数:设置请求头中属性为key的值为value,在设置请求头之前 需要先调用open()函数,设置header将随着send()函数一起发送。
XMLHttpRequest对象的属性
-
onreadystatechange
状态改变的事件触发器,每个状态改变时都会触发这个事件处理器,通常会调用一个JavaScript函 数。 -
readyState请求的的状态,有5个可取的值。
0:未初始化,XMLHttpRequest对象已经创建。
1:open()函数已调用,send()函数未调用,请求还未发送。
2:send()函数已调用,HTTP请求已经发送到服务器,未接受到响应。
3:所有响应头接受完成,响应体开始接受但未完成。
4:HTTP响应接收完成。
- reponseText 接收的数据文本格式的服务器响应体(不包括响应头)
- responseXML 服务器的响应,兼容DOM的XML对象,解析后可得到DOM对象。
- status 服务器返回的HTTP状态码,用数字表示,如200表示成功,404表示资源未找到。
- statusText HTTP状态码的文本表示,如状态码为200时,对应返回ok,状态码404时对应返回not found。
XMLHttpRequest对象生命周期
由于浏览器的差异性,创建XMLHttpRequest
对象时需要使用不同的方法,主要体现在IE浏览器与 其他浏览器之间。
下面提供一个标准的XMLHttpRequest对象创建方法:
创建XML
function createXMLHttp() {
//code for IE7 Firefox Chrome Opera Safari
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
//code for IE6 IE5
if (window.ActiveXObject) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("msxm12.XMLHTTP");
} catch (ex) {}
}
}
}
建立链接
当XMLHttpRequest对象创建完毕后,便可以通过open()函数建立链接,它指定了请求的url地址以 及通过url传递的参数,默认值为true,表示采用异步传输的方式。
var xhr = createXMLHttp();
xhr.open('post','/admin/w/saveUser',true);
发送请求并传递数据
在使用open()函数建立链接后,便可以使用send()函数发送请求,并传递数据content,由于传递的 数据并不是必需的,所以content值可以为空。
var content = {userName:'cao teacher',password:'123456'};
xhr.send(content);
处理响应
在XMLHttpRequest
对象中有一个很重要得onreadystatechange
属性,它表示XMLHttpRequest
对象状态改变的事件触发器,每次readyState
的取值变化时,属性onreadystatechange
对应的函数都会被 执行一次。
当readystate
的值为4时代表响应接受完成,需要注意的是响应接收完成并不代表请求是成功的,我们需要通过HTTP请求status状态码判断,当status值为200时代表请求成功。
因此在onreadystatechange()
回调函数中,我们需要同时判断readyState
和status
两个值才能对响应值做正确的处理。
xhr.onreadystatechange = function () {
//当readyState为4,且状态码为200时代表请求成功
if (xhr.readyState === 4 && xhr.status === 200) {
//处理响应值
document.write(xhr.responseText);
}
}
Ajax的优缺点
Ajax的优点
-
无刷新更新数据
Ajax的最大优点是在不需要刷新页面的情况下,能够与服务端保持数据通信,这使得Web应用程序 能够快速的响应用户需求,避免不必要的等待时间,提高用户体验。
-
异步通信
Ajax可以使用异步的方式与服务器进行通信,能够减少不必要的数据传输,降低网络数据流量,使得响应更加迅速。
-
前后端分离
Ajax可以使得前后端分离开发更加完善,后端专注于接受请求,响应数据,前端专注于页面逻辑处 理。
-
标准化支持
Ajax是一种基于Web标准化并被浏览器广泛支持的技术,不需要下载额外的插件,只需要客户允许 JavaScript在浏览器上执行即可。
缺点
-
破坏浏览器的正常后退功能
浏览器有一个很重要的功能就是对历史记录的追溯,通过后退按钮可以退到浏览器之前访问过的页面,但是这个功能不能和JavaScript进行很好的合作,从而导致Ajax不能使用这个功能。
-
安全性问题
Ajax的逻辑时可以将前端安全扫描技术隐藏起来,允许黑客从远端服务器上建立新的链接。
-
对搜素引擎不友好
浏览器在进行SEO优化的时候,会屏蔽到所有的Javascript代码,这就导致了ajax对SEO的不友好。
-
违背URL唯一资源定位的初衷
由于Ajax请求并不会改变浏览器地址中的URL,因此相同的URL用户看到的内容时不一样的,这就违背了URL定位唯一资源的初衷。
使用Node.js搭建服务器
使用Node.js搭建服务器
创建项目
在硬盘上创建一个项目ajaxTest
,并放入文件夹中
mkdir ajaxTest
cd ajaxText
项目初始化
如果要将项目变成一个Node项目,需要进行初始化,进入ajaxTest
项目根目录之后运行以下命令
npm init
后会有一些列的步骤,需要输入很多的信息,这些步骤都可以直接回车,全部使用默认的信息,初始 化完成后,会在项目根目录下生成一个package.json文件。
安装Express框架与body-parser插件
由于我们需要使用到Express框架,因此需要提前安装该框架。
npm install express --save-dev
安装方式的不同:
npm install moduleName # 安装模块到项目目录下
npm install -g moduleName # -g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看npm
config prefix 的位置。
npm install -save moduleName # -save 的意思是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖。
npm install -save-dev moduleName # -save-dev 的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖。
在处理post请求时,需要使用body-parser插件,因此也需要提前安装该插件。
npm install body-parser --save-dev
创建server.js文件
在项目根目录下创建一个server.js文件文件名可以自定义,用于最后启动服务器。
其中有几点需要注意的地方:
- 在接收post请求传递的数据时,需要使用body-parser插件。
- 通过处理’/'请求可以指定首页。
- 监听的端口号需要唯一,不可与其他应用端口号一样。
server.js文件内容如下所示:
var express = require('express');
//接收post请求体数据的插件
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser());
//接收"/"请求,指定首页
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
//处理get请求
app.get('/getUser', function (req, res) {
console.log(req.query);
});
//处理post请求
app.post('/saveUser', function (req, res) {
var responseObj = {
code: 200,
message: '请求成功'
};
res.write(JSON.stringify(responseObj));
res.end('end');
});
//执行监听的端口号
var server = app.listen(3000, function () {});
编写index.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
hello Ajax hello Node.js
</body>
</html>
运行server.js
在项目的根目录运行以下命令即可启动本地服务器
node server.js
查看运行效果
在浏览器输入http://127.0.0.1:3000 即可看到首页内容:
使用Ajax提交form表单
form表单的默认提交方式会刷新页面,而且会在页面之间跳转,如果需要保持当前用户对表单状态 的改变,就要在后台控制器和前端页面中传递更多的参数,因此对于前端与后台处理信息交互比较频繁 的场景,form表单默认的提交方式并不友好。
为了应对以上的场景,使用Ajax提交form表单时是一种很好的解决办法,因为Ajax可以在不刷新页 面的情况下提交请求,然后再处理响应时通过Javascript操作DOM,并展示后台处理的信息。
通用处理
在使Ajax提交form表单时,需要对form表单进行特殊的处理,包括以下节点。
- 将form标签的action属性和method属性去掉
- 将提交form表单按钮的type=submit改为type=button
<form name="userForm" id="userForm">
用户名: <input type="text" name="username" id="username"><br />
密码: <input type="password" name="password" id="password"><br />
电话: <input type="text" name="telphone" id="telphone"><br />
邮箱: <input type="text" name="email" id="email"><br />
<input type="button" value="提交" id="submit">
<input type="button" value="取消" id="cancel">
</form>
使用Ajax进行提交
使用原生Ajax进行form表单提交包含以下过程。
- 绑定提交按钮事件。
- 创建
XMLHttpRequest
对象。 - 建立链接。
- 设置请求头。
- 获取数据。
- 发送请求。
- 处理响应。
绑定提交按钮事件
在单击提交按钮时,触发Ajax请求操作,将整个Ajax操作封装在ajaxSubmitForm()函数里,按钮获 取与事件绑定使用原生的JavaScript语法。
var submitBtn = document.getElementById("submit");
submitBtn.addEventListener('click', function () {
ajaxSubmitForm();
});
创建XMlHttpRequest对象
创建XMlHttpRequest对象直接使用之前封装好的函数就可以了。
//创建XMLHttpRequest对象
function createXMLHttp() {
//code for IE7 Firefox Chrome Opera Safari
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
//code for IE6 IE5
if (window.ActiveXObject) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("msxm12.XMLHTTP");
} catch (ex) {}
}
}
return xmlhttp;
}
var xhr = createXMLHttp();
建立链接
本实例可以理解为一个用户的注册操作,发送请求为POST请求,使用异步处理的方式。
var xhr = createXMLHttp();
设置请求头
由于本实例中发送的是POST请求,需要设置数据传输格式,即设置Content-type
属性值,可以通 过setRequestHeader()
函数对其进行设置,将其值设置为比较普遍的JSON
数据格式。
xhr.setRequestHeader('Content-type','application/json;charset=UTF-8');
注意:在设置请求头之前,需要调用XMLHttpRequest
实例的open()函数,以保证已经建 立链接请求,即必须先open才能设置。
获取数据
通过原生DOM操作获取页面中数据。
//获取数据
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
var telphone = document.getElementById('telphone').value;
var email = document.getElementById('username').value;
var content = {
username: username,
password: password,
telphone: telphone,
email: email
}
因为在请求头中设置了数据传输格式为json,所以需要将content对象处理为json字符串。
content = JSON.stringify(content);
发送请求
只需要调用send()函数就可以发送请求。
xhr.send(content);
处理响应
设置onreadystatechange
属性对应的回调函数,在回调函数中进行判断,当响应接受完毕, readyState
的值为4,同时请求状态码为200时,即表示请求成功,然后就可以编写对象的处理逻辑。
xhr.onreadystatechange = function () {
//当readyState为4,且状态码为200时代表请求成功
if (xhr.readyState === 4 && xhr.status === 200) {
//处理响应值
document.write(xhr.responseText);
}
}
关于Ajax请求的get方式和post方式
get方式和post方式的区别
使用 get 方式和 post 方式都可以向服务器发送请求,只是发送的机制不同,主要体现在以下几 点:
-
参数传递
get 请求会将参数添加到请求URL的后面,没有请求主体,调用send()函数时,传递的参数为null, 即xhr.send(),post请求的数据会放请求体中,用户是无法通过URL直接看到的,调用send()函数 时,传递的参数为data,即 xhr.send(data)。
-
服务端参数获取
使用Express作为服务端框架,get请求通过Request.query来获取参数,而使用post请求时需要添 加中间件,同时通过 Request.body 来获取参数。
-
传递的数据量
get请求传输的数据量小,对于不同的浏览器有所差异,Chrome浏览器限制为8K,IE限制为2K, post 请求传递的数据量大,一般默认不受限制,但实际上服务器会规定post 请求传递的数据量大 小。
-
安全性
get请求安全性较低,因为其请求的参数会出现在 URL上,而且采用明文进行数据传输。通过浏览 器缓存或者历史记录可以很容易获取到某些隐私请求的参数,post请求通过请求体进行数据传输, 数据不会出现在URL上,隐藏了请求数据的信息,安全性较高
-
处理form表单的差异性
在使用 form 表单进行提交时,get 请求和 post请求也会体现出很大的差异性
form表单采用get请求时,action指定的url中的请求参数会被丢弃,提交时只会将form表 单内的元素值进行拼接并向服务器传递。
post却可以(from表单中的元素通过Request.body
请求体被服务器端接收到,而url
中携带的参数通 过Request.query
被服务器端接收到。)。
使用get方式和post方式需要注意的点
- 使用get请求时,如果请求的url不发生改变,可能会存在缓存的问题,因此在请求url后一般会拼接上一个时间戳,以避免出现缓存。
- 使用get方式请求时,请求的参数会拼接在url后,如果浏览器编码,服务器编码,数据库编码格式不一致,可能会导致乱码的问题,通过的做法是对请求的参数经过
encodeURIComponent()
函数处 理。
xhr.open('get','/getUser?username='+encodeURIComponent(username),true);
get方式和post方式的使用场景
Ajax使用get方式的场景
- 请求是为了检索资源,form表单的数据仅用于帮助搜索。
- 传递的数据量小,适合于url中传递参数。
- 数据安全性低,适合明文传输。
Ajax使用post方式的场景
- 请求会修改数据库中的资源,比如新增,修改,删除等操作。
- 传递的数据量大,超出url中携带参数长度的限制。
- 用于用户名,密码及身份证号等类似敏感信息的数据传输。
Ajax进度事件
通过监听readystatechange
事件,在回调函数中获取readyState
和 status
的值并判断请求是否成功。在 XHR2
草案中,增加了 Ajax 请求进度事件Progress Events
规范, 使得XMLHttpRequest
对象能在请求的不同阶段触发不同类型的事件,所以我们可以不再需要判断 readyState
的属性,也可以处理请求成功和失败的操作。
在 Progress Events 规范中增加了7个进度事件,如下所示。
-
**loadstart:**在开始接收响应时触发。
-
**progress:**在接收响应期间不断触发,直至请求完成。
-
error: 在请求失败时触发。
-
**abort:**在主动调用abort()函数时触发,表示请求终止。
-
load: 在数据接收完成时触发。
-
**loadend:**在通信完成或者error、abort、load事件后触发。
-
timeout: 在请求超时时触发。
一个完整的ajax 请求都会从loadstart事件开始,然后不间断地触发 progress 事件,然后触发 load、abort,timeout或者error事件中的一个,注意这里是只会触发 load、abort, timeout 或者 error 事件其中的一个,最后触发loadend 事件。
Ajax进度事件
load事件介绍
load事件的诞生是用以代替readystatechange
事件的,表示的是数据接收完成后触发。我们不用去 判断readyState
属性值的变化也可以执行事件处理成功的操作。
只要浏览器接收到了服务器的响应,不管其状态如何都会触发 load事件。
例如,
对于状态码为404 的请求,仍然会触发 load事件,所以在进行请求成功的处理时,需要判断 status的值。
一般我们判断status值大于等于200且小于300,或者status值等于 304时,都是当作请求 成功进行处理。
在loadstart,load
等事件的回调函数中,都会接收一个event
对象,通过event
对象的 target
属性 可以获取到 XMLHttpRequest
对象的实例,因此可以访问到XMLHttpRequest
对象的所有属性和函数。
load事件使用
-
创建一个XMLHttpRequest对象
//创建XMLHttpRequest对象 function createXMLHttp() { //code for IE7 Firefox Chrome Opera Safari var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } //code for IE6 IE5 if (window.ActiveXObject) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { try { xmlhttp = new ActiveXObject("msxm12.XMLHTTP"); } catch (ex) {} } } return xmlhttp; }
-
设置loadstart事件监听
xhr.onloadstart = function(event){ console.log('loadstart事件-开始接受数据'); };
-
设置error事件监听
xhr.onerror = function(event){ console.log('error事件-请求异常'); }
-
设置timeout事件监听
xhr.ontimeout=function(){ console.log('timeout事件-请求超时'); }
-
设置load事件监听
load事件并不一定是请求成功才会触发的,所以需要对status值进行判断,而获取status值得方式 也有两种,一种是直接通过外层的XMLHttpRequest对象获取,另一种是通过event对象的target属性获 取。
xhr.onload=function(event){ //方式1获取status var status = xhr.status; //方式2获取status var status = event.target.status; console.log('load事件状态码:'+status); if(status >=200 && status<300 || status ===304){ console.log('load事件-数据接收完成'); } };
-
设置loadend事件监听
xhr.onloadend = function(){ console.log('loadend事件-通信完成'); }
-
ajax请求发送
需要强调的一点是,ajax请求的发送,即send()函数的调用一定要出现在各种事件处理程序绑定之 后,否则会报错
xhr.open('post','/saveUser',true);xhr.send();
JSON序列化和反序列化
JSON序列化
JSON序列化即将JSON对象处理为JSON字符串的过程,以方便传输数据,JSON序列化可以通过两种 方式来实现,一种是调用JSON对象内置的stringify()函数一种是为对象自定义的toJSON()函数。
JSON.stringify()函数
JSON.stringify()函数是将一个JavaScript对象或者数组转换为JSON字符串,它的基本语法如下:
JSON.stringify(value[replacer[,space]]);
其中各个参数含义如下:
- value参数表示待处理成JSON字符串的JavaScript值,通常为对象或者数组。
- replacer参数是一个可选参数,如果其值为一个函数,则表示在序列化过程中,被序列化值的每个 属性,都会经过该函数的处理,如果其值是一个数组,则表示只有包含在这个数组中的属性名才会 被序列化到最终的JSON字符串中,如果该值为null或者未传递,则value参数对应值的所有属性都 会被序列化。
- space是一个可选参数,用于指定缩进用的空白字符串,美化输出,如果参数是个数字,则代表有 多少个空格,上限值为10,如果该参数的值小于1,则意味着没有空格,如果参数为字符串,则取 字符串的前十个字符作为空格,如果没有传入参数或者传入的值为null,将没有空格。
var obj = {
name: 'cao teacher',
age: 15,
address: String('上海'),
interest: ['basketball', 'football'],
email: 'zhoukeke@163.com'
}
console.log(JSON.stringify(obj));
输出结果:
{"name":"cao teacher","age":15,"address":"上海","interest":["basketball","football"],"email":"zhoukeke@163.com"}
当传递了replacer参数并且值为一个函数时,函数所做的处理时,加入属性中为字符串类型,则将值转换为大写。
var obj = {
name: 'cao teacher',
age: 15,
address: String('上海'),
interest: ['basketball', 'football'],
email: 'zhoukeke@163.com'
}
function replacerFn(key, value) {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
}
console.log(JSON.stringify(obj, replacerFn));
输出结果:
{"name":"CAO TEACHER","age":15,"address":"上海","interest":["BASKETBALL","FOOTBALL"],"email":"ZHOUKEKE@163.COM"}
name、address、email属性值为字符串类型,其值都转换成了大写字母,insterest值为数组类型,数组中的值也变成了大写字母
原因:在JSON序列化时,如果属性值为对象或者数组,则会继续序列化该属性 值,直到属性值为基本类型、函数或者Symbol类型才结束。
调用以下函数,并且只序列化 name 属性和 age 属性的值。
console.log(JSON.stringify(obj,['name','age']));
输出结果:
{"name":"cao teacher","age":15}
注意事项
- 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
- 布尔值,字符串,数字的包装对象在序列化过程中会被转换为对应的原始值。
自定义toJSON()函数
如果一个被序列化的对象拥有toJSON()函数,那么toJSON()函数就会覆盖默认的序列化行为,被序 列化的值将不再是原来的属性值,而是tosJSON()函数的返回值。
toJSON()函数用于更精准的控制序列化,可以看做是对stringify()函数的补充。
var obj2 = {
name: 'cao teacher',
age: 15,
adress: String('上海'),
interest: ['basketball', 'football'],
email: 'caoteacher@163.com',
toJSON: function () {
//只返回name和age属性,并且修改key
return {
Name: this.name,
Age: this.age
};
}
};
console.log(JSON.stringify(obj2));
JSON反序列化
JSON.parse()函数
JSON.parse()函数用来解析JSON字符串,构造由字符串描述的JavaScript值或对象,它的语法如下 所示:
JSON.parse(text,[,reviver]);
各个参数的含义如下:
-
text
表示待解析的JSON
字符串 -
reviver
是一个可选参数,如果是一个函数,则规定了原始值在返回之前如何被解析改造,如果被解析的JSON
字符串是非法的,则会抛出异常。
JSON.parse()函数的基本使用方法,包括对象,基本数据类型的处理
JSON.parse('[1,2,3,true]'); //(4) [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}');
JSON.parse('true');
JSON.parse('123.45');
JSON.parse()函数还可以接收一个函数,用来处理JSON字符串中的每个属性值,
当属性值为一个数组或 者对象时,数组中的每个元素或者对象的每个属性都会经过reviver参数对应的函数处理,执行的顺序是 从最内层开始,按照层级顺序,依次向外遍历。
var jsonStr = '{"name":"cao teacher","age":15}';
var result = JSON.parse(jsonStr, function (key, value) {
if (key === 'name') {
return value + '同学';
}
if (key === 'age') {
return value * 2;
}
return value;
});
console.log(result);//{ name: 'cao teacher同学', age: 30 }
**注意:**JSON字符串中的属性名必须用双引号括起来,否则会解析错误。
eval函数
eval()函数用于计算JavaScript字符串,并把它作为脚本来执行。在使用eval()函数进行JSON反序列 化时,其语法如下所示:
eval("("+str+")")
str表示待处理的字符串。
为什么要使用括号将拼接出来的字符串括起来呢?
因为JSON字符串是以"{}"开始和结束的,在JavaScript中它会被当做一个语句块来处理,所以必须强制将 它处理成一个表达式,所以采用括号。
var json = '{"name":"cao teacher"}';
console.log(eval("("+json+")"));
准的控制序列化,可以看做是对stringify()函数的补充。
var obj2 = {
name: 'cao teacher',
age: 15,
adress: String('上海'),
interest: ['basketball', 'football'],
email: 'caoteacher@163.com',
toJSON: function () {
//只返回name和age属性,并且修改key
return {
Name: this.name,
Age: this.age
};
}
};
console.log(JSON.stringify(obj2));
JSON反序列化
JSON.parse()函数
JSON.parse()函数用来解析JSON字符串,构造由字符串描述的JavaScript值或对象,它的语法如下 所示:
JSON.parse(text,[,reviver]);
各个参数的含义如下:
-
text
表示待解析的JSON
字符串 -
reviver
是一个可选参数,如果是一个函数,则规定了原始值在返回之前如何被解析改造,如果被解析的JSON
字符串是非法的,则会抛出异常。
JSON.parse()函数的基本使用方法,包括对象,基本数据类型的处理
JSON.parse('[1,2,3,true]'); //(4) [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}');
JSON.parse('true');
JSON.parse('123.45');
JSON.parse()函数还可以接收一个函数,用来处理JSON字符串中的每个属性值,
当属性值为一个数组或 者对象时,数组中的每个元素或者对象的每个属性都会经过reviver参数对应的函数处理,执行的顺序是 从最内层开始,按照层级顺序,依次向外遍历。
var jsonStr = '{"name":"cao teacher","age":15}';
var result = JSON.parse(jsonStr, function (key, value) {
if (key === 'name') {
return value + '同学';
}
if (key === 'age') {
return value * 2;
}
return value;
});
console.log(result);//{ name: 'cao teacher同学', age: 30 }
**注意:**JSON字符串中的属性名必须用双引号括起来,否则会解析错误。
eval函数
eval()函数用于计算JavaScript字符串,并把它作为脚本来执行。在使用eval()函数进行JSON反序列 化时,其语法如下所示:
eval("("+str+")")
str表示待处理的字符串。
为什么要使用括号将拼接出来的字符串括起来呢?
因为JSON字符串是以"{}"开始和结束的,在JavaScript中它会被当做一个语句块来处理,所以必须强制将 它处理成一个表达式,所以采用括号。
var json = '{"name":"cao teacher"}';
console.log(eval("("+json+")"));