前端手写题持续更新......

147 阅读6分钟

1.compose函数

介绍:将需要嵌套执行的函数扁平化处理。嵌套执行指的是,一个函数的返回值将作为另一个函数的参数。

compose函数的参数:若干个函数

compose函数的返回值:一个函数

简而言之:compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x)

blog.csdn.net/MRlaochen/a…

function compose(...arg) {
    const anony = function (val) {
        if(arg.length === 0) return val
        if(arg.length === 1) return arg[0](val)
        return arg.reduce((pre, cur) => {
            if (typeof pre === 'function') {
                return cur(pre(val))
            } else {
                return cur(pre)
            }
        })
    }
    return anony
}

function fun1(val) {
    return val + 1
}
function fun2(val) {
    return val + 2
}
function fun3(val) {
    return val + 3
}
console.log('结果', compose(fun1, fun2, fun3)(0))
// 结果 6

2.setTimeout实现setInterval

function mySetInterval(fun, delay) {
    let timeoutId = null
    function interval() {
        fun()
        timeoutId = setTimeout(interval, delay)
    }
    interval()
    return {
        claerMySetInterval: (timeoutId) => {
            clearTimeout(timeoutId)
        }
    }
}
function foo() {
    console.log('aaa')
}
mySetInterval(foo, 1000)

3.发布订阅模式

原理介绍:zhuanlan.zhihu.com/p/415653599

class EventEmitter {
    constructor() {
        this.events = {}
    }
    // 注册订阅事件
    on(eventName, handler) {
        if(this.events[eventName]) {
            this.events[eventName].push(handler)
        } else {
            this.events[eventName] = [handler]
        }
    }
    // 删除订阅事件
    off(eventName, handler) {
        if(this.events[eventName]) {
            this.events[eventName] = this.events[eventName].filter((handlerItem) => {
                return handlerItem !== handler
            })
        } else {
            return
        }
    }
    // 执行订阅事件
    emit(eventName, ...arg) {
        this.events[eventName] && this.events[eventName].forEach(handlerItem => {
            handlerItem.apply(this, arg)
            // this.events[eventName].pop[handlerItem]
        }); 
    }
    // 只执行一次的订阅事件
    once(eventName, handler) {
        function exeOnce() {
            handler()
            this.off(eventName, exeOnce)
        }
        this.on(eventName, exeOnce)
    }
}

// 使用如下
const event = new EventEmitter();

const handle = (...rest) => {
  console.log(rest);
};

event.on("click", handle); 
event.emit("click", 1, 2, 3, 4); //[ 1, 2, 3, 4 ]

event.off("click", handle);
event.emit("click", 1, 2);

event.once("dbClick", () => {
  console.log(123456);
});
event.emit("dbClick"); // 123456
event.emit("dbClick");



4.数组去重

使用Set(元素可谓基本数据类型和引用数据类型、元素不可重复)

function foo(arr) {
    return [... new Set(arr)]
}
let arr = [1, 2, 2, 3, 4, 4]
console.log(foo(arr))

5. 数组扁平化

5-1. 递归实现

function flatArr(arr) {
    if(!arr.length) return
    return arr.reduce((preValue, curValue) => 
        Array.isArray(curValue) ? [...preValue, ...flatArr(curValue)] : [...preValue, curValue], 
        []
    )
}
console.log(flatArr([1,2,[2,3,[5,6,7],4],[1,2,[2,1,3],3]]))
// [1, 2, 2, 3, 5, 6,7, 4, 1, 2, 2, 1,3, 3]

5-2. 迭代实现

function flatArr(arr) {
    if(!arr.length) return
    while(arr.some((item) => {
        Array.isArray(item)
    })) {
        arr = [].concat(...arr)
    }
}
console.log(flatArr([1,2,[2,3,[5,6,7],4],[1,2,[2,1,3],3]]))
// [1, 2, 2, 3, 5, 6,7, 4, 1, 2, 2, 1,3, 3]

6. 寄生组合继承

  1. 子类原型指向 Object.create(Parent.prototype) 创建的原型
  2. 父类作为工具函数为子类实例添加属性
  3. 将子类原型的 constructor 指向子类构造函数
function Parent(name) {
    this.name = name
    this.sayName = () => {
        console.log('sayName:', this.name)
    }
}

function Child(name, age) {
    Parent.call(this, name)
    this.age = age
    this.sayAge = () => {
        console.log('age:', this.age)
    }
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.construcor = Child
const childObj = new Child('zhangsan', 20)
console.log(childObj)
childObj.sayName()
childObj.sayAge()
//{
//     name: 'zhangsan',
//     sayName: [Function (anonymous)],
//     age: 20,
//     sayAge: [Function (anonymous)]
// }
// sayName: zhangsan
// age: 20

7. 手写 new 操作符实现

  1. 创建一个新的对象
  2. 函数中的 this 指向新创建的这个对象
  3. 修改对象的原型指向这个函数的原型
  4. 执行函数中的代码
  5. 返回新创建的对象(若函数的返回值是引用类型的数据,则返回这个引用类型的数据)
// 1. 创建一个新的对象
// 2. 函数中的 this 指向新创建的这个对象
// 3. 修改对象的原型指向这个函数的原型
// 4. 执行函数中的代码
// 5. 返回新创建的对象(若函数的返回值是引用类型的数据,则返回这个引用类型的数据)
function myNew(fun, ...args) {
    // 箭头函数不能作为构造函数使用,利用箭头函数没有(显式)原型的特点,判断传入的函数是否为箭头函数
    if(!fun.prototype) {
        throw TypeError (`${fun.name} is not a constructor`)
    }
    // 以 fun 函数的原型作为新创建对象的原型
    const newObj = Object.create(fun.prototype)
    let res = fun.call(newObj, ...args)
    if(fun() && (typeof res === 'object' || typeof res === 'function')) {
        return fun()
    } else {
        return newObj
    }
}
// 测试1:普通函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.say = function() {
  console.log(this.age);
};
let p1 = myNew(Person, "lihua", 18);
console.log(p1.name);
console.log(p1);
p1.say();
// lihua
// { name: 'lihua', age: 18 }
// 18


// 测试2:箭头函数
// let foo = () => {}
// myNew(foo) // Uncaught TypeError: foo is not a constructor

9. 实现 instanceOf

核心思想:判断构造函数的原型是否出现在对象的原型链上。

注意,原始类型使用 instanceOf 进行判断的时候会返回 false

function myInstanceof(obj, fun) {
    if(typeof obj !== 'object') {
        return false
    }
    let funPrototype = fun.prototype
    let objProto = Object.getPrototypeOf(obj)
    while(objProto) {
        if(objProto === funPrototype) {
            return true
        } else {
            objProto = Object.getPrototypeOf(objProto)
        }
    }
    return false
}

let obj = []
myInstanceof(obj, Array)
console.log('myInstanceof-1', myInstanceof(obj, Array)) // myInstanceof-1 true
console.log('myInstanceof-2', myInstanceof(obj, Object)) // myInstanceof-2 true
console.log('myInstanceof-3', myInstanceof(obj, Set)) // myInstanceof-3 true

// 注意,原始类型使用 instanceOf 进行判断的时候会返回false
// 例如:
console.log(null instanceof Object) // false 
console.log(1 instanceof Number) // false 
console.log('123' instanceof String) // false 
console.log(true instanceof Boolean) // false 

10. 实现 callapplybind

10-1. 实现 call

// call方法实现
// fun.call(thisObj, ...args)
// 核心思想:将fun函数添加为thisObj的方法,作为thisObj的方法来调用
// 简单实现:
// function myCall(thisObj, ...arg) {
//     thisObj.fun = this // this指向调用myCall的对象
//     return thisObj.fun() // 执行函数并返回执行结果
// }
// Function.prototype.myCall = myCall
// function foo() {
//     console.log(this)
// }
// foo.call({name: 'zhangsan', age: 20})

// 细节处理:
function myCall(thisObj, ...arg) {
    // 没有传入thisObj时,thisObj指向window
    thisObj = thisObj || window
    // 创建一个独一无二的值,防止覆盖thisObj原有的属性
    fun = Symbol('fun')
    thisObj[fun] = this
    // 执行函数
    const res = thisObj[fun](...arg)
    // 删除方法
    delete thisObj[fun]
    return res
}
Function.prototype.myCall = myCall
function foo(colorParam, sportParam) {
    this.color = colorParam
    this.sport = sportParam
    console.log(this)
}
foo.call({name: 'zhangsan', age: 21}, 'res', 'run')

10-2. 实现 apply

// apply 方法实现
// fun.apply(thisObj, argArr)

// 实现:
function myApply(thisObj, argArr) {
    // thisObj = thisObj || window
    // con
    // 判断是否传入thisObj,没传入的话thisObj指向window
    thisObj = thisObj || window
    // 创建一个独一无二的值,防止覆盖thisObj原有方法
    let fun = Symbol('fun')
    thisObj[fun] = this
    // 执行函数 
    const res = thisObj[fun](...argArr)
    // 删除方法
    delete thisObj[fun]
    return res
}
Function.prototype.myApply = myApply
function foo(colorParam, sportParam) {
    this.color = colorParam
    this.sportParam = sportParam
    console.log(this)
}
const thisObj = {name:'zhangsan', age:23}
foo.apply(thisObj, ['pink', 'jump'])

10-3. 实现 bind

// fun.bind(thisObj)
// 核心思想:对函数进行包装,包装时可利用call或者apply方法 

// 实现:
function myBind(thisObj, ...arg) {
    const fun = this
    thisObj = thisObj || window
    const argParam = arg
    const res = function(...innerArgParam) {
        // 参数合并
        innerArg = [...argParam, ...innerArgParam]
        if(this instanceof res) {
           return new fun(...innerArg)
        } else {
            fun.call(thisObj, ...innerArg)
        }
        
    }
    return res
}
Function.prototype.myBind = myBind
const thisObj = {
    name: 'hahaha',
    age: 24
}
function foo(colorParam, sportParam) {
    this.color = colorParam
    this.sport = sportParam
    console.log(this)
}
foo.myBind(thisObj, 'skyBlue', 'swim')()
foo.myBind(thisObj, 'skyBlue111', 'swim111')()
foo.myBind(thisObj)('skyBlue222', 'swim222')
foo.myBind(thisObj, 'skyBlue111', 'swim111')('skyBlue222', 'swim222')
foo.myBind(thisObj, 'skyBlue111')( 'swim222')
let foo1 = foo.myBind(thisObj, 'skyBlue111', 'swim111')
new foo1()

11. 判断两个值是否相等

function looseEqual (a, b) {
    if (a === b) return true
    const isObjectA = isObject(a)
    const isObjectB = isObject(b)
    if (isObjectA && isObjectB) {
      try {
        const isArrayA = Array.isArray(a)
        const isArrayB = Array.isArray(b)
        if (isArrayA && isArrayB) {
          return a.length === b.length && a.every((e, i) => {
            return looseEqual(e, b[i]) // b 包含 a
          })
        } else if (a instanceof Date && b instanceof Date) {
          return a.getTime() === b.getTime() // 单独处理 Date 类型, 时间戳应该是一样的
        } else if ( 0 ) {
          // 如果需要考虑其它类型, 添加 if 即可
        } else if (!isArrayA && !isArrayB) {
          const keysA = Object.keys(a)
          const keysB = Object.keys(b)
          // 先判断 key 的长度, 再判断 a 包含于 b
          return keysA.length === keysB.length && keysA.every(key => {
            return looseEqual(a[key], b[key])
          })
        } else {
          /* istanbul ignore next */
          return false
        }
      } catch (e) {
        /* istanbul ignore next */
        return false
      }
    } else if (!isObjectA && !isObjectB) {
      return String(a) === String(b)
    } else {
      return false
    }
  }
  
  function isObject (obj) {
    return obj !== null && typeof obj === 'object'
  }
  
  const _toString = Object.prototype.toString

12. Promise

  1. 构造函数
  2. then方法
  3. catch方法

构造函数:

  1. 定义状态
  2. roslve函数
  3. reject函数
  4. 执行executor

rosolve

  1. 判断状态
  2. 修改状态
  3. 修改值
  4. 执行回调

reject

  1. 判断状态
  2. 修改状态
  3. 修改值
  4. 执行回调

then方法

  1. fulfilled状态处理
  2. rejected状态处理
  3. pending状态处理
  4. 返回promise

catch方法