前端小伙伴们在面试中经常会遇到面试的时候让手写一些函数,那么下面我讲整理一些面试中可能遇到的手写函数,供大家参考。
一、防抖
function debounce(fn, wait) {
let timer
return function (...e) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, e)
}, wait)
}
}
// 测试
function test() {
console.log('run scroll')
}
const debounceTest = debounce(test, 1000)
window.addEventListener('scroll', debounceTest)
二、节流
function throttle(fn, time) {
let last = 0 // 上次触发时间
return (...e) => {
const now = Date.now()
if (now - last > time) {
last = now
fn.apply(this, e)
}
}
}
// 测试
function test() {
console.log('run scroll')
}
const throttleTest = throttle(test, 1000)
window.addEventListener('scroll', throttleTest)
三、深拷贝
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (cache.get(obj)) return cache.get(obj) // 如果出现循环引用,则返回缓存的对象,防止递归进入死循环
let cloneObj = new obj.constructor() // 使用对象所属的构造函数创建一个新对象
cache.set(obj, cloneObj) // 缓存对象,用于循环引用的情况
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], cache) // 递归拷贝
}
}
return cloneObj
}
// 测试
const obj = {
name: 'Jack', address: {
x: 100, y: 200 } }
obj.a = obj // 循环引用
const newObj = deepClone(obj)
console.log(newObj.address === obj.address) // false
在项目中一般只需要用到JSON序列化或者解构就可以满足。
JSON..parse(JSON.stringify(obj))
[...arr]
{
...obj}
JSON序列化只会处理简单属性和简单属性数组,如果需要拷贝比较复杂的数组或对象请用第一个方法。
四、手写Promise
class MyPromise {
constructor(executor) {
this.status = 'pending' // 初始状态为等待
this.value = null // 成功的值
this.reason = null // 失败的原因
this.onFulfilledCallbacks = [] // 成功的回调函数存放的数组
this.onRejectedCallbacks = [] // 失败的回调函数存放的数组
let resolve = value => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn()) // 调用成功的回调函数
}
}
let reject = reason => {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn()) // 调用失败的回调函数
}
};
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,则修改为函数,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// onRejected如果不是函数,则修改为函数,直接抛出错误
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err }
return new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (err) {
reject(err)
}
})
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (err) {
reject(err)
}
})
}
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
// 将成功的回调函数放入成功数组
setTimeout(() => {
let x = onFulfilled(this.value)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
})
})
this.onRejectedCallbacks.push(() => {
// 将失败的回调函数放入失败数组
setTimeout(() => {
let x = onRejected(this.reason)
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
})
})
}
})
}
}
// 测试
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(resolve, 1000, 1)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
setTimeout(resolve, 1000, 2)
})
}
p1().then(res => {
console.log(res) // 1
return p2()
}).then(ret => {
console.log(ret) // 2
})
五、异步处理并发数量
function limitRequest(urls = [], limit = 3) {
return new Promise((resolve, reject) => {
const len = urls.length
let count = 0
// 同时启动limit个任务
while (limit > 0) {
start()
limit -= 1
}
function start() {
const url = urls.shift() // 从数组中拿取第一个任务
if (url) {
axios.post(url).then(res => {
// todo
}).catch(err => {
// todo
}).finally(() => {
if (count == len - 1) {
// 最后一个任务完成
resolve()
} else {
// 完成之后,启动下一个任务
count++
start()
}
})
}
}
})
}
// 测试
limitRequest(['https://aaa', 'https://bbb', 'https://ccc', 'https://ddd', 'https://eee'])
六、继承
ES5继承
function Parent(name) {
this.name = name
}
Parent.prototype.eat = function () {
console.log(this.name + ' 巴拉巴拉')
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.contructor = Child
// 测试
let xm = new Child('啦啦啦', 12)
console.log(xm.name) // 啦啦啦
console.log(xm.age) // 12
xm.eat() // 啦啦啦 巴拉巴拉
ES6继承
class Parent {
constructor(name) {
this.name = name
}
eat() {
console.log(this.name + ' 巴拉巴拉')
}
}
class Child extends Parent {
constructor(name, age) {
super(name)
this.age = age
}
}
// 测试
let xm = new Child('啦啦啦', 12)
console.log(xm.name) // 啦啦啦
console.log(xm.age) // 12
xm.eat() // 啦啦啦 巴拉巴拉
七、数组排序
sort 排序
// 对数字进行排序,简写
const arr = [3, 2, 4, 1, 5]
arr.sort((a, b) => a - b)
console.log(arr) // [1, 2, 3, 4, 5]
// 对字母进行排序,简写
const arr = ['b', 'c', 'a', 'e', 'd']
arr.sort()
console.log(arr) // ['a', 'b', 'c', 'd', 'e']
冒泡排序
function swap(arr, indexA, indexB) {
var temp;
temp = arr[indexA];
arr[indexA] = arr[indexB];
arr[indexB] = temp;
}
function bubbleSort(arr, compareFunc) {
for (let i = arr.length - 1; i > 0; i--) {
for (let j = 0; j < i; j++) {
if (compareFunc(arr[j], arr[j + 1]) > 0) {
swap(arr, j, j + 1);
}
}
}
return arr;
}
var arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24];
console.log(bubbleSort(arr, (a, b) => a - b));
console.log(bubbleSort(arr, (a, b) => b - a));
数组内对象按时间排序算法
var jsonArr = [
{
date : "2017-8-3 13:50:56" , key:"XX"},
{
date : "2017-8-2 11:50:56" , key:"XX"},
{
date : "2017-7-5 13:52:56" , key:"XX"},
{
date : "2017-11-3 13:50:56" , key:"XX"},
{
date : "2017-2-3 12:50:56" , key:"XX"},
{
date : "2017-8-3 14:58:56" , key:"XX"}
];
//排序
jsonArr.sort(function(a,b){
if(typeof(a) == "object" && typeof(b) == "object"){
return new Date (a['date']).getTime() > new Date (b['date']).getTime() ? 1 : -1; //正序
//return new Date (a['date']).getTime() > new Date (b['date']).getTime() ? -1 : 1; //倒叙 ,
}
});
console.log(jsonArr);
八、数组去重
ES6 Set 去重
const newArr = [...new Set(arr)]
// 或
const newArr = Array.from(new Set(arr))
indexOf 去重
function resetArr(arr) {
let res = []
arr.forEach(item => {
if (res.indexOf(item) === -1) {
res.push(item)
}
})
return res
}
// 测试
const arr = [1, 1, 2, 3, 3]
console.log(resetArr(arr)) // [1, 2, 3]
数组内对象去重
let person = [
{
id: 0, name: "小明"},
{
id: 1, name: "小张"},
{
id: 2, name: "小李"},
{
id: 3, name: "小孙"},
{
id: 1, name: "小周"},
{
id: 2, name: "小陈"},
];
let obj = {
};
let peon = person.reduce((cur,next,index,arr) => {
console.log(cur,next,index,arr)
obj[next.id] ? "" : obj[next.id] = true && cur.push(next);
return cur;
},[]) //设置cur默认类型为数组,并且初始值为空的数组
console.log(peon);
九、获取Url参数
URLSearchParams 方法
// 创建一个URLSearchParams实例
const urlSearchParams = new URLSearchParams(window.location.search);
// 把键值对列表转换为一个对象
const params = Object.fromEntries(urlSearchParams.entries());
split 方法
function getParams(url) {
const res = {
}
if (url.includes('?')) {
const str = url.split('?')[1]
const arr = str.split('&')
arr.forEach(item => {
const key = item.split('=')[0]
const val = item.split('=')[1]
res[key] = decodeURIComponent(val) // 解码
})
}
return res
}
// 测试
const user = getParams('http://www.baidu.com?user=%E9%98%BF%E9%A3%9E&age=16')
console.log(user) // { user: '阿飞', age: '16' }
十、递归的方式写1到100求和
function add(num1,num2){
var num = num1+num2;
if(num2+1>100){
return num;
}else{
return add(num,num2+1)
}
}
var sum =add(1,2);
console.log(sum) // 5050
十一、闭包
function makeFunc() {
var name = "Mozilla"
function displayName() {
console.log(name)
}
return displayName
}
var myFunc = makeFunc()
myFunc()