1,事件冒泡,捕获,委托
早期的事件,是作为分担服务器运算负载的一种手段,实文档或者浏览器窗口中发生的一些特定的交互瞬间,如点击按钮,拖放文件等。我们可以使用侦听器来预定事件,当事件发布时候就可作出相应的响应,这种模式称为观察者模型。
事件流
事件冒泡
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>test</title>
- </head>
- <body>
- <div id="container"></div>
- </body>
- </html>
在如上代码里面,如果单击了div元素,那么接着<body>、<html>、document会按着顺序收到点击事件。
事件捕获
DOM事件流
事件处理程序
HTML事件处理程序
- <input type="button" value="click me" onclick="alert('click')" />
也可以调用在页面其他地方定义的脚本:
- <input type="button" value="click me" onclick="handler()">
- <script>
- function handler() {
- alert('click');
- }
- </script>
在HTML页面中指定事件处理程序有三个缺点:存在时差,当脚本较绑定了事件dom元素慢,那么用户在脚本没有加载出来的过程就触发事件会导致报错;另一个缺点是这样扩展事件处理程序的作用域链在不同的浏览器会导致不同的结果,即this的指向不一致;最后,这种紧密耦合的代码风格导致维护的成本升高,在以后修改代码的时候需要修改HTML代码和js代码。
DOM0级事件处理程序
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>test</title>
- </head>
- <body>
- <input id="myButton" type="button" value="click me">
- <script>
- var button = document.getElementById('myButton');
- function handler() {
- alert('click');
- }
- button.onclick = handler;
- // 移除事件
- // button.onclick = null;
- </script>
- </body>
- </html>
注意,此种方式无法为同一个元素对同一事件指定多个事件处理程序,如果绑定多次,就会以最后一次为准。
DOM2级事件处理程序
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>test</title>
- </head>
- <body>
- <input id="myButton" type="button" value="click me">
- <script>
- function addHandler(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;
- }
- }
- function removeHandler(element, type, handler) {
- if (element.removeEventListener) {
- element.removeEventListener(type, handler, false);
- } else if (element.detachEvent) {
- element.detachEvent('on' + type, handler);
- } else {
- element['on' + type] = null;
- }
- }
- var button = document.getElementById('myButton');
- function handler() {
- alert('click');
- }
- addHandler(button, 'click', handler);
- // removeEventListener(button, 'click', handler);
- </script>
- </body>
- </html>
事件委托
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>test</title>
- </head>
- <body>
- <ul id="myLinks">
- <li id="goSomewhere">Do something</li>
- <li id="doSomething">Do something</li>
- <li id="sayHi">Say Hi</li>
- </ul>
- <script>
- function addHandler(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;
- }
- }
- function removeHandler(element, type, handler) {
- if (element.removeEventListener) {
- element.removeEventListener(type, handler, false);
- } else if (element.detachEvent) {
- element.detachEvent('on' + type, handler);
- } else {
- element['on' + type] = null;
- }
- }
- var list = document.getElementById('myLinks');
- function handler(event) {
- event = event ? event : window.event;
- var target = event.target || event.srcElement;
- switch(target.id) {
- case 'doSomething':
- alert('doSomething');
- break;
- case 'goSomewhere':
- alert('goSomewhere');
- break;
- case 'sayHi':
- alert('hi');
- break;
- }
- }
- addHandler(list, 'click', handler);
- // removeEventListener(list, 'click', handler);
- </script>
- </body>
- </html>
由上面的代码可看出,只获取了一个DOM元素,只添加了一个事件处理程序,所以能够减少内存的使用。但是,不建议对mouseover和mouseout使用事件代理,因为当鼠标从一个元素移到其子节点时,或者当鼠标移除该元素时,都会触发mouseout事件。
js实现继承
JS作为面向对象的语言,继承是其主要特性之一。那么如何在JS中实现继承呢?让我们拭目以待。
1.原型链法:拿父类实例来充当子类原型对象,非常简单的继承,易于实现,实例是子类的实例,也是父类的实例,父类增加新特性子类也能访问。
![](https://i-blog.csdnimg.cn/blog_migrate/e5128438fe43e4e54e13157a6c44a29f.jpeg)
缺点:原型对象的引用属性是所有实例共享的,其中一个改变,其它都跟着改变。
2.构造函数继承:借父类的构造函数来增强子类实例,等于是把父类的实例属性复制了一份给子类实例化,解决了引用属性共享的问题。
![](https://i-blog.csdnimg.cn/blog_migrate/74b9124a0b20be2d1bd616881e40a496.jpeg)
缺点:无个法实现函数复用,每个子类实例都持有一新的fun函数,太多了就会影响性能,内存爆炸。
3.组合继承:把实例函数都放在原型对象上,以实现函数复用。同时还要保留借用构造函数方式的优点,通过call继承父类的基本属性和引用属性并保留能传参的优点;通过prototype继承父类函数,实现函数复用
![](https://i-blog.csdnimg.cn/blog_migrate/cb8171a4e880760871b6d701fe3e49f0.jpeg)
缺点:子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的。
4.圣杯继承(最佳方式):可以避开3的缺点。
![](https://i-blog.csdnimg.cn/blog_migrate/870ea70609079cf73405d00553c20e56.jpeg)
总结:只需要记住最后一种继承的写法即可。