一些手写练习

272 阅读2分钟

闭包应用(防抖与节流)

  1. 防抖:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次(只需执行最后那次)
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
    }
}
  1. 节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数
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)
	}
    }
}

函数柯里化

  1. 把接收多个参数的函数变换成接收一个单一参数的函数(单一参数为多个参数中的第一个)
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

  1. 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)
            }
        })
    }
}

基础排序算法

  1. 冒泡排序:两两比较
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]
  1. 选择排序:遍历自身外的元素,最小的元素跟当前元素调换位置
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]
  1. 插入排序:即将元素插入到已排序好的数组中
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]
  1. 快速排序:
  • 选择基准值(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]