【前端】记几道面试手写题

1,781 阅读2分钟

秋招开始以来面了几家公司,每次遇到手写题都很头疼,算法题还好,前端的手写题是真的要我老命,大部分直接当场尬住,感觉自己还是太菜了...甚至怀疑自己到底能不能干前端,在这里简单记录一下遇到的题目和解法。

1.最大并发请求队列 (猿辅导)

题目:有多个请求需要发出,存在input数组中,请实现一个限制最大并发数量为2的请求方法,将请求的返回保存在output数组,请求方法为fetch(url,callback)

解答

这里有两种思路,一种是将数组分割为两个一组的多个数组,每次传入一个数组,用promise.all的方法,当两个请求都返回了再调用下一个,但这种方法的局限性是,其中某一个请求很慢,就会阻塞整个队列。

另一种思路是维护一个请求队列,利用递归的方法不停的推入请求,可设置任意最大并发数,具体代码如下:

function handleFetchQueue(input, max) {
	const urlCount = input.length; // 请求总长度,用于判断边界
	const requestsQueue = []; // 请求队列
	const output = []; // 结果
    let i = 0;
    const handleRequest = (url) => {
   		const req = fetch(url,(res)=>{
        	const len = output.push(res);
            if (len < urlCount && i + 1 < urlCount) {
        		requestsQueue.shift(); // 请求成功后释放位置
          		handleRequest(input[++i]) // 控制何时进入下一次
        	} else if (len === urlCount) {
          		return output;
        	}
        })
        if (requestsQueue.push(req) < max) { //队列满了就不递归到下一个
        	handleRequest(input[++i]);
      	}
  	}
    handleRequest(input[i]);
}

参考:23行代码实现一个带并发数限制的fetch请求函数

2.实现一个eventBus (字节跳动、猿辅导)

题目:手动实现事件注册和监听,即vue中的emit,on,off,once

解答

 class EventBus{
        constructor(){
            this.event=Object.create(null);
        };
        //注册事件
        on(name,fn){
            if(!this.event[name]){
                //一个事件可能有多个监听者
                this.event[name]=[];
            };
            this.event[name].push(fn);
        };
        //触发事件
        emit(name,...args){
            //给回调函数传参
            this.event[name]&&this.event[name].forEach(fn => {
                fn(...args)
            });
        };
        //只被触发一次的事件
        once(name,fn){
            //在这里同时完成了对该事件的注册、对该事件的触发,并在最后取消该事件。
            const cb=(...args)=>{
                //触发
                fn(...args);
                //取消
                this.off(name,fn);
            };
            //监听
            this.on(name,cb);
        };
        //取消事件
        off(name,offcb){
            if(this.event[name]){
                let index=this.event[name].findIndex((fn)=>{
                    return offcb===fn;
                })
                this.event[name].splice(index,1);
                if(!this.event[name].length){
                    delete this.event[name];
                }
            }
        }
    }

3.实现简单的模板编译 (字节跳动)

题目:输入一个html页面,和已经注册的数据,将页面的相应数据部分替换为数据对象的相应部分:

//输入
const tmp = `
  <h1>{{person.name}}</h1>
  <address>{{person.address}}</address>
  <samll>{{person.mather}}</samll>
`;
//需要编写render函数
const html = render(tmp, {
  person: {
    name: 'petter',
    address: '409 Brookview Drive',
  },
});

//期望的输出
const expect = `
  <h1>petter</h1>
  <address>409 Brookview Drive</address>
`

解答

const tmp = `<h1>{{person.name}}</h1>
<address>{{person.address}}</address>
<small>{{person.mother}}</small>
`


function render(tmp, root) {
    let newTemp = tmp;
    function work(node, str) {
        for (let key in node) {
            if (typeof node[key] === 'object') {
                str += key + '.'
                work(node[key], str)
                str = str.substring(0, str.length - key.length - 1);
            }
            else {
                newTemp = newTemp.replace('{{' + str + key + '}}', node[key]);
            }
        }
    }
    work(root, '');
    newTemp = newTemp.replace(/\{\{((?:.|\r?\n)+?)\}\}/g, '')
    console.log(newTemp)
}

render(tmp, {
    person: {
        name: 'petter',
        address: '409 brookview'
    }
})