手写常用方法

193 阅读4分钟

合并两个有序数组

数组乱序

从现有数组里随机取一个,放入新数组:(Fisher–Yates洗牌算法)

function shuffle(arr) {
    let result = []
    while(arr.length > 0) {
        const randomIndex = Math.floor(Math.random() * arr.length)
        result.push(arr[randomIndex])
        arr.splice(randomIndex, 1)
    }
    return result
}

数组去重

遍历数组,不重复的放入新数组:

function unique(arr) {
    let result = []
    arr.forEach(item => {
        if (!result.includes(item)) {
            result.push(item)
        }
    })
    return result
}

Set:

Array.from(new Set(arr))

数组扁平

遍历数组,非array加入新数组,array拍平后展开加入:

function flat(arr) {
    let result = []
    arr.forEach(item => {
        if (item instanceof Array) {
            result.push(...flat(item))
        } else {
            result.push(item)
        }
    })
    return result
}

toString:

function flat(arr) {
    return arr.toString().split(',').map(item => Number(item))
}

数组自带的flat:

arr.flat()  // 参数是扁平化的深度,默认1

数组filter

遍历数组,符合callback条件的加入新数组:

Array.prototype.mockFilter = function(callback) {
    let arr = this
    let result = []
    arr.forEach((item, index) => {
        let flag = callback(item, index)
        if (flag) {
            result.push(item)
        }
    })
    return result
}

数组reduce

设置合适的初始值,遍历数组,累加:

Array.prototype.mockReduce = function(callback, init) {
    let arr = this
    let acc = init
    if (typeof init === 'undefined') {
        acc = arr[0]
        arr.shift()
    }
    arr.forEach(item => {
        acc = callback(acc, item)
    })
    return acc
}

call

暂时把函数绑定到一个对象上,调用后删除。

Function.prototype.mockCall = function(target = window, ...args) {  // ...是rest参数
    target.func = this
    let result = target.func(...args)  // ...是展开运算符
    delete target.func
    return result
}

apply ⭐

暂时把函数绑定到一个对象上,调用后删除。

Function.prototype.mockApply = function(target = window, args) {
    target.func = this
    let result = target.func(...args)
    delete target.func
    return result
}

bind

返回一个新函数,执行的时候apply传入的this指向和两份参数。

Function.prototype.mockBind = function(target, ...args) {
    let func = this
    let retFunc = function() {
        return func.apply(target, args.concat(...arguments))
    }
    return retFunc
}

instanceof

遍历对象的原型链,看是否等于构造函数的prototype或null。

function instanceOf(obj, func) {
    let proto = obj.__proto__
    let prototype = func.prototype
    while (true) {
        if (proto === prototype) return true
        if (proto === null) return false
        proto = proto.__proto__
    }
}

new

创建一个空对象,绑定原型,调用构造函数,返回对象。

function mockNew(func, ...args) {
    let obj = {}
    obj.__proto__ = func.prototype
    let result = func.apply(obj, args)
    return result instanceof Object ? result : obj
}

继承

ES5:寄生组合继承

调用父构造函数,绑定原型,修正原型的constructor指向。

function Parent() {}
function child() {
    Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child

ES6:extends

class Parent {}
class Child extends Parent {}

防抖 ⭐

触发后延迟执行,如果中间又被触发,就重新计时。

function debounce(func, wait) {
    let timer = null
    return function() {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            func.apply(this, arguments)
        }, wait)
    }
}

节流 ⭐

不管触发多少次,都按固定频率执行。

function throttle(func, wait) {
    let timer = null
    return function () {
        if (timer) return
        timer = setTimeout(() => {
            func.apply(this, arguments)
            timer = null
        }, wait)
    }
}

浅拷贝

基础类型直接返回,引用类型遍历赋值。

function shallowClone(obj) {
    if (obj instanceof Object) {
        let result = obj instanceof Array ? [] : {}
        for (let key in obj) {
            result[key] = obj[key]
        }
        return result
    } else {
        return obj
    }
}

深拷贝

基础类型直接返回,引用类型遍历递归赋值。

function deepClone(obj) {
    if (obj instanceof Object) {
        let result = obj instanceof Array ? [] : {}
        for (let key in obj) {
            result[key] = deepClone(obj[key])
        }
        return result
    } else {
        return obj
    }
}

eventEmitter

主要方法有:on、emit、off、once

class EventEmitter {
    constructor() {
        this.events = {}
    }
    on(name, cb) {
        if (!this.events[name]) {
            this.events[name] = [cb]
        } else {
            this.events[name].push(cb)
        }
    }
    emit(name, ...args) {
        if (this.evnets[name]) {
            this.events.forEach(func => {
                func.call(this, ...args)
            })
        }
    }
    off(name, cb) {
        if (this.events[name]) {
            this.events[name] = this.events[name].filter(func => {
                return func !== cb
            })
        }
    }
    once(name, cb) {
        let onlyOnce = () => {
            cb.apply(this, arguments)
            this.off(name, onlyOne)
        }
        this.on(name, onlyOnce)
    }
}

currying

如果实参大于等于形参,直接执行;如果小于形参,返回一个函数继续接收参数。

function curry(func) {
    function curried(...args) {
        if (args.length >= func.length) {
            return func.apply(this, args)
        } else {
            return function(...otherArgs) {
                return curried.apply(this, args.concat(otherArgs))
            }
        }
    }
    return curried
}

// 测试
function sum (a, b, c) {
  return a + b + c
}
const curriedSum = curry(sum)
console.log(curriedSum(1, 2, 3))
console.log(curriedSum(1)(2,3))
console.log(curriedSum(1)(2)(3))

冒泡排序

两两比较,大的往后放。

function bubbleSort(arr) {
    for (let i = 0; i < arr.length - 1; i++) {
        for (let j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            }
        }
    }
    return arr
}

插入排序

两两比较,小于往前放,大于停止。

function insertSort(arr) {
    for (let i = 0; i < arr.length - 1; i++) {
        for (let j = i+ 1; j > 0; j--) {
            if (arr[j] < arr[j - 1]) {
                [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]
            } else {
                break
            }
        }
    }
    return arr
}
// 冒泡比较的是无序部分,插入比较的是有序部分,利用有序减少了比较次数,所以比冒泡快。

快速排序

取一个中间数,其余大的放左边,小的放右边,然后递归。

function quickSort(arr) {
    if (arr.length <= 1) {
        return arr
    }
    let left = [], right = [], middle = arr.splice(0, 1)
    arr.forEach(item => {
        if (item < middle) {
            left.push(item)
        } else {
            right.push(item)
        }
    })
    return quickSort(left).concat(middle, quickSort(right))
}

斐波那契(递归)

function fibonacci(n) {
    if (n === 1 || n === 2) {
        return 1
    } else {
        return fibonacci(n - 1) + fibonacci(n - 2)
    }
}

斐波那契(迭代)

function fibonacci(n) {
    let a = 1, b = 1;
    while (n > 1) {
        [a, b] = [b, a + b]
        n--
    }
    return a
}

Promise

基础版本

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.error = undefined;
    
    const resolve = value => {
      // 状态不可逆
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
      }
    };
    
    const reject = error => {
      // 状态不可逆
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.error = error;
      }
    };
    
    try{
      // 作为参数传入的函数会立即执行
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  
  then(onFulfilled,onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    if (this.state === 'rejected') {
      onRejected(this.error);
    };
  }
}

解决异步

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.error = undefined;
    // 回调栈(用数组是因为Promise可以绑定多个回调函数,状态改变时一起执行)
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    
    const resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // 如果是异步,在这里执行回调
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    
    const reject = error => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.error = error;
        // 如果是异步,在这里执行回调
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  
  then(onFulfilled,onRejected) {
    // 如果是同步,即状态已经改变,直接在then中执行回调
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    if (this.state === 'rejected') {
      onRejected(this.error);
    };
    // 如果是异步,即处于pending状态,就先把回调存入回调栈,等状态改变后在resolve/reject中执行
    if (this.state === 'pending') {
      this.onResolvedCallbacks.push(()=>{
        onFulfilled(this.value);
      })
      this.onRejectedCallbacks.push(()=>{
        onRejected(this.error);
      })
    }
  }
}

Promise.resolve

当知道Promise将始终解决时,可以使用Promise.resolve。

Promise.resolve = (value) => {
    return new Promise((resolve, reject) => {
        resolve(value);
    })
}

Promise.resolve

当知道Promise将始终拒绝时,可以使用Promise.resolve。

Promise.reject = (error) => {
    return new Promise((resolve, reject) => {
        reject(error);
    })
}

Promise.race

传入的所有promise有一个成功,就执行resolve。

Promise.race = (promises) => {
    return new Promise((resolve, reject) => {
        promises.forEach(promise => {
            promise.then(resolve, reject);
        })
    })
}

Promise.all ⭐

传入的所有promise都成功,才执行resolve。

Promise.all = (promises) => {
    return new Promise((resolve, reject) => {
        let result = [];
        let index = 0;
        promises.forEach(promise => {
            promise.then(value => {
                result.push(value);
                index++;
                if (index === promises.length) {
                    resolve(result);
                })
            }, reject)
        })
    })
}

优化

  • 输入值类型判断。
  • 拷贝一份输入的数组,以免操作改变原数组。