闭包应用(防抖与节流)
- 防抖:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次(只需执行最后那次)
function debounce(fn, wait) {
let timer = null
let result = null
return function() {
const context = this
const args = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
result = fn.apply(context, args)
}, wait)
return result
}
}
- 节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数
function throttle(fn, wait) {
let timer = null
let startTime = Date.now()
return function() {
clearTimeout(timer)
const context = this
const args = arguments
let curTime = Date.now()
let remaining = wait - (curTime - startTime)
if (remaining <= 0) {
fn.apply(context, args)
startTime = Date.now()
} else {
timer = setTimeout(fn, remaining)
}
}
}
函数柯里化
- 把接收多个参数的函数变换成接收一个单一参数的函数(单一参数为多个参数中的第一个)
function add() {
// 获取第一个调用函数参数
let args = Array.prototype.slice.call(arguments)
let inner = function() {
args.push(...arguments)
// 递归,循环多层函数调用
return inner
}
// 改写toString方法
inner.toString = function() {
return args.reduce(function(pre, cur) {
return pre + cur
})
}
return inner
}
思考:
1. 利用闭包创建一个数组保存参数
2. 返回一个方法,用于接收下一个括号里的参数
3. 全部接收后,返回所有参数的和
add(1, 2)(3) // 6
add(1, 2, 3) // 6
Call、Apply、Bind
- Call:在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法
Function.prototype.myCall = function(context) {
// context为传入对象(如:obj)
// 判断调用对象(谁调用myCall,如:func)
// 这里的this是指调用myCall的对象
if (typeof this !== 'function') {
throw new Error('Type error')
}
// 获取传入context对象的参数
let args = [...arguments].slice(1)
let result = null
// 判断context是否传入(如:obj),如果没有传就设置为window
context = context || window
// 将被调用的方法设置为context的属性(如:obj.func)
context.fn = this
// 执行要被调用的方法
result = context.fn(...args)
// 删除手动增加的属性方法
delete context.fn
// 将执行结果返回
return result
}
let obj = {
value: '一哆'
}
function func() {
console.log(this.value)
}
// myCall实现obj添加func,obj.func()
func.myCall(obj) // 一哆
2.Apply
Function.prototype.myApply = function(context) {
if (type this !== 'function') {
throw new Error('Type error')
}
let result = null
context = context || window
let fnSymbol = Symbol()
context[fnSymbol] = this
if (arguments[1]) {
result = context[fnSymbol](...arguments[1])
delete context[fnSymbol]
} else {
result = context[fnSymbol]()
delete context[fnSymbol]
}
return result
}
3.Bind:
Function.prototype.myBind = function(context) {
if (type this !== 'function') {
throw new Error('Type error')
}
let args = [...arguments].slice(1)
context.fn = this
return function() {
context.fn(...args)
delete context.fn
}
}
深拷贝(递归实现)
function deepClone(obj) {
let result = null
if (typeof obj === 'object') {
if (Array.isArray(obj)) {
result = []
for (let key in obj) {
result.push(deepClone(obj[key]))
}
} else if (obj instanceof RegExp) {
result = obj
} else if (obj === null) {
result = null
} else {
result = {}
for (let key in obj) {
result[key] = deepClone(obj[key])
}
}
} else {
result = obj
}
return result
}
Promise A+
// 定义三个状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
constructor(executor) {
// executor 是一个执行器,实例化会立即调用
executor(this.resolve, this.reject)
}
// 状态,初始值是pending
status = PENDING
// 成功的值
vlue = null
// 失败的值
reason = null
// 储存成功回调函数
onFulfilledCallback = []
// 储存失败回调函数
onRejectedCallback = []
// resolve、reject函数使用箭头函数,是让this指向当前实例对象
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
// 判断是否存在成功回调函数,存在调用
while(this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(value)
}
}
}
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 判断是否存在失败回调函数,存在调用
while(this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
// 链式调用,return MyPromise实例对象
return new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = onFulfilled(this.value)
// 判断x是否MyPromise实例对象
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
if (this.status === REJECTED) {
onFulfilled(this.reason)
}
// 处理异步情况
if (this.status === PENDING) {
this.onFulfilledCallback.push(onFulfilled)
this.onRejectedCallback.push(onRejected)
}
})
}
}
基础排序算法
- 冒泡排序:两两比较
function bubbleSort(arr){
let len = arr.length
// 外循环控制循环总次数,数组length - 1
for (let outer = 0; outer < len - 1; outer++) {
for (let inner = len - 1; inner > -1; inner--) {
if (Number(arr[inner]) < Number(arr[inner - 1])) {
[arr[inner], arr[inner - 1]] = [arr[inner - 1], arr[inner]]
}
}
}
return arr
}
let arr = [11, 2, -10, 0, 21, -1]
bubbleSort(arr) // [-10, -1, 0, 2, 11, 21]
- 选择排序:遍历自身外的元素,最小的元素跟当前元素调换位置
function selectSort(arr) {
let len = arr.length
for (let i = 0; i < len - 1; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] > arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
}
return arr
}
let arr = [11, 2, -10, 0, 21, -1]
bubbleSort(arr) // [-10, -1, 0, 2, 11, 21]
- 插入排序:即将元素插入到已排序好的数组中
function insertSort(arr) {
let len = arr.length
// 外循环从1开始,默认arr[0]是有序段
for (let i = 1; i < len; i++) {
// j = i, 将arr[j]依次插入有序段中
for (let j = i; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
[arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]
}
}
}
return arr
}
let arr = [11, 2, -10, 0, 21, -1]
bubbleSort(arr) // [-10, -1, 0, 2, 11, 21]
- 快速排序:
- 选择基准值(base),原数组长度减一(基准值),使用 splice
- 循环原数组,小的放左边(left数组),大的放右边(right数组);
- concat(left, base, right)
- 递归继续排序 left 与 right
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
let left = [],
right = [],
base = arr.splice(0, 1)
for (let i = 0; i < arr.length; i++) {
if (arr[i] < base) {
// 放左边
left.push(arr[i])
} else {
// 放右边
right.push(arr[i])
}
}
return quickSort(left).concat(base, quickSort(right))
}
let arr = [11, 2, -10, 0, 21, -1]
bubbleSort(arr) // [-10, -1, 0, 2, 11, 21]