面试中经常会遇到面试官让手撕函数,我最近准备春招所以想总结一下,一部分根据自己理解实现,一部分借鉴,若有错误请指正。话不多说,上代码吧❤
1.函数柯里化
/**
* 函数柯里化
* @param {Function} fn
* @param {...any} args
* @returns
*/
function curry(fn, ...args) {
return function (...addArg) {
let totalArgs = [...args, ...addArg]
if (totalArgs.length >= fn.length) {
return fn.apply(this, totalArgs)
} else {
return curry.call(this, fn, ...totalArgs)
}
}
}
//test
let curryTest = curry(test)
console.log(curryTest(1, 2)(3, 4)(5, 6, 7))
console.log(curryTest(1, 2)(3, 4)(5, 6, 7)(8)(9))
2.实现Map
/**
* 实现Array.prototype.map
* @param {function} fn
* @param {Object} thisArg
* @returns
*/
Array.prototype.map = function (fn, thisArg) {
// 处理数组类型异常
if (this === null || this === undefined) {
throw new TypeError('this is not a array')
}
//处理函数异常
if (!fn instanceof Function) {
throw new Error(fn + 'is not a function')
}
let array = Object(this)//V8当中介绍先转换为对象我就这样写了
let len = array.length
let result = new Array(len)
for (let j = 0; j < len; j++) {
if (j in array) {
let value = array[j]
result[j] = fn.call(thisArg, value, j, array)
}
}
return result
}
function mapTest(value, ...args) {
return value * 2
}
let arr = [1, 2, 3, 4, 5]
console.log(arr.map(mapTest, undefined))
3.实现Reduce
/**
* 实现Array.prototype.reduce
* @param {function} fn
* @param {Number} initNum
* @returns
*/
function myReduce(fn, initNum) {
if (this === null || this === undefined) {
throw new TypeError('this is not a array')
}
if (!fn instanceof Function) {
throw new Error(fn + 'is not a function')
}
let array = Object(this)
let len = array.length
let i = 0
//这里处理当没有设置初始值的时候
if (initNum) {
for (; i < len; i++) {
if (array[i]) {
initNum = array[i]
break
}
}
}
//如果数组为空
if (i == 0 || initNum) {
throw new Error('this is a null Array')
}
i = 0
let sum = initNum
for (; i < len; i++) {
if (i in array) {
sum = fn.call(undefined, sum, array[i], i, array)
}
}
return sum
}
let arr = [1, 2, 3, 4, 5]
function testReduce(sum, value) {
return sum + value
}
console.log(arr.reduce(testReduce))
4.实现Call
/**
* 实现call
* @param {Object} thisArg
* @param {Function} fn
* @param {...any} args
*/
function call(context, ...args) {
context = context || window //如果传入null or undefined,默认绑定Window
let fn = Symbol('fn')//创建一个唯一值
context.fn = this
let result = eval('context.fn(...args)')//call传入的是参数列表
delete context.fn
return result
}
5.实现apply
/**
* 实现apply
* @param {Object} thisArg
* @param {Function} fn
* @param {...any} args
*/
function apply(context, ...args) {
context = context || window
let fn = Symbol('fn')
context.fn = this
let result = eval('context.fn(args)')//apply传入的是参数数组
delete context.fn
return result
}
6.实现bind
/**
* 实现bind
* @param {Object} context
* @param {...any} args
*/
Function.prototype.bind = function (context, ...args) {
//处理函数异常
if (!this instanceof Function) {
throw new Error('this is not a function,cant use bind')
}
let _this = this//保存当前this
let bindFun = function () {//返回绑定传入context的函数
_this.apply(context, args.concat(Array.from(arguments)))//支持柯里化传值
}
bindFun.prototype = Object.create(_this.prototype)
return bindFun
}
7.Promise
(1)Promise.resolve
/**
* 实现Promise.resolve
* @param {Promise} promise
* @returns resolve()
*/
Promise.resolve = function (params) {
//传入的并非一个promise,直接返回
if (!params instanceof Promise) {
return params
}
return new Promise((resolve, reject) => {
//传入的是一个thenable,将其状态作为最终状态返回
if (params && params.then && typeof params === 'function') {
params.then(resolve, reject)
} else {
//其他情况
resolve(params)
}
})
}
(2)Promise.all
/**
* 实现Promise.all
* @param {Promise} promises
* @returns resolve(array)
*/
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let len = promises.length
let result = []
//传入的数组为空
if (len == 0) {
resolve(result)
return
}
let count = 0
for (let i = 0; i < len; i++) {
//Promise.resolve(promises[i]):可能存在某一个不是promise
Promise.resolve(promises[i]).then(res => {
result.push(res)
count++
//全部执行成功,返回数组
if (count == len) {
return resolve(result)
}
}).catch(err => {
//出现一个失败直接reject
reject(err)
return
})
}
})
}
(3)Promise.race
/**
* 实现Promise.race
* @param {Promise} promises
*
*/
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
let len = promises.length
if (len == 0) {
return
}
//与all相反,仅一次成功或失败就结束
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(res => {
resolve(res)
return
}).catch(err => {
reject(err)
return
})
}
})
}
8.防抖
/**
* 防抖:指触发事件后在 n秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
* @param {Function} fn
* @param {Number} delay
* @returns
*/
function debounce(fn,delay){
let timer = null
return function(...args){
/* 进入该分支语句,
说明当前正在一个计时过程中,
并且又触发了相同事件。
所以要取消当前的计时,
重新开始计时*/
if(timer){
clearTimeout(timer)
}
//说明当前并没有在计时,那么就开始一个计时
timer = setTimeout(()=>{
fn.apply(this,args)
},delay)
}
}
9.节流
/**
* 节流:指连续触发事件但是在 n秒中只执行一次函数
* @param {Function} fn
* @param {Number} delay
* @returns
*/
function throttle(fn,delay){
let start = 0
return function(...args){
let end = Date.now()
if(end-start >= delay){
fn.apply(this,args)
start = end
}
}
}
后续会继续更新,有错请指教,别骂我就行,我会哭哈哈哈哈❤