防抖函数
每次触发事件时都取消之前的延时调用方法
/**
* @description 在一定时间内,持续触发,会重新计时
* @param (fn, wait) fn-函数, wait-延迟时间
* @return 新函数
*/
function debounce(fn, wait = 500) {
let timer = null
return function(...args) {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, wait)
}
}
- 使用
// HTML部分
<input type="input" id="username" />
// JS部分
const inputEL = document.querySelector('#username')
const listenFn = debounce(function(event) {
console.log(event.target.value)
}, 200)
inputEL.addEventListener('input', listenFn)
节流函数
无论如何触发,在一定时间内必定执行一次
/**
* @description 无论如何触发,在一定时间内必定执行一次
* @param (fn, wait) fn-函数, wait-延迟时间
* @return 新函数
*/
function throttle(fn, wait = 500) {
let timer = null
return function(...args) {
// 上一次定时器任务还没执行,什么都不做
if(timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, wait)
}
}
- 使用
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight)
}
window.addEventListener('resize', throttle(sayHi, 2000))
斐波那契数列
题目来自
描述:
- 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 结果大于1000000007时,需要做取模处理,如计算初始结果为:1000000008,请返回 1。
方式一:递归+闭包
/**
* @description 使用递归+闭包
* @param {number} n
* @return {number}
*/
const fib = function (n) {
// 缓存, 提前有n=0,1,2的值
const cache = new Map([
[0, 0],
[1, 1],
[2, 1],
])
// 内部函数
function inner(n) {
// 缓存中有,直接取出
if (cache.has(n)) {
return cache.get(n)
}
// 公式 f(n) = f(n-2) + f(n-1)
let result = inner(n - 2) + inner(n - 1)
// 结果超过1000000007,做取模处理
if (result > 1000000007) {
result = result % 1000000007
}
// 把当前n的结果存储
cache.set(n, result)
return result
}
return inner(n)
}
方式二:纯遍历(动态规划)
/**
* @param {number} n
* @return {number}
*/
const fib = function(n) {
// 有n=0,1,2的值
const map = new Map([
[0, 0],
[1, 1],
[2, 1],
])
if(n <= 2) {
return map.get(n)
}
let i = 3
let first = map.get(1)
let second = map.get(2)
while(i <= n) {
let result = second + first
if(result > 1000000007) {
result = result % 1000000007
}
// 移动结果,为下次计算做准备
first = second
second = result
i++
}
return second
}
手写call函数
-
原理:函数作为对象的属性调用时,this指向对象
-
思想思路:
- 获取
thisArg参数和剩余参数 - 获取调用
call的函数,把该函数设置到thisArg.func属性上 - 执行thisArg.func函数,并接收函数执行后的结果result
- 移除thisArg上的func函数
- 返回执行结果
Function.prototype.myCall = function(thisArg, ...args) {
// 值为 null, undefined时, 自动指向全局对象window
thisArg = thisArg == null ? window : thisArg
// 获取调用myCall时的方法,并把函数设置到thisArg.func属性上
thisArg.func = this
// 执行thisArg.func函数,接收结果
const result = thisArg.func(...args)
// 移除thisArg上的func
delete thisArg.func
// 返回结果
return result
}
- 测试myCall
function sayHello(time) {
console.log(`${this.name} say ${this.word} at ${time}`);
}
const bottle = {
name: 'violet-mio',
word: 'hello'
}
// myCall把sayHello中this指向变为bottle对象,可以获取到bottle上的属性
sayHello.myCall(bottle, new Date().toLocaleTimeString()) // violet-mio say hello at 下午4:46:28
手写bind函数
- 获取
thisArg和剩余参数args - 获取调用myBind的函数,保存到
func - 新建一个返回函数
resultFn,在resultFn中做如下逻辑- 获取执行resultFn时的secondArgs, 把之前的参数args和secondArgs做合并
- 判断是否使用new关键字执行resultFn函数
- 如果是,resultFn函数内部this不变
- 否则,resultFn函数内部this修改为
thisArg
- 通过apply执行
func函数,并返回执行结果
- 修改resultFn函数的原型,绑定到
Object.create(func.prototype)
Function.prototype.myBind = function(thisArg, ...args) {
thisArg = thisArg == null ? window : thisArg
// 保持调用myBind的函数
const func = this
const resultFn = function(...secondArgs) {
// 合并参数
const fullArgs = args.concat(secondArgs)
// 如果是通过 new 关键字调用的,绑定 this 为实例对象
const realThis = this instanceof resultFn ? this : thisArg
return func.apply(realThis, fullArgs)
}
// 绑定原型
resultFn.prototype = Object.create(func.prototype)
return resultFn
}
- 测试myBind
function Person(name, age) {
console.log('函数调用')
// 如果是new 执行的,实例对象的__proto__属性会指向构造函数的prototype
console.log('是否使用new', this.__proto__ === Person.prototype)
this.name = name
this.age = age
}
Person.prototype.sayHi = function() {
console.log('sayHi函数执行');
console.log(this.name, this.age);
}
const obj = {
name: '我是obj传进来的name',
age: '我是obj传进来的age'
}
const Student = Person.bind(obj, '实际参数name')
// 通过new创建Point执行bind函数后返回值的实例,并为构造函数传入参数
const stu = new Student('实际参数age')
stu.sayHi()
普通函数调用
const teaher = Person.bind(obj, '实际参数name')
teaher()
手写curry函数柯理化
- 获取原始函数
fn的可接受参数的长度 - 定义一个数组,缓存递归过程中的参数
- 返回柯理化函数,进行参数的收集
- 在柯里化函数中
- 合并本次参数和以前的参数
- 判断参数是否大于等于可接受参数的长度
- 若成立,执行原函数
- 否则,返回柯理化函数,继续参数的收集
const curry = function (fn) {
// 原函数可接受参数的数量
const limitLen = fn.length
// 存储递归过程的所有参数,可以在递归出口计算值
let params = []
return function _innerCurry(...args) {
// 合并参数
params = params.concat(args)
// 如果合并的参数个数大于等于fn接受参数的数量
if(params.length >= limitLen) {
// 就执行fn函数
return fn.apply(null, params)
} else {
// 返回一个柯里化函数, 继续收集参数
return _innerCurry
}
}
}
- 不使用柯理化求
sum(2)(3)(5)的计算结果
function add(num1, num2, num3) {
return num1 + num2 + num3
}
const sum = function(a) {
return function(b) {
return function(c) {
return add(a, b, c)
}
}
}
console.log(sum(2)(3)(5))
- 使用函数柯理化
const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)(5))
实现new操作符
- 创建空对象,链接原型到构造函数的显式原型上
- 执行构造函数,改变this为空对象,保存执行结果
- 构造函数执行结果是对象就返回该结果;否则返回obj对象
function myNew(fn, ...args) {
// 创建空对象,链接原型到构造函数的显式原型上,obj对象就可以访问原型上的方法和属性
const obj = Object.create(fn.prototype)
// 使用apply执行构造函数,this指向为obj对象
const result = fn.apply(obj, args)
// 如果构造函数执行结果是对象就返回该结果;否则,返回obj对象
return result instanceof Object ? result : obj
}
es5实现es6的继承(寄生式组合继承)
- 创建一个
空对象,空对象的原型链接到父类的显式原型上 空对象的constructor指向子类- 子类的
显式原型指向空对象
/**
* @description 寄生组合继承
* @params childClass - 子类,parentClass - 父类
*/
function inherit(Child, Parent) {
// 创建一个`空对象`,`空对象`的原型链接到`父类`的`显式原型`上
const obj = Object.create(Parent.prototype)
// `空对象`的`constructor`指向`子类`
obj.construct = Child
// 子类的显式原型指向`副本对象`
Child.prototype = obj
}
function Person(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
Person.prototype.sayName = function () {
console.log(this.name)
}
function Student(name, age) {
// 调用父类函数构造函数
Person.call(this, name)
this.age = age
}
// 寄生式组合继承
inherit(Student, Person)
// 子类方法必须在继承之后设置
Student.prototype.sayAge = function () {
console.log(this.age)
}
const stu = new Student('violet', 20)
console.log(stu)
stu.sayName()
stu.sayAge()
手写promise
实现构造函数
promise有三个状态pending,fulfilled,rejectedpromise可以由pending改变为fulfilled或者pending改变为rejected,状态不可逆- 创建
promise实例,会传递一个函数executorFn,在实例过程中,会立即调用该函数,把resolve和reject传递给使用者 - 如果业务正常,使用者会调用
resolve()函数,把成功结果传递进去- 在
resolve()函数中,修改状态为fulfilled并存储成功的值
- 在
- 如果业务失败,使用者会调用
reject()函数,把失败原因传递进去- 在
reject()函数中,修改状态为rejected并存储失败原因
- 在
class MyPromise {
// promise有三个状态 pending, fulfilled, rejected
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executorFn) {
this.state = MyPromise.PENDING // 初始化状态为pending
this.value = undefined // 成功后的值
this.reason = undefined // 失败后的原因
// 调用此方法就是成功
const resolve = (value) => {
if(this.state === MyPromise.PENDING) {
// 将状态修改为FULFILLED
this.state = MyPromise.FULFILLED
// 存储成功的值
this.value = value
}
}
// 调用此方法就是失败
const reject = (reason) => {
if(this.state === MyPromise.PENDING) {
// 将状态修改为REJECTED
this.state = MyPromise.REJECTED
// 存储失败原因
this.reason = reason
}
}
try {
// 立即执行executorFn函数,将resove和reject传递给使用者
executorFn(resolve, reject)
} catch (error) {
// executorFn执行过程出现异常,调用reject函数
reject(error)
}
}
}
// 使用
const p = new MyPromise((resolve, reject) => {
resolve('已解决')
// reject('已拒绝')
})
console.log(p)
实现then方法
调用then时,promise实例对象出处于PENDING, FULFILLED, REJECTED中的一个状态
PENDING(等待状态)时,需要先收集回调函数,再状态变更为FULFILLED或者REJECTED时,执行回调函数FULFILLED(已解决)时,可以直接执行成功的回调函数FULFILLED(已解决)时,可以直接执行失败的回调函数- 为了支持链式调用,then方法需要返回一个新的
promise实例对象
class MyPromise {
constructor(executorFn) {
// 省略内容。。。
this.state = MyPromise.PENDING // 初始化状态为pending
this.onResolvedCallbacks = [] // 存储成功的回调函数
this.onRejectedCallbacks = [] // 存储失败的回调函数
// 调用此方法就是成功
const resolve = (value) => {
if(this.state === MyPromise.PENDING) {
// 取出onResolvedCallbacks中函数依次执行
this.onResolvedCallbacks.forEach(fn => fn())
}
}
// 调用此方法就是失败
const reject = (reason) => {
if(this.state === MyPromise.PENDING) {
this.onRejectedCallbacks.forEach(fn => fn())
}
}
// 省略内容。。。
}
/**
* @description then函数
* @param 接收两个函数作为参数,分别是onFulfilled, onRejected
*/
then(onFulfilled, onRejected) {
// 避免传递进来的不是函数
// 将非函数值放到函数中
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v
onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err }
const p1 = new MyPromise((resolve, reject) => {
// 如果是异步任务,状态还没有变化,需要存储then中的回调函数,等待状态变化后,在执行
if(this.state === MyPromise.PENDING) {
// 缓存成功回调
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
const newValue = onFulfilled(this.value)
resolve(newValue) // p1.value = newValue
} catch (err) {
reject(err)
}
})
})
// 缓存失败回调
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const newReason = onRejected(this.reason)
resolve(newReason) // p1.reason = newReason
} catch (err) {
reject(err)
}
})
})
}
// 已解决状态
if(this.state === MyPromise.FULFILLED) {
// 使用setTimeout模拟异步,保证在调用栈为空后才执行
setTimeout(() => {
try {
// 取出存储的成功值
const newValue = onFulfilled(this.value)
resolve(newValue)
} catch (err) {
reject(err)
}
})
}
// 已拒绝状态
if(this.state === MyPromise.REJECTED) {
// 使用setTimeout模拟异步,保证在调用栈为空后才执行
setTimeout(() => {
try {
// 取出存储的成功值
const newReason = onRejected(this.reason)
resolve(newReason)
} catch (err) {
reject(err)
}
})
}
})
return p1
}
}
优化then:提取公共部分
上面then内部代码,有部分代码时类似的,可以坐下提取
// then方法,接收两个参数 onFulfilled成功回调函数和onRejected失败时的回调函数
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val) => val
onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err }
const asyncHander = (cb, currentResult) => {
setTimeout(() => {
try {
const newResult = cb(currentResult)
// 传递给下一个
resolve(newResult)
} catch (error) {
reject(error)
}
})
}
// 当前promise是FULFILLED已解决状态
if(this.state === MyPromise.FULFILLED) {
asyncHander(onFulfilled, this.value)
}
// 当前promise是REJECTED已解决状态
if(this.state === MyPromise.REJECTED) {
asyncHander(onRejected, this.reason)
}
// 状态还是PENDING等待状态,缓存回调函数
if(this.state === MyPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
asyncHander(onFulfilled, this.value)
})
this.onRejectedCallbacks.push(() => {
asyncHander(onRejected, this.reason)
})
}
})
}
实现catch方法
catch就是then的精简版,接收参数onRejected只处理onRejected
catch(onRejected) {
return this.then(null, onRejected)
}
实现resolve静态方法
实例化一个promise实例对象,数据传递给promise实例对象.resolve方法
class MyPromise {
static resolve(result) {
return new MyPromise((resolve, reject) => {
resolve(result)
})
}
}
实现reject静态方法
实例化一个promise实例对象,原因传递给promise实例对象.reject方法
class MyPromise {
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
}
实现race静态方法
- 接收一个数组,数组中存储有promise实例
- 实例化一个
新的promise实例对象 - 在新的promise实例对象中,遍历promise实例数组
- 取出一个promise实例,执行该promise实例的
then方法,只要有一个完成状态变更(成功或者失败)就返回
- 取出一个promise实例,执行该promise实例的
- 返回
新的promise实例对象
class MyPromise {
// 只要有一个完成(成功或者失败)就返回
static race(promiseList) {
return new MyPromise((resolve, reject) => {
promiseList.forEach(p => {
p.then(resolve, reject)
})
})
}
}
实现all静态方法
-
接收一个数组,数组中存储有promise实例
-
实例化一个
新的promise实例对象 -
在新的promise实例对象中,遍历promise实例数组
- 取出一个promise实例,执行该promise实例的
then方法,全部成功就返回结果,有一个失败,就返回失败的
- 取出一个promise实例,执行该promise实例的
-
返回
新的promise实例对象
class MyPromise {
// 执行数组所有promise,全部成功就返回结果,有一个失败,就返回失败的
static all(arr) {
return new MyPromise((resolve, reject) => {
const result = []
let index = 0
let length = arr.length
arr.forEach(p => {
p.then(res => {
result.push(res)
// 最后一个了,说明全部执行成功,可以返回了
if(index++ === length-1) {
resolve(result)
}
}).catch(err => {
reject(err)
})
})
})
}
}