面试中的那些手写代码题目

话不多说,直接进入正题

JS防抖函数

基本的防抖函数

1
2
3
4
5
6
7
8
9
10
11
export function debounce (fn: Function, wait: number = 1000) {
  let t: any
  return function () {
    const _this = this
    t && clearTimeout(t)

    t = setTimeout(function() {
      fn.apply(_this, arguments)
    }, wait)
  }
}

添加 immediate 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export function debounce (fn: Function, wait: number = 1000, immediate: boolean = true) {
  let t: any
  return function () {
    const _this = this
    const _arg = arguments
    t && clearTimeout(t)

    if (immediate) {
      let isDo = !t
      t = setTimeout(function() {
        t = null
      }, wait)
      if (isDo) {
        fn.apply(_this, _arg)
      }
    } else {
      t = setTimeout(function() {
        fn.apply(_this, _arg)
      }, wait)
    }
  }
}

JS节流函数

节流的概念为多次点击的过程中只会在每次大于wait等待时长执行方法

1
2
3
4
5
6
7
8
9
10
11
export function throttle (fn: Function, wait: number = 1000) {
  let prev: number = +new Date()

  return function () {
    const now: number = +new Date
    if (now - prev > wait) {
      fn.apply(this, arguments)
      prev = +new Date()
    }
  }
}

手写call

call返回一个执行结果,第二个及以后的参数为执行函数的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Function.prototype.selfCall = function (context: Window = window) {
  const ctx = context || {}
  ctx.fn = this
  const args = Array.from(arguments).slice(1)
  const result = ctx.fn(args)
  delete ctx.fn
  return result
}

const bar = {
  a: 'this is bar'
}
const foo = {
  a: 1,
  getA: function (name, age = 0) {
    console.log(`prop: ${this.a}`)
    console.log(`name: ${name}`)
    console.log(`age: ${age}`)
    console.log('-------------------')
  }
}

foo.getA.selfCall(bar, 'selfCall')

// prop: this is bar
// name: selfCall
// age: 0
// -------------------

手写apply

apply同样也是返回一个执行结果,只是第二个参数为数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.selfApply = function (context: Window = window) {
  const ctx = context || {}
  ctx.fn = this
  const arg = Array.from(arguments).slice(1)[0]
  const result = ctx.fn(...arg)
  delete ctx.fn
  return result
}

// 接着call的对象信息执行代码
foo.getA.selfApply(bar, ['selfApply', 22])

// prop: this is bar
// name: selfApply
// age: 22
// -------------------

手写bind

注意:bind方法只是返回一个可执行函数,执行需要由用户自己执行返回的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.selfBind = function (context: Window = window) {
  const fn = this
  const ctx = context || {}
  const arg = Array.from(arguments).slice(1)
  return function () {
    fn.call(ctx, ...arg)
  }
}

foo.getA.selfBind(null, 'selfBind', 22)()

// prop: undefined
// name: selfBind
// age: 22
// -------------------

手写 Object.create 方法

科里化函数

通过判断参数是否符合实际方法所需参数的数量进行执行函数或者继续递归判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
export function curry (fn: Function) {
  const _this = this
  const args = Array.from(arguments).slice(1)
  // fn.length 属性指明函数的形参个数。
  const len = fn.length

  return function () {
    const _args = Array.from(arguments)
    args.push(..._args)
    if (args.length < len) {
      return curry.call(_this, fn, ...args)
    }

    return fn.apply(_this, args)
  }
}

// test
const addCur = function (a, b, c) {
  console.log('a + b + c', a + b + c)
}

const reduceCur = function (a, b, c) {
  console.log('a - b - c', a - b - c)
} 

const add = curry(addCur, 2)
s(1)(2)   // a + b + c 6
s(1, 3)   // a + b + c 6

const reduce = curry(reduceCur)
reduce(1)(2)(3)    // a - b - c -1
reduce(1, 2, 3)    // a - b - c -3

compose

类似react中组件, compose(fn1, fn2, fn3) (...args) = > fn1(fn2(fn3(...args)))

1
2
3
4
5
6
7
export function compose (...fn: Function[]) {
  return function (...args: any) {
    return fn.reduceRight((prevResult, currentFn) => {
      return currentFn.call(this, ...prevResult)
    }, args)
  }
}

实现一个 instanceof

instanceof 的原理就是判断这个变量是否来自于某一个构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function selfInstanceOf (left, right) {
  while (true) {
    if (left === null) return false
    if (left === right) return true
    if (left.__proto__ === right.prototype) return true
    left = left.__proto__
  }
}

// test
selfInstanceOf({}, Object)    // true
selfInstanceOf({}, Array)     // false
selfInstanceOf([], Array)     // true
selfInstanceOf([], Object)     // true

const Fn = function() {}
const a = new Fn()
selfInstanceOf(a, Fn)     // true
selfInstanceOf(a, Object)     // true

数组去重

就不使用 Set 去实现去重了,使用 filter + indexOf 实现数组去重

方法一

1
2
3
4
5
6
export function uniqueArray(arr: any[]) {
  return arr.filter((item, index) => arr.indexOf(item) === index)
}

console.log(uniqueArray([1, 2, 3, 2, 1, 3, 4, 5, 6, 3, 8]))
// [1, 2, 3, 4, 5, 6, 8]

方法二

1
2
3
4
5
6
7
8
9
10
export function uniqueArray(arr: any[]) {
  const newArr = arr.sort()
  let result = [newArr[0]]
  for (let i = 1; i < newArr.length; i++) {
    newArr[i] !== newArr[i - 1] && result.push(newArr[i])
  }
  return result
}
console.log(uniqueArray([1, 2, 3, 2, 1, 3, 4, 5, 6, 3, 8]))
// [1, 2, 3, 4, 5, 6, 8]

JS深拷贝

JSON.stringify 的问题导致我们需要自己手写基本的深拷贝方法 以下只做了object array的判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export function deepClone (obj: Object | any) {
  let result: any = {}
  for (let k in obj) {
    let typeStr = Object.prototype.toString.call(obj[k])
                .match(/\[object (.*?)\]/)[1].toLowerCase();
    switch (typeStr) {
      case 'object':
        result[k] = deepClone(obj[k])
        break
      case 'array':
        result[k] = obj[k].slice()
        break
      default:
        result[k] = obj[k]
    }
  }
  return result
}
上一篇 : js数据类型转换下一篇 : 关于闭包