面试了两个月,发现大大小小的公司无论面试多久,最后必须来两三道手撕题,考验候选人的代码能力
平时简单题手到擒来,但到了面试官面前就大脑宕机
简简单单一道【实现apply】都能卡住,我的总结是还得多练,同一道题多打几次,面试的时候才能思路清晰
整理了最基础的常见手撕,啃下这些,基本可以应对70%的手撕题
当然大厂手撕并不都是简单基础题,很多都是在这13道基础上拓展,先练好这13道基础题,面试的时候就算run不通,也可以先说个大概思路,从面试官那里获取一点提示
稍复杂的手撕,后续更新~~
function curry(fn: any) {
return function judgeCurry(...args: any) {
return fn.length > args.length ?
(...args1: any) => judgeCurry(...args,...args1):
fn(...args);
}
}
function compose(...args: any[]) {
return (subArgs: any) => {
// 方法一
// for(let i = args.length - 1; i >= 0; i--) {
// res = args[i](res);
// }
// 方法二
return args.reverse().reduce((acc, func,index) => {
return func(acc);
}, subArgs);
}
}
export function pipe(...args: any[]) {
return (subArgs: any) => {
// 方法一
// for(let i = args.length - 1; i >= 0; i--) {
// res = args[i](res);
// }
// 方法二
return args.reduce((acc, func,index) => {
return func(acc);
}, subArgs);
}
}
function throttle(fn: any, wait: number){
let last: any;
return function() {
let now: any = Date.now();
// 初次执行
if (!last) {
fn.apply(this, arguments);
last = now;
return;
}
// 以后触发,需要判断是否到延迟
if(now - last >= wait) {
fn.apply(this, arguments);
last = now;
}
}
}
function debounce(func: any, delay: number) {
// 初次触发定时器为null,后面产生一份定时器并记下定时器id
let timer: any = null;
// 闭包使定时器id逃逸
return function() {
let args = arguments;
// 如果已有定时器id,则需要清除,重新开始延迟执行
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout( () => {
func.apply(this, args);
// 销毁定时器id,以便下次节流函数触发
timer = null;
}, delay);
}
}
function fmoney(num: number){
/* 正则实现 */
// 参考:https://www.cnblogs.com/lvmylife/p/8287247.html
let [integer, decimal] = String(num).split('.');
let regExp = /\d{1,3}(?=(\d{3})+$)/g;
integer = integer.replace(regExp, '$&,');
return `
${integer}${decimal === undefined ? '': '.'+decimal}`;
// 正则解释
// 正则表达式 \d{1,3}(?=(\d{3})+$) 表示前面有1~3个数字,后面的至少由一组3个数字结尾
// 先行肯定断言(?=)会作为匹配校验,但不会出现在匹配结果字符串里面
// ?=表示正向引用,可以作为匹配的条件,但匹配到的内容不获取,并且作为下一次查询的开始
// $& 表示与正则表达式相匹配的内容,具体的可查看 w3school的replace()方法
/* Number.prototype.toLocaleString()实现 */
// Number.prototype.toLocaleString()
// return num.toLocaleString('en');
/* Intl.NumberFormat().format(number)实现 */
// Intl.NumberFormat().format(number)
// return Intl.NumberFormat('en').format(num);
// reduce 方案
// let arr = String(num).split('.');
// let char = arr[0].split('').reverse();
// let IntStr = char.reduce((acc, value, index) => {
// return `
${index % 3 === 0 ? String(value)+',' : String(value)}${acc}`;
// }, '').slice(0, -1);
// return `
${IntStr}${arr[1]? '.'+arr[1] : '' }`;
}
// 说明:通过new WeakMap()来避免循环引用(拷贝引用类型时并保存其地址,
// 后面遇到引用类型先检查是否已经保存了)
// 通过Reflect.ownKeys(obj)遍历出obj自身的所有可枚举和不可枚举的属性以及symbol属性
// 拷贝对应属性的属性描述符
function checkType(obj) {
const type = Object.prototype.toString.call(obj);
return type.slice(8, -1);
}
// 深拷贝(hash = new WeakMap()考虑循环引用的问题)
export function deepClone(obj, hash = new WeakMap()) {
if (checkType(obj) === 'RegExp') {
// regExp.source 正则对象的源模式文本;
// regExp.flags 正则表达式对象的标志字符串;
// regExp.lastIndex 下次匹配开始的字符串索引位置
let temp = new RegExp(obj.source, obj.flags);
temp.lastIndex = obj.lastIndex;
return temp;
}
if (checkType(obj) === 'Date') {
return new Date(obj);
}
// 非复杂类型(null、undefined、string、number、symbol、boolean、function)
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 还可以扩展其他类型。。。
// 与后面hash.set()防止循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
let newObj = new obj.constructor();
hash.set(obj, newObj);
// Object.keys(obj)类型于 for in 和 obj.hasOwnProperty
// 是否应该拷贝自身属性(可枚举的和不可枚举的以及symbol)
Reflect.ownKeys(obj).forEach(function (key) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
newObj[key] = deepClone(obj[key], hash);
} else {
// 直接赋值
// newObj[key] = obj[key];
// 是否应该保留属性描述符
Object.defineProperty(newObj, key, Object.getOwnPropertyDescriptor(obj, key));
}
});
return newObj;
}
function instance_of(L: Object, R: any){
let protoChain = Object.getPrototypeOf(L);
const Lprototype = R.prototype;
// 最坏情况递归查到Object.prototype === null
while(protoChain) {
// 两个对象指向同一个内存地址,则为同一个对象
if(protoChain === Lprototype) {
return true;
}
protoChain = Object.getPrototypeOf(protoChain);
}
// 找到终点还没找到,那就没有了呗
return false;
}
实现call方法
Function.prototype.myCall = function myCall() {
let [thisArg, ...args] = Array.from(arguments);
if (!thisArg) {
//context 为 null 或者是 undefined
thisArg = typeof window === 'undefined' ? global : window;
}
// this 的指向的是当前函数 func (func.call)
// 为thisArg对象添加func方法,func方法又指向myCall,所以在func中this指向thisArg
thisArg.func = this;
// 执行函数
let result = thisArg.func(...args);
// thisArg 上并没有 func 属性,因此需要移除
delete thisArg.func;
return result;
}
Function.prototype.myApply = function myApply() {
// 第一个参数为this对象,第二个参数为数组(与myCall唯一的区别就在第二个参数是数组)
let [thisArg, args] = Array.from(arguments);
if (!thisArg) {
//context 为 null 或者是 undefined
thisArg = typeof window === 'undefined' ? global : window;
}
// this 的指向的是当前函数 func (func.call)
thisArg.func = this;
// 执行函数
let result = thisArg.func(...args);
// thisArg 上并没有 func 属性,因此需要移除
delete thisArg.func;
return result;
}
Function.prototype.myBind = function myBind() {
let [thisArg, ...args] = [...arguments];
if (!thisArg) {
//context 为 null 或者是 undefined
thisArg = typeof window === 'undefined' ? global : window;
}
let that = this;
return function() {
// 防止第二次调用 func 是,该func已经被delete了,需要重新赋值
if(!thisArg.func) {
thisArg.func = that;
}
let result = thisArg.func(...args);
// thisArg原本没有func方法
delete thisArg.func;
return result;
}
}
目前还存在参数适配的问题
var p1 = function(){
return new Promise((resolve, reject) => {setTimeout(function(){resolve('12')}, 1000)})
};
var p2 = function(){
return new Promise((resolve, reject) => {setTimeout(function(){resolve(2)}, 2000)})
};
var p3 = function(){
return new Promise((resolve, reject) => {setTimeout(function(){resolve(3)}, 1000)})
};
function promiseAll(tasks) {
let ary = new Array(tasks.length).fill(1).map(item => {return {val: undefined, success: false}});
return new Promise((resolve, reject) => {
for(let i = 0; i < tasks.length; i++) {
tasks[i]().then(res => {
ary[i].val = res;
ary[i].success = true;
if(ary.every(item => item.success === true)){
resolve(ary.map(item => item.val))
}
}).catch(err => {
reject(err);
});
}
});
}
// test
promiseAll([p1, p2, p3]).then(res => console.log(res)).catch(err => {
console.log(err);
});
function parallelPromises1(tasks){
var result = [];
return tasks.reduce((accumulator,item,index)=>{
return accumulator.then(res => {
item = typeof item === 'function' ? item() : item;
return item.then(res => {
result[index] = res
return index == tasks.length - 1 ? result : item
})
})
}, )
}
async function parallelPromises2(tasks) {
let ary = [];
for (let task of tasks) {
let temp = await task();
ary.push(temp);
}
return ary;
}