# 避免重复造轮子,常用方法封装

返回:js 进阶

# 真值和虚值

虚值:false,0, "",null,undefinedNaN。

真值:"Values",0",{},[]
1
2
3

# null vs undefined

null =>它是一个值,而 undefined 不是。

const fn = (x = "default value") => console.log(x);

fn(undefined); // default value
fn(); // default value

fn(null); // null
1
2
3
4
5
6

# 数组操作

# 数组的对象解构

const csvFileLine = "1997,John Doe,US,john@doe.com,New York";
const { 2: country, 4: state } = csvFileLine.split(",");

country; // US
state; // New Yourk
1
2
3
4
5

# negate:将不满足函数条件的内容筛选出来

const negate = (func) => (...args) => !func(...args);

[1, 2, 3, 4, 5, 6].filter(negate((n) => n % 2 === 0)); // [ 1, 3, 5 ]
1
2
3

# 使用 Boolean 过滤数组中的所有假值

我们知道 JS 中有一些假值:false,null,0,"",undefined,NaN,怎样把数组中的假值快速过滤呢,可以使用 Boolean 构造函数来进行一次转换

const compact = (arr) => arr.filter(Boolean);
compact([0, 1, false, 2, "", 3, "a", "e" * 23, NaN, "s", 34]); // [ 1, 2, 3, 'a', 's', 34 ]
1
2

# 用 length 重新设置数组大小

var array = [11, 12, 13, 14, 15];
console.log(array.length); // 5

array.length = 3;
console.log(array.length); // 3
console.log(array); // [11,12,13]

array.length = 0;
console.log(array.length); // 0
console.log(array); // []
1
2
3
4
5
6
7
8
9
10

# 数组排序,{type} 1:从小到大 2:从大到小 3:随机

export const sort = (arr, type = 1) => {
  return arr.sort((a, b) => {
    switch (type) {
      case 1:
        return a - b;
      case 2:
        return b - a;
      case 3:
        return Math.random() - 0.5;
      default:
        return arr;
    }
  });
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 数组元素随机排序(洗牌算法)

var list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(
  list.sort(function() {
    return Math.random() - 0.5;
  })
);
// [4, 8, 2, 9, 1, 3, 6, 5, 7]
1
2
3
4
5
6
7

# 数组去重

新手

let array = [100, 23, 23, 23, 23, 67, 45];
let outputArray = [];
let flag = false;
for (j = 0; < array.length; j++) {
   for (k = 0; k < outputArray.length; k++) {
      if (array[j] == outputArray[k]) {
         flag = true;
       }
    }
    if (flag == false) {
      outputArray.push(array[j]);
     }
     flag = false;
}
// outputArray = [100, 23, 67, 45]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const my_array = [1, 2, 2, 3, 3, 4, 5, 5];
const unique_array = [...new Set(my_array)];
console.log(unique_array); // [1, 2, 3, 4, 5]
1
2
3

这个技巧可以支持包含原始类型的数组:undefined、null、boolean、string 和 number。但如果你的数组包含了对象、函数或其他嵌套数组,就不能使用这种方法了。

# 对象数组去重

const data = [
  {
    name: "Kris",
    age: "24",
  },
  {
    name: "Andy",
    age: "25",
  },
  {
    name: "Kitty",
    age: "25",
  },
  {
    name: "Andy",
    age: "25",
  },
  {
    name: "Kitty",
    age: "25",
  },
  {
    name: "Andy",
    age: "25",
  },
  {
    name: "Kitty",
    age: "25",
  },
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

现在我们要去重里面 name 重复的对象,这时候我们可以利用 reduce

const dataReducer = (prev, cur, idx) => {
  let obj = {};
  const { name } = cur;
  obj[name] = cur; //obj:Kris: {name: "Kris", age: "24"}
  return {
    ...prev,
    ...obj,
  };
};
const reducedData = data.reduce(dataReducer, {});
let newData = Object.values(reducedData);
// 0: {name: "Kris", age: "24"}
// 1: {name: "Andy", age: "25"}
// 2: {name: "Kitty", age: "25"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 从数组中删除虚值

在某些情况下, 我们想从数组中删除虚值。虚值是 JavaScript 中的值为 FALSE 的值。JavaScript 中只有六个虚值,它们是:

undefined;
null;
NaN;
0;
""(空字符);
false;
1
2
3
4
5
6

过滤这些虚值的最简单方法是使用下面的函数:

myArray.filter(Boolean);
1

# drop-数组从左边开始删除 n 个元素

此段代码将给定的数组从左边开始删除 n 个元素

const drop = (arr, n = 1) => arr.slice(n);

drop([1, 2, 3]); // [2,3]
drop([1, 2, 3], 2); // [3]
drop([1, 2, 3], 42); // []
1
2
3
4
5

# dropRight-数组从右边开始删除 n 个元素

此段代码将给定的数组从右边开始删除 n 个元素

const dropRight = (arr, n = 1) => arr.slice(0, -n);

dropRight([1, 2, 3]); // [1,2]
dropRight([1, 2, 3], 2); // [1]
dropRight([1, 2, 3], 42); // []
1
2
3
4
5

# dropRightWhile-按照给定的函数条件从右开始删除

此段代码将给定的数组按照给定的函数条件从右开始删除,直到当前元素满足函数条件为 True 时,停止删除,并返回数组剩余元素。

const dropRightWhile = (arr, func) => {
  while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1);
  return arr;
};

dropRightWhile([1, 2, 3, 4], (n) => n < 3); // [1, 2]
1
2
3
4
5
6

# dropWhile-按照给的的函数条件筛选数组

按照给的的函数条件筛选数组,不满足函数条件的将从数组中移除。

const dropWhile = (arr, func) => {
  while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
  return arr;
};

dropWhile([1, 2, 3, 4], (n) => n >= 3); // [3,4]
1
2
3
4
5
6

# findLast-按照给定的函数条件筛选数组,将最后一个满足条件的元素进行删除

const findLast = (arr, fn) => arr.filter(fn).pop();

findLast([1, 2, 3, 4], (n) => n % 2 === 1); // 3
1
2
3

# initial-数组中除最后一个元素的所有元素

const initial = (arr) => arr.slice(0, -1);

initial([1, 2, 3]); // [1,2]
1
2
3

# 此段代码将非数值的值转换成数组对象

const castArray = (val) => (Array.isArray(val) ? val : [val]);

castArray("foo"); // ['foo']
castArray([1]); // [1]
1
2
3
4

# isArrayLike-检测对象是否为类数组对象,是否可迭代

const isArrayLike = (obj) =>
  obj != null && typeof obj[Symbol.iterator] === "function";

isArrayLike(document.querySelectorAll(".className")); // true
isArrayLike("abc"); // true
isArrayLike(null); // false
1
2
3
4
5
6

# flatten-照指定数组的深度,将嵌套数组进行展平

const flatten = (arr, depth = 1) =>
  arr.reduce(
    (a, v) =>
      a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v),
    []
  );

flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]
1
2
3
4
5
6
7
8
9

# forEachRight-照给定的函数条件,从数组的右边往左依次进行执行

const forEachRight = (arr, callback) =>
  arr
    .slice(0)
    .reverse()
    .forEach(callback);

forEachRight([1, 2, 3, 4], (val) => console.log(val)); // '4', '3', '2', '1'
1
2
3
4
5
6
7

# 获取数组中的最后 n 项

如果要获取数组的末尾元素,可以使用 slice 方法。

let array = [0, 1, 2, 3, 4, 5, 6, 7];
console.log(array.slice(-1));
// >>>[7]

console.log(array.slice(-2));
// >>>[6, 7]

console.log(array.slice(-3));
// >>>[5, 6, 7]
1
2
3
4
5
6
7
8
9

# minN:输出数组中前 n 位最小的数

const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);

minN([1, 2, 3]); // [1]
minN([1, 2, 3], 2); // [1,2]
1
2
3
4

# 截取数组

如果你想从一个数组尾部移除某些元素,可以使用一种比 splice() 更快的方法
例如,如果你知道初始数组的大小,可以像下面这样重新定义它的 length 属性:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array.length = 4;
console.log(array); // Result: [0, 1, 2, 3]
1
2
3

这显然是一种更简洁的解决方案。不过,我发现 slice() 的运行速度更快,所以,如果你更看重速度,可以像下面这样:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array = array.slice(0, 4);
console.log(array); // Result: [0, 1, 2, 3]
1
2
3

# 获取数组最后的元素

数组的 slice() 方法可以接受负整数,并从数组的尾部开始获取元素。

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(array.slice(-1)); // Result: [9]
console.log(array.slice(-2)); // Result: [8, 9]
console.log(array.slice(-3)); // Result: [7, 8, 9]
1
2
3
4

# 统计数组中相同项的个数

var cars = ["BMW", "Benz", "Benz", "Tesla", "BMW", "Toyota"];
var carsObj = cars.reduce(function(obj, name) {
  obj[name] = obj[name] ? ++obj[name] : 1;
  return obj;
}, {});
carsObj; // => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }
1
2
3
4
5
6

# countOccurrences-统计数组中某个值出现的次数

统计数组中某个值出现的次数

const countOccurrences = (arr, val) =>
  arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
1
2
3

# indexOfAll-回数组中某个值对应的所有索引值,如果不包含该值,则返回一个空数组

const indexOfAll = (arr, val) =>
  arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);

indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
indexOfAll([1, 2, 3], 4); // []
1
2
3
4
5

# 数组包含另一个数组

function includes(arr1, arr2) {
  return arr2.every(val => arr1.includes(val));
}
1
2
3

# all-组所有元素满足函数条件

如果数组所有元素满足函数条件,则返回 true。
调用时,如果省略第二个参数,则默认传递布尔值。

const all = (arr, fn = Boolean) => arr.every(fn);

all([4, 2, 3], (x) => x > 1); // true
all([1, 2, 3]); // true
1
2
3
4

# allEqual-数组中的元素是否都相等

const allEqual = (arr) => arr.every((val) => val === arr[0]);

allEqual([1, 2, 3, 4, 5, 6]); // false
allEqual([1, 1, 1, 1]); // true
1
2
3
4

# arrayToCSV

此段代码将没有逗号或双引号的元素转换成带有逗号分隔符的字符串即 CSV 格式识别的形式。

const arrayToCSV = (arr, delimiter = ",") =>
  arr.map((v) => v.map((x) => `"${x}"`).join(delimiter)).join("\n");

arrayToCSV([
  ["a", "b"],
  ["c", "d"],
]); // '"a","b"\n"c","d"'
arrayToCSV(
  [
    ["a", "b"],
    ["c", "d"],
  ],
  ";"
); // '"a";"b"\n"c";"d"'
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# arrayToHtmlList

此段代码将数组元素转换成<li>标记,并将此元素添加至给定的 ID 元素标记内。

const arrayToHtmlList = (arr, listID) =>
  ((el) => (
    (el = document.querySelector("#" + listID)),
    (el.innerHTML += arr.map((item) => `<li>${item}</li>`).join(""))
  ))();

arrayToHtmlList(["item 1", "item 2"], "myListID");
1
2
3
4
5
6
7

# bifurcate-条件为真的放入第一个数组,其它的放入第二个数组

此函数包含两个参数,类型都为数组,依据第二个参数的真假条件,将一个参数的数组进行分组,条件为真的放入第一个数组,其它的放入第二个数组。这里运用了Array.prototype.reduce()Array.prototype.push() 相结合的形式。

const bifurcate = (arr, filter) =>
  arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [
    [],
    [],
  ]);
bifurcate(["beep", "boop", "foo", "bar"], [true, true, false, true]);
// [ ['beep', 'boop', 'bar'], ['foo'] ]
1
2
3
4
5
6
7

# bifurcateBy-逻辑为真,放入第一个数组中,其它不满足的放入第二个数组

此段代码将数组按照指定的函数逻辑进行分组,满足函数条件的逻辑为真,放入第一个数组中,其它不满足的放入第二个数组 。这里运用了 Array.prototype.reduce() 和 Array.prototype.push() 相结合的形式,基于函数过滤逻辑,通过 Array.prototype.push() 函数将其添加到数组中。

const bifurcateBy = (arr, fn) =>
  arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [
    [],
    [],
  ]);

bifurcateBy(["beep", "boop", "foo", "bar"], (x) => x[0] === "b");
// [ ['beep', 'boop', 'bar'], ['foo'] ]
1
2
3
4
5
6
7
8

# difference-数组的差异

此段代码查找两个给定数组的差异,查找出前者数组在后者数组中不存在元素。

const difference = (a, b) => {
  const s = new Set(b);
  return a.filter((x) => !s.has(x));
};

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

# differenceBy-给定的函数来处理需要对比差异的数组

通过给定的函数来处理需要对比差异的数组,查找出前者数组在后者数组中不存在元素。

const differenceBy = (a, b, fn) => {
  const s = new Set(b.map(fn));
  return a.filter((x) => !s.has(fn(x)));
};

differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], (v) => v.x); // [ { x: 2 } ]
1
2
3
4
5
6
7

# differenceWith-按照给定函数逻辑筛选需要对比差异的数组

此段代码按照给定函数逻辑筛选需要对比差异的数组,查找出前者数组在后者数组中不存在元素。

const differenceWith = (arr, val, comp) =>
  arr.filter((a) => val.findIndex((b) => comp(a, b)) === -1);

differenceWith(
  [1, 1.2, 1.5, 3, 0],
  [1.9, 3, 0],
  (a, b) => Math.round(a) === Math.round(b)
);
// [1, 1.2]
1
2
3
4
5
6
7
8
9

# intersection-个数组元素之间的交集

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter((x) => s.has(x));
};

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

# intersectionBy-找出交集

按照给定的函数处理需要对比的数组元素,然后根据处理后的数组,找出交集,最后从第一个数组中将对应的元素输出。

const intersectionBy = (a, b, fn) => {
  const s = new Set(b.map(fn));
  return a.filter((x) => s.has(fn(x)));
};

intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1]
1
2
3
4
5
6

# intersectionWith-找出交集

按照给定的函数对比两个数组的差异,然后找出交集,最后从第一个数组中将对应的元素输出。

const intersectionWith = (a, b, comp) =>
  a.filter((x) => b.findIndex((y) => comp(x, y)) !== -1);

intersectionWith(
  [1, 1.2, 1.5, 3, 0],
  [1.9, 3, 0, 3.9],
  (a, b) => Math.round(a) === Math.round(b)
); // [1.5, 3, 0]
1
2
3
4
5
6
7
8

# 填充数组

新手

for(let i=0; i < arraySize; i++){
  filledArray[i] {'hello' : 'goodbye'};
}
1
2
3

老手

let filledArray = new Array(arraysize)
  .fill(null)
  .map(() => ({ hello: "goodbye" }));
1
2
3

# 初始化大小为 n 的数组并填充默认值

const size = 5;
const defaultValue = 0;
const arr = Array(size).fill(defaultValue);
console.log(arr); // [0, 0, 0, 0, 0]
1
2
3
4

# 无 loop 生成指定长度的数组

const List = (len) => [...new Array(len).keys()];
const list = List(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1
2

# 接收函数返回的多个结果

async function getFullPost() {
  return await Promise.all([fetch("/post"), fetch("/comments")]);
}
const [post, comments] = getFullPost();
1
2
3
4

# 数字操作

# 判断是否为质数

back

const mathIsPrime = (n) => {
  if (n === 2 || n === 3) {
    return true;
  }
  if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 || n % 3 == 0) {
    return false;
  }
  for (let x = 6; x <= Math.sqrt(n) + 1; x += 6) {
    if (n % (x - 1) == 0 || n % (x + 1) == 0) {
      return false;
    }
  }
  return true;
};
mathIsPrime(0); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 判断奇偶数

const num = 3;
!!(num & 1); // true
!!(num % 2); // true
1
2
3

# approximatelyEqual-两个数字是否近似相等

此代码示例检查两个数字是否近似相等,差异值可以通过传参的形式进行设置

const approximatelyEqual = (v1, v2, epsilon = 0.001) =>
  Math.abs(v1 - v2) < epsilon;

approximatelyEqual(Math.PI / 2.0, 1.5708); // true
1
2
3
4

# 判断小数是否相等

function epsEqu(x, y) {
  return Math.abs(x - y) < Math.pow(2, -52);
}
// 举例
0.1 + 0.2 === 0.3; // false
epsEqu(0.1 + 0.2, 0.3); // true
1
2
3
4
5
6

# average-返回两个或多个数的平均数

const average = (...nums) =>
  nums.reduce((acc, val) => acc + val, 0) / nums.length;
average(...[1, 2, 3]); // 2
average(1, 2, 3); // 2
1
2
3
4

# averageBy

一个 map()函数和 reduce()函数结合的例子,此函数先通过 map() 函数将对象转换成数组,然后在调用 reduce()函数进行累加,然后根据数组长度返回平均值。

const averageBy = (arr, fn) =>
  arr
    .map(typeof fn === "function" ? fn : (val) => val[fn])
    .reduce((acc, val) => acc + val, 0) / arr.length;

averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], (o) => o.n); // 5
averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], "n"); // 5
1
2
3
4
5
6
7

# maxN-输出数组中前 n 位最大的数

const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n);

maxN([1, 2, 3]); // [3]
maxN([1, 2, 3], 2); // [3,2]
1
2
3
4

# 快速取整

我们可以使用 Math.floor()、Math.ceil() 或 Math.round() 将浮点数转换成整数,但有另一种更快的方式,即使用位或运算符 |

console.log(23.9 | 0); // Result: 23
console.log(-23.9 | 0); // Result: -23
1
2

| 的实际行为取决于操作数是正数还是负数,所以在使用这个运算符时要确保你知道操作数是正是负。

如果 n 是正数,那么 n|0 向下取整,否则就是向上取整。它会移除小数部分,也可以使用~~ 达到同样的效果。

# 移除整数尾部数字

| 运算符也可以用来移除整数的尾部数字,这样就不需要像下面这样:

let str = "1553";
Number(str.substring(0, str.length - 1));
1
2

相反,我们可以这样:

console.log((1553 / 10) | 0); // Result: 155
console.log((1553 / 100) | 0); // Result: 15
console.log((1553 / 1000) | 0); // Result: 1
1
2
3

# 优雅地取整

可以使用双位操作符来替代正数的 Math.floor( )替代负数的Math.ceil( )。双否定位操作符的优势在于它执行相同的操作运行速度更快。

Math.floor(4.9) === 4; //true
// 简写为:
~~4.9 === 4; //true

~~4.5; // 4
Math.floor(4.5); // 4
Math.ceil(4.5); // 5

~~-4.5; // -4
Math.floor(-4.5); // -5
Math.ceil(-4.5); // -4

var a = ~~2.33; // ---- > 2;
var b = 2.33 | 0; // ---- > 2);
var c = 2.33 >> 0; // ---- > 2;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 去掉小数部分

//下面几种方式都行
parseInt(num);
~~num;
num >> 0;
num | 0;
1
2
3
4
5

# 精确到指定位数的小数

将数字四舍五入到指定的小数位数。使用 Math.round() 和模板字面量将数字四舍五入为指定的小数位数。 省略第二个参数 decimals ,数字将被四舍五入到一个整数。

const round = (n, decimals = 0) =>
  Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
round(1.345, 2); // 1.35
round(1.345, 1); // 1.3
1
2
3
4

# floor向下取整

Math.floor(0.20); // 0
Math.floor(0.90); // 0
Math.floor(-0.90); // -1
Math.floor(-0.20); // -1
1
2
3
4

# round四舍五入

Math.round(0.2) // 0
Math.round(0.9) // 1
Math.round(-0.9) // -1
Math.round(-0.2) // 0
1
2
3
4

# ceil向上取整

Math.ceil(0.2) // 1
Math.ceil(0.9) // 1
Math.ceil(-0.9) // 0
Math.ceil(-0.2) // 0
1
2
3
4

# 不同进制表示法

ES6 中新增了不同进制的书写格式,在后台传参的时候要注意这一点。

29; // 10进制
035; // 8进制29      原来的方式
0o35; // 8进制29      ES6的方式
0x1d; // 16进制29
0b11101; // 2进制29
1
2
3
4
5

# 快速幂运算——**

从 ES7 开始,可以使用 ** 进行幂运算,比使用 Math.power(2,3) 要快得多

console.log(2 ** 3); // Result: 8
1

但要注意不要把这个运算符于 ^ 混淆在一起了^ 通常用来表示指数运算,但在 JavaScript 中,^ 表示位异或运算
在 ES7 之前,可以使用位左移运算符 << 来表示以 2 为底的幂运算:

// 以下表达式是等效的:

Math.pow(2, n);
2 << (n - 1);
2 ** n;
1
2
3

例如,2 << 3 = 16 等同于 2 ** 4 = 16。

# 递归求阶乘

function factorial(n) {
  return n > 1 ? n * factorial(n - 1) : n;
}
1
2
3

# 使用^检查数字是否相等

if(a!=123) // before // 一般开发者

if(a^123) // after // B格比较高的
1
2
3

# digitize-数字拆分成单个数字组成的数组

将输入的数字拆分成单个数字组成的数组。

const digitize = (n) => [...`${n}`].map((i) => parseInt(i));

digitize(431); // [4, 3, 1]
1
2
3

# 字符串操作

# 格式化 JSON

你之前可能使用过 JSON.stringify,但你是否知道它还可以用来给 JSON 添加缩进?
stringify() 方法可以接受两个额外的参数,一个是函数(形参为 replacer),用于过滤要显示的 JSON,另一个是空格个数(形参为 space)

space 可以是一个整数,表示空格的个数,也可以是一个字符串(比如’\t’表示制表符),这样得到的 JSON 更容易阅读。

console.log(JSON.stringify({ alpha: "A", beta: "B" }, null, "\t"));
// Result:
// '{
// "alpha": A,
// "beta": B
// }'
1
2
3
4
5
6

# 获取字符串中的字符数

const characterCount = (str, char) => str.split(char).length - 1
1

# byteSize-字符串的字节长度

此代码返回字符串的字节长度。这里用到了 Blob 对象,Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口。其他操作二进制数据的 API(比如 File 对象),都是建立在 Blob 对象基础上的,继承了它的属性和方法。生成 Blob 对象有两种方法:一种是使用 Blob 构造函数,另一种是对现有的 Blob 对象使用 slice 方法切出一部分。

const byteSize = (str) => new Blob([str]).size;

byteSize("😀"); // 4
byteSize("Hello World"); // 11
1
2
3
4

# 字符转换(大小写),type: 1:首字母大写 2:首字母小写 3:大小写转换 4:全部大写 5:全部小写

export const changeCase = (str, type) => {
  type = type || 4;
  switch (type) {
    case 1:
      return str.replace(/\b\w+\b/g, function(word) {
        return (
          word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase()
        );
      });
    case 2:
      return str.replace(/\b\w+\b/g, function(word) {
        return (
          word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase()
        );
      });
    case 3:
      return str
        .split("")
        .map(function(word) {
          if (/[a-z]/.test(word)) {
            return word.toUpperCase();
          } else {
            return word.toLowerCase();
          }
        })
        .join("");
    case 4:
      return str.toUpperCase();
    case 5:
      return str.toLowerCase();
    default:
      return str;
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# decapitalize-将字符串的首字母转换成小写字母

const decapitalize = ([first, ...rest]) => first.toLowerCase() + rest.join("");

decapitalize("FooBar"); // 'fooBar'
1
2
3

# capitalize-字符串的首字母转成大写

将字符串的首字母转成大写,这里主要运用到了 ES6 的展开语法在数组中的运用。

const capitalize = ([first, ...rest]) => first.toUpperCase() + rest.join("");

capitalize("fooBar"); // 'FooBar'
capitalize("fooBar", true); // 'FooBar'
1
2
3
4

# 每个单词首字母转换成大写字母

const capitalizeEveryWord = (str) =>
  str.replace(/\b[a-z]/g, (char) => char.toUpperCase());

capitalizeEveryWord("hello world!"); // 'Hello World!'
1
2
3
4

# pad:按照指定的长度生成字符串,如果字符串不够,可以按照设定的字符串内容在左右两边进行填充,默认空格为填充字符

const pad = (str, length, char = " ") =>
  str.padStart((str.length + length) / 2, char).padEnd(length, char);

pad("cat", 8); // '  cat   '
pad(String(42), 6, "0"); // '004200'
pad("foobar", 3); // 'foobar'
1
2
3
4
5
6

# 字符串长度截取

function cutstr(str, len) {
    var temp,
        icount = 0,
        patrn = /[^\x00-\xff]/,
        strre = "";
    for (var i = 0; i < str.length; i++) {
        if (icount < len - 1) {
            temp = str.substr(i, 1);
                if (patrn.exec(temp) == null) {
                   icount = icount + 1
            } else {
                icount = icount + 2
            }
            strre += temp
            } else {
            break;
        }
    }
    return strre + "..."
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# isLowerCase-判断当前字符串是否都为小写

const isLowerCase = (str) => str === str.toLowerCase();

isLowerCase("abc"); // true
isLowerCase("a3@$"); // true
isLowerCase("Ab4"); // false
1
2
3
4
5

# isAnagram-检测两个单词是否相似

const isAnagram = (str1, str2) => {
  const normalize = (str) =>
    str
      .toLowerCase()
      .replace(/[^a-z0-9]/gi, "")
      .split("")
      .sort()
      .join("");
  return normalize(str1) === normalize(str2);
};

isAnagram("iceman", "cinema"); // true
1
2
3
4
5
6
7
8
9
10
11
12

# 版本对比

function compareVersion(v1, v2) {
  v1 = v1.split(".");
  v2 = v2.split(".");

  var len = Math.max(v1.length, v2.length);

  while (v1.length < len) {
    v1.push("0");
  }

  while (v2.length < len) {
    v2.push("0");
  }

  for (var i = 0; i < len; i++) {
    var num1 = parseInt(v1[i]);
    var num2 = parseInt(v2[i]);

    if (num1 > num2) {
      return 1;
    } else if (num1 < num2) {
      return -1;
    }
  }
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 判断是否以某个字符串结束

String.prototype.endWith = function(s) {
  var d = this.length - s.length;
  return d >= 0 && this.lastIndexOf(s) == d;
};
1
2
3
4

# 判断是否以某个字符串开头

String.prototype.startWith = function(s) {
  return this.indexOf(s) == 0;
};
1
2
3

# 字符串比较时间先后

var a = "2014-08-08";
var b = "2014-09-09";

console.log(a > b, a < b); // false true
console.log("21:00" < "09:10"); // false
console.log("21:00" < "9:10"); // true   时间形式注意补0
1
2
3
4
5
6

# 实现 base64 解码

function base64_decode(data) {
  var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var o1,
    o2,
    o3,
    h1,
    h2,
    h3,
    h4,
    bits,
    i = 0,
    ac = 0,
    dec = "",
    tmp_arr = [];
  if (!data) {
    return data;
  }
  data += "";
  do {
    h1 = b64.indexOf(data.charAt(i++));
    h2 = b64.indexOf(data.charAt(i++));
    h3 = b64.indexOf(data.charAt(i++));
    h4 = b64.indexOf(data.charAt(i++));
    bits = (h1 << 18) | (h2 << 12) | (h3 << 6) | h4;
    o1 = (bits >> 16) & 0xff;
    o2 = (bits >> 8) & 0xff;
    o3 = bits & 0xff;
    if (h3 == 64) {
      tmp_arr[ac++] = String.fromCharCode(o1);
    } else if (h4 == 64) {
      tmp_arr[ac++] = String.fromCharCode(o1, o2);
    } else {
      tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
    }
  } while (i < data.length);
  dec = tmp_arr.join("");
  dec = utf8_decode(dec);
  return dec;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# 实现 utf8 解码

function utf8_decode(str_data) {
  var tmp_arr = [],
    i = 0,
    ac = 0,
    c1 = 0,
    c2 = 0,
    c3 = 0;
  str_data += "";
  while (i < str_data.length) {
    c1 = str_data.charCodeAt(i);
    if (c1 < 128) {
      tmp_arr[ac++] = String.fromCharCode(c1);
      i++;
    } else if (c1 > 191 && c1 < 224) {
      c2 = str_data.charCodeAt(i + 1);
      tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
      i += 2;
    } else {
      c2 = str_data.charCodeAt(i + 1);
      c3 = str_data.charCodeAt(i + 2);
      tmp_arr[ac++] = String.fromCharCode(
        ((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)
      );
      i += 3;
    }
  }
  return tmp_arr.join("");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 替换全部

String.prototype.replaceAll = function(s1, s2) {
  return this.replace(new RegExp(s1, "gm"), s2);
};
1
2
3

# 去除空格,type: 1-所有空格 2-前后空格 3-前空格 4-后空格

export const trim = (str, type) => {
  type = type || 1;
  switch (type) {
    case 1:
      return str.replace(/\s+/g, "");
    case 2:
      return str.replace(/(^\s*)|(\s*$)/g, "");
    case 3:
      return str.replace(/(^\s*)/g, "");
    case 4:
      return str.replace(/(\s*$)/g, "");
    default:
      return str;
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 清除空格

String.prototype.trim = function() {
  var reExtraSpace = /^\s*(.*?)\s+$/;
  return this.replace(reExtraSpace, "$1");
};

// 清除左空格
function ltrim(s) {
  return s.replace(/^(\s*|&emsp;*)/, "");
}

// 清除右空格
function rtrim(s) {
  return s.replace(/(\s*|&emsp;*)$/, "");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 按字母排序,对每行进行数组排序

function setSort() {
  var text = K1.value
    .split(/[\r\n]/)
    .sort()
    .join("\r\n"); //顺序
  var test = K1.value
    .split(/[\r\n]/)
    .sort()
    .reverse()
    .join("\r\n"); //反序
  K1.value = K1.value != text ? text : test;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 对象操作

# 对象动态属性名

新手

let dynamic = "value";
let user = {
  id: 1,
};
user[dynamic] = "other value";
1
2
3
4
5

老手

const dynamic = "color";
var item = {
  brand: "Ford",
  [dynamic]: "Blue",
};
console.log(item);
// { brand: "Ford", color: "Blue" }
1
2
3
4
5
6
7

# 对象遍历

const age = {
  Rahul: 20,
  max: 16,
};

// 方案1:先得 key 在便利 key
const keys = Object.keys(age);
keys.forEach((key) => age[key]++);

console.log(age); // { Rahul: 21, max: 16 }

// 方案2 - `for...in` 循环
for (let key in age) {
  age[key]++;
}

console.log(age); // { Rahul: 22, max: 18 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 实现深拷贝

var b = JSON.parse(JSON.string(a));
1

# default-去重对象的属性

去重对象的属性,如果对象中含有重复的属性,以前面的为准。

const defaults = (obj, ...defs) =>
  Object.assign({}, obj, ...defs.reverse(), obj);
defaults({ a: 1 }, { b: 2 }, { b: 6 }, { a: 3 }); // { a: 1, b: 2 }
1
2
3

# 使用解构删除不必要属性

我们希望删除_internal 和 tooBig 参数。我们可以把它们赋值给 internal 和 tooBig 变量,然后在 cleanObject 中存储剩下的属性以备后用。

let { _internal, tooBig, ...cleanObject } = {
  el1: "1",
  _internal: "secret",
  tooBig: {},
  el2: "2",
  el3: "3",
};

console.log(cleanObject); // {el1: '1', el2: '2', el3: '3'}
1
2
3
4
5
6
7
8
9

# 利用关键字delete删除对象属性

const o = {
    p: 10,
    m: 20
}
delete o.p
console.log(o) // { m: 20 }
// 删除对象的属性后,在访问返回 undefined
console.log(o.p) // undefined
1
2
3
4
5
6
7
8

# 如何从对象检索给定选择器指示的一组属性

const get = (from, ...selectors) =>
  [...selectors].map((s) =>
    s
      .replace(/\[([^\[\]]*)\]/g, ".$1.")
      .split(".")
      .filter((t) => t !== "")
      .reduce((prev, cur) => prev && prev[cur], from)
  );
const obj = {
  selector: { to: { val: "val to select" } },
  target: [1, 2, { a: "test" }],
};

// Example
get(obj, "selector.to.val", "target[0]", "target[2].a");
// ['val to select', 1, 'test']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# findKey-按照给定的函数条件,查找第一个满足条件对象的键值

const findKey = (obj, fn) =>
  Object.keys(obj).find((key) => fn(obj[key], key, obj));

findKey(
  {
    barney: { age: 36, active: true },
    fred: { age: 40, active: false },
    pebbles: { age: 1, active: true },
  },
  (o) => o["active"]
); // 'barney'
1
2
3
4
5
6
7
8
9
10
11

# forOwn-照给定的函数条件,进行迭代对象

const forOwn = (obj, fn) =>
  Object.keys(obj).forEach((key) => fn(obj[key], key, obj));
forOwn({ foo: "bar", a: 1 }, (v) => console.log(v)); // 'bar', 1
1
2
3

# matches-确定第一个对象是否包含与第二个对象相同的属性值

此函数功能用于比较两个对象,以确定第一个对象是否包含与第二个对象相同的属性值。

onst matches = (obj, source) =>
Object.keys(source).every(key => obj.hasOwnProperty(key) && obj[key] === source[key]);

matches({ age: 25, hair: 'long', beard: true }, { hair: 'long', beard: true }); // true
matches({ hair: 'long', beard: true }, { age: 25, hair: 'long', beard: true }); // false
1
2
3
4
5

# 判断两个对象是否键值相同

export const isObjectEqual = (a, b) => {
  var aProps = Object.getOwnPropertyNames(a);
  var bProps = Object.getOwnPropertyNames(b);
  if (aProps.length !== bProps.length) {
    return false;
  }
  for (var i = 0; i < aProps.length; i++) {
    var propName = aProps[i];
    if (a[propName] !== b[propName]) {
      return false;
    }
  }
  return true;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 检查对象是否为空

// 我们检查对象的键的长度是否等于 0,以及传递的参数是否为实际对象。
const isEmpty = obj => Reflect.ownKeys(obj).length === 0 && obj.constructor === Object
1
2

# 日期时间操作

# 如何获得给定毫秒数的可读格式

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(", ");
};

// 事例
formatDuration(1001); // '1 second, 1 millisecond'
formatDuration(34325055574);
// '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 如何获得两个日期之间的差异(以天为单位)

const getDaysDiffBetweenDates = (dateInitial, dateFinal) =>
  (dateFinal - dateInitial) / (1000 * 3600 * 24);
// 事例
getDaysDiffBetweenDates(new Date("2017-12-13"), new Date("2017-12-22")); // 9

const daysBetween = (date1, date2) => Math.ceil(Math.abs(date1 - date2) / (1000 * 60 * 60 * 24))
1
2
3
4
5
6

# getColonTimeFromDate-从 Date 对象里获取当前时间

const getColonTimeFromDate = (date) => date.toTimeString().slice(0, 8);
getColonTimeFromDate(new Date()); // "08:38:00"
1
2

# 当前是今年的第几天

返回当前是今年的第几天

const dayOfYear = (date) =>
  Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24);

dayOfYear(new Date()); // 272
1
2
3
4

# 随机数时间戳

function uniqueId() {
  var a = Math.random,
    b = parseInt;
  return (
    Number(new Date()).toString() + b(10 * a()) + b(10 * a()) + b(10 * a())
  );
}
1
2
3
4
5
6
7

# 每秒更新当前时间

setInterval(
  () => (document.body.innerHTML = new Date().toLocaleString().slice(10, 18))
);
1
2
3

# isAfterDate-日期是否晚于后者的日期

接收两个日期类型的参数,判断前者的日期是否晚于后者的日期。

const isAfterDate = (dateA, dateB) => dateA > dateB;
isAfterDate(new Date(2010, 10, 21), new Date(2010, 10, 20)); // true
1
2

# isBeforeDate-日期是否早于后者的日期

const isBeforeDate = (dateA, dateB) => dateA < dateB;
isBeforeDate(new Date(2010, 10, 20), new Date(2010, 10, 21)); // true
1
2

# isSameDate-判断给定的两个日期是否是同一天

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

isSameDate(new Date(2010, 10, 20), new Date(2010, 10, 20)); // true
1
2
3
4

# maxDate-查找日期数组中最大的日期进行输出

const maxDate = (...dates) => new Date(Math.max.apply(null, ...dates));

const array = [
  new Date(2017, 4, 13),
  new Date(2018, 2, 12),
  new Date(2016, 0, 10),
  new Date(2016, 0, 9),
];
maxDate(array); // 2018-03-11T22:00:00.000Z
1
2
3
4
5
6
7
8
9

# minDate-查找日期数组中最早的日期进行输出

const minDate = (...dates) => new Date(Math.min.apply(null, ...dates));

const array = [
  new Date(2017, 4, 13),
  new Date(2018, 2, 12),
  new Date(2016, 0, 10),
  new Date(2016, 0, 9),
];
minDate(array); // 2016-01-08T22:00:00.000Z
1
2
3
4
5
6
7
8
9

# 时间日期格式转换

Date.prototype.format = function(formatStr) {
  var str = formatStr;
  var Week = ["日", "一", "二", "三", "四", "五", "六"];
  str = str.replace(/yyyy|YYYY/, this.getFullYear());
  str = str.replace(
    /yy|YY/,
    this.getYear() % 100 > 9
      ? (this.getYear() % 100).toString()
      : "0" + (this.getYear() % 100)
  );
  str = str.replace(
    /MM/,
    this.getMonth() + 1 > 9
      ? (this.getMonth() + 1).toString()
      : "0" + (this.getMonth() + 1)
  );
  str = str.replace(/M/g, this.getMonth() + 1);
  str = str.replace(/w|W/g, Week[this.getDay()]);
  str = str.replace(
    /dd|DD/,
    this.getDate() > 9 ? this.getDate().toString() : "0" + this.getDate()
  );
  str = str.replace(/d|D/g, this.getDate());
  str = str.replace(
    /hh|HH/,
    this.getHours() > 9 ? this.getHours().toString() : "0" + this.getHours()
  );
  str = str.replace(/h|H/g, this.getHours());
  str = str.replace(
    /mm/,
    this.getMinutes() > 9
      ? this.getMinutes().toString()
      : "0" + this.getMinutes()
  );
  str = str.replace(/m/g, this.getMinutes());
  str = str.replace(
    /ss|SS/,
    this.getSeconds() > 9
      ? this.getSeconds().toString()
      : "0" + this.getSeconds()
  );
  str = str.replace(/s|S/g, this.getSeconds());
  return str;
};

// 或
Date.prototype.format = function(format) {
  var o = {
    "M+": this.getMonth() + 1, //month
    "d+": this.getDate(), //day
    "h+": this.getHours(), //hour
    "m+": this.getMinutes(), //minute
    "s+": this.getSeconds(), //second
    "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
    S: this.getMilliseconds(), //millisecond
  };
  if (/(y+)/.test(format))
    format = format.replace(
      RegExp.$1,
      (this.getFullYear() + "").substr(4 - RegExp.$1.length)
    );
  for (var k in o) {
    if (new RegExp("(" + k + ")").test(format))
      format = format.replace(
        RegExp.$1,
        RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
      );
  }
  return format;
};
alert(new Date().format("yyyy-MM-dd hh:mm:ss"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 时间个性化输出功能

/*
1、< 60s, 显示为“刚刚”
2、>= 1min && < 60 min, 显示与当前时间差“XX分钟前”
3、>= 60min && < 1day, 显示与当前时间差“今天 XX:XX”
4、>= 1day && < 1year, 显示日期“XX月XX日 XX:XX”
5、>= 1year, 显示具体日期“XXXX年XX月XX日 XX:XX”
*/
function timeFormat(time) {
  var date = new Date(time),
    curDate = new Date(),
    year = date.getFullYear(),
    month = date.getMonth() + 10,
    day = date.getDate(),
    hour = date.getHours(),
    minute = date.getMinutes(),
    curYear = curDate.getFullYear(),
    curHour = curDate.getHours(),
    timeStr;

  if (year < curYear) {
    timeStr = year + "年" + month + "月" + day + "日 " + hour + ":" + minute;
  } else {
    var pastTime = curDate - date,
      pastH = pastTime / 3600000;

    if (pastH > curHour) {
      timeStr = month + "月" + day + "日 " + hour + ":" + minute;
    } else if (pastH >= 1) {
      timeStr = "今天 " + hour + ":" + minute + "分";
    } else {
      var pastM = curDate.getMinutes() - minute;
      if (pastM > 1) {
        timeStr = pastM + "分钟前";
      } else {
        timeStr = "刚刚";
      }
    }
  }
  return timeStr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 时间格式化

const dateFormatter = (formatter, date) => {
  date = date ? new Date(date) : new Date();
  const Y = date.getFullYear() + "",
    M = date.getMonth() + 1,
    D = date.getDate(),
    H = date.getHours(),
    m = date.getMinutes(),
    s = date.getSeconds();
  return formatter
    .replace(/YYYY|yyyy/g, Y)
    .replace(/YY|yy/g, Y.substr(2, 2))
    .replace(/MM/g, (M < 10 ? "0" : "") + M)
    .replace(/DD/g, (D < 10 ? "0" : "") + D)
    .replace(/HH|hh/g, (H < 10 ? "0" : "") + H)
    .replace(/mm/g, (m < 10 ? "0" : "") + m)
    .replace(/ss/g, (s < 10 ? "0" : "") + s);
};

dateFormatter("YYYY-MM-DD HH:mm", "1995/02/15 13:55"); // 1995-02-15 13:55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  • 时间格式化
export function formatDate(oldDate, fmt) {
    let date = new Date();
    if (typeof oldDate === 'string' || typeof oldDate === 'number'){
        date = new Date(+oldDate);
    } else {
        date = oldDate;
    }
    ……
}
1
2
3
4
5
6
7
8
9

# 类型转换

# 转换成布尔值

除了标准的布尔值 true 和 false,在 JavaScript 中,所有的值要么是“真值”要么是“假值”。
在 JavaScript 中,除了 0、“”、null、undefined、NaN 和 false 是假值之外,其他的都是真值。

我们可以使用! 运算法来切换 true 和 false。

const isTrue = !0;
const isFalse = !1;
const alsoFalse = !!0;
console.log(true); // Result: true
console.log(typeof true); // Result: "boolean"
1
2
3
4
5

# 转换成字符串

要快速将数字转换成字符串,我们可以使用 + 运算符,然后在后面跟上一个空字符串。

const val = 1 + "";
console.log(val); // Result: "1"
console.log(typeof val); // Result: "string"
1
2
3

# 转换成数字

要把字符串转成数字,也可以使用 + 运算符。

let int = "15";
int = +int;
console.log(int); // Result: 15
console.log(typeof int);
Result: "number";
1
2
3
4
5

也可以使用这种方式将布尔值转成数字,例如:

console.log(+true); // Return: 1
console.log(+false); // Return: 0
1
2

在某些情况下,+ 运算符会被解析成连接操作,而不是加法操作。对于这种情况,可以使用两个波浪号:~~
一个波浪号表示按位取反操作,例如,~15 等于 -16。

const int = ~~"15";
console.log(int); // Result: 15
console.log(typeof int);
Result: "number";
1
2
3
4

使用两个波浪号可以再次取反,因为 -(-n-1)=n+1-1=n,所以~-16 等于 15。

# 数字转字符串/字符串转数字

新手

let num = 15;
let s = num.toString(); // number to string
let n = Number(s); // string to number
1
2
3

老手

let num = 15;
let s = num + ""; // 数字转字符串
let n = +s; // 字符串转数字
1
2
3

# string 强制转换为数字

可以用*1来转化为数字(实际上是调用.valueOf方法) 然后使用Number.isNaN来判断是否为NaN,或者使用 a !== a 来判断是否为NaN,因为 NaN !== NaN

"32" * 1; // 32
"ds" * 1; // NaN
null * 1; // 0
undefined * 1; // NaN
1 * { valueOf: () => "3" }; // 3
1
2
3
4
5

也可以使用+来转化字符串为数字

+"123" + // 123
"ds" + // NaN
"" + // 0
null + // 0
undefined + // NaN
  { valueOf: () => "3" }; // 3
1
2
3
4
5
6

# object 强制转化为 string

可以使用 字符串+Object 的方式来转化对象为字符串(实际上是调用 .toString() 方法)

"the Math object:" + Math; // "the Math object:[object Math]"
"the JSON object:" + JSON; // "the JSON object:[object JSON]"
1
2

当然也可以覆盖对象的 toString 和 valueOf 方法来自定义对象的类型转换:

2 * { valueOf: () => "3" }; // 6
"J" + { toString: () => "S" }; // "JS"
1
2

注意:当+用在连接字符串时当一个对象既有toString方法又有valueOf方法时候,JS 通过盲目使用 valueOf 方法来解决这种含糊。 对象通过 valueOf 方法强制转换为数字,通过 toString 方法强制转换为字符串

"" + { toString: () => "S", valueOf: () => "J" }; // J
1

# 数组到对象/对象到数组

新手

let arr = ["value1", "value2", "value3"];
let arrObject = {};
for (let i = 0; i < arr.length; ++i) {
  if (arr[i] !== undefined) {
    arrObject[i] = arr[i];
  }
}
1
2
3
4
5
6
7

老手

let arr = ["value1", "value2", "value3"];
let arrObject = { ...arr }; // {0:'value1',1:'value2',2:'value3'}
1
2

新手

let number = {
  one: 1,
  two: 2,
};
let keys = [];
for (let numbers in numbers) {
  if (number.hasOwnProperty(number)) {
    keys.push(number);
  }
}
// key = [ 'one', 'two' ]
1
2
3
4
5
6
7
8
9
10
11

老手

let number = {
  one: 1,
  two: 2,
};
let key = Object.keys(numbers); // key = [ 'one', 'two' ]
let value = Object.values(numbers); // value = [ 1, 2 ]
let entry = Object.entries(numbers); // entry = [['one' : 1], ['two' : 2]]
1
2
3
4
5
6
7

# 全角半角转换

//iCase: 0全到半,1半到全,其他不转化
function chgCase(sStr, iCase) {
  if (
    typeof sStr != "string" ||
    sStr.length <= 0 ||
    !(iCase === 0 || iCase == 1)
  ) {
    return sStr;
  }
  var i,
    oRs = [],
    iCode;
  if (iCase) {
    /*半->全*/
    for (i = 0; i < sStr.length; i += 1) {
      iCode = sStr.charCodeAt(i);
      if (iCode == 32) {
        iCode = 12288;
      } else if (iCode < 127) {
        iCode += 65248;
      }
      oRs.push(String.fromCharCode(iCode));
    }
  } else {
    /*全->半*/
    for (i = 0; i < sStr.length; i += 1) {
      iCode = sStr.charCodeAt(i);
      if (iCode == 12288) {
        iCode = 32;
      } else if (iCode > 65280 && iCode < 65375) {
        iCode -= 65248;
      }
      oRs.push(String.fromCharCode(iCode));
    }
  }
  return oRs.join("");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 全角转换为半角函数

function toCDB(str) {
  var result = "";
  for (var i = 0; i < str.length; i++) {
    code = str.charCodeAt(i);
    if (code >= 65281 && code <= 65374) {
      result += String.fromCharCode(str.charCodeAt(i) - 65248);
    } else if (code == 12288) {
      result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
    } else {
      result += str.charAt(i);
    }
  }
  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 半角转换为全角函数

function toDBC(str) {
  var result = "";
  for (var i = 0; i < str.length; i++) {
    code = str.charCodeAt(i);
    if (code >= 33 && code <= 126) {
      result += String.fromCharCode(str.charCodeAt(i) + 65248);
    } else if (code == 32) {
      result += String.fromCharCode(str.charCodeAt(i) + 12288 - 32);
    } else {
      result += str.charAt(i);
    }
  }
  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 金额大写转换函数

function transform(tranvalue) {
  try {
    var i = 1;
    var dw2 = new Array("", "万", "亿"); //大单位
    var dw1 = new Array("拾", "佰", "仟"); //小单位
    var dw = new Array(
      "零",
      "壹",
      "贰",
      "叁",
      "肆",
      "伍",
      "陆",
      "柒",
      "捌",
      "玖"
    );
    //整数部分用
    //以下是小写转换成大写显示在合计大写的文本框中
    //分离整数与小数
    var source = splits(tranvalue);
    var num = source[0];
    var dig = source[1];
    //转换整数部分
    var k1 = 0; //计小单位
    var k2 = 0; //计大单位
    var sum = 0;
    var str = "";
    var len = source[0].length; //整数的长度
    for (i = 1; i <= len; i++) {
      var n = source[0].charAt(len - i); //取得某个位数上的数字
      var bn = 0;
      if (len - i - 1 >= 0) {
        bn = source[0].charAt(len - i - 1); //取得某个位数前一位上的数字
      }
      sum = sum + Number(n);
      if (sum != 0) {
        str = dw[Number(n)].concat(str); //取得该数字对应的大写数字,并插入到str字符串的前面
        if (n == "0") sum = 0;
      }
      if (len - i - 1 >= 0) {
        //在数字范围内
        if (k1 != 3) {
          //加小单位
          if (bn != 0) {
            str = dw1[k1].concat(str);
          }
          k1++;
        } else {
          //不加小单位,加大单位
          k1 = 0;
          var temp = str.charAt(0);
          if (temp == "万" || temp == "亿")
            //若大单位前没有数字则舍去大单位
            str = str.substr(1, str.length - 1);
          str = dw2[k2].concat(str);
          sum = 0;
        }
      }
      if (k1 == 3) {
        //小单位到千则大单位进一
        k2++;
      }
    }
    //转换小数部分
    var strdig = "";
    if (dig != "") {
      var n = dig.charAt(0);
      if (n != 0) {
        strdig += dw[Number(n)] + "角"; //加数字
      }
      var n = dig.charAt(1);
      if (n != 0) {
        strdig += dw[Number(n)] + "分"; //加数字
      }
    }
    str += "元" + strdig;
  } catch (e) {
    return "0元";
  }
  return str;
}
//拆分整数与小数
function splits(tranvalue) {
  var value = new Array("", "");
  temp = tranvalue.split(".");
  for (var i = 0; i < temp.length; i++) {
    value = temp;
  }
  return value;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

# 优雅的金钱格式化

  • 1、使用正则实现
var test1 = '1234567890'var format = test1.replace(/\B(?=(\d{3})+(?!\d))/g, ',')console.log(format) // 1,234,567,8902、
1
  • 使用骚操作
function formatCash(str) {
  return str
    .split("")
    .reverse()
    .reduce((prev, next, index) => {
      return (index % 3 ? next : next + ",") + prev;
    });
}
console.log(format); // 1,234,567,890
1
2
3
4
5
6
7
8
9

# arguments 对象转成数组

arguments 对象是函数内可访问的类数组对象,包含了传给函数的参数值。但它跟数组又不太一样,虽然可以访问值和获取长度,却无法使用数组的其他方法。所幸的是,我们可以很方便地把它转成普通的数组:

var argArray = Array.prototype.slice.call(arguments);
1

# radsToDegrees:此函数功能将弧度转换成度数

const radsToDegrees = (rad) => (rad * 180.0) / Math.PI;
radsToDegrees(Math.PI / 2); // 90
1
2

# degreesToRads-标准的度数转换成弧度

此段代码将标准的度数,转换成弧度。

const degreesToRads = deg => (deg \* Math.PI) / 180.0;

degreesToRads(90.0); // ~1.5708
1
2
3

# 工具函数

function getCookie(name) {
  var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
  if (arr != null) return unescape(arr[2]);
  return null;
}
1
2
3
4
5
function setCookie(name, value, Hours) {
  var d = new Date();
  var offset = 8;
  var utc = d.getTime() + d.getTimezoneOffset() * 60000;
  var nd = utc + 3600000 * offset;
  var exp = new Date(nd);
  exp.setTime(exp.getTime() + Hours * 60 * 60 * 1000);
  document.cookie =
    name +
    "=" +
    escape(value) +
    ";path=/;expires=" +
    exp.toGMTString() +
    ";domain=360doc.com;";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 返回当前访问的 URL 地址

const currentURL = () => window.location.href;

// 事例
currentURL(); // 'https://google.com'
1
2
3
4

# 检验 URL 链接是否有效

function getUrlState(URL) {
  var xmlhttp = new ActiveXObject("microsoft.xmlhttp");
  xmlhttp.Open("GET", URL, false);
  try {
    xmlhttp.Send();
  } catch (e) {
  } finally {
    var result = xmlhttp.responseText;
    if (result) {
      if (xmlhttp.Status == 200) {
        return true;
      } else {
        return false;
      }
    } else {
      return false;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 如何创建一个包含当前 URL 参数的对象

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

// 事例
getURLParameters("http://url.com/page?n=Adam&s=Smith"); // {n: 'Adam', s: 'Smith'}
getURLParameters("google.com"); // {}
1
2
3
4
5
6
7
8
9
10
11

# 获取URL上的参数-2

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


getURLParameters('google.com'); // {}
getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'}
1
2
3
4
5
6
7
8
9
10
11

# 获取 URL 上的参数

// 获取URL中的某参数值,不区分大小写
// 获取URL中的某参数值,不区分大小写,
// 默认是取'hash'里的参数,
// 如果传其他参数支持取‘search’中的参数
// @param {String} name 参数名称
export function getUrlParam(name, type = "hash") {
  let newName = name,
    reg = new RegExp("(^|&)" + newName + "=([^&]*)(&|$)", "i"),
    paramHash = window.location.hash.split("?")[1] || "",
    paramSearch = window.location.search.split("?")[1] || "",
    param;

  type === "hash" ? (param = paramHash) : (param = paramSearch);

  let result = param.match(reg);

  if (result != null) {
    return result[2].split("/")[0];
  }
  return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 获取 URL 中的参数

var urlParams = new URLSearchParams("?post=1234&action=edit");
console.log(urlParams.get("action")); // "edit"
1
2

# 如何向传递的 URL 发出 GET 请求

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();
};

httpGet("https://jsonplaceholder.typicode.com/posts/1", console.log);

// {"userId": 1, "id": 1, "title": "sample title", "body": "my text"}
1
2
3
4
5
6
7
8
9
10
11

# 获得 URL 中 GET 参数值

// 用法:如果地址是 test.htm?t1=1&t2=2&t3=3, 那么能取得:GET["t1"], GET["t2"], GET["t3"]
function getGet() {
  querystr = window.location.href.split("?");
  if (querystr[1]) {
    GETs = querystr[1].split("&");
    GET = [];
    for (i = 0; i < GETs.length; i++) {
      tmp_arr = GETs.split("=");
      key = tmp_arr[0];
      GET[key] = tmp_arr[1];
    }
  }
  return querystr[1];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 如何对传递的 URL 发出 POST 请求

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);
};

const newPost = {
  userId: 1,
  id: 1337,
  title: "Foo",
  body: "bar bar bar",
};
const data = JSON.stringify(newPost);
httpPost("https://jsonplaceholder.typicode.com/posts", data, console.log);

// {"userId": 1, "id": 1337, "title": "Foo", "body": "bar bar bar"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 将键值对拼接成 URL 带参数

export default const fnParams2Url = obj=> {
      let aUrl = []
      let fnAdd = function(key, value) {
        return key + '=' + value
      }
      for (var k in obj) {
        aUrl.push(fnAdd(k, obj[k]))
      }
      return encodeURIComponent(aUrl.join('&'))
 }
1
2
3
4
5
6
7
8
9
10

# 去掉 url 前缀

function removeUrlPrefix(a) {
  a = a
    .replace(/:/g, ":")
    .replace(/./g, ".")
    .replace(///g, "/");
  while (
    trim(a)
      .toLowerCase()
      .indexOf("http://") == 0
  ) {
    a = trim(a.replace(/http:\/\//i, ""));
  }
  return a;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 重定向到另一个 URL

// location 是全局 window 对象上的一个方法,设置 href 属性的行为与用户点击链接的行为相同
const redirect = url => location.href = url
1
2

# js下载文件

/**
 * 保存文件
 * @param blob 文件流
 * @param fileName 文件名
 */
export const saveFile = ({ blob, fileName }) => {
  if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, fileName);
  } else {
    const link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    link.download = fileName;
    // 此写法兼容可火狐浏览器
    document.body.appendChild(link);
    const evt = document.createEvent('MouseEvents');
    evt.initEvent('click', false, false);
    link.dispatchEvent(evt);
    document.body.removeChild(link);
    // 释放内存
    window.URL.revokeObjectURL(link.href);
  }
};

/**
 * 根据文件地址获取下载流
 * @param {string} url
 */
export const getBolbByUrl = url =>
new Promise(resolve => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', `${url}?random=${Math.random()}`, true);
  xhr.responseType = 'blob';
  xhr.onload = () => {
    if (xhr.status === 200) {
      resolve(xhr.response);
    }
  };
  xhr.send();
});

onDownload = file => {
  getBolbByUrl(file.filePath).then(blob => {
    saveFile({ blob, fileName: file.fileName });
  });
  // console.info('有在下载文件。。。', file);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 下载页面DOM中的内容

/**
 * 浏览器下载静态文件
 * @param {String} name 文件名
 * @param {String} content 文件内容
 */
function downloadFile(name, content) {
    if (typeof name == 'undefined') {
        throw new Error('The first parameter name is a must')
    }
    if (typeof content == 'undefined') {
        throw new Error('The second parameter content is a must')
    }
    if (!(content instanceof Blob)) {
        content = new Blob([content])
    }
    const link = URL.createObjectURL(content)
    download(link, name)
}
//下载一个链接
function download(link, name) {
    if (!name) {//如果没有提供名字,从给的Link中截取最后一坨
        name =  link.slice(link.lastIndexOf('/') + 1)
    }
    let eleLink = document.createElement('a')
    eleLink.download = name
    eleLink.style.display = 'none'
    eleLink.href = link
    document.body.appendChild(eleLink)
    eleLink.click()
    document.body.removeChild(eleLink)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# getType-返回数据的类型

const getType = (v) =>
  v === undefined
    ? "undefined"
    : v === null
    ? "null"
    : v.constructor.name.toLowerCase();

getType(new Set([1, 2, 3])); // 'set'
1
2
3
4
5
6
7
8

# 如何在等待指定时间后调用提供的函数

const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args);
delay(
  function(text) {
    console.log(text);
  },
  1000,
  "later"
);

// 1秒后打印 'later'
1
2
3
4
5
6
7
8
9
10

# functionName-输出函数的名称

const functionName = (fn) => (console.debug(fn.name), fn);
functionName(Math.max); // max (logged in debug channel of console)
1
2

# 方法参数验证

ES6 中可以为函数的参数设置默认值,有了这个,我们可以实现一个验证方法参数不能为空的巧妙技巧。

const isRequired = () => {
  throw new Error("param is required");
};

const print = (num = isRequired()) => {
  console.log(`printing ${num}`);
};

print(2); //printing 2
print(); // error
print(null); //printing null
1
2
3
4
5
6
7
8
9
10
11

# 函数参数为对象型参数的验证

虽然将 foo.bar 写成 foo ['bar']是一种常见的做法,但是这种做法构成了编写可重用代码的基础。

function validate(values) {
  if (!values.first) return false;
  if (!values.last) return false;
  return true;
}
console.log(validate({ first: "Bruce", last: "Wayne" })); // true
1
2
3
4
5
6

简化通用后,如下:

// object validation rules
const schema = {
  first: {
    required: true,
  },
  last: {
    required: true,
  },
};

// universal validation function
const validate = (schema, values) => {
  for (field in schema) {
    if (schema[field].required) {
      if (!values[field]) {
        return false;
      }
    }
  }
  return true;
};
console.log(validate(schema, { first: "Bruce" })); // false
console.log(validate(schema, { first: "Bruce", last: "Wayne" })); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# attempt-执行一个函数,将剩余的参数传回函数当参数

此段代码执行一个函数,将剩余的参数传回函数当参数,返回相应的结果,并能捕获异常。

attempt = (fn, ...args) => {
  try {
    return fn(...args);
  } catch (e) {
    return e instanceof Error ? e : new Error(e);
  }
};
var elements = attempt(function(selector) {
  return document.querySelectorAll(selector);
}, ">_>");
if (elements instanceof Error) elements = []; // elements = []
1
2
3
4
5
6
7
8
9
10
11

# distance-两点之间的距离

计算两点之间的距离

const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);

distance(1, 1, 2, 3); // 2.23606797749979
1
2
3

# 函数节流器

export const debouncer = (fn, time, interval = 200) => {
  if (time - (window.debounceTimestamp || 0) > interval) {
    fn && fn();
    window.debounceTimestamp = time;
  }
};
1
2
3
4
5
6

# 等待一定时间后执行

const wait = async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
1

# 获取一个随机布尔值

const getRandomBoolean = () => Math.random() >= 0.5
1

# 生成随机 ID

// 生成长度为10的随机字母数字字符串
Math.random()
  .toString(36)
  .substring(2);
1
2
3
4

# 生成随机 UID

const genUid = () => {
  var length = 20;
  var soupLength = genUid.soup_.length;
  var id = [];
  for (var i = 0; i < length; i++) {
    id[i] = genUid.soup_.charAt(Math.random() - soupLength);
  }
  return id.join("");
};
genUid.soup_ =
  "!#$%()*+,-./:;=?@[]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
genUid(); // ;l`yCPc9A8IuK}?N6,%}
1
2
3
4
5
6
7
8
9
10
11
12

# 生成随机 16 进制 颜色 码 如 # ffffff

"#" +
  Math.floor(Math.random() * 0xffffff)
    .toString(16)
    .padEnd(6, "0");
1
2
3
4

# RGB 色值生成 16 进制色值

const rgb2Hex = (rgb) => {
  let rgbList = rgb.toString().match(/\d+/g);
  let hex = "#";
  for (let i = 0, len = rgbList.length; i < len; ++i) {
    hex += ("0" + Number(rgbList[i]).toString(16)).slice(-2);
  }
  return hex;
};
rgb2Hex("100, 50, 0"); // '#643200'
1
2
3
4
5
6
7
8
9

# 颜色混合

const colourBlend = (c1, c2, ratio) => {
  ratio = Math.max(Math.min(Number(ratio), 1), 0);
  let r1 = parseInt(c1.substring(1, 3), 16);
  let g1 = parseInt(c1.substring(3, 5), 16);
  let b1 = parseInt(c1.substring(5, 7), 16);
  let r2 = parseInt(c2.substring(1, 3), 16);
  let g2 = parseInt(c2.substring(3, 5), 16);
  let b2 = parseInt(c2.substring(5, 7), 16);
  let r = Math.round(r1 - (1 - ratio) + r2 - ratio);
  let g = Math.round(g1 - (1 - ratio) + g2 - ratio);
  let b = Math.round(b1 - (1 - ratio) + b2 - ratio);
  r = ("0" + (r || 0).toString(16)).slice(-2);
  g = ("0" + (g || 0).toString(16)).slice(-2);
  b = ("0" + (b || 0).toString(16)).slice(-2);
  return "#" + r + g + b;
};
colourBlend("#ff0000", "#3333ff", 0.5); // "#991a80"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 转换rgb颜色为对象

const toRGBObject = rgbStr => {
  const [red, green, blue] = rgbStr.match(/\d+/g).map(Number);
  return { red, green, blue };
};
toRGBObject('rgb(255, 12, 0)'); // {red: 255, green: 12, blue: 0}
1
2
3
4
5

# 转换hsl颜色为对象

const toHSLObject = hslStr => {
  const [hue, saturation, lightness] = hslStr.match(/\d+/g).map(Number);
  return { hue, saturation, lightness };
};
toHSLObject('hsl(50, 10%, 10%)'); // { hue: 50, saturation: 10, lightness: 10 }
1
2
3
4
5

# 几种方法实现值交换

var temp = a; a = b; b = temp; (传统,但需要借助临时变量)
1
a ^= b;
b ^= a;
a ^= b; // (需要两个整数)
1
2
3
b = [a, (a = b)][0]; // (借助数组)
1
[a, b] = [b, a]; // (ES6,解构赋值)
1
a = a + b;
b = a - b;
a = a - b; // (小学奥赛题)
1
2
3

# 真假判断

# 判断数据类型

# typeof 是否能正确判断类型

因为由于历史原因,在判断原始类型时,typeof null会等于object。而且对于对象来说,除了函数,都会转换成 object。

typeof 1; // 'number'
typeof "1"; // 'string'
typeof null; //"object"

typeof []; // 'object'
typeof {}; // 'object'
typeof window.alert; // 'function'
1
2
3
4
5
6
7

# instanceof 是否能正确判断类型

虽然 instanceof 是通过原型链来判断的,但是对于对象来说,Array也会被转换成Object,而且也不能区分基本类型string和boolean

function Func() {}
const func = new Func();
console.log(func instanceof Func); // true

const obj = {};
const arr = [];
obj instanceof Object; // true
arr instanceof Object; // true
arr instanceof Array; // true

const str = "abc";
const str2 = new String("abc");
str instanceof String; // false
str2 instanceof String; // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Object.prototype.toString.call();

因为每个对象都有一个 toString()方法,当要将对象表示为文本值或以预期字符串的方式引用对象时,会自动调用该方法。默认情况下,从 Object 派生的每个对象都会继承 toString()方法。如果此方法未在自定义对象中被覆盖,则toString()返回[Object type],其中 type 是对象类型。所以就有以下例子:

Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call("1"); // [object String]
Object.prototype.toString.call(1); // [object Numer]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(null); // [object Null]
1
2
3
4
5
var type = function(data) {
  var toString = Object.prototype.toString;
  var dataType =
    data instanceof Element
      ? "element" // 为了统一DOM节点类型输出
      : toString
          .call(data)
          .replace(/\[object\s(.+)\]/, "$1")
          .toLowerCase();
  return dataType;
};

type("a"); // string
type(1); // number
type(window); // window
type(document.querySelector("h1")); // element
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const isType = (type) => (target) =>
  `[object ${type}]` === Object.prototype.toString.call(target);
const isArray = isType("Array");
console.log(isArray([])); //true,isntanceof

Object.prototype.toString.call(param).replace(/\[object\s(.+)]/, '$1').toLowerCase()

const type = (data) => Object.prototype.toString.call(data).replace(/^\[object (.+)\]$/, "$1").toLowerCase();
1
2
3
4
5
6
7
8

使用 Object.prototype.toString 配合闭包,通过传入不同的判断类型来返回不同的判断函数,一行代码,简洁优雅灵活(注意传入 type 参数时首字母大写

Object.prototype.toString.call(a);
// "[object Object]"
Object.prototype.toString.call([]);
// "[object Array]"
Object.prototype.toString.call(5);
// "[object Number]"
Object.prototype.toString.call("9");
// "[object String]"
Object.prototype.toString.call(true);
// "[object Boolean]"
Object.prototype.toString.call(NaN);
// "[object Number]"
Object.prototype.toString.call(null);
// "[object Null]"
Object.prototype.toString.call(() => a);
// "[object Function]"
Object.prototype.toString.call(undefined);
// "[object Undefined]"

let b = () => a;
// undefined
typeof b;
// "function"
typeof a;
// "object"
typeof [];
// "object"
typeof 7;
// "number"
typeof true;
// "boolean"
typeof "0";
// "string"
typeof undefined;
// "undefined"
typeof null;
// "object"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

不推荐将这个函数用来检测可能会产生包装类型的基本数据类型上,因为 call 始终会将第一个参数进行装箱操作,导致基本类型和包装类型无法区分

# is-判断数据是否为指定的数据类型

const is = (type, val) => ![, null].includes(val) && val.constructor === type;

is(Array, [1]); // true
is(ArrayBuffer, new ArrayBuffer()); // true
is(Map, new Map()); // true
is(RegExp, /./g); // true
is(Set, new Set()); // true
is(WeakMap, new WeakMap()); // true
is(WeakSet, new WeakSet()); // true
is(String, ""); // true
is(String, new String("")); // true
is(Number, 1); // true
is(Number, new Number(1)); // true
is(Boolean, true); // true
is(Boolean, new Boolean(true)); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 检查值是否为数组

const arr = [1, 2, 3];
console.log(typeof arr); // object
console.log(Array.isArray(arr)); // true
1
2
3

# 是否数组

export const isArray = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Array";
};
1
2
3

# isString-检查当前的值是否为字符串类型

const isString = (val) => typeof val === "string";

isString("10"); // true
1
2
3

# 是否字符串

export const isString = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "String";
};
1
2
3

# isSymbol-判断参数的值是否是 Symbol 类型

const isSymbol = (val) => typeof val === "symbol";

isSymbol(Symbol("x")); // true
1
2
3

# 是否 Symbol 函数

export const isSymbol = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Symbol";
};
1
2
3

# isUpperCase-判断当前字符串的字母是否都为大写

const isUpperCase = (str) => str === str.toUpperCase();

isUpperCase("ABC"); // true
isLowerCase("A3@$"); // true
isLowerCase("aB4"); // false
1
2
3
4
5

# isValidJSON-判断给定的字符串是否是 JSON 字符串

const isValidJSON = (str) => {
  try {
    JSON.parse(str);
    return true;
  } catch (e) {
    return false;
  }
};

isValidJSON('{"name":"Adam","age":20}'); // true
isValidJSON('{"name":"Adam",age:"20"}'); // false
isValidJSON(null); // true
1
2
3
4
5
6
7
8
9
10
11
12

# isObject-判断参数的值是否是对象

用于判断参数的值是否是对象,这里运用了 Object 构造函数创建一个对象包装器,如果是对象类型,将会原值返回。

const isObject = (obj) => obj === Object(obj);

isObject([1, 2, 3, 4]); // true
isObject([]); // true
isObject(["Hello!"]); // true
isObject({ a: 1 }); // true
isObject({}); // true
isObject(true); // false
1
2
3
4
5
6
7
8

# 是否对象

export const isObj = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Object";
};
1
2
3

# isObjectLike-检查参数的值是否为 null 以及类型是否为对象

用于检查参数的值是否为 null 以及类型是否为对象。

const isObjectLike = (val) => val !== null && typeof val === "object";

isObjectLike({}); // true
isObjectLike([1, 2, 3]); // true
isObjectLike((x) => x); // false
isObjectLike(null); // false
1
2
3
4
5
6

# isPlainObject-是否是由 Object 构造函数创建的对象

此代码段检查参数的值是否是由 Object 构造函数创建的对象。

const isPlainObject = (val) =>
  !!val && typeof val === "object" && val.constructor === Object;

isPlainObject({ a: 1 }); // true
isPlainObject(new Map()); // false
1
2
3
4
5

# isPromiseLike-检查当前的对象是否类似 Promise 函数

const isPromiseLike = (obj) =>
  obj !== null &&
  (typeof obj === "object" || typeof obj === "function") &&
  typeof obj.then === "function";

isPromiseLike({
  then: function() {
    return "";
  },
}); // true
isPromiseLike(null); // false
isPromiseLike({}); // false
1
2
3
4
5
6
7
8
9
10
11
12

# 是否 Promise 对象

export const isPromise = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Promise";
};
1
2
3

# isNil-判断当前变量的值是否为 null 或 undefined 类型

const isNil = (val) => val === undefined || val === null;

isNil(null); // true
isNil(undefined); // true
1
2
3
4

# isNull-判断当前变量的值是否为 null 类型

const isNull = (val) => val === null;

isNull(null); // true
1
2
3

# 是否为 null

export const isNull = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Null";
};
1
2
3

# 是否 undefined

export const isUndefined = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Undefined";
};
1
2
3

# isNumber-检查当前的值是否为数字类型

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

isNumber("1"); // false
isNumber(1); // true
1
2
3
4
5
6

# 判断是否为数字类型

function isDigit(value) {
  var patrn = /^[0-9]*$/;
  if (patrn.exec(value) == null || value == "") {
    return false;
  } else {
    return true;
  }
}
1
2
3
4
5
6
7
8

# 是否数字

export const isNumber = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Number";
};
1
2
3

# 是否 boolean

export const isBoolean = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Boolean";
};
1
2
3

# 是否函数

export const isFunction = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Function";
};
1
2
3

# 是否时间

export const isDate = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Date";
};
1
2
3

# 是否正则

export const isRegExp = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "RegExp";
};
1
2
3

# 邮箱

export const isEmail = (s) => {
  return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(
    s
  );
};
1
2
3
4
5

# 是否 Set 对象

export const isSet = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Set";
};

export const ua = navigator.userAgent.toLowerCase();
1
2
3
4
5

# 是否错误对象

export const isError = (o) => {
  return Object.prototype.toString.call(o).slice(8, -1) === "Error";
};
1
2
3

# 是否是某类手机型号

// 用devicePixelRatio和分辨率判断
const isIphonex = () => {
  // X XS, XS Max, XR
  const xSeriesConfig = [
    {
      devicePixelRatio: 3,
      width: 375,
      height: 812,
    },
    {
      devicePixelRatio: 3,
      width: 414,
      height: 896,
    },
    {
      devicePixelRatio: 2,
      width: 414,
      height: 896,
    },
  ];
  // h5
  if (typeof window !== "undefined" && window) {
    const isIOS = /iphone/gi.test(window.navigator.userAgent);
    if (!isIOS) return false;
    const { devicePixelRatio, screen } = window;
    const { width, height } = screen;
    return xSeriesConfig.some(
      (item) =>
        item.devicePixelRatio === devicePixelRatio &&
        item.width === width &&
        item.height === height
    );
  }
  return false;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 判断是否移动设备

function isMobile() {
  if (typeof this._isMobile === "boolean") {
    return this._isMobile;
  }
  var screenWidth = this.getScreenWidth();
  var fixViewPortsExperiment =
    rendererModel.runningExperiments.FixViewport ||
    rendererModel.runningExperiments.fixviewport;
  var fixViewPortsExperimentRunning =
    fixViewPortsExperiment && fixViewPortsExperiment.toLowerCase() === "new";
  if (!fixViewPortsExperiment) {
    if (!this.isAppleMobileDevice()) {
      screenWidth = screenWidth / window.devicePixelRatio;
    }
  }
  var isMobileScreenSize = screenWidth < 600;
  var isMobileUserAgent = false;
  this._isMobile = isMobileScreenSize && this.isTouchScreen();
  return this._isMobile;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 判断吗是否手机号码

function isMobileNumber(e) {
  var i =
      "134,135,136,137,138,139,150,151,152,157,158,159,187,188,147,182,183,184,178",
    n = "130,131,132,155,156,185,186,145,176",
    a = "133,153,180,181,189,177,173,170",
    o = e || "",
    r = o.substring(0, 3),
    d = o.substring(0, 4),
    s =
      !!/^1\d{10}$/.test(o) &&
      (n.indexOf(r) >= 0
        ? "联通"
        : a.indexOf(r) >= 0
        ? "电信"
        : "1349" == d
        ? "电信"
        : i.indexOf(r) >= 0
        ? "移动"
        : "未知");
  return s;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 手机号码

export const isMobile = (s) => {
  return /^1[0-9]{10}$/.test(s);
};
1
2
3

# 电话号码

export const isPhone = (s) => {
  return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s);
};
1
2
3

# 严格的身份证校验

export const isCardID = (sId) => {
  if (!/(^\d{15}$)|(^\d{17}(\d|X|x)$)/.test(sId)) {
    console.log("你输入的身份证长度或格式错误");
    return false;
  }
  //身份证城市
  var aCity = {
    11: "北京",
    12: "天津",
    13: "河北",
    14: "山西",
    15: "内蒙古",
    21: "辽宁",
    22: "吉林",
    23: "黑龙江",
    31: "上海",
    32: "江苏",
    33: "浙江",
    34: "安徽",
    35: "福建",
    36: "江西",
    37: "山东",
    41: "河南",
    42: "湖北",
    43: "湖南",
    44: "广东",
    45: "广西",
    46: "海南",
    50: "重庆",
    51: "四川",
    52: "贵州",
    53: "云南",
    54: "西藏",
    61: "陕西",
    62: "甘肃",
    63: "青海",
    64: "宁夏",
    65: "新疆",
    71: "台湾",
    81: "香港",
    82: "澳门",
    91: "国外",
  };
  if (!aCity[parseInt(sId.substr(0, 2))]) {
    console.log("你的身份证地区非法");
    return false;
  }

  // 出生日期验证
  var sBirthday = (
      sId.substr(6, 4) +
      "-" +
      Number(sId.substr(10, 2)) +
      "-" +
      Number(sId.substr(12, 2))
    ).replace(/-/g, "/"),
    d = new Date(sBirthday);
  if (
    sBirthday !=
    d.getFullYear() + "/" + (d.getMonth() + 1) + "/" + d.getDate()
  ) {
    console.log("身份证上的出生日期非法");
    return false;
  }

  // 身份证号码校验
  var sum = 0,
    weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2],
    codes = "10X98765432";
  for (var i = 0; i < sId.length - 1; i++) {
    sum += sId[i] * weights[i];
  }
  var last = codes[sum % 11]; //计算出来的最后一位身份证号码
  if (sId[sId.length - 1] != last) {
    console.log("你输入的身份证号非法");
    return false;
  }

  return true;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# 判断是否是移动设备访问

function isMobileUserAgent() {
  return /iphone|ipod|android.*mobile|windows.*phone|blackberry.*mobile/i.test(
    window.navigator.userAgent.toLowerCase()
  );
}
1
2
3
4
5

# 是否是移动端

export const isDeviceMobile = () => {
  return /android|webos|iphone|ipod|balckberry/i.test(ua);
};
1
2
3

# 是否 ios

export const isIos = () => {
  var u = navigator.userAgent;
  if (u.indexOf("Android") > -1 || u.indexOf("Linux") > -1) {
    //安卓手机
    return false;
  } else if (u.indexOf("iPhone") > -1) {
    //苹果手机
    return true;
  } else if (u.indexOf("iPad") > -1) {
    //iPad
    return false;
  } else if (u.indexOf("Windows Phone") > -1) {
    //winphone手机
    return false;
  } else {
    return false;
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 是否为 PC 端

export const isPC = () => {
  var userAgentInfo = navigator.userAgent;
  var Agents = [
    "Android",
    "iPhone",
    "SymbianOS",
    "Windows Phone",
    "iPad",
    "iPod",
  ];
  var flag = true;
  for (var v = 0; v < Agents.length; v++) {
    if (userAgentInfo.indexOf(Agents[v]) > 0) {
      flag = false;
      break;
    }
  }
  return flag;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 判断鼠标是否移出事件

function isMouseOut(e, handler) {
  if (e.type !== "mouseout") {
    return false;
  }
  var reltg = e.relatedTarget
    ? e.relatedTarget
    : e.type === "mouseout"
    ? e.toElement
    : e.fromElement;
  while (reltg && reltg !== handler) {
    reltg = reltg.parentNode;
  }
  return reltg !== handler;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 判断是否 Touch 屏幕

function isTouchScreen() {
  return (
    "ontouchstart" in window ||
    (window.DocumentTouch && document instanceof DocumentTouch)
  );
}
1
2
3
4
5
6

# 判断是否为网址

function isURL(strUrl) {
  var regular = /^\b(((https?|ftp):\/\/)?[-a-z0-9]+(\.[-a-z0-9]+)*\.(?:com|edu|gov|int|mil|net|org|biz|info|name|museum|asia|coop|aero|[a-z][a-z]|((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]\d)|\d))\b(\/[-a-z0-9_:\@&?=+,.!\/~%\$]*)?)$/i;
  if (regular.test(strUrl)) {
    return true;
  } else {
    return false;
  }
}
1
2
3
4
5
6
7
8

# 判断是否打开视窗

function isViewportOpen() {
  return !!document.getElementById("wixMobileViewport");
}
1
2
3

# 是否是微信浏览器

export const isWeiXin = () => {
  return ua.match(/microMessenger/i) == "micromessenger";
};
1
2
3

# 是否是 QQ 浏览器

export const isQQBrowser = () => {
  return !!ua.match(/mqqbrowser|qzone|qqbrowser|qbwebviewtype/i);
};
1
2
3

# 是否是爬虫

export const isSpider = () => {
  return /adsbot|googlebot|bingbot|msnbot|yandexbot|baidubot|robot|careerbot|seznambot|bot|baiduspider|jikespider|symantecspider|scannerlwebcrawler|crawler|360spider|sosospider|sogou web sprider|sogou orion spider/.test(
    ua
  );
};
1
2
3
4
5

# 判断类型集合(各种类型)

export const checkStr = (str, type) => {
  switch (type) {
    case "phone": //手机号码
      return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(str);
    case "tel": //座机
      return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
    case "card": //身份证
      return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(str);
    case "pwd": //密码以字母开头,长度在6~18之间,只能包含字母、数字和下划线
      return /^[a-zA-Z]\w{5,17}$/.test(str);
    case "postal": //邮政编码
      return /[1-9]\d{5}(?!\d)/.test(str);
    case "QQ": //QQ号
      return /^[1-9][0-9]{4,9}$/.test(str);
    case "email": //邮箱
      return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
    case "money": //金额(小数点2位)
      return /^\d*(?:\.\d{0,2})?$/.test(str);
    case "URL": //网址
      return /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/.test(
        str
      );
    case "IP": //IP
      return /((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))/.test(
        str
      );
    case "date": //日期时间
      return (
        /^(\d{4})\-(\d{2})\-(\d{2}) (\d{2})(?:\:\d{2}|:(\d{2}):(\d{2}))$/.test(
          str
        ) || /^(\d{4})\-(\d{2})\-(\d{2})$/.test(str)
      );
    case "number": //数字
      return /^[0-9]$/.test(str);
    case "english": //英文
      return /^[a-zA-Z]+$/.test(str);
    case "chinese": //中文
      return /^[\\\u4E00-\\\u9FA5]+$/.test(str);
    case "lower": //小写
      return /^[a-z]+$/.test(str);
    case "upper": //大写
      return /^[A-Z]+$/.test(str);
    case "HTML": //HTML标记
      return /<("[^"]*"|'[^']*'|[^'">])*>/.test(str);
    default:
      return true;
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# DOM、浏览器、输入输出设备等

# nodeListToArray:返回给定的 DOM 节点,并以数组的形式输出

const nodeListToArray = (nodeList) => [...nodeList];
nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]
1
2

# getStyle-返回 DOM 元素节点对应的属性值

const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
getStyle(document.querySelector("p"), "font-size"); // '16px'
1
2

# httpsRedirect-将 http 网址重定向 https 网址

const httpsRedirect = () => {
  if (location.protocol !== "https:")
    location.replace("https://" + location.href.split("//")[1]);
};

httpsRedirect(); // If you are on http://mydomain.com, you are redirected to https://mydomain.com
1
2
3
4
5
6

# 如何为指定选择器创建具有指定范围,步长和持续时间的计数器

const counter = (selector, start, end, step = 1, duration = 2000) => {
  let current = start,
    _step = (end - start) * step < 0 ? -step : step,
    timer = setInterval(() => {
      current += _step;
      document.querySelector(selector).innerHTML = current;
      if (current >= end) document.querySelector(selector).innerHTML = end;
      if (current >= end) clearInterval(timer);
    }, Math.abs(Math.floor(duration / (end - start))));
  return timer;
};

// 事例
counter("#my-id", 1, 1000, 5, 2000);
// 让 `id=“my-id”`的元素创建一个2秒计时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 如何在给定元素上触发特定事件且能选择地传递自定义数据

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

// 事例
triggerEvent(document.getElementById("myId"), "click");
triggerEvent(document.getElementById("myId"), "click", { username: "bob" });
1
2
3
4
5
6

# 如何从元素中移除事件监听器

const off = (el, evt, fn, opts = false) =>
  el.removeEventListener(evt, fn, opts);

const fn = () => console.log("!");
document.body.addEventListener("click", fn);
off(document.body, "click", fn);
1
2
3
4
5
6

# 如何隐藏所有指定的元素

const hide = (el) => Array.from(el).forEach((e) => (e.style.display = "none"));

// 事例:隐藏页面上所有`<img>`元素?
hide(document.querySelectorAll("img"));
1
2
3
4

# 如何检查元素是否具有指定的类

页面 DOM 里的每个节点上都有一个 classList 对象,程序员可以使用里面的方法新增、删除、修改节点上的 CSS 类。使用 classList,程序员还可以用它来判断某个节点是否被赋予了某个 CSS 类。

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

// 事例
hasClass(document.querySelector("p.special"), "special"); // true
1
2
3
4

# 如何切换一个元素的类

const toggleClass = (el, className) => el.classList.toggle(className);

// 事例 移除 p 具有类`special`的 special 类
toggleClass(document.querySelector("p.special"), "special");
1
2
3
4

# insertAfter-在给定的 DOM 节点后插入新的节点内容

const insertAfter = (el, htmlString) =>
  el.insertAdjacentHTML("afterend", htmlString);

insertAfter(document.getElementById("myId"), "<p>after</p>"); // <div id="myId">...</div> <p>after</p>
1
2
3
4

# insertBefore-在给定的 DOM 节点前插入新的节点内容

const insertBefore = (el, htmlString) =>
  el.insertAdjacentHTML("beforebegin", htmlString);

insertBefore(document.getElementById("myId"), "<p>before</p>"); // <p>before</p> <div id="myId">...</div>
1
2
3
4

# 在网页上获取选定的文本

const getSelectedText = () => window.getSelection().toString()
1

# 如何检查父元素是否包含子元素

const elementContains = (parent, child) =>
  parent !== child && parent.contains(child);

// 事例
elementContains(
  document.querySelector("head"),
  document.querySelector("title")
);
// true
elementContains(document.querySelector("body"), document.querySelector("body"));
// false
1
2
3
4
5
6
7
8
9
10
11

# 如何检查指定的元素在视口中是否可见

const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
  const { top, left, bottom, right } = el.getBoundingClientRect();
  const { innerHeight, innerWidth } = window;
  return partiallyVisible
    ? ((top > 0 && top < innerHeight) ||
        (bottom > 0 && bottom < innerHeight)) &&
        ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
    : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
};

// 事例
elementIsVisibleInViewport(el); // 需要左右可见
elementIsVisibleInViewport(el, true); // 需要全屏(上下左右)可以见
1
2
3
4
5
6
7
8
9
10
11
12
13

# resize 的操作

(function() {
  var fn = function() {
    var w = document.documentElement
        ? document.documentElement.clientWidth
        : document.body.clientWidth,
      r = 1255,
      b = Element.extend(document.body),
      classname = b.className;
    if (w < r) {
      //当窗体的宽度小于1255的时候执行相应的操作
    } else {
      //当窗体的宽度大于1255的时候执行相应的操作
    }
  };
  if (window.addEventListener) {
    window.addEventListener("resize", function() {
      fn();
    });
  } else if (window.attachEvent) {
    window.attachEvent("onresize", function() {
      fn();
    });
  }
  fn();
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 如何获取元素中的所有图像

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

// 事例:includeDuplicates 为 true 表示需要排除重复元素
getImages(document, true); // ['image1.jpg', 'image2.png', 'image1.png', '...']
getImages(document, false); // ['image1.jpg', 'image2.png', '...']
1
2
3
4
5
6
7
8
9
10

# getColonTimeFromDate-判断程序运行环境是否在浏览器

用于判断程序运行环境是否在浏览器,这有助于避免在 node 环境运行前端模块时出错。

const isBrowser = () => ![typeof window, typeof document].includes("undefined");

isBrowser(); // true (browser)
isBrowser(); // false (Node)
1
2
3
4

# isBrowserTabFocused-判断当前页面是否处于活动状态

用于判断当前页面是否处于活动状态(显示状态)。

const isBrowserTabFocused = () => !document.hidden;
isBrowserTabFocused(); // true
1
2

# 检查设备上的触摸支持

const touchSupported = () => ('ontouchstart' in window || DocumentTouch && document instanceof DocumentTouch)
1

# 如何确定设备是移动设备还是台式机/笔记本电脑

const detectDeviceType = () =>
  /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  )
    ? "Mobile"
    : "Desktop";

// 事例
detectDeviceType(); // "Mobile" or "Desktop"
1
2
3
4
5
6
7
8
9

# 获取移动设备初始化大小

function getInitZoom() {
  if (!this._initZoom) {
    var screenWidth = Math.min(screen.height, screen.width);
    if (this.isAndroidMobileDevice() && !this.isNewChromeOnAndroid()) {
      screenWidth = screenWidth / window.devicePixelRatio;
    }
    this._initZoom = screenWidth / document.body.offsetWidth;
  }
  return this._initZoom;
}
1
2
3
4
5
6
7
8
9
10

# 获取移动设备最大化大小

function getZoom() {
  var screenWidth =
    Math.abs(window.orientation) === 90
      ? Math.max(screen.height, screen.width)
      : Math.min(screen.height, screen.width);
  if (this.isAndroidMobileDevice() && !this.isNewChromeOnAndroid()) {
    screenWidth = screenWidth / window.devicePixelRatio;
  }
  var FixViewPortsExperiment =
    rendererModel.runningExperiments.FixViewport ||
    rendererModel.runningExperiments.fixviewport;
  var FixViewPortsExperimentRunning =
    FixViewPortsExperiment &&
    (FixViewPortsExperiment === "New" || FixViewPortsExperiment === "new");
  if (FixViewPortsExperimentRunning) {
    return screenWidth / window.innerWidth;
  } else {
    return screenWidth / document.body.offsetWidth;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 获取窗体可见范围的宽与高

function getViewSize() {
  var de = document.documentElement;
  var db = document.body;
  var viewW = de.clientWidth == 0 ? db.clientWidth : de.clientWidth;
  var viewH = de.clientHeight == 0 ? db.clientHeight : de.clientHeight;
  return Array(viewW, viewH);
}
1
2
3
4
5
6
7

# 判断是否安卓移动设备访问

function isAndroidMobileDevice() {
  return /android/i.test(navigator.userAgent.toLowerCase());
}
1
2
3

# 判断是否苹果移动设备访问

function isAppleMobileDevice() {
  return /iphone|ipod|ipad|Macintosh/i.test(navigator.userAgent.toLowerCase());
}
1
2
3

# 获取当前路径

var currentPageUrl = "";
if (typeof this.href === "undefined") {
  currentPageUrl = document.location.toString().toLowerCase();
} else {
  currentPageUrl = this.href.toString().toLowerCase();
}
1
2
3
4
5
6

# 提取页面代码中所有网址

var aa = document.documentElement.outerHTML
  .match(
    /(url\(|src=|href=)[\"\']*([^\"\'\(\)\<\>\[\] ]+)[\"\'\)]*|(http:\/\/[\w\-\.]+[^\"\'\(\)\<\>\[\] ]+)/gi
  )
  .join("\r\n")
  .replace(/^(src=|href=|url\()[\"\']*|[\"\'\>\) ]*$/gim, "");
alert(aa);
1
2
3
4
5
6
7

# 替换地址栏

function locationReplace(url) {
  if (history.replaceState) {
    history.replaceState(null, document.title, url);
    history.go(0);
  } else {
    location.replace(url);
  }
}
1
2
3
4
5
6
7
8

# 如何将一组表单元素转化为对象

const formToObject = (form) =>
  Array.from(new FormData(form)).reduce(
    (acc, [key, value]) => ({
      ...acc,
      [key]: value,
    }),
    {}
  );

// 事例
formToObject(document.querySelector("#form"));
// { email: 'test@email.com', name: 'Test Name' }
1
2
3
4
5
6
7
8
9
10
11
12

# 如何将字符串复制到剪贴板

  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);
  }
};

// 事例
copyToClipboard('Lorem ipsum');
// 'Lorem ipsum' copied to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 劫持粘贴板

export const copyTextToClipboard = (value) => {
  var textArea = document.createElement("textarea");
  textArea.style.background = "transparent";
  textArea.value = value;
  document.body.appendChild(textArea);
  textArea.select();
  try {
    var successful = document.execCommand("copy");
  } catch (err) {
    console.log("Oops, unable to copy");
  }
  document.body.removeChild(textArea);
};
1
2
3
4
5
6
7
8
9
10
11
12
13

# 如何确定页面的浏览器选项卡是否聚焦

const isBrowserTabFocused = () => !document.hidden;

// 事例
isBrowserTabFocused(); // true
1
2
3
4

# 如何创建目录(如果不存在)

此代码段使用 existSync() 检查目录是否存在,然后使用 mkdirSync() 创建目录(如果不存在)。

const fs = require("fs");
const createDirIfNotExists = (dir) =>
  !fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined;

// 事例
createDirIfNotExists("test");
1
2
3
4
5
6

# 禁用鼠标右键

有些情况,我们想在网页上禁用鼠标的右键,可以使用下面的方式来禁用:

<body oncontextmenu="return false">
  <div></div>
</body>
1
2
3

# 为元素添加 on 方法

Element.prototype.on = Element.prototype.addEventListener;

NodeList.prototype.on = function (event, fn) {[]['forEach'].call(this, function (el) {
        el.on(event, fn);
    });
    return this;
};
1
2
3
4
5
6
7
8

# 为元素添加 trigger 方法

Element.prototype.trigger = function(type, data) {
  var event = document.createEvent("HTMLEvents");
  event.initEvent(type, true, true);
  event.data = data || {};
  event.eventName = type;
  event.target = this;
  this.dispatchEvent(event);
  return this;
};

NodeList.prototype.trigger = function(event) {
  []["forEach"].call(this, function(el) {
    el["trigger"](event);
  });
  return this;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 转义 html 标签

function HtmlEncode(text) {
  return text
    .replace(/&/g, "&")
    .replace(/\"/g, '"')
    .replace(/</g, "<")
    .replace(/>/g, ">");
}
1
2
3
4
5
6
7

# HTML 标签转义

// HTML 标签转义
// @param {Array.<DOMString>} templateData 字符串类型的tokens
// @param {...} ..vals 表达式占位符的运算结果tokens
//
function SaferHTML(templateData) {
  var s = templateData[0];
  for (var i = 1; i < arguments.length; i++) {
    var arg = String(arguments[i]);
    // Escape special characters in the substitution.
    s += arg
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;");
    // Don't escape special characters in the template.
    s += templateData[i];
  }
  return s;
}
// 调用
var html = SaferHTML`<p>这是关于字符串模板的介绍</p>`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 跨浏览器绑定事件

function addEventSamp(obj, evt, fn) {
  if (!oTarget) {
    return;
  }
  if (obj.addEventListener) {
    obj.addEventListener(evt, fn, false);
  } else if (obj.attachEvent) {
    obj.attachEvent("on" + evt, fn);
  } else {
    oTarget["on" + sEvtType] = fn;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 跨浏览器删除事件

function delEvt(obj, evt, fn) {
  if (!obj) {
    return;
  }
  if (obj.addEventListener) {
    obj.addEventListener(evt, fn, false);
  } else if (oTarget.attachEvent) {
    obj.attachEvent("on" + evt, fn);
  } else {
    obj["on" + evt] = fn;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 解决 offsetX 兼容性问题

// 针对火狐不支持offsetX/Y
function getOffset(e) {
  var target = e.target, // 当前触发的目标对象
    eventCoord,
    pageCoord,
    offsetCoord;

  // 计算当前触发元素到文档的距离
  pageCoord = getPageCoord(target);

  // 计算光标到文档的距离
  eventCoord = {
    X: window.pageXOffset + e.clientX,
    Y: window.pageYOffset + e.clientY,
  };

  // 相减获取光标到第一个定位的父元素的坐标
  offsetCoord = {
    X: eventCoord.X - pageCoord.X,
    Y: eventCoord.Y - pageCoord.Y,
  };
  return offsetCoord;
}

function getPageCoord(element) {
  var coord = { X: 0, Y: 0 };
  // 计算从当前触发元素到根节点为止,
  // 各级 offsetParent 元素的 offsetLeft 或 offsetTop 值之和
  while (element) {
    coord.X += element.offsetLeft;
    coord.Y += element.offsetTop;
    element = element.offsetParent;
  }
  return coord;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 打开一个窗体通用方法

function openWindow(url, windowName, width, height) {
  var x = parseInt(screen.width / 2.0) - width / 2.0;
  var y = parseInt(screen.height / 2.0) - height / 2.0;
  var isMSIE = navigator.appName == "Microsoft Internet Explorer";
  if (isMSIE) {
    var p = "resizable=1,location=no,scrollbars=no,width=";
    p = p + width;
    p = p + ",height=";
    p = p + height;
    p = p + ",left=";
    p = p + x;
    p = p + ",top=";
    p = p + y;
    retval = window.open(url, windowName, p);
  } else {
    var win = window.open(
      url,
      "ZyiisPopup",
      "top=" +
        y +
        ",left=" +
        x +
        ",scrollbars=" +
        scrollbars +
        ",dialog=yes,modal=yes,width=" +
        width +
        ",height=" +
        height +
        ",resizable=no"
    );
    eval("try { win.resizeTo(width, height); } catch(e) { }");
    win.focus();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 加入收藏夹

function addFavorite(sURL, sTitle) {
  try {
    window.external.addFavorite(sURL, sTitle);
  } catch (e) {
    try {
      window.sidebar.addPanel(sTitle, sURL, "");
    } catch (e) {
      alert("加入收藏失败,请使用Ctrl+D进行添加");
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11

# 设为首页

function setHomepage() {
  if (document.all) {
    document.body.style.behavior = "url(#default#homepage)";
    document.body.setHomePage("http://w3cboy.com");
  } else if (window.sidebar) {
    if (window.netscape) {
      try {
        netscape.security.PrivilegeManager.enablePrivilege(
          "UniversalXPConnect"
        );
      } catch (e) {
        alert(
          "该操作被浏览器拒绝,如果想启用该功能,请在地址栏内输入 about:config,然后将项 signed.applets.codebase_principal_support 值该为true"
        );
      }
    }
    var prefs = Components.classes[
      "@mozilla.org/preferences-service;1"
    ].getService(Components.interfaces.nsIPrefBranch);
    prefs.setCharPref("browser.startup.homepage", "http://w3cboy.com");
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 返回键盘

// 用字符串返回一个键盘图形
((_) =>
  [..."`1234567890-=~~QWERTYUIOP[]~ASDFGHJKL;'~~ZXCVBNM,./~"].map(
    (x) =>
      ((o += `/${(b = "_".repeat(
        (w =
          x < y
            ? 2
            : " 667699"[
                ((x = ["BS", "TAB", "CAPS", "ENTER"][p++] || "SHIFT"), p)
              ])
      ))}\|`),
      (m += y + (x + "    ").slice(0, w) + y + y),
      (n += y + b + y + y),
      (l += " __" + b))[73] && (k.push(l, m, n, o), (l = ""), (m = n = o = y)),
    (m = n = o = y = "|"),
    (p = l = k = [])
  ) && k.join``)();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 确认是否是键盘有效输入值

function checkKey(iKey) {
  if (iKey == 32 || iKey == 229) {
    return true;
  } /*空格和异常*/
  if (iKey > 47 && iKey < 58) {
    return true;
  } /*数字*/
  if (iKey > 64 && iKey < 91) {
    return true;
  } /*字母*/
  if (iKey > 95 && iKey < 108) {
    return true;
  } /*数字键盘1*/
  if (iKey > 108 && iKey < 112) {
    return true;
  } /*数字键盘2*/
  if (iKey > 185 && iKey < 193) {
    return true;
  } /*符号1*/
  if (iKey > 218 && iKey < 223) {
    return true;
  } /*符号2*/
  return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 加载 css 样式文件

function loadStyle(url) {
  try {
    document.createStyleSheet(url);
  } catch (e) {
    var cssLink = document.createElement("link");
    cssLink.rel = "stylesheet";
    cssLink.type = "text/css";
    cssLink.href = url;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(cssLink);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 动态引入 js

export const injectScript = (src) => {
  const s = document.createElement("script");
  s.type = "text/javascript";
  s.async = true;
  s.src = src;
  const t = document.getElementsByTagName("script")[0];
  t.parentNode.insertBefore(s, t);
};
1
2
3
4
5
6
7
8

# 动态加载脚本文件

function appendscript(src, text, reload, charset) {
  var id = hash(src + text);
  if (!reload && in_array(id, evalscripts)) return;
  if (reload && $(id)) {
    $(id).parentNode.removeChild($(id));
  }

  evalscripts.push(id);
  var scriptNode = document.createElement("script");
  scriptNode.type = "text/javascript";
  scriptNode.id = id;
  scriptNode.charset = charset
    ? charset
    : BROWSER.firefox
    ? document.characterSet
    : document.charset;
  try {
    if (src) {
      scriptNode.src = src;
      scriptNode.onloadDone = false;
      scriptNode.onload = function() {
        scriptNode.onloadDone = true;
        JSLOADED[src] = 1;
      };
      scriptNode.onreadystatechange = function() {
        if (
          (scriptNode.readyState == "loaded" ||
            scriptNode.readyState == "complete") &&
          !scriptNode.onloadDone
        ) {
          scriptNode.onloadDone = true;
          JSLOADED[src] = 1;
        }
      };
    } else if (text) {
      scriptNode.text = text;
    }
    document.getElementsByTagName("head")[0].appendChild(scriptNode);
  } catch (e) {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 返回脚本内容

function evalscript(s) {
  if (s.indexOf("<script") == -1) return s;
  var p = /<script[^\>]*?>([^\x00]*?)<\/script>/gi;
  var arr = [];
  while ((arr = p.exec(s))) {
    var p1 = /<script[^\>]*?src=\"([^\>]*?)\"[^\>]*?(reload=\"1\")?(?:charset=\"([\w\-]+?)\")?><\/script>/i;
    var arr1 = [];
    arr1 = p1.exec(arr[0]);
    if (arr1) {
      appendscript(arr1[1], "", arr1[2], arr1[3]);
    } else {
      p1 = /<script(.*?)>([^\x00]+?)<\/script>/i;
      arr1 = p1.exec(arr[0]);
      appendscript("", arr1[2], arr1[1].indexOf("reload=") != -1);
    }
  }
  return s;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 清除脚本内容

function stripscript(s) {
  return s.replace(/<script.*?>.*?<\/script>/gi, "");
}
1
2
3

# 压缩 CSS 样式代码

function compressCss(s) {
  //压缩代码
  s = s.replace(/\/\*(.|\n)*?\*\//g, ""); //删除注释
  s = s.replace(/\s*([\{\}\:\;\,])\s*/g, "$1");
  s = s.replace(/\,[\s\.\#\d]*\{/g, "{"); //容错处理
  s = s.replace(/;\s*;/g, ";"); //清除连续分号
  s = s.match(/^\s*(\S+(\s+\S+)*)\s*$/); //去掉首尾空白
  return s == null ? "" : s[1];
}
1
2
3
4
5
6
7
8
9

# 格式化 CSS 样式代码

function formatCss(s) {
  //格式化代码
  s = s.replace(/\s*([\{\}\:\;\,])\s*/g, "$1");
  s = s.replace(/;\s*;/g, ";"); //清除连续分号
  s = s.replace(/\,[\s\.\#\d]*{/g, "{");
  s = s.replace(/([^\s])\{([^\s])/g, "$1 {\n\t$2");
  s = s.replace(/([^\s])\}([^\n]*)/g, "$1\n}\n$2");
  s = s.replace(/([^\s]);([^\s\}])/g, "$1;\n\t$2");
  return s;
}
1
2
3
4
5
6
7
8
9
10

# 简洁易用操作符

# 短路求值

  • &&为取假运算,从左到右依次判断,如果遇到一个假值,就返回假值,以后不再执行,否则返回最后一个真值
  • ||为取真运算,从左到右依次判断,如果遇到一个真值,就返回真值,以后不再执行,否则返回最后一个假值

使用三元运算符可以很快地写出条件语句,例如:

x > 100 ? "Above 100" : "Below 100";
x > 100 ? (x > 200 ? "Above 200" : "Between 100-200") : "Below 100";
1
2

但有时候三元运算符仍然很复杂,我们可以使用逻辑运算符 &&||来替代,让代码更简洁一些。这种技巧通常被称为“短路求值”。
假设我们想要返回两个或多个选项中的一个,使用 && 可以返回第一个 false。如果所有操作数的值都是 true,将返回最后一个表达式的值。

let one = 1,
  two = 2,
  three = 3;
console.log(one && two && three); // Result: 3
console.log(0 && null); // Result: 0
1
2
3
4
5

使用||可以返回第一个 true。如果所有操作数的值都是 false,将返回最后一个表达式的值

let one = 1,
  two = 2,
  three = 3;
console.log(one || two || three); // Result: 1
console.log(0 || null); // Result: null
1
2
3
4
5
  • 示例 1

假设我们想要返回一个变量的 length,但又不知道变量的类型。
我们可以使用 if/else 来检查 foo 是否是一个可接受的类型,但这样会让代码变得很长。这个时候可以使用短路求值:

return (foo || []).length;
1

对于上述两种情况,如果变量 foo 具有 length 属性,这个属性的值将被返回,否则将返回 0。

  • 示例 2

你是否曾经在访问嵌套对象属性时遇到过问题?你可能不知道对象或某个子属性是否存在,所以经常会碰到让你头疼的错误。
假设我们想要访问 this.state 中的一个叫作 data 的属性,但 data 却是 undefined 的。在某些情况下调用 this.state.data 会导致 App 无法运行。为了解决这个问题,我们可以使用条件语句:

if (this.state.data) {
  return this.state.data;
} else {
  return "Fetching Data";
}
1
2
3
4
5

但这样似乎有点啰嗦,而||提供了更简洁的解决方案:

return this.state.data || "Fetching Data";
1

# 逗号操作符

逗号操作符(,)从左到右计算每个操作数的值,并返回最后一个操作数的值

let x = 1;

x = (x++, x);
console.log(x); // 2

x = (2, 3);
console.log(x); // 3
1
2
3
4
5
6
7

# 波浪号操作符

~表示按位取反

自定义事件的函数有 Event、CustomEvent 和 dispatchEvent

// 向 window派发一个resize内置事件
window.dispatchEvent(new Event('resize'))

// 直接自定义事件,使用 Event 构造函数:
var event = new Event('build');
var elem = document.querySelector('#id')
// 监听事件
elem.addEventListener('build', function (e) { ... }, false);
// 触发事件.
elem.dispatchEvent(event);
1
2
3
4
5
6
7
8
9
10

CustomEvent 可以创建一个更高度自定义事件,还可以附带一些数据,具体用法如下:

var myEvent = new CustomEvent(eventname, options);
1

其中 options 可以是:

{
  detail: {
    ...
  },
  bubbles: true,    //是否冒泡
  cancelable: false //是否取消默认事件
}
1
2
3
4
5
6
7

其中 detail 可以存放一些初始化的信息,可以在触发的时候调用。其他属性就是定义该事件是否具有冒泡等等功能。
内置的事件会由浏览器根据某些操作进行触发,自定义的事件就需要人工触发。
dispatchEvent 函数就是用来触发某个事件:

element.dispatchEvent(customEvent);
1

上面代码表示,在 element 上面触发 customEvent 这个事件。

// add an appropriate event listener
obj.addEventListener("cat", function(e) {
  process(e.detail);
});

// create and dispatch the event
var event = new CustomEvent("cat", { detail: { hazcheeseburger: true } });
obj.dispatchEvent(event);
1
2
3
4
5
6
7
8

使用自定义事件需要注意兼容性问题,而使用 jQuery 就简单多了:

// 绑定自定义事件
$(element).on("myCustomEvent", function() {});

// 触发事件
$(element).trigger("myCustomEvent");
// 此外,你还可以在触发自定义事件时传递更多参数信息:

$("p").on("myCustomEvent", function(event, myName) {
  $(this).text(myName + ", hi there!");
});
$("button").click(function() {
  $("p").trigger("myCustomEvent", ["John"]);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 90、randomHexColorCode:此段代码用于生成随机的 16 进制颜色代码。
const randomHexColorCode = () => {
  let n = (Math.random() * 0xfffff * 1000000).toString(16);
  return "#" + n.slice(0, 6);
};

randomHexColorCode(); // "#e34155"
1
2
3
4
5
6
  • 91、randomIntArrayInRange:按照指定的数字范围随机生成长度为 n 的数组。
const randomIntArrayInRange = (min, max, n = 1) =>
  Array.from(
    { length: n },
    () => Math.floor(Math.random() * (max - min + 1)) + min
  );

randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]
1
2
3
4
5
6
7
  • 92、randomIntegerInRange:按照指定的范围内生成一个随机整数。
const randomIntegerInRange = (min, max) =>
  Math.floor(Math.random() * (max - min + 1)) + min;

randomIntegerInRange(0, 5); // 3
1
2
3
4
  • 93、randomNumberInRange:该代码段可用于返回指定范围内的随机数(包含小数部分)。
const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;

randomNumberInRange(2, 10); // 6.0211363285087005
1
2
3
  • 94、readFileLines:此段代码将读取到的文本内容,按行分割组成数组进行输出。
const fs = require("fs");
const readFileLines = (filename) =>
  fs
    .readFileSync(filename)
    .toString("UTF8")
    .split("\n");

let arr = readFileLines("test.txt");
console.log(arr); // ['line1', 'line2', 'line3']
1
2
3
4
5
6
7
8
9
  • 95、Redirect to a URL:此函数功能将页面导向一个指定的网站地址。
const redirect = (url, asLink = true) =>
  asLink ? (window.location.href = url) : window.location.replace(url);

redirect("https://www.qianduandaren.com");
1
2
3
4
  • 96、reverse:此段代码用于将一段字符串进行颠倒。
const reverseString = (str) => [...str].reverse().join("");

reverseString("foobar"); // 'raboof'
1
2
3
  • 97、round:将小数按照指定的位数,进行四舍五入保留。
const round = (n, decimals = 0) =>
  Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);

round(1.005, 2); // 1.01
1
2
3
4
  • 98、runPromisesInSeries:通过数组的形式,连续运行多个 promise。
const runPromisesInSeries = (ps) =>
  ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = (d) => new Promise((r) => setTimeout(r, d));

runPromisesInSeries([() => delay(1000), () => delay(2000)]);
// Executes each promise sequentially, taking a total of 3 seconds to complete
1
2
3
4
5
6
  • 99、sample:从数组中获取一个随机数。
const sample = (arr) => arr[Math.floor(Math.random() * arr.length)];

sample([3, 7, 9, 11]); // 9
1
2
3
  • 100、sampleSize:在数组中随机生选择 n 个元素生成新的数组,如果 n 大于数组长度,则为随机整个数组的排序。这里使用到了 Fisher–Yates shuffle 洗牌算法。

简单来说 Fisher–Yates shuffle 算法是一个用来将一个有限集合生成一个随机排列的算法(数组随机排序)。这个算法生成的随机排列是等概率的。同时这个算法非常高效。

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

sampleSize([1, 2, 3], 2); // [3,1]
sampleSize([1, 2, 3], 4); // [2,3,1]
1
2
3
4
5
6
7
8
9
10
11
  • 102、serializeCookie:此段代码用于将 cookie 序列化成 name-value 的形式方便你存储在 Set-Cookie 头信息里。
const serializeCookie = (name, val) =>
  `${encodeURIComponent(name)}=${encodeURIComponent(val)}`;

serializeCookie("foo", "bar"); // 'foo=bar'
1
2
3
4
  • 103、setStyle:此段代码用于在相应的 DOM 节点添加属性和值。
const setStyle = (el, ruleName, val) => (el.style[ruleName] = val);

setStyle(document.querySelector("p"), "font-size", "20px");
// The first <p> element on the page will have a font-size of 20px
1
2
3
4
  • 104、 shallowClone:此段代码用于浅复制一个对象。
const shallowClone = (obj) => Object.assign({}, obj);

const a = { x: true, y: 1 };
const b = shallowClone(a); // a !== b
1
2
3
4
  • 105、show:从段代码用于显示所有指定的 DOM 元素。
const show = (...el) => [...el].forEach((e) => (e.style.display = ""));

show(...document.querySelectorAll("img")); // Shows all <img> elements on the page
1
2
3
  • 2、allEqual:判断数组中的元素是否都相等
const allEqual = (arr) => arr.every((val) => val === arr[0]);

allEqual([1, 2, 3, 4, 5, 6]); // false
allEqual([1, 1, 1, 1]); // true
1
2
3
4
  • 3、approximatelyEqual:此代码示例检查两个数字是否近似相等,差异值可以通过传参的形式进行设置
const approximatelyEqual = (v1, v2, epsilon = 0.001) =>
  Math.abs(v1 - v2) < epsilon;

approximatelyEqual(Math.PI / 2.0, 1.5708); // true
1
2
3
4
  • 4、arrayToCSV:此段代码将没有逗号或双引号的元素转换成带有逗号分隔符的字符串即 CSV 格式识别的形式。
const arrayToCSV = (arr, delimiter = ",") =>
  arr.map((v) => v.map((x) => `"${x}"`).join(delimiter)).join("\n");

arrayToCSV([
  ["a", "b"],
  ["c", "d"],
]); // '"a","b"\n"c","d"'
arrayToCSV(
  [
    ["a", "b"],
    ["c", "d"],
  ],
  ";"
); // '"a";"b"\n"c";"d"'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 5、arrayToHtmlList:此段代码将数组元素转换成<li>标记,并将此元素添加至给定的ID元素标记内。
const arrayToHtmlList = (arr, listID) =>
  ((el) => (
    (el = document.querySelector("#" + listID)),
    (el.innerHTML += arr.map((item) => `<li>${item}</li>`).join(""))
  ))();

arrayToHtmlList(["item 1", "item 2"], "myListID");
1
2
3
4
5
6
7
  • 6、attempt:此段代码执行一个函数,将剩余的参数传回函数当参数,返回相应的结果,并能捕获异常。
const attempt = (fn, ...args) => {
  try {
    return fn(...args);
  } catch (e) {
    return e instanceof Error ? e : new Error(e);
  }
};
var elements = attempt(function(selector) {
  return document.querySelectorAll(selector);
}, ">_>");
if (elements instanceof Error) elements = []; // elements = []
1
2
3
4
5
6
7
8
9
10
11
  • 7、average:此段代码返回两个或多个数的平均数。
const average = (...nums) =>
  nums.reduce((acc, val) => acc + val, 0) / nums.length;
average(...[1, 2, 3]); // 2
average(1, 2, 3); // 2
1
2
3
4
  • 8、averageBy:一个 map()函数和 reduce()函数结合的例子,此函数先通过 map() 函数将对象转换成数组,然后在调用 reduce()函数进行累加,然后根据数组长度返回平均值。
const averageBy = (arr, fn) =>
  arr
    .map(typeof fn === "function" ? fn : (val) => val[fn])
    .reduce((acc, val) => acc + val, 0) / arr.length;

averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], (o) => o.n); // 5
averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], "n"); // 5
1
2
3
4
5
6
7
  • 9、bifurcate:此函数包含两个参数,类型都为数组,依据第二个参数的真假条件,将一个参数的数组进行分组,条件为真的放入第一个数组,其它的放入第二个数组。这里运用了Array.prototype.reduce()Array.prototype.push() 相结合的形式。
const bifurcate = (arr, filter) =>
  arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [
    [],
    [],
  ]);
bifurcate(["beep", "boop", "foo", "bar"], [true, true, false, true]);
// [ ['beep', 'boop', 'bar'], ['foo'] ]
1
2
3
4
5
6
7
  • 10、bifurcateBy:此段代码将数组按照指定的函数逻辑进行分组,满足函数条件的逻辑为真,放入第一个数组中,其它不满足的放入第二个数组 。这里运用了 Array.prototype.reduce() 和 Array.prototype.push() 相结合的形式,基于函数过滤逻辑,通过 Array.prototype.push() 函数将其添加到数组中。
const bifurcateBy = (arr, fn) =>
  arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [
    [],
    [],
  ]);

bifurcateBy(["beep", "boop", "foo", "bar"], (x) => x[0] === "b");
// [ ['beep', 'boop', 'bar'], ['foo'] ]
1
2
3
4
5
6
7
8
  • 12、byteSize:此代码返回字符串的字节长度。这里用到了 Blob 对象,Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口。其他操作二进制数据的 API(比如 File 对象),都是建立在 Blob 对象基础上的,继承了它的属性和方法。生成 Blob 对象有两种方法:一种是使用 Blob 构造函数,另一种是对现有的 Blob 对象使用 slice 方法切出一部分。
const byteSize = (str) => new Blob([str]).size;

byteSize(""); // 4
byteSize("Hello World"); // 11
1
2
3
4
  • 13、capitalize:将字符串的首字母转成大写,这里主要运用到了 ES6 的展开语法在数组中的运用。
const capitalize = ([first, ...rest]) => first.toUpperCase() + rest.join("");

capitalize("fooBar"); // 'FooBar'
capitalize("fooBar", true); // 'FooBar'
1
2
3
4

# 好习惯

# 三等号和双等号的区别

// 双等号 - 将两个操作数转换为相同类型,再比较
console.log(0 == "o"); // true

// 三等号 - 不转换为相同类型
console.log(0 === "0"); // false
1
2
3
4
5

# 接收参数更好的方式(利用对象)

function downloadData(url, resourceId, searchTest, pageNo, limit) {}

downloadData(...); // need to remember the order
1
2
3

更简单的方法

function downloadData({ url, resourceId, searchTest, pageNo, limit } = {}) {}

downloadData({ resourceId: 2, url: "/posts", searchText: "WebDev" });
1
2
3

# 打印试试(console骚气打印)

console.log(([][[]] + [])[+!![]] + ([] + {})[!+[] + !![]]);// nb
console.log(
  (!~+[] + {})[--[~+""][+[]] * [~+[]] + ~~!+[]] + ({} + [])[[~!+[]] * ~+[]]
);// sb
1
2
3
4

# console 美化

console.info(
  "%c哈哈",
  "color: #3190e8; font-size: 30px; font-family: sans-serif"
);
1
2
3
4

说明符作用%s元素转换为字符串,%d 或 %i元素转换为整数,%f元素转换为浮点数,%o元素以最有效的格式显示,%O元素以最有效的格式显示,%c应用提供的CSS