前言

在前端面试有一个非常重要的环节,也是面试者最担心的一个环节。对“手撕代码”的考察需要面试者平时总结和积累(临时抱佛脚是不好使的),在这里笔者就自己如何攻破“手撕代码”环节总结了一些经验,希望能帮助你挑战高薪,迎娶白富美 。 1. 使用IDE时尽量避免直接使用提示API,亲自输入(孰能生巧,当然感觉没问题的API就不用浪费时间了) 2. 遇到不熟悉的API,一定要查文档研究清楚(参数个数和具体意义以及返回值) 3. 如在模拟某个原生API时,先写出原生API并分析出形参和返回值 4. 感觉功能完成时,需要在考虑一下边界条件(参数非比填情况、undefined、null) 5. 平常有空时多刷刷一二线大厂的面试题(扩充自己的知识广度) 6. 多关照一些前端动态(比如说curry、compose你没听过,这就有点尴尬)

常见的“手撕代码”,都是高频题哦

curry(柯里化)

function curry(fn: any) {return function judgeCurry(...args: any) {return fn.length > args.length ? (...args1: any) => judgeCurry(...args,...args1):fn(...args);}
}

compose(函数组合)

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);}
}

pipe(函数管道)

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);}
}

throttle(函数节流)

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;}}
}

debounce(函数防抖)

function debounce(func: any, delay: number) {              // 初次触发定时器为null,后面产生一份定时器并记下定时器idlet 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); }
}

formatMoney(千分位)

function fmoney(num: number){/* 正则实现 */// 参考:https://www.cnblogs.com/lvmylife/p/8287247.htmllet [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] : '' }`;
}

deepClone(深拷贝)

说明:通过new WeakMap()来避免循环引用(拷贝引用类型时并保存其地址,后面遇到引用类型先检查是否已经保存了)
通过Reflect.ownKeys(obj)遍历出obj自身的所有可枚举和不可枚举的属性以及symbol属性
拷贝对应属性的属性描述符

function checkType(obj: any): string {const type = Object.prototype.toString.call(obj);return type.slice(8, -1);
}// 深拷贝(hash = new WeakMap()考虑循环引用的问题)
export function deepClone(obj: any, hash = new WeakMap()) : any{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;
}

模拟instanceof

function instance_of(L: Object, R: any){let protoChain = Object.getPrototypeOf(L);const Lprototype = R.prototype;// 最坏情况递归查到Object.prototype === nullwhile(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 或者是 undefinedthisArg = typeof window === 'undefined' ? global : window;}// this 的指向的是当前函数 func (func.call)// 为thisArg对象添加func方法,func方法又指向myCall,所以在func中this指向thisArgthisArg.func = this;// 执行函数let result = thisArg.func(...args);// thisArg 上并没有 func 属性,因此需要移除delete thisArg.func; return result;
}

实现apply方法

Function.prototype.myApply = function myApply() {// 第一个参数为this对象,第二个参数为数组(与myCall唯一的区别就在第二个参数是数组)let [thisArg, args] = Array.from(arguments);if (!thisArg) {//context 为 null 或者是 undefinedthisArg = typeof window === 'undefined' ? global : window;}// this 的指向的是当前函数 func (func.call)thisArg.func = this;// 执行函数let result = thisArg.func(...args);// thisArg 上并没有 func 属性,因此需要移除delete thisArg.func; return result;
}

实现bind方法

Function.prototype.myBind = function myBind() {let [thisArg, ...args] = [...arguments];if (!thisArg) {//context 为 null 或者是 undefinedthisArg = 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;}
}

模拟Promise.all(多个Promise并行执行)

目前还存在参数适配的问题

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);
});

多个Promise串行执行(两种方式)

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=>{// debuggerresult[index] = resreturn index == tasks.length - 1 ? result : item})})},Promise.resolve())
}async function parallelPromises2(tasks) {let ary = [];for (let task of tasks) {let temp = await task();ary.push(temp);}return ary;
}

写在后面

上面代码完全是笔者手敲,难免有错误,还望斧正。这种题目还有很多(实现简易版的EventEmitter、简易版模版引擎等),笔者会持续更新。如果对你有帮助,俺希望送上你的github小星星,在此感谢。
本文同步发布于

个人博客​fanerge.github.iofanerge 的个人主页 - 掘金​juejin.imWeb应用(实践+性能+安全)​zhuanlan.zhihu.com

前端date format_前端面试-手撕代码篇相关推荐

  1. 蛇形打印数组(某宝典公司面试手撕代码题)

    背景杂谈 不知道为什么,可能脑袋一下放空了,一不小心就想到了大约2年前,在某个知名的宝典公司面试中,遇到了一道手撕代码题,和多年前的google的那道螺旋遍历数据有异曲同工之妙.现脑洞大开,想写下与大 ...

  2. 2023华为OD面试手撕代码经验分享

    我们先来看下这个同学的面试经历吧,非常有借鉴的意义. [22届考研渣渣的od求职之旅,推荐一下两个人,德科hr和牛客的老哥] "*********",hr给了机会吧,一开始我都没想 ...

  3. C/C++笔试面试手撕代码注意事项

    C/C++笔试和面试过程中难免会要手撕代码,那么手撕代码,面试官或者看试卷的人一般会看哪些点呢?我列举了一些我认为的点(码农适用): 算法思想是否正确 代码逻辑是否清晰明了 代码风格是否美观简洁 注释 ...

  4. 2023华为OD面试手撕代码真题

    很多小伙伴后台私信我,让我出一些面试中的手撕代码题. 一般面试的时候每一轮技术面都会出一到两个手撕代码题,这些题的特点就是,非常短小,易于理解.不可能会再出阅读理解一样的机试题的.但是这些题目也非常注 ...

  5. FPGA秋招面试手撕代码20+

    目录 前言 1.序列检测器 (1)三段式状态机实现方式 (2)移位寄存器实现方式 2.序列生成器 (1)移位寄存器方式实现 (2)计数器方式实现 (3)三段式状态机方式实现 3.分频 (1)偶数分频 ...

  6. 华为面试手撕代码 leetcode 上重点题 附C++解法

    剑指18 删除链表的节点 ListNode* deleteNode(ListNode* head, int val) {if(!head) return head;if(head->val == ...

  7. 数字IC面试手撕代码(一)

    1.最近看别人有面试说遇到这样一个问题.用状态机实现类似序列检测的题目:生成01011011101111-依次类推.针对这个问题,我设计如下的三段式状态机代码,用了4个状态,2个计数器. 设计之初,本 ...

  8. 算法面试手撕代码高频题汇集

    目录 一.字符串与数组 二.链表专题 三.栈与队列 四.树 五.图与回溯

  9. 数字IC手撕代码-兆易创新笔试真题

    前言: 本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析.代码及波形,所有代码均经过本人验证. 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手 ...

最新文章

  1. 利淘优选——青龙羊毛
  2. 手机抓包app_Python爬取网站上面的数据很简单,但是如何爬取APP上面的数据呢
  3. HTML5 classList使用
  4. 小屏党失望!iPhone 13四款机型,mini版没了
  5. 前端-requests-flask对应关系 restful
  6. python验证身份证最后一位数字代表什么_身份证号码最后一位代表什么?
  7. Markdown(六)——表格合并单元格增加标题
  8. Count-Min Sketch 算法
  9. Spring+Netty4实现的简单通信框架
  10. ROI和widthStep
  11. access ribbon 编程_彻底玩转MS ACCESS 2016功能区编程设计(01)
  12. maven-replacer-plugin的使用
  13. edp接口规范_EDP接口是什么接口?EDP是什么的缩写?
  14. 进击的DApp:区块链上将长出怎么样的新事物?
  15. 计算机文档字体替换,在word中巧妙使用字体替换
  16. 量子点、量子点发光、量子点屏幕究竟是什么?
  17. python 路边停车
  18. Beyond Compare 安装说明
  19. Vue是什么?在前端学习中有什么作用呢?
  20. 一些兼容性的问题收集

热门文章

  1. go高性能tcp服务器,在Go中构建并发TCP服务器
  2. 命令行开启一个unity实例和执行其中的脚本方法的使用和注意
  3. oracle按用户采集信息,oracle 11g 手动收集用户统计信息
  4. OpenShift 4 之Service Mesh教程(2)- 用Kiali监控微服务运行
  5. 曾经的独角兽 Docker,如今资金紧张
  6. mysql中表格 列变行_MySQL数据透视表的列数据作为行
  7. c语言 vscode代码自动补全_借助C/C++ Extension实现VSCode C++代码补全
  8. 阿里用什么替代了dubbo_踢脚线怎么装才好看?如果不装踢脚线,用什么替代?...
  9. pythonredis实例_Python读写Redis数据库操作示例
  10. 64位指针膨胀 java_64位JVM带来的问题及解决方案