共计 3458 个字符,预计需要花费 9 分钟才能阅读完成。
前言
不论是寒冬还是暖冬,找工作之前都需要做好充足的准备,面试的时候才能做到游刃有余。此文是把我最近找工作准备的以及笔试面试中涉及到的手写题做一个总结。给自己,也给需要的同学。
手写题是比较好准备的一个环节,大部分公司考察的题也就那么多,大都不会超出范围。
往期
- “ 寒冬 ” 三年经验前端面试总结(含头条、百度、饿了么、滴滴等)
防抖 & 节流
原理都是利用闭包保存变量。防抖是任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行,一般用于输入框实时搜索;节流是规定函数在指定的时间间隔内只执行一次,一般用于 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;
}
写在最后
有错误之处还请小伙伴们及时指出,以免误人子弟。想看往期内容,翻到页面最上面有链接~