经典又常用的JS代码片段

2,169 阅读8分钟

前言

日常开发中,一般的程序员通常会遇到以下场景:

  1. vue中遍历一个列表,但接口数据没有唯一标识,需要手动生成UUID来v-bind:key,怎么生成呢?Google一下
  2. 运行一段复杂代码,记得好像H5新的Api Worker可以开启多线程执行还能避免阻塞,但是具体怎么用呢?Google一下
  3. 当脱离框架,要实现删除一个节点、为节点添加或删除类、插入节点等操作时,隐隐记得好像都做过,但是忘了,Google一下
  4. 遇到设备兼容问题要对某个环境做特殊处理时,怎么判断移动端下的ios设备的自带浏览器?Google一下
  5. 有一个打乱数组的需求,这个我做过,(⊙o⊙)…好像写不太出来。算了,Google一下

不知道各位程序猿兄弟姐妹有没有出现以上类似场景,笔者是历历在目了。在开发时,很多功能我们都实现过,或者死记硬背过,或者看过实现原理。但是一旦到了应用的时候,脑袋便一片空白。所以还是那句老话,好记性不如烂笔头。开发不比面试,遇到问题随心所欲,要百度就百度,要谷歌就谷歌,但是查到的内容就参差不齐了,如果查到的文章本身就有问题,那试错成本就太高了。为了避免一般的程序员日复一日的Google,本文旨在打造一份满足日常开发的原生js代码片段库。可让一般的程序员可开箱即用,节省额外搜索的时间。

js代码片段使用 ES6 编写,已尽量精简和考虑兼容问题,大家可点赞、收藏一波,以便使用,闲暇时可常打开看看推敲其实现原理。

笔者会不定期更新哟,有问题可在评论区一起讨论,谢谢大家..

感谢.gif

工具

生成 UUID

const UUIDGeneratorBrowser = () =>
  ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
    (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16)
  );

// Examples
UUIDGeneratorBrowser(); // '7982fcfe-5721-4632-bede-6000885be57d'

解析 cookie

const parseCookie = (str) =>
  str
    .split(";")
    .map((v) => v.split("="))
    .reduce((acc, v) => {
      acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());
      return acc;
    }, {});

// Examples
parseCookie("foo=bar; equation=E%3Dmc%5E2");
// { foo: 'bar', equation: 'E=mc^2' }

获取网址参数

const getURLParameters = (url) =>
  (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
    (a, v) => (
      (a[v.slice(0, v.indexOf("="))] = v.slice(v.indexOf("=") + 1)), a
    ),
    {}
  );

// Examples
getURLParameters("google.com"); // {}
getURLParameters("http://url.com/page?name=Adam&surname=Smith");
// {name: 'Adam', surname: 'Smith'}

复制到剪切板

以下方式仅在用户执行操作时有效,如:click 事件

const copyToClipboard = (str) => {
  const el = document.createElement("textarea");
  el.value = str;
  el.setAttribute("readonly", "");
  el.style.position = "absolute";
  el.style.left = "-9999px";
  document.body.appendChild(el);
  const selected =
    document.getSelection().rangeCount > 0
      ? document.getSelection().getRangeAt(0)
      : false;
  el.select();
  document.execCommand("copy");
  document.body.removeChild(el);
  if (selected) {
    document.getSelection().removeAllRanges();
    document.getSelection().addRange(selected);
  }
};

// Examples
copyToClipboard("Lorem ipsum"); // 'Lorem ipsum' copied to clipboard.

简版 jquery 选择器

// 仅选中第一个元素
const $ = document.querySelector.bind(document);
// 选中所有
const $$ = document.querySelectorAll.bind(document);

const mainContent = $(".main-content");
const externalLinks = $$('a[target="_blank"]');

多线程执行函数

const runAsync = (fn) => {
  const worker = new Worker(
    URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
      type: "application/javascript; charset=utf-8",
    })
  );
  return new Promise((res, rej) => {
    worker.onmessage = ({ data }) => {
      res(data), worker.terminate();
    };
    worker.onerror = (err) => {
      rej(err), worker.terminate();
    };
  });
};

// Examples
const longRunningFunction = () => {
  let result = 0;
  for (let i = 0; i < 1000; i++)
    for (let j = 0; j < 700; j++)
      for (let k = 0; k < 300; k++) result = result + i + j + k;

  return result;
};
/*
  NOTE: Since the function is running in a different context, closures are not supported.
  The function supplied to `runAsync` gets stringified, so everything becomes literal.
  All variables and functions must be defined inside.
*/
runAsync(longRunningFunction).then(console.log); // 209685000000
runAsync(() => 10 ** 3).then(console.log); // 1000
let outsideVariable = 50;
runAsync(() => typeof outsideVariable).then(console.log); // 'undefined'

业务功能

判断所有数据类型

@param obj 要判断类型的数据

@return {string} 数据类型(小写)

const type = (function () {
  // 映射类型
  const classType =
    "Boolean Number String Function Array Date RegExp Object Error Null Undefined"
      .split(" ")
      .reduce((obj, item) => {
        obj["[object " + item + "]"] = item.toLowerCase();
        return obj;
      }, {});

  return function (obj) {
    // 仅 undefined 与 null 成立
    if (obj == null) {
      return obj + "";
    }
    return typeof obj === "object"
      ? classType[Object.prototype.toString.call(obj)]
      : typeof obj;
  };
})();

// Examples
console.log(type(new Date())); // date
console.log(type([1, 2])); // array
console.log(type(1)); // number
console.log(type({})); // object

判断空对象

function isEmptyObject(obj) {
  if (Object.prototype.toString.call(obj) !== "[object Object]") return false;
  var name;
  for (name in obj) {
    return false;
  }
  return true;
}

// Examples
console.log(isEmptyObject({})); // true
console.log(isEmptyObject([])); // false
console.log(isEmptyObject(null)); // false
console.log(isEmptyObject(undefined)); // false
console.log(isEmptyObject(1)); // false
console.log(isEmptyObject("")); // false
console.log(isEmptyObject(true)); // false

判断当前运行环境

function userAgent() {
  var u = navigator.userAgent;
  return {
    // 移动终端浏览器版本信息
    trident: u.indexOf("Trident") > -1, // IE内核
    presto: u.indexOf("Presto") > -1, // opera内核
    webKit: u.indexOf("AppleWebKit") > -1, // 苹果、谷歌内核
    gecko: u.indexOf("Gecko") > -1 && u.indexOf("KHTML") === -1, // 火狐内核
    mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
    iPad: u.indexOf("iPad") > -1, // 是否iPad
    webApp: u.indexOf("Safari") === -1, // 是否web应该程序,没有头部与底部,
    isiOS: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端
    isAndroid: u.indexOf("Android") > -1 || u.indexOf("Adr") > -1,
  };
}

// Examples
const browser = userAgent();
if (browser.mobile) {
  // 移动端 => todo something
  if (browser.isiOS && browser.webApp) {
    // IOS系统 && web程序 => todo something
  } else {
    // 安卓 => todo something
  }
} else {
  // PC => todo something
}

平滑滚动到页面顶部

const scrollToTop = () => {
  const c = document.documentElement.scrollTop || document.body.scrollTop;
  if (c > 0) {
    window.requestAnimationFrame(scrollToTop);
    window.scrollTo(0, c - c / 8);
  }
};

// Examples
scrollToTop();

将视口平滑滚动到指定元素

const smoothScroll = (element) =>
  document.querySelector(element).scrollIntoView({
    behavior: "smooth",
  });

// Examples
smoothScroll("#fooBar");

监听滚动结束

const onScrollStop = (callback) => {
  let isScrolling;
  window.addEventListener(
    "scroll",
    (e) => {
      clearTimeout(isScrolling);
      isScrolling = setTimeout(() => {
        callback();
      }, 150);
    },
    false
  );
};

// Examples
onScrollStop(() => {
  console.log("The user has stopped scrolling");
});

监听点击指定元素外部

const onClickOutside = (element, callback) => {
  document.addEventListener("click", (e) => {
    if (!element.contains(e.target)) callback();
  });
};

// Examples
onClickOutside("#my-element", () => console.log("Hello"));

获取浏览器当前语言

// defaultLang 为默认语言
const detectLanguage = (defaultLang = "en-US") =>
  navigator.language ||
  (Array.isArray(navigator.languages) && navigator.languages[0]) ||
  defaultLang;

// Examples
detectLanguage(); // 'nl-NL'

控制浏览器全屏、退出全屏

const fullscreen = (mode = true, el = "body") =>
  mode
    ? document.querySelector(el).requestFullscreen()
    : document.exitFullscreen();

// Examples
fullscreen(); // Opens `body` in fullscreen mode
fullscreen(false); // Exits fullscreen mode

封装原生 GET、POST 请求

const httpGet = (url, callback, err = console.error) => {
  const request = new XMLHttpRequest();
  request.open("GET", url, true);
  request.onload = () => callback(request.responseText);
  request.onerror = () => err(request);
  request.send();
};

const httpPost = (url, data, callback, err = console.error) => {
  const request = new XMLHttpRequest();
  request.open("POST", url, true);
  request.setRequestHeader("Content-type", "application/json; charset=utf-8");
  request.onload = () => callback(request.responseText);
  request.onerror = () => err(request);
  request.send(data);
};

// Examples
httpGet("https://jsonplaceholder.typicode.com/posts/1", console.log); /*
Logs: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
*/

httpPost(
  "https://jsonplaceholder.typicode.com/posts",
  null, // does not send a body
  console.log
); /*
Logs: {
  "id": 101
}
*/

DOM 操作

元素添加、移除、切换类

const addClass = (el, className) => el.classList.add(className);
const removeClass = (el, className) => el.classList.remove(className);
const toggleClass = (el, className) => el.classList.toggle(className);

// Examples
addClass(document.querySelector("p"), "special");
removeClass(document.querySelector("p.special"), "special");
toggleClass(document.querySelector("p.special"), "special");

移除一个元素

const removeElement = (el) => el.parentNode.removeChild(el);

// Examples
removeElement(document.querySelector("#my-element"));

判断元素上是否包含指定的类

const hasClass = (el, className) => el.classList.contains(className);

// Examples
hasClass(document.querySelector("p.special"), "special"); // true

获取一个元素下所有图片地址

// includeDuplicates 是否去重
const getImages = (el, includeDuplicates = false) => {
  const images = [...el.getElementsByTagName("img")].map((img) =>
    img.getAttribute("src")
  );
  return includeDuplicates ? images : [...new Set(images)];
};

// Examples
getImages(document, true); // ['image1.jpg', 'image2.png', 'image1.png', '...']
getImages(document, false); // ['image1.jpg', 'image2.png', '...']

创建字符串片段的元素

// 注意:最外层不可有同级兄弟元素,如果有只会返回第一个
const createElement = (str) => {
  const el = document.createElement("div");
  el.innerHTML = str;
  return el.firstElementChild;
};

// Examples
const el = createElement(
  `<div class="container">
    <p>Hello!</p>
  </div>`
);

主动触发 dom 事件

const triggerEvent = (el, eventType, detail) =>
  el.dispatchEvent(new CustomEvent(eventType, { detail }));

// Examples
triggerEvent(document.getElementById("myId"), "click");
triggerEvent(document.getElementById("myId"), "click", { username: "bob" });

Date

获取月份的总天数

利用 setDatesh(0)时,会将日期设置为对应月份的最后一天。

const daysInMonth = (year, month) => new Date(year, month, 0).getDate();

// Examples
daysInMonth(2020, 12)); // 31
daysInMonth(2024, 2)); // 29

将日期转换为 yyyy-MM-dd

const getISODate = (date) => data.toISOString().split("T")[0];

// Examples
getISODate(new Date()); // "2021-07-28"

将日期转换为 HH:MM:SS

const getColonTimeFromDate = (date) => date.toTimeString().slice(0, 8);

// Examples
getColonTimeFromDate(new Date()); // '08:38:00'

返回天、时、分、秒、毫秒

const formatDuration = (ms) => {
  if (ms < 0) ms = -ms;
  const time = {
    day: Math.floor(ms / 86400000),
    hour: Math.floor(ms / 3600000) % 24,
    minute: Math.floor(ms / 60000) % 60,
    second: Math.floor(ms / 1000) % 60,
    millisecond: Math.floor(ms) % 1000,
  };
  return Object.entries(time)
    .filter((val) => val[1] !== 0)
    .map(([key, val]) => `${val} ${key}${val !== 1 ? "s" : ""}`)
    .join(", ");
};

// examples
formatDuration(1001); // '1 second, 1 millisecond'
formatDuration(34325055574);
// '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'

返回给定秒数的 ISO 格式('00:00:00')

const formatSeconds = (s) => {
  const [hour, minute, second, sign] =
    s > 0
      ? [s / 3600, (s / 60) % 60, s % 60, ""]
      : [-s / 3600, (-s / 60) % 60, -s % 60, "-"];

  return (
    sign +
    [hour, minute, second]
      .map((v) => `${Math.floor(v)}`.padStart(2, "0"))
      .join(":")
  );
};

// Examples
formatSeconds(200); // '00:03:20'
formatSeconds(-200); // '-00:03:20'
formatSeconds(99999); // '27:46:39'

判断两个时间是否相同

const isSameDate = (dateA, dateB) =>
  dateA.toISOString() === dateB.toISOString();

// Examples
isSameDate(new Date(2010, 10, 20), new Date(2010, 10, 20)); // true

判断给定年份是否是闰年

const isLeapYear = (year) => new Date(year, 1, 29).getMonth() === 1;

// Examples
isLeapYear(2019); // false
isLeapYear(2020); // true

判断给定日期是否是周末

利用 getDay 方法,周 6 时返回 6,周日时返回 0。

const isWeekend = (d = new Date()) => d.getDay() % 6 === 0;

// Examples
isWeekend(new Date(2021, 6, 29)); // false
isWeekend(new Date(2021, 6, 31)); // true

返回给指定日期添加增量时间后的时间

利用setDate方法会自动换算大于 31 天的日期。

const addDaysToDate = (date, n) => {
  const d = new Date(date);
  d.setDate(d.getDate() + n);
  return d.toISOString().split("T")[0];
};

// Examples
addDaysToDate("2020-10-15", 10); // '2020-10-25'
addDaysToDate("2020-10-15", -10); // '2020-10-05'

算法

快速排序

const quickSort = (arr) => {
  const a = [...arr];
  if (a.length < 2) return a;
  const pivotIndex = Math.floor(arr.length / 2);
  const pivot = a[pivotIndex];
  const [lo, hi] = a.reduce(
    (acc, val, i) => {
      if (val < pivot || (val === pivot && i != pivotIndex)) {
        acc[0].push(val);
      } else if (val > pivot) {
        acc[1].push(val);
      }
      return acc;
    },
    [[], []]
  );
  return [...quickSort(lo), pivot, ...quickSort(hi)];
};

// Examples
quickSort([1, 6, 1, 5, 3, 2, 1, 4]); // [1, 1, 1, 2, 3, 4, 5, 6]

选择排序

const selectionSort = (arr) => {
  const a = [...arr];
  for (let i = 0; i < a.length; i++) {
    const min = a
      .slice(i + 1)
      .reduce((acc, val, j) => (val < a[acc] ? j + i + 1 : acc), i);
    if (min !== i) [a[i], a[min]] = [a[min], a[i]];
  }
  return a;
};

// Examples
selectionSort([5, 1, 4, 2, 3]); // [1, 2, 3, 4, 5]

插入排序

const insertionSort = (arr) =>
  arr.reduce((acc, x) => {
    if (!acc.length) return [x];
    acc.some((y, j) => {
      if (x <= y) {
        acc.splice(j, 0, x);
        return true;
      }
      if (x > y && j === acc.length - 1) {
        acc.splice(j + 1, 0, x);
        return true;
      }
      return false;
    });
    return acc;
  }, []);

// Examples
insertionSort([6, 3, 4, 1]); // [1, 3, 4, 6]

冒泡排序

const bubbleSort = (arr) => {
  let swapped = false;
  const a = [...arr];
  for (let i = 1; i < a.length; i++) {
    swapped = false;
    for (let j = 0; j < a.length - i; j++) {
      if (a[j + 1] < a[j]) {
        [a[j], a[j + 1]] = [a[j + 1], a[j]];
        swapped = true;
      }
    }
    if (!swapped) return a;
  }
  return a;
};

// Examples
bubbleSort([2, 1, 4, 3]); // [1, 2, 3, 4]

归并排序

const mergeSort = (arr) => {
  if (arr.length < 2) return arr;
  const mid = Math.floor(arr.length / 2);
  const l = mergeSort(arr.slice(0, mid));
  const r = mergeSort(arr.slice(mid, arr.length));
  return Array.from({ length: l.length + r.length }, () => {
    if (!l.length) return r.shift();
    else if (!r.length) return l.shift();
    else return l[0] > r[0] ? r.shift() : l.shift();
  });
};

// Examples
mergeSort([5, 1, 4, 2, 3]); // [1, 2, 3, 4, 5]

桶排序

const bucketSort = (arr, size = 5) => {
  const min = Math.min(...arr);
  const max = Math.max(...arr);
  const buckets = Array.from(
    { length: Math.floor((max - min) / size) + 1 },
    () => []
  );
  arr.forEach((val) => {
    buckets[Math.floor((val - min) / size)].push(val);
  });
  return buckets.reduce((acc, b) => [...acc, ...b.sort((a, b) => a - b)], []);
};

// Examples
bucketSort([6, 3, 4, 1]); // [1, 3, 4, 6]

二分搜索

const binarySearch = (arr, item) => {
  let l = 0,
    r = arr.length - 1;
  while (l <= r) {
    const mid = Math.floor((l + r) / 2);
    const guess = arr[mid];
    if (guess === item) return mid;
    if (guess > item) r = mid - 1;
    else l = mid + 1;
  }
  return -1;
};

// Examples
binarySearch([1, 2, 3, 4, 5], 1); // 0
binarySearch([1, 2, 3, 4, 5], 5); // 4
binarySearch([1, 2, 3, 4, 5], 6); // -1

打乱数组

const shuffle = ([...arr]) => {
  let m = arr.length;
  while (m) {
    const i = Math.floor(Math.random() * m--);
    [arr[m], arr[i]] = [arr[i], arr[m]];
  }
  return arr;
};

// Examples
shuffle([1, 2, 3]); // [2, 3, 1]

有趣的 JS

如何在 JS 实现睡眠功能

同步版本

Date.prototype.getTime()可以在 while 循环内使用以暂停执行一段时间。

const sleepSync = (ms) => {
  const end = new Date().getTime() + ms;
  while (new Date().getTime() < end) {
    /* do nothing */
  }
};

const printNums = () => {
  console.log(1);
  sleepSync(500);
  console.log(2);
  console.log(3);
};

printNums(); // Logs: 1, 2, 3 (2 and 3 log after 500ms)

异步版本

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const printNums = async () => {
  console.log(1);
  await sleep(500);
  console.log(2);
  console.log(3);
};

printNums(); // Logs: 1, 2, 3 (2 and 3 log after 500ms)

后语

参考文章:

该文章笔者会不定期更新,大家可以收藏一下。

我也会不定时的更新一些前端方面的知识内容以及自己的原创文章🎉

你的鼓励就是我持续创作的主要动力 😊.

相关推荐