JS手写,应对面试的利器,自身基础的体现(手写常用工具函数篇)

可能我们最常见的需要手写代码的地方就是面试了,往往很多时候,聊着聊着面试官突然就说让你实现某某方法。经常见到很多人抱怨,面试造火箭,真的很反感八股文。可是没有办法,面试官需要在短短一个小时或者两小时内判断你的水平,你总得展示点什么。最简单的判断方式之一就是看你实现一些方法,包括但不仅限于手写场景题或简单算法题,一些常用的工具方法,以及一些JS内置库函数。

除此之外,手写一些常见的方法能提升自己的代码掌控能力,测试自己JS基础是否扎实,理解底层的一些实现也更有利于自己写出高质量,少bug的代码,就比如知道forEach的实现原理,就不会在里面写return,更不会想用个变量去接收它的返回值。

我整理了一下自己面试中遇到,以及看各厂面经中出现高频的一些手写题,分为两篇:手写JS内置库函数,手写常用工具类函数,此为第二篇,以下是一些JS常用工具函数的简单实现

实现防抖函数

核心原理:事件触发后在指定时间内只执行一次,若在指定时间内再次触发,则重新计算时间。

  • 声明一个定时器变量timer
  • 返回一个新的函数,这是防抖的核心逻辑,利用闭包
  • 在返回的新函数内部,每次调用时都需要清除之前的定时器(如果存在),这样可以保证只有在最后一次调用之后的 wait 时间内没有新的调用发生时,才执行 fu

该函数有简单一点的,先等待再执行,也可以立即执行,实现会多一步。

js 复制代码
// 延迟执行,先等待再执行
function debounce(fn, wait) {
  let timer = null;
  return function () {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(function () {
      fn.apply(this, arguments);
    }, wait)
  }
}

// 立即执行
function debounce(fn, wait) {
  let timer = null;
  return function () {
    timer && clearTimeout(timer);
    let callNow = !timer;
    timer = setTimeout(function () {
      timer = null
    }, wait)
    callNow && fn.apply(this, arguments)
  }
}

实现节流函数

核心原理:连续发生的事件在指定时间内只触发一次。

也是利用闭包特性,判断连续发生的事件间隔是否超过给定的频率,该函数有两种实现方式:

  1. 利用时间戳,计算两次事件触发间隔,大于给定频率则执行函数
  2. 使用定时器
js 复制代码
// 使用时间戳
function throttle(fn, wait) {
  let pre = 0;
  return function () {
    const cur = Date.now();
    if (cur - pre > wait) {
      fn.apply(this, arguments);
      pre = cur
    }
  }
}

// 使用定时器
function throttle(fn, wait) {
  let timer = null;
  return function () {
    if (!timer) {
      timer = setTimeout(function () {
        timer = null;
        fn.apply(this, arguments)
      }, wait)
    }
  }
}

实现深拷贝函数

用法:拷贝一个对象的属性值 如果遇到属性值为引用类型的时候,会新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。以下只是最简单的实现,没有考虑时间,函数,正则等特殊对象,也没有处理循环引用。

js 复制代码
/** 深拷贝
 */
const deepClone = obj => {
  if (!obj || typeof obj !== 'object') {
    return obj
  }
  const temp = Array.isArray(obj) ? [] : {};
  for (let k in obj) {
    if (typeof obj[k] === 'object') {
      temp[k] = deepClone(obj[k])
    } else {
      temp[k] = obj[k]
    }
  }
  return temp
}

实现函数柯里化

函数柯里化是一种将使用多个参数的函数转换成一系列使用一个参数的函数的技术。

js 复制代码
function curry(fn, ...args) {
  return (..._args) => {
    let newArgs = [...args, ..._args];
    if (newArgs.length < fn.length) {
      return curry.call(this, fn, ...newArgs)
    } else {
      return fn.apply(this, newArgs)
    }
  }
}
// test
function sum(a, b, c) {
  return a + b + c;
}
console.log(curry(sum, 1, 2, 3)()); // 输出:6
console.log(curry(sum, 1)(2, 3));   // 输出:6
console.log(curry(sum, 1, 2)(3));   // 输出:6

实现数组扁平化

es6 有提供新的特性flat方法,用于将多维数组拉平(扁平化),不影响原数组,返回新的数组。写该函数纯粹是为了面试或者测试下自身基础及对代码掌控能力,或者自行处理es5以下的兼容问题。

js 复制代码
function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    const cur = arr[i];
    if (Array.isArray(cur)) {
      result = result.concat(flatten(cur))
    } else {
      result.push(cur)
    }
  }
  return result
}

将数字每千分位用逗号隔开

笔试部分非常常见的一道题目

js 复制代码
function formatNumber(num) {
  const numStr = num.toString();
  let result = '';
  let counter = 0;
  for (let i = numStr.length - 1; i >= 0; i--) {
    counter++;
    // result = numStr.charAt(i) + result;
    result = numStr[i] + result;
    if (!(counter % 3) && i !== 0) {
      result = ',' + result;
    }
  }
  return result
}

数组转树

非常高频的笔试题

js 复制代码
// [{ id: 1,  pid: 0,  name: 'body'},{}]
function arrToTree(arr) {
  const map = {};
  const tree = [];
  arr.forEach(item => {
    map[item.id] = item;
  });
  arr.forEach(item => {
    if (item.pid !== 0 || !map[item.pid]) { //根节点根据具体情况而定
      map[item.pid].children = item.children || [];
      map[item.pid].children.push(map[item.id]);
    } else {
      tree.push(item)
    }
  })
  return tree
}
// test
let source = [
  {
    id: 1,
    pid: 0,
    name: 'body',
  },
  {
    id: 2,
    pid: 1,
    name: 'title',
  },
  {
    id: 3,
    pid: 2,
    name: 'div',
  },
  {
    id: 4,
    pid: 0,
    name: 'html',
  },
  {
    id: 5,
    pid: 4,
    name: 'div',
  },
  {
    id: 6,
    pid: 5,
    name: 'span',
  },
  {
    id: 7,
    pid: 5,
    name: 'img',
  },
]
// 转为
[{
  id: 1,
  pid: 0,
  name: 'body',
  children: [
    {
      id: 2,
      pid: 1,
      name: 'title',
      children: [{ id: 3, pid: 2, name: 'div' }],
    },
  ],
},
  {
    id: 4,
    pid: 0,
    name: 'html',
    children: [
      {
        id: 5,
        pid: 4,
        name: 'div',
        children: [{ id: 7, pid: 5, name: 'img' }],
      },
    ],
  }]

树转数组

非常常见的笔试题

js 复制代码
// 简单实现
function treeToArr(tree) {
  const result = [];
  tree.forEach(item => {
    result.push(item);
    if (item.children) {
      result.push(...treeToArr(item.children))
    }
  });
  return result
}
// 完整实现
function treeToArr(tree) {
  const result = [];
  const addNode = (node) => {
    const { id, pid, name } = node;
    result.push({ id, pid, name });
    if (node.children && node.children.length) {
      node.children.forEach(child => addNode(child))
    }
  }
  tree.forEach(rootNode => addNode(rootNode));
  return result
}

//test
let source1 = [
  {
    id: 1,
    pid: 0,
    name: 'body',
    children: [
      {
        id: 2,
        pid: 1,
        name: 'title',
        children: [{ id: 3, pid: 2, name: 'div' }],
      },
    ],
  },
  {
    id: 4,
    pid: 0,
    name: 'html',
    children: [
      {
        id: 5,
        pid: 4,
        name: 'div',
        children: [{ id: 7, pid: 5, name: 'img' }],
      },
    ],
  },
]
// 转为
[
  { id: 1, pid: 0, name: 'body' },
  { id: 4, pid: 0, name: 'html' },
  { id: 2, pid: 1, name: 'title' },
  { id: 5, pid: 4, name: 'div' },
  { id: 3, pid: 2, name: 'div' },
  { id: 7, pid: 5, name: 'img' }
]

9. 获取给定范围内的随机数

该方法笔试经常碰到,也比较有实用性

js 复制代码
const getRandom = (start, end) => {
  const range = end - start,
    random = Math.random();
  return Math.round(random * range) + start
}

// 一行代码简写
// const getRandom = (start, end) => Math.round(Math.random() * (end  - start)) + start;
console.log(getRandom(3, 10))
相关推荐
上官花雨25 分钟前
什么是axios?怎么使用axios封装Ajax?
前端·ajax·okhttp
米奇妙妙wuu26 分钟前
React中 setState 是同步的还是异步的?调和阶段 setState 干了什么?
前端·javascript·react.js
李刚大人28 分钟前
react-amap海量点优化
前端·react.js·前端框架
闹闹没有闹1 小时前
socket连接封装
前端
qq_364371722 小时前
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
前端·vue.js·缓存
y先森3 小时前
CSS3中的弹性布局之侧轴的对齐方式
前端·css·css3
new出一个对象6 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥7 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森8 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy8 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html