1.函数柯里化
参数复用
function CurryingCheck(reg) {
return function(text) {
return reg.test(text)
}
}
const hasNumber = CurryingCheck(/\d+/g)
const hasLetter = CurryingCheck(/[a-z]+/g)
console.log(hasNumber('abc1')) // true
console.log(hasLetter('1234')) // false
循环调用
/*
* 经典面试题,手写一个函数,使以下结果成立
* add(1)(2)(3) => 6
* add(1, 2, 3)(4) => 10
* add(1, 2, 3)(4)(5) => 15
*/
function add() {
let args = [...arguments]
function fn() {
args.push(...arguments)
return fn
}
fn.toString = _ => args.reduce((prev, next) => prev + next)
return fn
}
2.函数自执行的几种方式
注意:自执行函数前面的JS语句必须以分号结尾。
! function () { // 函数前加特殊符号 ! + - ~ . void
console.log(1)
}();
(function () {
console.log(2)
})();
(function () {
console.log(3)
}());
3.防抖
在指定时间连续调用防抖函数,防抖函数只执行一次。
1.立即执行版(即在指定时间内连续调用防抖函数,只执行第一次的定时函数)
2.非立即执行版(即在指定时间内连续调用防抖函数,只会执行最后一次的定时函数)
function debounce(fn, wait, immediate) {
let T
return function (...args) {
if (T) {
clearTimeout(T)
} else if (immediate) {
fn.apply(this, args)
}
T = setTimeout(() => {
T = null
if (!immediate) {
fn.apply(this, args)
}
}, wait)
}
}
4.节流
如果连续执行节流函数,只会在每个指定的时间内执行一次
1.时间戳版
缺点:如果倒数第一次的调用和倒数第二次的调用时间间隔小于指定时间,则最后一次的调用不会执行
function throttle(fn, wait) {
let time = 0
return function (...args) {
const nowTime = Date.now()
if (nowTime - time >= wait) {
fn.apply(this, args)
time = nowTime
}
}
}
2.定时器版
缺点:第一次触发,会在指定时间后才会执行
function throttle(fn, wait) {
let T = null
return function (...args) {
if (!T) {
T = setTimeout(() => {
T = null
fn.apply(this, args)
}, wait)
}
}
}
3.时间戳与定时器结合版
优点:有时我们更希望第一次触发立即执行处理函数,最后一次触发也执行一次处理函数
function throttle(fn, wait) {
let time = 0
let T = null
return function (...args) {
const nowTime = Date.now()
const remaining = nowTime - time - wait
if (T) clearTimeout(T)
if (remaining >= 0) {
fn.apply(this, args)
time = nowTime
} else {
T = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
}
5.手写call、apply、bind 函数
1.call
Function.prototype.myCall = function (ctx, ...args) {
let result
if (ctx) {
ctx._$ = this
result = ctx._$(...args)
delete ctx._$
} else {
result = this()
}
return result
}
2.apply
Function.prototype.myapply = function(ctx, args){
let result
if (ctx) {
ctx._$ = this
result = ctx._$(...args)
delete ctx._$
} else {
result = this()
}
}
3.bind
Function.prototype.mybind = function (ctx, ...args1) {
let func = this
return function (...args2) {
if(!ctx){
return func(...args2)
}
return func.call(ctx, ...args1, ...args2)
}
}
平级数据转换为树形
function listToTree(list, key = "id", parentKey = "pid", rootValue = 0, childKey = "children") {
const map = new Map();
return list.reduce((res, item) => {
if (item[parentKey] === rootValue) res.push(item);
if (map.has(item[parentKey])) {
map.get(item[parentKey])[childKey].push(item);
} else {
map.set(item[parentKey], { [childKey]: [item] });
}
map.set(item[key], Object.assign(item, map.get(item[key]) || { [childKey]: [] }));
return res;
}, []);
}