面试中常见的手写题汇总

2 阅读4分钟

防抖

<body>
  <button class="btn">发请求</button>

  <script>
    const btn = document.querySelector('.btn')

    function handle() {
      console.log('向后端发请求');
    }

    btn.addEventListener('click', debounce(handle, 1000))

    function debounce(fn, wait) {
      let timer = null
      return function (...args) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
          fn.call(this, ...args)
        }, wait)
      }
    }
  </script>
</body>

节流

<body>
  <button class="btn">发请求</button>

  <script>
    const btn = document.querySelector('.btn')

    function handle() {
      console.log('向后端发请求');
    }

    btn.addEventListener('click', throttle(handle, 1000))

    function throttle(fn, wait) {
      let preTime = null
      return function (...args) {
        let nowTime = Date.now()
        if (nowTime - preTime >= wait) {
          fn.call(this, ...args)
          preTime = nowTime
        }
      }
    }
  </script>
</body>

call,apply,bind

// call 将一个函数强行绑定到一个对象身上

function foo(x) {
  console.log(this.a, x);
}

const obj = {
  a: 1
}

foo.call(obj, 3)   // 1 3

Function.prototype.myCall = function (...args) {
  const context = args[0]
  const arg = args.splice(1)
  context.fn = this
  const res = context.fn(...arg)
  delete context.fn
  return res
}

foo.myCall(obj, 3)
// apply 以数组形式接收参数

function foo(x, y) {
  console.log(this.a, x, y);
}

const obj = {
  a: 1
}

foo.apply(obj, [2, 3])   // 1 2 3

Function.prototype.myApply = function (obj, args) {
  const context = obj
  context.fn = this
  const res = context.fn(...args)
  delete context.fn
  return res
}
foo.myApply(obj, [2, 3])   // 1 2 3
// bind 内部返回一个函数

function foo(x, y) {
  console.log(this.a, x, y);
}

const obj = {
  a: 1
}

const res = foo.bind(obj, 2)
res(3)  // 1 2 3

Function.prototype.myBind = function (context, ...args) {
  if (typeof this !== 'function') {
    throw new TypeError("Error")
  }

  const fn = this

  return function Fn(...arg) {
    const arr = args.concat(arg)
    fn.apply(context, arr)
  }
}

const res1 = foo.myBind(obj, 2)
res1(3)  // 1 2 3

关于this

new 实现

Person.prototype.sayName = function () {
  console.log(this.name);
}

function Person(name, age) {
  this.name = name
  this.age = 18

  // return []  如果这个是引用类型就输出这个,反之输出原来的
}

let p = new Person('张三', 18)
console.log(p);   // Person { name: '张三', age: 18 }
p.sayName();     // 张三
// myNew 实现
function myNew(constructor, ...args) {
  let obj = {}
  // obj.__proto__ = constructor.prototype
  obj = Object.create(constructor.prototype)
  const result = constructor.apply(obj, args)
  return result instanceof Object ? result : obj
}

let p = myNew(Person, '张三', 18)

console.log(p);   // Person { name: '张三', age: 18 }
p.sayName();     // 张三

instanceof

// instanceof 是用来判断一个对象是否是一个类的实例

function Car() {
  this.run = 'running'
}

Bus.prototype = new Car()

function Bus() {
  this.name = 'su7'
}

let bus = new Bus()

console.log(bus instanceof Bus);    // true
console.log(bus instanceof Car);    // true
console.log(bus instanceof Object);  // true

console.log([1, 2, 3] instanceof Array);  // true
console.log([1, 2, 3] instanceof Object);  //true
function myinstanceof(L, R) {
  while (L !== null) {
    L = L.__proto__
    if (L === R.prototype) {
      return true
    }
  }

  return false
}

console.log(myinstanceof([1, 2, 3], Array));   // true
console.log(myinstanceof([1, 2, 3], Object));   // true

console.log(myinstanceof({ a: 1 }, Array));    // false

js中的类型判断

浅拷贝

常见方法:Object.assign({},obj),[].concat(arr),数组解构,arr.slice()等。

Object.prototype.say = 'hello'


const obj = {
  name: '张三',
  age: 18,
  like: {
    n: 'running'
  }
}

function shallow(obj) {
  let res = {}
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      res[key] = obj[key]
    }
  }
  return res
}

console.log(shallow(obj));    // { name: '张三', age: 18, like: { n: 'running' } }

深拷贝

常见方法:JSON.parse(JSON.stringify(obj)),structuredClone(obj)

Object.prototype.say = 'hello'

const obj = {
  name: '张三',
  age: 18,
  like: {
    n: 'running'
  }
}

function deepClone(obj) {
  let res = obj instanceof Array ? [] : {}
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (typeof obj[key] === 'object' && typeof obj[key] !== null) {
        res[key] = deepClone(obj[key])
      } else {
        res[key] = obj[key]
      }
    }
  }

  return res
}

let newObj = deepClone(obj)
newObj.like.n = 'say'
console.log(newObj);    // { name: '张三', age: 18, like: { n: 'say' } }

深浅拷贝

数组去重

常见方法:双重for循环,for+indexOf,for+includes

// filter + sort
// 先排序再拿该值与前一个值比较,return 出改值与前面一个值不相等的值
const arr = [1, 2, 3, 4, 1, 2]

function unique(arr) {
  let newArr = arr
  let res = newArr.sort().filter((item, index, arr) => {
    return index === 0 || item !== arr[index - 1]
  })

  return res
}

console.log(unique(arr));   // [ 1, 2, 3, 4 ]
// 存入一个对象中
const arr = [1, 2, 3, 4, 1, 2]
// 若已有该值为true    
// {
//   1:true,
//   2:true
// }
function unique(arr) {
  let obj = {}

  for (let i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      obj[arr[i]] = true
    }
  }

  return Object.keys(obj).map(Number)
}

console.log(unique(arr));   // [ 1, 2, 3, 4 ]
// Set 数据结构 
let s = new Set()
s.add(1)
s.add(2)
s.add(1)
console.log(s);   // Set(2) { 1, 2 }
console.log(...s);   // 1 2

// 去重实现
const arr = [1, 2, 3, 4, 1, 2]

function unique(arr) {
  let res = new Set(arr)
  return [...res]
}

console.log(unique(arr));   //[ 1, 2, 3, 4 ]

数组去重

map遍历

// 数组遍历,接收三个参数,返回符合条件的参数
const arr = [1, 2, 3, 4, 5, 6]

// let res = arr.map((item, index, arr) => {
//   return item * 2
// })

// console.log(res);   // [ 2, 4, 6, 8, 10, 12 ]

Array.prototype.myMap = function (callback) {
  let res = []
  for (let i = 0; i < this.length; i++) {
    res.push(callback(this[i], i, this))
  }
  return res
}

let res = arr.myMap((item, index, arr) => {
  return item * 2
})

console.log(res);

数组遍历方法

for...of

// for...of 是遍历有迭代器属性的对象

function forOf(obj, cb) {
  if (!obj[Symbol.iterator]) {
    throw new TypeError(obj + "is not iterator")
  }

  let iterator = obj[Symbol.iterator]()
  let res = iterator.next()
  while (!res.done) {
    cb(res.value)
    res = iterator.next()
  }
}

let arr = [1, 2, 3, 4]
forOf(arr, function (value) {
  console.log(value);
})

扁平化

数组扁平化:

let arr = [1, [2, [3, 4]]]

// console.log(arr.flat(Infinity));  // [ 1, 2, 3, 4 ]

function flatten(arr) {
  let result = []

  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      // result = result.concat(flatten(item))
      // result.push(...flatten(item))
      result = [...result, ...flatten(item)]
    } else {
      res.push(arr[i])
    }
  }

  return result
}

console.log(flatten(arr));
// concat 拼接数组时,会去掉一层[]
function flatten(arr) {
  while (arr.some(item => Array.isArray(item))) {  // 只要有一个满足条件就返回true
    arr = [].concat(...arr)
  }
  return arr
}

console.log(flatten(arr));   // [ 1, 2, 3, 4 ]

对象扁平化:

// 对象的扁平化

const obj = {
  a: 1,
  b: [1, 2, { c: 3 }, [4]],
  d: {
    e: 2,
    f: 3
  }
}

// {
//   a: 1,
//   'b[0]': 1,
//   'b[1]': 2,
//   'b[2].c': 3,
//   'b[3][0]': 4,
//   'd.e': 2,
//   'd.f': 3
// }

function flatObj(obj) {
  let res = {}

  function help(obj, oldKey) {
    for (let key in obj) {
      let newKey
      if (oldKey) {
        if (Array.isArray(oldKey)) {
          newKey = `${oldKey}[${key}]`
        } else {
          newKey = `${oldKey}.${key}`
        }
      } else {
        newKey = key
      }

      if (Object.prototype.toString.call(obj[key]) === '[object Object]' || Array.isArray(obj[key])) {
        help(obj[key], newKey)
      } else {
        res[newKey] = obj[key]
      }
    }
  }

  help(obj, '')
  return res
}

console.log(flatObj(obj));

快排

const arr = [1, 5, 2, 1, 4, 9, 6]

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr
  }
  let left = []
  let right = []
  let middleIndex = Math.floor((arr.length / 2))
  let middle = arr.splice(middleIndex, 1)[0]
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] <= middle) {
      left.push(arr[i])
    } else {
      right.push(arr[i])
    }
  }

  return [...quickSort(left), middle, ...quickSort(right)]
}

console.log(quickSort(arr));

归并排序

image.png

const arr = [1, 5, 2, 1, 4, 9, 6]

function mergeSort(arr) {
  const len = arr.length
  if (len <= 1) {
    return arr
  }

  let mid = Math.floor(len / 2)
  const leftArr = mergeSort(arr.slice(0, mid))
  const rightArr = mergeSort(arr.slice(mid))

  arr = merge(leftArr, rightArr)
  return arr
}

function merge(arr1, arr2) {
  let res = []
  let i = 0, j = 0
  let len1 = arr1.length, len2 = arr2.length
  while (i < len1 && j < len2) {
    if (arr1[i] < arr2[j]) {
      res.push(arr1[i])
      i++
    } else {
      res.push(arr2[j])
      j++
    }
  }

  if (i < len1) {
    res = res.concat(arr1.slice(i))
  } else {
    res = res.concat(arr2.slice(j))
  }

  return res
}

console.log(mergeSort(arr));

斐波那契

function fib(n) {
  if (n === 0 || n === 1) return 1
  return fib(n - 1) + fib(n - 2)
}

function fibonacci(n) {
  if (n <= 1) return n
  let fib = [0, 1]
  for (let i = 0; i < n; i++) {
    fib[i] = fib[i - 1] + fib[i - 2]   // 计算第 i 个斐波那契数列的结果
  }

  return fib[n]
}

如有哪些补充,欢迎在评论区留言分享