一、DOM0 和 DOM2
语法上的区别
box.onclick=function(){}
box.addEventListener(‘click’,function(){})底层运行机制上的区别
DOM0 就是给元素的某个属性绑定方法(有效绑定的方法只有一个)
DOM2 是基于事件池机制完成,每增加一个绑定的方法,都会往事件池中存放一个…当事件触发会依次执行事件池中的事情 =>发布订阅其实就是模拟的事件池机制 (可以给同一个元素的某个事件绑定多个不同的方法)DOM2 中可以给一些特殊的事件类型绑定方法,这些事件类型 DOM0 不支持绑定,例如:DOMContentLoaded、transitionend…
$(document).ready() => $(function(){}) VS window.onloadDOM2的事件池机制
基于addEventListener / attachEvent(IE68不可以
当事件行为触发,会把事件池中的方法按照增加的顺序依次执行,但是IE6~8中执行的顺序是不固定的
html部分
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<title></title>
<meta name="keyword"
content="技术体系:HTML5、CSS3、JAVASCRIPT、ES6、AJAX、NODE、VUE、REACT、小程序、FLUTTER等;课程体系:零基础就业课、在线框架课、全栈架构课;学前端就选珠峰,十年专注做前端!">
<!-- IMPORT CSS -->
</head>
<body>
<button class="submit">点我啊~~</button>
<!-- IMPORT JS -->
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script>
// 创建一个事件池 $.Callbacks()
let $pond1 = $.Callbacks();
$('.submit').click(function () {
// 点击的时候通知事件池中的方法执行,而且还可以给每个方法都传递实参
$pond1.fire(100, 200);
});
let fn1 = function () {
console.log(1);
};
let fn2 = function () {
console.log(2);
};
let fn3 = function () {
console.log(3);
};
// 把需要做的事情陆续添加到事件池中 $pond.add(func) / $pond.remove(func)
$pond1.add(fn1);
// $pond1.add(fn1); // JQ中没有做去重处理
$pond1.add(fn2);
$pond1.add(fn3);
let fn4 = function (n, m) {
console.log(4, n + m);
};
$pond1.add(fn4); */
</script>
<script src="subscribe.js"></script>
<script>
let pond = _subscribe();
document.querySelector('.submit').onclick = function (ev) {
pond.fire(ev);
};
let fn1 = function () {
console.log(1);
};
let fn2 = function () {
console.log(2);
pond.remove(fn1);
};
let fn3 = function () {
console.log(3);
};
let fn4 = function () {
console.log(4);
};
pond.add(fn1);
pond.add(fn2);
pond.add(fn3);
pond.add(fn4);
</script>
</body>
</html>
js部分
let _subscribe = (function () {
// SUB:发布订阅类
class Sub {
constructor() {
// 创建一个事件池,用来存储后期需要执行的方法
this.$pond = [];
}
// 向事件池中追加方法(重复处理)
add(func) {
let flag = this.$pond.some(item => {
return item === func;
});
!flag ? this.$pond.push(func) : null;
}
// 从事件池中移除方法
remove(func) {
let $pond = this.$pond;
for (let i = 0; i < $pond.length; i++) {
let item = $pond[i];
if (item === func) {
// 移除(顺序不变的情况下基本上只能用 SPLICE了),但是不能这样写,这样会导致数组塌陷问题,我们移除不能真移除,只能把当前项赋值为 null
// $pond.splice(i, 1);
$pond[i] = null;
break;
}
}
}
// 通知事件池中的方法,按照顺序依次执行
fire(...args) {
let $pond = this.$pond;
for (let i = 0; i < $pond.length; i++) {
let item = $pond[i];
if (typeof item !== 'function') {
// 此时再删除
$pond.splice(i, 1);
i--;
continue;
}
item.call(this, ...args);
}
}
}
// 暴露给外面用
return function subscribe() {
return new Sub();
}
})();
数组塌陷的问题