「一劳永逸」送你21道高频JavaScript手写面试题(上)

简介: 「一劳永逸」送你21道高频JavaScript手写面试题

前言


基本上面试的时候,经常会遇到手撕 XXX 之类的问题,这次准备梳理总结一遍,巩固我们原生 JS 基础的同时,下次想复习面试手撕题的时候,找起来方便,也节省时间。


代码在这里 👉GitHub


梳理的顺序是随机的,不按照难以程度。


实现一个事件委托(易错)


事件委托这里就不阐述了,比如给 li 绑定点击事件


看错误版,(容易过的,看「面试官水平了」)👇

ul.addEventListener("click", function (e) {
  console.log(e, e.target);
  if (e.target.tagName.toLowerCase() === "li") {
    console.log("打印"); // 模拟fn
  }
});

「有个小 bug,如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对」👇

<ul id="xxx">
  下面的内容是子元素1
  <li>
    li内容>>> <span> 这是span内容123</span>
  </li>
  下面的内容是子元素2
  <li>
    li内容>>> <span> 这是span内容123</span>
  </li>
  下面的内容是子元素3
  <li>
    li内容>>> <span> 这是span内容123</span>
  </li>
</ul>

这样子的场景就是不对的,那我们看看高级版本 👇

function delegate(element, eventType, selector, fn) {
  element.addEventListener(
    eventType,
    (e) => {
      let el = e.target;
      while (!el.matches(selector)) {
        if (element === el) {
          el = null;
          break;
        }
        el = el.parentNode;
      }
      el && fn.call(el, e, el);
    },
    true
  );
  return element;
}

实现一个可以拖拽的 DIV


这个题目看起来简单,你可以试一试 30 分钟能不能完成,直接贴出代码吧 👇

<div id="xxx"></div>
var dragging = false;
var position = null;
xxx.addEventListener("mousedown", function (e) {
  dragging = true;
  position = [e.clientX, e.clientY];
});
document.addEventListener("mousemove", function (e) {
  if (dragging === false) return null;
  const x = e.clientX;
  const y = e.clientY;
  const deltaX = x - position[0];
  const deltaY = y - position[1];
  const left = parseInt(xxx.style.left || 0);
  const top = parseInt(xxx.style.top || 0);
  xxx.style.left = left + deltaX + "px";
  xxx.style.top = top + deltaY + "px";
  position = [x, y];
});
document.addEventListener("mouseup", function (e) {
  dragging = false;
});

手写防抖和节流函数


「节流 throttle」,规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。场景 👇


  • scroll 滚动事件,每隔特定描述执行回调函数
  • input 输入框,每个特定时间发送请求或是展开下拉列表,(防抖也可以)


节流重在加锁「flag = false」

function throttle(fn, delay) {
  let flag = true,
    timer = null;
  return function (...args) {
    let context = this;
    if (!flag) return;
    flag = false;
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(context, args);
      flag = true;
    }, delay);
  };
}

「防抖 debounce」,在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。场景 👇


  • 浏览器窗口大小 resize 避免次数过于频繁
  • 登录,发短信等按钮避免发送多次请求
  • 文本编辑器实时保存


防抖重在清零「clearTimeout(timer)」

function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    let context = this;
    if (timer) clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, delay);
  };
}

实现数组去重


这个是 Array 数组测试用例 👇

var array = [
  1,
  1,
  "1",
  "1",
  null,
  null,
  undefined,
  undefined,
  new String("1"),
  new String("1"),
  /a/,
  /a/,
  NaN,
  NaN,
];

如何通过一个数组去重,给面试官留下深印象呢 👇


使用 Set

let unique_1 = (arr) => [...new Set(arr)];

使用 filter

function unique_2(array) {
  var res = array.filter(function (item, index, array) {
    return array.indexOf(item) === index;
  });
  return res;
}

使用 reduce

let unique_3 = (arr) =>
  arr.reduce((pre, cur) => (pre.includes(cur) ? pre : [...pre, cur]), []);

使用 Object 键值对 🐂🐂,这个也是去重最好的效果 👇

function unique_3(array) {
  var obj = {};
  return array.filter(function (item, index, array) {
    return obj.hasOwnProperty(typeof item + item)
      ? false
      : (obj[typeof item + item] = true);
  });
}

使用obj[typeof item + item] = true,原因就在于对象的键值只能是字符串,所以使用typeof item + item代替


实现柯里化函数


柯里化就是把接受「多个参数」的函数变换成接受一个「单一参数」的函数,并且返回接受「余下参数」返回结果的一种应用。


思路:


  • 判断传递的参数是否达到执行函数的 fn 个数
  • 没有达到的话,继续返回新的函数,并且返回 curry 函数传递剩余参数
let currying = (fn, ...args) =>
  fn.length > args.length
    ? (...arguments) => currying(fn, ...args, ...arguments)
    : fn(...args);

测试用例 👇

let addSum = (a, b, c) => a + b + c;
let add = curry(addSum);
console.log(add(1)(2)(3));
console.log(add(1, 2)(3));
console.log(add(1, 2, 3));

实现数组 flat


「将多维度的数组降为一维数组」

Array.prototype.flat(num)
// num表示的是维度
// 指定要提取嵌套数组的结构深度,默认值为 1
使用 Infinity,可展开任意深度的嵌套数组

写这个给面试官看的话,嗯嗯,应该会被打死,写一个比较容易的 👇

let flatDeep = (arr) => {
  return arr.reduce((res, cur) => {
    if (Array.isArray(cur)) {
      return [...res, ...flatDep(cur)];
    } else {
      return [...res, cur];
    }
  }, []);
};

「你想给面试官留下一个深刻印象的话」,可以这么写,👇

function flatDeep(arr, d = 1) {
  return d > 0
    ? arr.reduce(
        (acc, val) =>
          acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val),
        []
      )
    : arr.slice();
}
// var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
// flatDeep(arr1, Infinity);

可以传递一个参数,数组扁平化几维,简单明了,看起来逼格满满 🐂🐂🐂


深拷贝


深拷贝解决的就是「共用内存地址所导致的数据错乱问题」


思路:


  • 递归
  • 判断类型
  • 检查环(也叫循环引用)
  • 需要忽略原型
function deepClone(obj, map = new WeakMap()) {
  if (obj instanceof RegExp) return new RegExp(obj);
  if (obj instanceof Date) return new Date(obj);
  if (obj == null || typeof obj != "object") return obj;
  if (map.has(obj)) {
    return map.get(obj);
  }
  let t = new obj.constructor();
  map.set(obj, t);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      t[key] = deepClone(obj[key], map);
    }
  }
  return t;
}
//测试用例
let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
  d: new RegExp(/^\s+|\s$/g),
};
let clone_obj = deepClone(obj);
obj.d = /^\s|[0-9]+$/g;
console.log(clone_obj);
console.log(obj);

实现一个对象类型的函数


核心:Object.prototype.toString

let isType = (type) => (obj) =>
  Object.prototype.toString.call(obj) === `[object ${type}]`;
// let isArray = isType('Array')
// let isFunction = isType('Function')
// console.log(isArray([1,2,3]),isFunction(Map))

isType 函数 👆,也属于「偏函数」的范畴,偏函数实际上是返回了一个包含「预处理参数」的新函数。


「一劳永逸」送你21道高频JavaScript手写面试题(下):https://developer.aliyun.com/article/1483403

相关文章
|
3月前
|
JSON JavaScript 前端开发
Javascript基础 86个面试题汇总 (附答案)
该文章汇总了JavaScript的基础面试题及其答案,涵盖了JavaScript的核心概念、特性以及常见的面试问题。
63 3
|
3月前
|
前端开发 JavaScript
JavaScript 面试系列:如何理解 ES6 中 Generator ?常用使用场景有哪些?
JavaScript 面试系列:如何理解 ES6 中 Generator ?常用使用场景有哪些?
|
4月前
|
JavaScript 前端开发
常见的JS面试题
【8月更文挑战第5天】 常见的JS面试题
63 3
|
1月前
|
JSON JavaScript 前端开发
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
本文介绍了JSONP的工作原理及其在解决跨域请求中的应用。首先解释了同源策略的概念,然后通过多个示例详细阐述了JSONP如何通过动态解释服务端返回的JavaScript脚本来实现跨域数据交互。文章还探讨了使用jQuery的`$.ajax`方法封装JSONP请求的方式,并提供了具体的代码示例。最后,通过一个更复杂的示例展示了如何处理JSON格式的响应数据。
35 2
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
|
2月前
|
Web App开发 JavaScript 前端开发
前端Node.js面试题
前端Node.js面试题
|
4月前
|
存储 JavaScript 前端开发
2022年前端js面试题
2022年前端js面试题
46 0
|
4月前
|
JavaScript 前端开发 程序员
JS小白请看!一招让你的面试成功率大大提高——规范代码
JS小白请看!一招让你的面试成功率大大提高——规范代码
|
4月前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
4月前
|
JavaScript 前端开发
JS:类型转换(四)从底层逻辑让你搞懂经典面试问题 [ ] == ![ ] ?
JS:类型转换(四)从底层逻辑让你搞懂经典面试问题 [ ] == ![ ] ?
|
5月前
|
缓存 JavaScript 前端开发
js高频面试题,整理好咯
中级前端面试题,不低于12k,整理的是js较高频知识点,可能不够完善,大家有兴趣可以留言补充,我会逐步完善,若发现哪里有错,还请多多斧正。