1、使用原生代码实现一个EventEmitter类,这个类包含以下方法: on(监听事件,该事件可以被触发多次)- once(也是监听事件,但只能被触发一次)- fire(触发指定的事件)- off(移除指定事件的某个回调方法或者所有回调方法)
/*
const fn1 = (... args)=>console.log('I want sleep1', ... args)
const fn2 = (... args)=>console.log('I want sleep2', ... args)
const event = new Events();
event.on('sleep', fn1, 1, 2, 3);
event.on('sleep', fn2, 1, 2, 3);
event.fire('sleep', 4, 5, 6);
// I want sleep1 1 2 3 4 5 6
// I want sleep2 1 2 3 4 5 6
event.off('sleep', fn1);
event.once('sleep', ()=>console.log('I want sleep));
event.fire('sleep');
*/
function EventEmitter() {
// 放置所有添加的监听事件
this._events = {}
}
EventEmitter.prototype = {
on: function(name, fn, ...args) {
if (!name || !fn) {
throw new Error(`[Events TypeError] failed to`)
return
}
// 阻止重复添加相同的监听
let fns = this._events[name] || []
if(fns.find(item => item.fnOrg === fn)) {
return;
}
this._events[name] = fns.contact({
fn: arg => fn.apply(nulll, [...argOrg, ...arg]),
fnOrg: fn
})
},
once: function(name, fn) {
const onFn = (...args) => {
fn.apply(null, args)
this.off(name,onFn)
}
this.on(name,onFn)
return this
},
off: function(name, fn) {
if(!arguments.length) {
this._events = Object.create(null)
}
if(arguments.length === 1) {
delete this._events(name)
}
let fns = this._events(name)
if(!fns || !fns.length) return
this._events[name] = fns && fns.filter(item => {
return item !== fns
})
return this
},
fire: function(name, ...args) {
let fns = this._events[name] || []
if(fns.find(item => item.fnOrg === fn)) {
return;
}
this._events[name].map(fn => {
fn(...args)
})
return this
}
}
2.DOM 的体积过大会影响页面性能,假如你想在用户关闭页面时统计(计算并反馈给服务器)
当前页面中元素节点的数量总和、元素节点的最大嵌套深度以及最大子元素个数,请用 JS 配合 原生 DOM API 实现该需求(不用考虑陈旧浏览器以及在现代浏览器中的兼容性,可以使用任意 浏览器的最新特性;不用考虑 shadow DOM)。比如在如下页面中运行后:
<html>
<head></head>
<body>
<div>
<span>f</span>
<span>o</span>
<span>o</span>
</div>
</body>
</html>
会输出:
{
totalElementsCount: 7,
maxDOMTreeDepth: 4,
maxChildrenCount: 3
}
const obj = {}
class Element {
constructor(ele) {
this.ele = ele
this.funum = 1
}
// 取当前节点的元素深度
getEleDepth() {
let fuele = this.ele.parentNode
if(fuele !== document) {
this.funum++
this.ele = fuele;
return this.getEleDepth
} else {
return this.funum
}
}
// 取当前节点的元素个数
getEleSubNum() {
let eles = thisele.childNodes, num = 0
for(let i = 0; 0 < eles.length; i++) {
if(eles[i].nodeName !== '#text') {
num++
}
}
return num
}
}
const totalElements=document.getElementsByTagName("*")
obj.totalElementsCount=totalElements.length;//dom中的所有节点数量
let eleDepthArr=[];
let eleSubArr=[];
for(let i=0;i<totalElements.length;i++){
eleDepthArr.push(new Ele(totalElements[i]).getEleDepth())
eleSubArr.push(new Ele(totalElements[i]).getEleSubNum())
}
eleDepthArr=eleDepthArr.sort((a,b)=>(b-a))
eleSubArr=eleSubArr.sort((a,b)=>(b-a))
obj.maxDOMTreeDepth=eleDepthArr[0]//元素节点的最大嵌套深度
obj.maxChildrenCount=eleSubArr[0]//最大子元素个数
console.log(obj)