“寒冬”三年经验前端面试总结(含头条、百度、饿了么、滴滴等)之手写题(一)
前言
不论是寒冬还是暖冬,找工作之前都需要做好充足的准备,面试的时候才能做到游刃有余。此文是把我最近找工作准备的以及笔试面试中涉及到的手写题做一个总结。给自己,也给需要的同学。
手写题是比较好准备的一个环节,大部分公司考察的题也就那么多,大都不会超出范围。
往期
- "寒冬"三年经验前端面试总结(含头条、百度、饿了么、滴滴等)
防抖 & 节流
原理都是利用闭包保存变量。防抖是任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行,一般用于输入框实时搜索;节流是规定函数在指定的时间间隔内只执行一次,一般用于scroll事件。
// 防抖 function debounce(fn,time){ let timer = null; return function(){ if(timer){ clearTimeout(timer) } timer = setTimeout(()=>{ fn.apply(this,arguments) },time) } } // 节流 function throttle(fn,time){ let canRun = true; return function(){ if(!canRun){ return } canRun = false; setTimeout(() => { fn.apply(this,arguments); canRun = true; },time) } }
深拷贝
深拷贝是一个老生常谈的问题。几年前面试就考,现在面试仍然会考。主要考察的是递归、数组和对象的存储。
function deepClone(obj) { var result = Array.isArray(obj) ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === 'object' && obj[key]!==null) { result[key] = deepCopy(obj[key]); } else { result[key] = obj[key]; } } } return result; }
function deepClone(arr){ return JSON.parse(JSON.stringify(arr)) }
数组乱序
乱序也是常考的一道题。
// 取巧的一种算法,但是每个位置乱序的概率不同 function mixArr(arr){ return arr.sort(() => { return Math.random() - 0.5; }) }
// 著名的Fisher–Yates shuffle 洗牌算法 function shuffle(arr){ let m = arr.length; while(m > 1){ let index = parstInt(Math.random() * m--); [arr[index],arr[m]] = [arr[m],arr[index]]; } return arr; }
数组去重
数组去重的方法有很多种,如果要是手写的话,一般我都会写下面这种。也会顺便说一下ES6的set方法。
function removeDup(arr){ var result = []; var hashMap = {}; for(var i = 0; i < arr.length; i++){ var temp = arr[i] if(!hashMap[temp]){ hashMap[temp] = true result.push(temp) } } return result; }
Array.from(new Set(arr))
[...new Set(arr)]
数组flat
数组flat方法是ES6新增的一个特性,可以将多维数组展平为低维数组。如果不传参默认展平一层,传参可以规定展平的层级。
// 展平一级 function flat(arr){ var result = []; for(var i = 0; i < arr.length; i++){ if(Array.isArray(arr[i])){ result = result.concat(flat(arr[i])) }else{ result.push(arr[i]); } } return result; }
//展平多层 function flattenByDeep(array,deep){ var result = []; for(var i = 0 ; i < array.length; i++){ if(Array.isArray(array[i]) && deep > 1){ result = result.concat(flattenByDeep(array[i],deep -1)) }else{ result.push(array[i]) } } return result; }
数组filter
filter方法经常用,实现起来也比较容易。需要注意的就是filter接收的参数依次为数组当前元素、数组index、整个数组,并返回结果为ture的元素。
Array.prototype.filter = function(fn,context){ if(typeof fn != 'function'){ throw new TypeError(`${fn} is not a function`) } let arr = this; let reuslt = [] for(var i = 0;i < arr.length; i++){ let temp= fn.call(context,arr[i],i,arr); if(temp){ result.push(arr[i]); } } return result }
手写call & apply & bind
call、apply、bind是ES5中能改变this指向的方法。一般都会问一下这三个方法的区别。call和apply的传参不同,call接收逗号分隔的参数,apply接收数组(如何记不清这两个方法的区别的话,可以记apply接收array,都是a开头的,这样比较好记),调用都会立即执行。而bind调用完返回的是一个函数,需要再次调用才会执行。
接下来就会引申到能实现一个call/apply吗?或者能用apply实现一个bind吗?
Function.prototype.myCall = function(context){ if(typeof this != 'function'){ throw new TypeError('this is not a function') } context.fn = this; var arr = []; for(var i = 1; i< arguments.length; i++){ arr.push('argument[' + i + ']') } var result = eval('context.fn(' +arr+ ')'); delete context.fn; return result; }
Function.prototype.myApply = function(context,arr){ if(typeof this != 'function'){ throw new TypeError('this is not a function') } context.fn = this; var result= []; if(!arr){ result = context.fn() }else{ var args = []; for(var i = 1; i< arr.length; i++){ args.push('arr[' + i + ']') } result = eval('context.fn(' +args+ ')'); } delete context.fn; return result; }
Function.prototype.myBind = function(context){ if(typeof this != 'function'){ throw new TypeError('this is not a function') } var self = this; var args = Array.prototype.slice.call(arguments,1); var F = function(){}; F.prototype = this.prototype; var bound = function(){ var bindArgs = Array.prototype.slice.call(arguments); return self.apply(this instanceof F ? this: context, args.concat(bindArgs)) }; bound.prototype = new F(); return bound; }
写在最后
有错误之处还请小伙伴们及时指出,以免误人子弟。想看往期内容,翻到页面最上面有链接~