1. 防抖
函数防抖是指再事件被出发n秒后再执行回调,如果再n秒内又被出发则重新计时。 一般使用再点击时间上面避免过多的请求
// 非立即执行版本
function debounce(callback, time) {
let timer = null
return function () {
if (timer) {
clearInterval(timer)
}
timer = setTimeout(() => {
callback.call(this, arguments)
}, time)
}
}
// 立即执行版本
function debounce(callBack, time) {
let timer = null
return function () {
if (timer) clearInterval(timer)
let triggerNow = !timer
timer = setTimeout(() => {
timer = null
}, time)
triggerNow && callBack.apply(this, arguments)
}
}
2. 节流
节流函数是指在规定的时间内只执行一次函数,如果在规定的时间内触发函数只会执行一次,并且是立即执行的节流可以使用在scroll函数的监听上,通过事件的节流来降低使用频率
function throttle(callback, time) {
let curTime = 0;
return function () {
if (Data.now() - curTime >= time) {
curTime = Date.now()
callback.apply(this.arguments)
}
}
}
3. 数柯里化
function curry(fn) {
return function curryed(...args) {
if (args.length < fn.length) {
return (...args1) => {
return curryed(...args, ...args1)
}
} else {
return fn(...args)
}
}
}
fucntion test (x,y,z){
return x+y+z
}
let addTest = curry(test)
addTest(1,2,3) // 6
4. 函数组合
function compose(...fns) {
return function (args) {
return fns.reverse().reduce((pre, cur) => {
return cur(pre)
}, args)
}
}
//es6 写法
const compose = (...fns) => args => fns.reverse().reduce((pre,cur)=> cur(pre),args)
function a(x) {
return x + 1;
}
function b(x) {
return x + 2;
}
function c(x) {
return x + 3;
}
const addNum = compose(a, b, c);
addNum(0) // 6
5. call函数
- 判断调用是否是一个函数,即使我们是定义在函数的原型上,但是可能出现call等调用情况
- 判断传入的上下文是否存在,如果不存在的话设置为window
- 处理传入的参数,截取第一个参数后的所有参数
- 将函数作为上下文对象的一个属性
- 使用上下文调用这个函数 并保存这个结果
- 删除刚刚新增的属性
- 返回结果
Function.prototype.myCall = function (context) {
if (this instanceof Function) throw new TypeError('error') //1
context = context || window //2
let args = [...arguments].slice(1) //3
context.fn = this //4
let result = context.fn(...args) //5
delete context.fn //6
return result //7
}
6. apply函数
- 判断调用是否是一个函数,即使我们是定义在函数的原型上,但是可能出现call等调用情况
- 判断传入的上下文是否存在,如果不存在的话设置为window
- 处理传入的参数,因为apply函数第二个参数是一个数组,所有通过下标找到参数
- 将函数作为上下文对象的一个属性
- 使用上下文调用这个函数 判断参数是否存在 并保存这个结果
- 删除刚刚新增的属性
- 返回结果
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') throw new TypeError('Error');//1
context = context || window//2
let args = arguments[1], resutl = null //3
context.fn = this //4
let result = args ? context.fn(...args) : context.fn() // 5
delete context.fn // 6
return result // 7
}
7. bind函数
- 判断调用的是否是一个函数,即使我们定义在函数原型上面,但是会出现call调用的情况
- 获取剩余的参数
- 保存当前函数的引用
- 创建一个函数返回
- 函数内部使用apply来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的this给apply调用,其他情况都可以传入指定的上下文对象
Function.prototype.myBind = function (context) {
if (!(this instanceof Function)) throw new TypeError('error')//1
let args = [...arguments].slice(1) //2
let fn = this //3
const _this = this
return function F() { // 4
return fn.apply(this instanceof F ? this : context, [...args, ...arguments])//5
}
}
8. new 操作符
在调用new的过程中会发生四件事情
- 首先创建一个新的对象
- 设置原型,将新对象的原型设置为函数的prototype对象(将this指向这个新对象)
- ,然后执行构造函数里面的代码(为这个对象添加穿过来的属性)
- 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
function MyNew(...args) {
// 获取要操作的构造函数 并且判断传入的是不是一个构造函数
if (args[0] instanceof Function) {
var constructor = args[0]
}else {
throw new TypeError('not a function')
}
// 基于这个构造函数创建一个新的对象(将this指向这个新的对象)
let newObj = Object.create(constructor.prototype)
// 执行这个构造函数
let res = constructor.apply(newObj, args.splice(1))
return (res instanceof Object || res instanceof Function) ? res : newObj
}
9. instanceof
intanceOf 运算符是用于判断构造函数的prototype属性是否出现在对象的原型链中的任何位置
- 首先获取类型的原型
- 然后获取对象的原型
- 然后一直判断对象的原型是否等于类型的原型,直到对象原型为null 因为原型链的最终也是null
function myInstanceOf(left, right){
let proto = Object.getPrototypeOf(left), // 获取对象的原型
prototype = right.prototype; // 获取构造函数的 prototype 对象
while (true) {
if (!proto) return false
if (proto === protoType) return true
proto = Object.getPrototypeOf(proto)
}
}
10. 深拷贝
function deepClone(obj, cache = new WeakMap()) {
if (typeof obj !== 'object') return obj // 普通类型,直接返回
if (obj === null) return obj
if (cache.get(obj)) return cache.get(obj) // 防止循环引用,程序进入死循环
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 找到所属原型上的constructor,所属原型上的constructor指向当前对象的构造函数
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
}
11. 冒泡排序
一次比较如果前面一个数大于后面一个数进行替换以此内推
function bubbl(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
12. 插入排序
- 有2个牌组 old是传入的数组,new是old中的第一项
- 每次从old中拿到一项A 和new中从后往前进行比对,new中的当前项为B ,如果发先A>B 找到B的位置插入到B的后面,如果没有找到的话 则在数组的第一项添加一个内容
- 返回new
function insert(arr) {
let newArr = [arr[0]]
for (let i = 1; i < arr.length; i++) {
for (let j = newArr.length - 1; j >= 0; j--) {
if (arr[i] > newArr[j]) {
newArr.splice(j + 1, 0, arr[i])
break
}
if (j === 0) {
newArr.unshift(arr[i])
}
}
}
return newArr
}
13. 快速排序
- 获取数组中中间的一项 midI,midV 。
- 再把当前数组中的minV的哪项去除
- 依次比较比midV大的放在arrRIght,反之 放在右边
- 递归这个函数
- 结束递归的条件 当数组的长度小于等于1的时候,则不需要再进行比对
function quick(arr) {
if (arr.length <= 1) return arr
let midIndex = Math.floor(arr.length / 2),
midValue = arr.splice(midIndex, 1),
arrLeft = [],
arrRight = []
for (let i = 0; i < arr.length; i++) {
arr[i] > midValue ? arrRight.push(arr[i]) : arrLeft.push(arr[i])
}
return quick(arrLeft).concat(midValue, quick(arrRight))
}
14. 手写Promise
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
function resolvePromise(promise, res, resolve, reject) {
if (promise === res) {
reject('Chaining cycle detected for promise #<Promise>')
}
if (res instanceof MyPromise) {
res.then(value => resolve(value), reason => reject(reason))
} else {
resolve(res)
}
}
class MyPromise {
constructor(exector) {
try {
exector(this.resolve, this.rejcet)
} catch (e) {
this.rejcet(e)
}
}
value = undefined
reason = undefined
status = PENDING
// 成功回调函数数组
successCallBack = []
// 失败的回调函数
failCallBack = []
resolve = (value) => {
if (this.status == PENDING) {
this.status = RESOLVED
this.value = value
this.successCallBack.forEach(item => { item(value) })
}
}
rejcet = (reason) => {
if (this.status == PENDING) {
this.status = REJECTED
this.reason = reason
this.failCallBack.forEach(item => { item(reason) })
}
}
then(successCallBack, failCallBack) {
successCallBack = successCallBack ? successCallBack : value => value
failCallBack = failCallBack ? failCallBack : reason => { throw reason }
const promise = new MyPromise((resolve, reject) => {
if (this.status == RESOLVED) {
setTimeout(() => {
try {
let res = successCallBack(this.value)
resolvePromise(promise, res, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status == REJECTED) {
setTimeout(() => {
try {
let res = failCallBack(this.reason)
resolvePromise(promise, res, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
this.successCallBack.push(() => {
setTimeout(() => {
try {
let res = successCallBack(this.value)
resolvePromise(promise, res, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.failCallBack.push(() => {
setTimeout(() => {
try {
let res = failCallBack(this.reason)
resolvePromise(promise, res, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise
}
catch(failCallBack) {
return this.then(undefined, failCallBack)
}
static all(arr) {
return new MyPromise((resolve, reject) => {
let results = []
let count = 0
function addData(index, item) {
count++
results[index] = item
if (count == results.length) {
resolve(results)
}
}
arr.forEach((item, index) => {
if (item instanceof MyPromise) {
item.then(value => { addData(index, value) }, reason => reject(reason))
} else {
addData(index, item)
}
})
})
}
finally(callBack) {
return this.then(value => {
return MyPromise.resolve(callBack()).then(() => value)
}, reaosn => {
return MyPromise.resolve(callBack()).then(() => { throw reaosn })
})
}
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise((resolve => resolve(value)))
}
static reject = (reason) => {
if (reason instanceof MyPromise) return reason
return new MyPromise((resolve, reject) => reject(reason))
}
}