前端面试与进阶指南
20W字囊括上百个前端面试题的项目开源了
实现防抖函数(debounce)
防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
那么与节流函数的区别直接看这个动画实现即可。
手写简化版:
1 | // 防抖函数const debounce = (fn, delay) => { |
适用场景:
- 按钮提交场景:防止多次提交按钮,只执行最后提交的一次
- 服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似
生存环境请用lodash.debounce
实现节流函数(throttle)
防抖函数原理:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
1 | // 手写简化版 |
适用场景:
- 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
- 缩放场景:监控浏览器resize
- 动画场景:避免短时间内多次触发动画引起性能问题
深克隆(deepclone)
简单版:
1 | const newObj = JSON.parse(JSON.stringify(oldObj)); |
局限性:
- 他无法实现对函数 、RegExp等特殊对象的克隆
- 会抛弃对象的constructor,所有的构造函数会指向Object
- 对象有循环引用,会报错
面试版:
1 | /** |
局限性:
- 一些特殊情况没有处理: 例如Buffer对象、Promise、Set、Map
- 另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间
原理详解实现深克隆
实现Event(event bus)
event bus既是node中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计模式,是非常重要的基础。
简单版:
1 | classEventEmeitter{ |
面试版:
1 | classEventEmeitter{ |
实现具体过程和思路见实现event
实现instanceOf
1 | // 模拟 instanceof |
模拟new
new操作符做了这些事:
- 它创建了一个全新的对象
- 它会被执行[[Prototype]](也就是proto)链接
- 它使this指向新创建的对象
- 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
- 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
1 | // objectFactory(name, 'cxk', '18') |
实现一个call
call做了什么:
- 将函数设为对象的属性
- 执行&删除这个函数
- 指定this到函数并传入给定参数执行函数
- 如果不传入参数,默认指向为 window
1 | // 模拟 call bar.mycall(null); |
实现apply方法
apply原理与call很相似,不多赘述
1 | // 模拟 apply |
实现bind
实现bind要做什么
- 返回一个函数,绑定this,传递预置参数
- bind返回的函数可以作为构造函数使用。故作为构造函数时应使得this失效,但是传入的参数依然有效
1 | // mdn的实现 |
模拟Object.create
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
1 | // 模拟 Object.create |
实现类的继承
类的继承在几年前是重点内容,有n种继承方式各有优劣,es6普及后越来越不重要,那么多种写法有点『回字有四样写法』的意思,如果还想深入理解的去看红宝书即可,我们目前只实现一种最理想的继承方式。
1 | function Parent(name) { |
实现JSON.parse
1 | var json = '{"name":"cxk", "age":25}'; |
此方法属于黑魔法,极易容易被xss攻击,还有一种new Function
大同小异。
简单的教程看这个半小时实现一个 JSON 解析器
实现Promise
我很早之前实现过一版,而且注释很多,但是居然找不到了,这是在网络上找了一版带注释的,目测没有大问题,具体过程可以看这篇史上最易读懂的 Promise/A+ 完全实现
1 | var PromisePolyfill = (function () { |
解析 URL Params 为对象
1 | let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled'; |
模板引擎实现
1 | let template = '我是{{name}},年龄{{age}},性别{{sex}}'; |
转化为驼峰命名
1 | var s1 = "get-element-by-id"// 转化为 getElementById |
查找字符串中出现最多的字符和个数
例: abbcccddddd -> 字符最多的是d,出现了5次
1 | let str = "abcabcabcbbccccc"; |
字符串查找
请使用最基本的遍历来实现判断字符串 a 是否被包含在字符串 b 中,并返回第一次出现的位置(找不到返回 -1)。
1 | a='34';b='1234567'; // 返回 2 |
实现千位分隔符
1 | // 保留三位小数 |
正则表达式(运用了正则的前向声明和反前向声明):
1 | function parseToMoney(str){ |
判断是否是电话号码
1 | function isPhone(tel) { |
验证是否是邮箱
1 | function isEmail(email) { |
验证是否是身份证
1 | function isCardNo(number) { |