面试遇到的手写函数

247 阅读6分钟

防抖函数

// 防抖函数
function debounce(fn, delay, immediate) {
    let timer = null;
    return function() {
        const _this = this;
        const args = [...argumets];
        if (immediate && !timer) {
            fn.apply(_this, args);
        }
        timer && clearTimeout(timer);
        timer = setTimeout(_ => fn.apply(_this, args), delay);
    }
}

截流函数

// 截流函数
function thtottle(fn, delay) {
    let start = Date.now();
    let timer = null;
    return funciton() {
        const _this = this;
        const args = [...argumets];
        let current = Date.now();
        let remain = delay - (current - start);
        timer && clearTimeout(timer);
        if (remain <= 0) {
            fn.apply(_this, args);
            start = Date.now();
        } else {
            setTimeout(_ => fn.apply(_this, args), remain)
        }
    }
}

对象深拷贝

// 对象深拷贝
function deepClone(obj) {
    let result;
    if (Array.isArray(obj)) {
        result = [];
    } else if (obj && typeof obj === 'object') {
        result = {};
    } else {
        return obj;
    }
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = deepClone(obj[key])
        }
    }
    return result;
}

模拟promise

// 模拟promise
class MockPromise {
    constructor(fn) {
        this.state = pending;
        this.value = undefined;
        this.reason = undefined;
        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
            }
        };
        const reject = value => {
            if (this.state === 'pending') {
                this.state = 'rejected'
                this.reason = value
            }
        };
        try {
            fn(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onResolve, onReject) {
        switch (this.state) {
            case 'fulfilled':
                onFulfilled();
                break;
            case 'rejected':
                onRejected();
                break;
            default:
        }
    }
}

模拟create

// 模拟create
function mockCreate(obj) {
    Function F() {};
    F.prototype = obj;
    return new F();
}

模拟call实现

// 模拟call实现
if (!Function.prototype.call) {
    Function.prototype.call = function(context) {
        if (typeof this !== 'function') {
            // throw new Error('must function');
        }
        context = context || window;
        const args = arguments.slice(1);
        context.fn = this;
        const result = context.fn(...args);
        delete context.fn;
        return result;
    }
}

模拟apply实现

// 模拟apply实现
if (!Function.prototype.apply) {
    Function.prototype.apply = function(context) {
        if (typeof this !== 'function') {
            // throw new Error('must function');
        }
        context = context || window;
        context.fn = this;
        const args = arguments.slice(1);
        const result = context.fn(...args);
        delete context.fn;
        return result;
    }
}

模拟bind实现

// 模拟bind实现
if(!Function.prototype.bind){
  Function.prototype.bind = function(context){
    // 首先判断this是不是function
    if(typeof this !== 'function'){
      // 抛出错误
    }
    var _this = this,
          fNOP = function(){},    // 用于后面维护原型关系
          args = Array.prototype.slice.call(arguments, 1),    // 取出bind收到的参数,除去第一个作为上下文的参数并保存
          fBound = function(){
            _this.call(context, args.concat(Array.prototype.slice.call(arguments)))   // 将bind接收的的参数和实际接收到的参数相连接
          };
    // 维护原型关系
    if(this.prototype){
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
    }
    return fBound;
  }
}

模拟new操作

// 模拟new操作
function mockNew () {
    const newObj = {}
    const _constructor = Array.prototype.shift.apply(arguments)
    newObj.__proto__ = _constructor.prototype
    _constructor.apply(newObj, arguments)
    return newObj
}

模拟instanceOf操作

// 模拟instanceOf操作
function mockInstanceOf (L, R) {
    //L 表示左表达式,R 表示右表达式
    const o = R.prototype
    let L = L.__proto__
    while (true) {
        if (L === null) return false
        if (L === o) return true
        L = L.__proto__
    }
}

顺序查找

// 顺序查找
function sequentialSearch(item, arr) {
    for (let i = 0, len = arr.length; i < len; i++) {
        if (arr[i] === item) return i;
    }
    return -1;
}

二分查找

// 二分查找
function binarySearch(item, arr) {
    arr.sort((a, b) => a - b);
    let low = 0,
        height = arr.length - 1;
    while(low <= height) {
        let mid = Math.floor(height - (height - low) / 2),
        element = arr[mid];
        if (item > element) {
            low = mid + 1;
        } else if (item < element) {
            height < mid - 1;
        } else {
            return mid;
        }
    }
    return -1;
}

冒泡排序

// 冒泡排序
function advanceBubbleSort(arr) {
    for (let i = 1, len = arr.length; i < len; i++) {
        for (let j = 0; j < len - i; j++) {
            if (arr[j] > a[j + 1]) {
                const temp = a[j + 1];
                a[j + 1] = a[j];
                a[j] = temp;
            }
        }
    }
    return arr;
}

选择排序

// 选择排序
function selectionSort(arr) {
    for (let i = 0, len = arr.length; i < len; i++) {
        let minIndex = i;
        for (let j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) minIndex = j;
        }
        const temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

插入排序

// 插入排序
function InsertionSort(arr){
    for (let i = 0, len = arr.length; i < len; i++) {
        let cur = arr[i];
        let j = i;
        while(j > 0 && arr[j - 1] > cur) {
            arr[j] = arr[j - 1];
            j--;
        }
        arr[j] = cur;
    }
    return arr;
}

快速排序

// 快速排序
function quickSort(arr) {
    const left = [],
        right = [],
        pivotIndex = Math.floor(arr.length / 2),
        pivot = arr.splice(pivotIndex, 1);
    arr.forEach(val => {
        if (val < pivot) {
            left.push(val);
        }
        if (val > pivot) {
            right.push(val);
        }
    })
    return quickSort(left).concat([pivot], quickSort(right));
}

从一个数组中找出 N 个数,其和为 M 的所有可能

// 从一个数组中找出 N 个数,其和为 M 的所有可能
const n = (num) => {
    return num.toString(2).replace(/0/g, '').length
}
const n = num => {
    let count = 0
    while(num) {
      num &= (num - 1)
      count++
    }
    return count
}
function search(arr, count, num) {
    const result = [];
    for (let i = 1; i < 1 << arr.length; i++) {
        const s = 0, temp = [];
        if (n(i) === count) {
            for (let j = 0; j < arr.length; j++) {
                if (i & (1 << j) !== 0) {
              s += arr[j];      temp.push(arr[j]);
                }
            }
        }
        if (s === sum) {
           result.push(temp);
        }
    }
    return result;
}

JS字符串全排列

// JS字符串全排列
function permutate(str, result = []) {
    const tempArr = [];
    if (str.length === 0) return result;
    if (result.length === 0) {
        tempArr.push(str[0]);
    } else {
        for (let i = 0; i < result.length; i++) {
            let item = result[i];
            let itemLen = item.length;
            for (j = 0; j < itemLen + 1; j++) {
                tempArr.push(item.slice(0, j) + str[0] + item.slice(j))
            }
        }
    }
    return permutate(str.slice(1), tempArr);
}

链表反转

// 链表反转
function reverseList(head) {
    if (head === null || head.next === null) {
        return head;
    } else {
        let next = null;
        let pre = null;
        while(head) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
    }
}

URL格式化

query格式化使用URLSearchParams

URLSearchParams
URL格式化使用URL
URL

对象转换

let input = {
    a: {
    b: {
        c: {
            dd: 'abcdd'
        }
    },
    d: {
        xx: 'adxx'
    },
    e: 'ae'
}
let output = {
    'a.b.c.dd': 'abcdd',
    'a.d.xx': 'adxx',
    'a.e': 'ae'
}
// input => output
const isObj = obj => obj && typeof obj === 'object'
function flatObj (obj, parentKey = '', res = {}) {
  if (!isObj(obj)) return
  for (const [key, val] of Object.entries(obj)) {
    const curentKey = parentKey ? `${parentKey}.${key}` : key
    isObj(val) ? flatObj(val, curentKey, res) : res[curentKey] = val
  }
  return res
}
// output => input
function setObj (res, keyArr, val) {
  let subObj = {}
  if (!res[keyArr[0]]) res[keyArr[0]] = {}
  subObj = res[keyArr[0]]
  for (let i = 1; i < keyArr.length; i++) {
    const val = keyArr[i]
    if (!subObj[val]) subObj[val] = i === keyArr.length - 1 ? val : {}
    subObj = subObj[val]
  }
}
function mapObj (obj, res = {}) {
  if (!isObj) return
  for (const [key, val] of Object.entries(obj)) {
    const keyArr = key.split('.')
    setObj(res, keyArr, val)
  }
  return res
}

数组去重

1. 如传入的数组元素为[123, "meili", "123", "mogu", 123],则输出:[123, "meili", "123", "mogu"]
2. 如传入的数组元素为[123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"],则输出:[123, [1, 2, 3], [1, "2", 3], "meili"]
3. 如传入的数组元素为[123, {a: 1}, {a: {b: 1}}, {a: "1"}, {a: {b: 1}}, "meili"],则输出:[123, {a: 1}, {a: {b: 1}}, {a: "1"}, "meili"]
// 实现
const isObj = obj => obj && typeof obj === 'object'
const arrFlat = arr => {
  if (!Array.isArray(arr)) return
  const arrFormat = []
  const arrRes = []
  arr.forEach(val => {
    const valFormat = isObj(val) ? JSON.stringify(val) : val
    arrFormat.includes(valFormat) ? console.log('continue') : (arrRes.push(val) && arrFormat.push(valFormat))
  })
  return arrRes
}

找出字符串中连续出现最多的字符和个数

'abcaakjbb' => {'a':2,'b':2}
'abbkejsbcccwqaa' => {'c':3}
// 实现
const countChat = str => {
    const matchStr = str.match(/(\w)\1*/g)
    const max = Math.max(...matchStr.map(val => val.length))
    return matchStr.reduce((pre, curent, index) => {
    if (matchStr[index].length === max) pre[matchStr[index]] = matchStr[index].length
    return pre
    }, {})
}

实现~功能

输入 '1, 2, 3, 5, 7, 8, 10' 输出 '1~3, 5, 7~8, 10'
// 实现
const transformStr = str => {
  const res = []
  const arrStr = str.split(',').map(val => Number(val.trim())).sort((a, b) => a - b)
  let flag = arrStr[0]
  arrStr.reduce((pre, current, index) => {
    if (current !== arrStr[index + 1] - 1) {
      if (flag === current) {
        pre.push(current)
      } else {
        pre.push(`${flag}~${current}`)
      }
      flag = arrStr[index + 1]
    }
    return pre.join(',')
  }, res)
  return res
}

深度遍历&&广度遍历

// 深度遍历
function dfs (node, nodeList = []) {
  if (node) {
    nodeList.push(node)
    const children = [...node.children]
    children.forEach(val => {
      dfs(val, nodeList)
    })
  }
  return nodeList
}
console.log('dfs:', dfs(document.body))
// 广度遍历
function bfs (node, nodeList = []) {
  const stacks = []
  if (node) {
    stacks.push(node)
    while (stacks.length) {
      const item = stacks.pop()
      const children = [...item.children]
      nodeList.push(item)
      children.forEach(val => stacks.push(val))
    }
  }
  return nodeList
}
console.log('bfs:', bfs(document.body))

长整数相加

function bigNumSum (num1, num2, decimal = 10) {
    const max = Math.max(num1.length, num2.length)
    num1 = num1.padStart(max, '0')
    num2 = num2.padStart(max, '0')
    let res = ''
    let anotherAdd = 0
    for (let i = max - 1; i >= 0; i--) {
        res = `${(parseInt(num1[i]) + parseInt(num2[i]) + anotherAdd) % decimal}${res}`
        anotherAdd = Math.floor((Number(num1[i]) + Number(num2[i]) + anotherAdd) / decimal)
    }
    return anotherAdd ? `1${res}` : res
}

排序二叉树

class Node {
  constructor (key) {
    this.key = key
    this.left = null
    this.right = null
  }
}

class BinaryTree {
    constructor () {
      this.root = null
    }
    insert (key) {
      const newNode = new Node(key)
      if (this.root) {
        this.insertNode(newNode, this.root)
      } else {
        this.root = newNode
      }
    }
    insertNode (node, root) {
      if (node.key > root.key) {
        root.right ? this.insertNode(node, root.right) : root.right = node
      } else {
        root.left ? this.insertNode(node, root.left) : root.left = node
      }
    }
    inOrderTraverse (cb) {
      this.inOrderTraverseNode(this.root, cb)
    }
    inOrderTraverseNode (root, cb) {
      if (root) {
        this.inOrderTraverseNode(root.left, cb)
        cb(root.key)
        this.inOrderTraverseNode(root.right, cb)
      }
    }
    preOrderTraverse (cb) {
      this.preOrderTraverseNode(this.root, cb)
    }
    preOrderTraverseNode (root, cb) {
      if (root) {
        cb(root.key)
        this.preOrderTraverseNode(root.left, cb)
        this.preOrderTraverseNode(root.right, cb)
      }
    }
    postOrderTraverse (cb) {
      this.postOrderTraverseNode(this.root, cb)
    }
    postOrderTraverseNode (root, cb) {
      if (root) {
        this.postOrderTraverseNode(root.left, cb)
        this.postOrderTraverseNode(root.right, cb)
        cb(root.key)
      }
    }
    min () {
      return this.minNode(this.root)
    }
    minNode (node) {
      if (node) {
        while (node && node.left) {
          node = node.left
        }
        return node.key
      }
      return null
    }
    max () {
      return this.maxNode(this.root)
    }
    maxNode (node) {
      if (node) {
        while (node && node.right) {
          node = node.right
        }
        return node.key
      }
      return null
    }
    search (key) {
      return this.searchNode(this.root, key)
    }
    searchNode (node, key) {
      if (node) {
        if (node.key < key) {
          return this.searchNode(node.right, key)
        } else if (node.key > key) {
          return this.searchNode(node.left, key)
        } else {
          return true
        }
      }
      return false
    }
    remove (key) {
      this.removeNode(this.root, key)
      return this.root
    }
    findMinNode (node) {
      if (node) {
        while (node && node.left) {
          node = node.left
        }
        return node
      }
      return null
    }
    removeNode (node, key) {
      if (node === null) return null
      if (key < node.key) {
        node.left = this.removeNode(node.left, key)
        return node
      } else if (key > node.key) {
        node.right = this.removeNode(node.right, key)
        return node
      } else {
        if (node.left === null && node.right === null) {
          node = null
          return node
        }
        if (node.left === null) {
          node = node.right
          return node
        } else if (node.right === null) {
          node = node.left
          return node
        } else {
          const minNode = this.findMinNode(node.right)
          node.key = minNode.key
          node.right = this.removeNode(node.right, minNode.key)
          return node
        }
      }
    }
}

约瑟夫问题

function countOff (N, M) {
  if (N < 1 || M < 1) {
    return
  }
  const source = Array(...Array(N)).map((_, i) => i + 1)
  let index = 0
  while (source.length > 1) {
    index = (index + M - 1) % source.length
    source.splice(index, 1)
  }
  return source[0]
}