DOM事件和事件监听
DOM事件可以细分为三个类型:普通型事件,捕获型事件和冒泡型事件,普通型事件就是指直接在目标元素上绑定一个事件,而捕获型事件和冒泡型事件是DOM事件流的两种方式。众所周知,DOM结构是一个树型结构,当用户触发某个事件时,先从document节点一级一级的往下传播,直到目标节点,这个事件过程就是事件捕获;事件再从目标节点一级级往上传播,直到根节点,这个过程就是事件冒泡。
在DOM标准中,是同时支持事件捕获和事件冒泡的,但IE8及以下版本只支持事件冒泡,也因此而导致了在实际开发中浏览器兼容问题。
在javascript中,有三种常用的绑定事件:在DOM元素中直接绑定,在js代码中获取目标元素绑定及通过监听事件来实现绑定。对于事件监听,W3C定义了三个事件阶段,依次是捕获阶段,目标阶段和冒泡阶段,相比其它两种,使用事件监听绑定事件需要考虑IE低级版本的兼容性。因此在PC端必须对监听器和event对象进行封装:
var EventUtils = {
// 注册监听器
addHandler: function(element, type, handler) {
if(element.addEventListener) {
element.addEventListener(type, handler, false);
}else if(element.attachEvent) {
element.attachEvent("on" + type, handler);
}else{
element["on" + type] = handler;
}
},
// 移除监听器
removeHandler: function(element, type, handler) {
if(element.removeEventListener) {
element.removeEventListener(type, handler, false);
}else if(element.detachEvent) {
element.detach("on" + type, handler);
}else{
element["on" + type] = null;
}
},
// 获取event对象
getEvent: function(event) {
return event || window.event;
},
// 获取target对象
getTarget: function(event) {
return event.target || event.srcElement;
},
// 阻止浏览器默认行为
preventDefault: function(event) {
if(event.preventDefalut) {
event.preventDefalut();
}else{
window.event.returnValue = false;
}
},
// 阻止浏览器冒泡
stopPropagation: function(event) {
if(event.stopPropagation) {
event.stopPropagation();
}else{
window.event.cancelBubble = true;
}
}
};
浏览器对同步与异步的处理机制
javascript语言的一大特点就是单线程,同一时间内只能做一件事。既然是单线程,也就意味着多个任务时需要排队,等前一个任务执行完毕,才能开始下一个任务。一旦碰到耗时长的任务,便会阻塞后面任务的执行,这明显是不合理的。尤其对于如ajax操作从网络上获取数据,不可能每次都要等待服务器响应后才能往下执行,毕竟IO设备很慢。
因此,javascript就有了异步执行方式,即采用回调函数的方式,后一个任务不等前一个任务结束就执行,优先把同步任务处理,再执行异步任务。
异步执行的具体运行机制如下:
- 所有的任务都在主线程上执行,形成一个执行栈。
- 主线程之外还存在一个“任务队列”,系统会把异步任务(即回调函数中的任务)放到“任务列表”中,然后继续执行后续主线任务。
- 一旦“执行栈”中的所有任务执行完毕,系统就会读取“任务队列”,如果此时异步任务已经结束等待状态,就会从“任务队列”中进入执行栈,恢复执行。
- 主线程不断重复上面的第三步。
从上面的运行机制可看出几点:
- 浏览器会优先执行所有的同步任务,即主线程任务,再执行异步任务,即回调函数中的任务。
- “任务队列”中的任务排序是按照先到先执行原理,与主线程一致。
- setTimeout和setInterval定时器中设置的时间值是指执行栈清空完毕隔多长时间执行。因此,回调函数并不能保证一定会在指定时间执行。
- 后期页面上产生的事件(如点击/失焦),如果绑定了事件函数,均是添加在“任务队列”。
- 同步执行相当于没有异步任务的异步执行。
js的四种异步模式
- 回调函数(callback)。
- 事件监听(Listener)。
- 观察者模式。
- promise对象。(bluebird, Q库)
浏览器同源策略
同源策略是浏览器的一种安全策略,是对javascript施加的安全限制,旨在禁止浏览器执行其它非同源网站的脚本。同源是指 域名,端口,协议均相同。
不同源举例:
域名不同:
http://www.begin.com 与 http://www.end.com --- 主域名不同(begin/end)
http://www.begin.com 与 http://asd.begin.com --- 子域名不同(www/asd)
端口不同
http://www.begin.com:8081 与 http://www.begin.com:3000
协议不同(http/https)
http://www.begin.com 与 https://www.begin.com
跨域
js跨域是指通过js在不同域之间进行数据传输或通信。只要域名,协议,端口有任何一个不同都可被当作不同的域。一般情况下,浏览器是不允许进行跨域的,但可通过以下几种方法解决跨域:
- jsonp
jsonp的基本原理: 动态添加一个script标签,因为script标签中的src属性是没有跨域限制的,因此该解决方案与ajax的XmlHttpRequest协议无关。因此,无论是原生的ajax还是jQ的ajax,其实现的方式最终都是构造script标签。
如:
<script src="http://localhost:3000/home/jsonp?name=李某某&callback=funcJsop"></script>
function funcJsop(json) {}
$.ajax({
url: "http://localhost:3000/home/jsonp?name=李某某",
dataType: "jsonp",
jsonpCallback:"success_jsonpCallback", // 不设置默认jq返回随机生成的回调函数名
success: function(json) {}, // 成功执行回调函数后的回调
error: function(json){}
})
这里需要后端配合,jsonp返回的数据格式应当是“客户端传递的回调方法名称(json数据)”
- window.name + iframe
window有个name属性,在同一个窗口中加载的所有页面都能共享同一个window.name,每个页面都对window.name有读写权限 - window.postMessage(message,targetOrigin)
H5新特性,用来向其它的window对象发送消息。message-发送的消息,只能为字符串类型;targetOrigin--发送对象所在域,如果不想限定域,可以使用通配符 * 。
接收消息的对象,可通过监听自身的message来获取消息内容,该内容储存在该事件对象的data属性中。
window.onmessage = function(event) {
var data = event.data;
}
- location.hash + iframe
原理是利用location.hash来进行传值,而改变url中的hash值不会引起浏览器刷新。但需要镶套两层iframe。因为不同域的两个页面是不允许修改parent.location.hash的值,因此需要在iframe中再添加一个与主页面的域一样的iframe。
//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
- CORS
通过使用自定义的响应HTTP头部让浏览器与服务器进行沟通。
Access-Control-Allow-Origin:*
Access-Control-Allow-Methods:POST,GET,UPDATE,PUT
Access-Control-Allow-Credentials:true
- 服务器代理
同源策略只是客户端的一种安全策略机制,在服务器端是不存在该限制的,因此可通过在服务器端获取其他域名下的数据再返回给前端,例如微信页面授权。