手撕代码系列

644 阅读2分钟

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)