给大家推荐一个实用面试题库
1、前端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
1、手写实现防抖和节流
1.1 实现防抖函数
防抖函数原理:把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算
// func是用户传入需要防抖的函数 // time是等待时间 const debounce = (callback, time = 50) => { let timer = 0 return function(...args) { if (timer) clearTimeout(timer) timer = setTimeout(() => { callback.apply(this, args) }, time) } } 复制代码
1.2 实现节流函数
节流函数原理: 指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的时间才会执行回调函数。
// func是用户传入需要防抖的函数 // wait是等待时间 const throttle = (func, wait = 50) => { let lastTime = 0 return function(...args) { let now = +new Date() if (now - lastTime > wait) { lastTime = now func.apply(this, args) } } } 复制代码
2、手写深拷贝
调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历。这就是我们在解这一类题时的核心的方法。
function deepClone(obj) { if(typeof obj !== 'object' || obj === null) { return obj } let copy = {} if(obj.constructor === Array) { copy = [] } for(let key in obj) { if(obj.hasOwnProperty(key)) { copy[key] = deepClone(obj[key]) } } return copy } 复制代码
3、手写实现call方法
call做了什么
- 将函数设为对象的属性
- 执行和删除这个函数
- 指定
this
到函数并传入给定参数执行函数- 如果不传入参数,默认指向为
window
Function.prototype.myCall = function(context = window, ...args) { if (typeof this !== "function") { throw new Error('type error') } let key = Symbol('key') context[key] = this; let result = context[key](...args); delete context[key]; return result; }; 复制代码
4 手写apply方法
Function.prototype.myApply = function(context = window, args) { let key = Symbol('key') context[key] = this; // let args = [...arguments].slice(1) let result = context[key](...args); delete context[key]; return result; } 复制代码
5、手写forEach方法
Array.prototype.myForEach = function(callback, context=window) { let self = this, i = 0, len = self.length; for(;i<len;i++) { typeof callback == 'function' && callback.call(context,self[i], i) } } 复制代码
6、手写filter方法
Array.prototype.myFilter=function(callback, context=window){ let len = this.length newArr = [], i=0 for(; i < len; i++){ if(callback.apply(context, [this[i], i , this])){ newArr.push(this[i]); } } return newArr; } 复制代码
7、手写reduce方法
Array.prototype.myReduce = function(fn, initialValue) { var arr = Array.prototype.slice.call(this); var res, startIndex; res = initialValue ? initialValue : arr[0]; startIndex = initialValue ? 0 : 1; for(var i = startIndex; i < arr.length; i++) { res = fn.call(null, res, arr[i], i, this); } return res; } 复制代码
8 查找字符串中出现最多的字符和个数
字符最多的是char,出现了num次
function myString(str){ let num = 0; let char = ''; let re = /(\w)\1+/g; str.replace(re,($0,$1) => { if(num < $0.length){ num = $0.length; char = $1; } }); return {num, char} } 复制代码
9 冒泡排序
冒泡排序的原理如下,从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到
length - 1
的位置
function bubbleSort(list) { var n = list.length; if (!n) return []; for (var i = 0; i < n; i++) { // 注意这里需要 n - i - 1 for (var j = 0; j < n - i - 1; j++) { if (list[j] > list[j + 1]) { var temp = list[j + 1]; list[j + 1] = list[j]; list[j] = temp; } } } return list; } 复制代码
10 快速排序
快排的原理如下。随机选取一个数组中的值作为基准值,从左至右取值与基准值对比大小。比基准值小的放数组左边,大的放右边,对比完成后将基准值和第一个比基准值大的值交换位置。然后将数组以基准值的位置分为两部分,继续递归以上操作
function quickSort(arr) { if (arr.length<=1){ return arr; } var baseIndex = Math.floor(arr.length/2); var base = arr.splice(baseIndex,1)[0]; var left=[]; var right=[]; for (var i = 0; i<arr.length; i++){ if (arr[i] < base){ left.push(arr[i]); } }else{ right.push(arr[i]);, } return quickSort(left).concat([base],quickSort(right)); } 复制代码
11 插入排序
function insertSort(arr) { for (let i = 1; i < arr.length; i++) { let j = i; let target = arr[j]; while (j > 0 && arr[j - 1] > target) { arr[j] = arr[j - 1]; j--; } arr[j] = target; } return arr; } 复制代码
12 对象扁平化
function objectFlat(obj = {}) { const res = {} function flat(item, preKey = '') { Object.entries(item).forEach(([key, val]) => { const newKey = preKey ? `${preKey}.${key}` : key if (val && typeof val === 'object') { flat(val, newKey) } else { res[newKey] = val } }) } flat(obj) return res } 复制代码
13、手写发布订阅模式
- 创建一个对象
on
方法用来把回调函数fn
都加到缓存列表中emit
根据key
值去执行对应缓存列表中的函数off
方法可以根据key
值取消订阅
class EventEmiter { constructor() { this._events = {} } on(eventName,callback) { if(!this._events) { this._events = {} } this._events[eventName] = [...(this._events[eventName] || []),callback] } emit(eventName, ...args) { if(!this._events[eventName]) { return } this._events[eventName].forEach(fn=>fn(...args)) } off(eventName,cb) { if(!this._events[eventName]) { return } this._events[eventName] = this._events[eventName].filter(fn=>fn != cb && fn.l != cb) } once(eventName,callback) { const one = (...args)=>{ callback(args) this.off(eventName,one) } one.l = callback this.on(eventName,one) } } 复制代码
14、手写Ajax
- 创建
XMLHttpRequest
实例 - 发出 HTTP 请求
- 服务器返回 XML 格式的字符串
- JS 解析 XML,并更新局部页面
- 不过随着历史进程的推进,XML 已经被淘汰,取而代之的是 JSON。
function ajax(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest() xhr.open('get', url) xhr.onreadystatechange = () => { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status <= 300) { resolve(JSON.parse(xhr.responseText)) } else { reject('请求出错') } } } xhr.send() }) } 复制代码
15、数组去重
function uniqueArr(arr) { return [...new Set(arr)]; } 复制代码
16、实现new的过程
new操作符做了这些事:
- 创建一个全新的对象
- 这个对象的
__proto__
要指向构造函数的原型prototype - 执行构造函数,使用
call/apply
改变 this 的指向 - 返回值为
object
类型则作为new
方法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) { let newObj = Object.create(fn.prototype); let res = fn.apply(newObj, args); return typeof res === 'object' ? res: newObj; }
给大家推荐一个实用面试题库
1、前端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库