javascript-一些学习笔记

JavaScript 事件

事件类型

按照事件添加的区别,分为DOM 0 级事件和DOM 2 级事件。

DOM 0 级事件类似于下面的这样添加:

1
2
3
4
<a onclick="alert(event.type)" >link</a>
<script type="text/javascript">
document.getElementByTag('a')[0].onclick = alert(event.type);
</script>

这样添加的优点是,几乎所有的浏览器都是兼容的。而且简单。缺点是一个元素的事件只能绑定一个事件处理程序。而且这个事件处理程序添加方式不能支持事件捕获,只能使用事件冒泡机制。

DOM 2 级事件添加方式:

1
2
3
4
5
6
e.addEventListener('click',function(){
alert('hello world');
},false);
e.addEventListener('click',function(){
alert('hello javascript');
},false)

这样做的优点是一个元素的一个事件可以绑定多个事件处理程序,事件执行的顺序与添加的顺许相关,例如上面的示例,依次输出 ‘hello world’ , ‘hello javascript’ . 方法的第三个参数表示处理程序是在冒泡阶段执行还是在捕获阶段执行。

但是这样的添加方式可能会在不同版本的浏览器存在兼容性问题,例如在IE中DOM 2 级的事件处理程序添加是这样来写的:

1
2
3
4
5
6
e.attachEvent('onclick',function(){
alert('hello world');
});
e.attachEvent('onclick',function(){
alert('hello javascript');
});

区别主要是第一个参数需要添加 ‘on’ ,其次还有主要是添加的事件处理程序的执行顺序不同,这次首先输出 ‘hello javascript ‘ , 然后再输出 ‘hello’,IE和opera 只支持事件冒泡。所以没有第三个参数。

各大浏览器之间的事件绑定程序添加方式不同,所以需要添加一个兼容的事件添加程序来保证兼容性。可以像这样一个简单的对象来处理事件添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Event = {
add: function(type, e, foo) {
if (e.addEventListener) {
e.addEventListener(type, foo, false);
} else if (e.attachEvent) {
e.attachEvent('on' + type, foo, false);
} else {
e['on' + type] = foo;
}

},
remove: function(type, e, foo) {
if (e.removeEventListener) {
e.removeEventListener(type, foo, false);
} else if (e.detachEvent) {
e.detachEvent('on' + type, foo);
} else {
e['on' + type] = null;
}

}

}

事件出发后,浏览器的处理方式也是不一样的,主要有冒泡和捕获方式。例如:IE 只支持冒泡。NetScape 只支持捕获,为了保持浏览器之间的兼容性,W3C制定了首先进入捕获阶段,然后进入冒泡阶段的事件处理机制。
img
由于浏览器之间的标准不统一,所以在事件绑定的机制上,一般建议使用冒泡的机制(IE不支持捕获)。

支持冒泡机制的浏览器可以使用 事件委托 :

事件委托是一种编程技巧,在父级元素监听事件,就可以处理子元素的事件活动,例如一个ul 列表 有 10 个 li 元素,如果给 li 元素都绑定 ‘click’ 事件处理程序的话,就会很麻烦,但是我们只要在 ul元素上绑定 ‘click’ 事件处理程序,使用冒泡方式,就能处理子元素的事件活动。

使用事件代理主要有两个优势:

  1. 减少事件绑定,提升性能。之前你需要绑定一堆子节点,而现在你只需要绑定一个父节点即可。减少了绑定事件监听函数的数量。
  2. 动态变化的 DOM 结构,仍然可以监听。当一个 DOM 动态创建之后,不会带有任何事件监听,除非你重新执行事件监听函数,而使用事件委托监听无须担忧这个问题。

javascript-闭包

今天在看 javascript 秘密花园 的时候,看到了之前 腾讯笔试的一个笔试的知识点,当时做题的时候没有想清楚,今天看到闭包,总算搞清楚了。

题目是:

1
2
3
4
5
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}

问输出是什么?

当时认为是按照顺许输出 0~9,但是答案是 输出 10 次 10 。

输出 10 次 10 的原因 是, setTimeout 函数里的 匿名函数保持的是对 i 的引用,由于它是匿名调用,所以这个 i 相当与是 window.i (浏览器环境中),他是全局对象中的 i 的一个引用。由于 setTimeout 执行 10 次之后,相当与将一个匿名函数加入到任务队列中,在执行完循环之后,调用任务队列中的匿名函数。所以在循环之后,i的值变成了 10 ,所以任务队列中的匿名函数也就打印 10 次 10 。 要依次输出 0~9,就要避免对 i 的引用,可以使用以下代码:

1
2
3
4
5
6
for(var i = 0; i < 10; i++) {
setTimeout((function() {

console.log(i);
})(i), 1000);
}

为什么这样写就能输出 0 ~ 9 呢? 这里在每一次的循环中创建了一个匿名函数