不管是寒冬仍是暖冬,找工做以前都须要作好充足的准备,面试的时候才能作到游刃有余。此文是把我最近找工做准备的以及笔试面试中涉及到的手写题作一个总结。给本身,也给须要的同窗。
手写题是比较好准备的一个环节,大部分公司考察的题也就那么多,大都不会超出范围。前端
原理都是利用闭包保存变量。防抖是任务频繁触发的状况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行,通常用于输入框实时搜索;节流是规定函数在指定的时间间隔内只执行一次,通常用于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)) }
乱序也是常考的一道题。segmentfault
// 取巧的一种算法,可是每一个位置乱序的几率不一样 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 = parseInt(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方法是ES6新增的一个特性,能够将多维数组展平为低维数组。若是不传参默认展平一层,传参能够规定展平的层级。promise
// 展平一级 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接收的参数依次为数组当前元素、数组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是ES5中能改变this指向的方法。通常都会问一下这三个方法的区别。call和apply的传参不一样,call接收逗号分隔的参数,apply接收数组(如何记不清这两个方法的区别的话,能够记apply接收array,都是a开头的,这样比较好记),调用都会当即执行。而bind调用完返回的是一个函数,须要再次调用才会执行。
接下来就会引伸到能实现一个call/apply吗?或者能用apply实现一个bind吗?app
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; }
有错误之处还请小伙伴们及时指出,以避免误人子弟。想看往期内容,翻到页面最上面有连接~dom