⌈前端工具函数⌋❤️‍🔥第一弹❤️‍🔥

1,032 阅读5分钟

typeOf

返回一个 对象或原始值表达式 的类型

参数名参数类型参数说明
operandany一个对象或原始值表达式

实现

export function typeOf(operand: any): string {
  const toString = Object.prototype.toString;
  let type = toString.call(operand).split(" ")[1];
  type = type.substring(0, type.length - 1).toLowerCase();
  return type;
}

示例

typOf('botaoxy') === "string";
typOf(1) === "number";
typOf(true) === "boolean";
typOf(undefined) === "undefined";
typOf(null) === "null";
typOf({}) === "object";
typOf(Symbol()) === "symbol";
typOf(Symbol) === "function";
typOf(42n) === "bigint";
typOf([]) === "array";
typOf(new Date()) === "date";
typOf(/regexp/) === "regexp";

instanceOf

检测构造函数的 prototype 属性是否出现在某个实例对象的原型链

参数名参数类型参数说明
targetany检测的构造函数
ctorany实例对象

实现

export function instanceOf(target: any, ctor: any): boolean {
  let proto = Reflect.getPrototypeOf(target);
  while (proto) {
    if (proto === ctor.prototype) {
      return true;
    }
    proto = Reflect.getPrototypeOf(proto);
  }
  return false;
}

示例

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const mycar = new Car("Honda", "Accord", 1998);
let a = instanceOf(mycar, Car);    // 返回 true
let b = instanceOf(mycar, Object); // 返回 true

curry

函数柯里化

参数名参数类型参数说明
fnany需要柯里化函数
argsanyfn 函数参数

实现

export function curry(fn: any, ...args: any[]) {
  const len = fn.length;
  return function (this: any) {
    const anonArgs = Array.prototype.slice.call(arguments);
    Array.prototype.push.apply(args, anonArgs);
    if (args.length < len) {
      return curry.call(this, fn, ...args);
    } else {
      return fn.apply(this, args);
    }
  };
}

示例

function add(a, b, c) {
  return a + b + c;
}
const add1 = curry(add, 1);
add1(2)(3); // 6

partial

偏函数

参数名参数类型参数说明
fnany需要偏函数函数

实现

export function partial(fn: any) {
  const args = Array.prototype.slice.call(arguments, 1);
  return function (this: any) {
    const anonArgs = Array.prototype.slice.call(arguments);
    Array.prototype.push.apply(args, anonArgs);
    return fn.apply(this, args);
  };
}

示例

// 实现一个类型判断
function isType(type: string, val: any) {
  return Object.prototype.toString.call(val) === `[object ${type}]`;
}

let isString = partial(isType, "String");
let isArray = partial(isType, "Array");
let isDate = partial(isType, "Date");

isString("chengbotao"); // true
isArray([]); // true
isDate(new Date()); // true

deepClone

深克隆

参数名参数类型参数说明
targetany要克隆的对象
mapWeakMap<object, any>用来存对象做循环引用的判断

实现

interface DuckTyping {
  [key: string]: any;
}

export function deepClone(target: any, map = new WeakMap()): any {
  if (target === null || typeof target !== "object") {
    return target;
  }

  if (map.get(target)) {
    return target;
  }

  const Ctor = target.constructor;
  const ctorName = Ctor.name;
  if (/^(RegExp|Date|Number|String|Boolean|Error)$/i.test(ctorName)) {
    return new Ctor(target);
  }

  if (ctorName === "Symbol") {
    return Object(Object.prototype.valueOf.call(target));
  }

  if (ctorName === "Map") {
    let cloneMap = new Map();
    map.set(target, true);
    target.forEach((value, key) => {
      cloneMap.set(deepClone(key, map), deepClone(value, map));
    });
    return cloneMap;
  }

  if (ctorName === "Set") {
    let cloneSet = new Set();
    map.set(target, true);

    target.forEach((value) => {
      cloneSet.add(deepClone(value, map));
    });
    return cloneSet;
  }

  map.set(target, true);

  let cloneResult: DuckTyping =
    Object.prototype.toString.call(target) === "[object Array]" ? [] : {};

  Object.getOwnPropertyNames(target).forEach((key) => {
    cloneResult[key] = deepClone(target[key], map);
  });

  return cloneResult;
}

示例

const map = new Map();
map.set("name", "botaoxy");

const set = new Set();
set.add("billows");
set.add("botaoxy");

const obj = {
  field: 1,
  fieldUn: undefined,
  fieldObj: {
    age: 28,
  },
  fieldArr: [2, 4, 8],
  empty: null,
  map,
  set,
  bool: new Boolean(true),
  num: new Number(2),
  str: new String(2),
  symbol: Object(Symbol(1)),
  date: new Date(),
  reg: /\d+/,
  error: new Error(),
  fun: () => {
    console.log("Hello Botaoxy!");
  },
  fun1: function (a, b) {
    return a + b;
  },
};

const copy = deepClone(obj);

flatToTree

扁平数据结构转树状结构

参数名参数类型参数说明
targetany[]扁平化数据结构
options?Partial<Record<'idKey' | 'pidKey' | 'childrenKey' | 'topVal', string>>树形结构关键 keys(唯一标志、 父级id、 子集 keytopVal顶层值)

实现

interface DuckTyping {
  [key: string]: any;
}

export function flatToTree(
  target: any[],
  options?: Partial<
    Record<"idKey" | "pidKey" | "childrenKey" | "topVal", string>
  >
) {
  const copyFlat = Array.prototype.slice.call(target);
  const record: DuckTyping = Object.create(null);
  const defaultOpts = Object.assign(
    {
      idKey: "id",
      pidKey: "pid",
      childrenKey: "children",
      topVal: "",
    },
    options
  );
  const { idKey, pidKey, childrenKey, topVal } = defaultOpts;
  const tree = [];
  for (let i = 0, len = copyFlat.length; i < len; i++) {
    const item = copyFlat[i];
    const { [idKey]: idVal, [pidKey]: pidVal } = item;
    if (record[idVal]) {
      item[childrenKey] = record[idVal];
    } else {
      item[childrenKey] = record[idVal] = [];
    }

    if (pidVal && pidVal !== topVal) {
      if (!record[pidVal]) {
        record[pidVal] = [];
      }
      record[pidVal].push(item);
    } else {
      tree.push(item);
    }
  }
  return tree;
}

示例

const provinceFlat = [
  {
    id: "1000",
    label: "山西省",
  },
  {
    id: "1001",
    pid: "1000",
    label: "太原市",
  },
  {
    id: "1020",
    pid: "1000",
    label: "运城市",
  },
  {
    id: "1022",
    pid: "1000",
    label: "大同市",
  },
  {
    id: "1034",
    pid: "1000",
    label: "长治市",
  },
  {
    id: "1003",
    pid: "1000",
    label: "临汾市",
  },
  {
    id: "100101",
    pid: "1001",
    label: "小店区",
  },
  {
    id: "100102",
    pid: "1001",
    label: "迎泽区",
  },
  {
    id: "100103",
    pid: "1001",
    label: "万柏林区",
  },
  {
    id: "1200",
    label: "北京市",
  },
  {
    id: "1201",
    pid: "1200",
    label: "朝阳区",
  },
  {
    id: "1202",
    pid: "1200",
    label: "顺义区",
  },
  {
    id: "1204",
    pid: "1200",
    label: "昌平区",
  },
];

const provinceList = flatToTree(provinceFlat);

treeToFlat

树状结构扁平化

参数名参数类型参数说明
targetany[] & DuckTyping树结构对象数组或一个树结构对象
subsetKeystring树形子集的键名(默认是children)

实现

interface DuckTyping {
  [key: string]: any;
}

export function treeToFlat(
  target: any[] & DuckTyping,
  subsetKey: string = "children"
) {
  const copyTree =
    Object.prototype.toString.call(target) === "[object Array]"
      ? Array.prototype.slice.call(target)
      : [target];

  const flat = [];

  while (copyTree.length) {
    const node = copyTree.shift();
    const { [subsetKey]: children, ...rest } = node;
    flat.push(rest);
    if (children) {
      Array.prototype.push.apply(copyTree, node.children);
    }
  }

  return flat;
}

示例

const provinceList = [
  {
    id: "1000",
    label: "山西省",
    children: [
      {
        id: "1001",
        pid: "1000",
        label: "太原市",
        children: [
          { id: "100101", pid: "1001", label: "小店区" },
          { id: "100102", pid: "1001", label: "迎泽区" },
          { id: "100103", pid: "1001", label: "万柏林区" },
        ],
      },
      { id: "1020", pid: "1000", label: "运城市" },
      { id: "1022", pid: "1000", label: "大同市" },
      { id: "1034", pid: "1000", label: "长治市" },
      { id: "1003", pid: "1000", label: "临汾市" },
    ],
  },
  {
    id: "1200",
    label: "北京市",
    children: [
      { id: "1201", pid: "1200", label: "朝阳区" },
      { id: "1202", pid: "1200", label: "顺义区" },
      { id: "1204", pid: "1200", label: "昌平区" },
    ],
  },
];

const provinceFlat = treeToFlat(provinceList);

getNodeFromTree

获取树状结构的节点

参数名参数类型参数说明
targetany[] & DuckTyping树结构对象数组或一个树结构对象
markany要找的节点的标志
options?Partial<Record<'idKey' | 'childrenKey', string>>标志的 key 、子集的 key

实现

interface DuckTyping {
  [key: string]: any;
}

export function getNodeFromTree(
  target: any[] & DuckTyping,
  mark: any,
  options?: Partial<Record<"idKey" | "childrenKey", string>>
) {
  const copyTree =
    Object.prototype.toString.call(target) === "[object Array]"
      ? Array.prototype.slice.call(target)
      : [target];
  const defaultOpts = Object.assign(
    {
      idKey: "id",
      childrenKey: "children",
    },
    options
  );
  const { idKey, childrenKey } = defaultOpts;

  while (copyTree.length) {
    const node = copyTree.shift();
    if (node[idKey] === mark) {
      return node;
    }
    if (node[childrenKey]) {
      Array.prototype.push.apply(copyTree, node.children);
    }
  }
  return null;
}

示例

const provinceList = [
  {
    id: "1000",
    label: "山西省",
    children: [
      {
        id: "1001",
        pid: "1000",
        label: "太原市",
        children: [
          { id: "100101", pid: "1001", label: "小店区" },
          { id: "100102", pid: "1001", label: "迎泽区" },
          { id: "100103", pid: "1001", label: "万柏林区" },
        ],
      },
      { id: "1020", pid: "1000", label: "运城市" },
      { id: "1022", pid: "1000", label: "大同市" },
      { id: "1034", pid: "1000", label: "长治市" },
      { id: "1003", pid: "1000", label: "临汾市" },
    ],
  },
  {
    id: "1200",
    label: "北京市",
    children: [
      { id: "1201", pid: "1200", label: "朝阳区" },
      { id: "1202", pid: "1200", label: "顺义区" },
      { id: "1204", pid: "1200", label: "昌平区" },
    ],
  },
];

const province1201 = getNodeFromTree(provinceList, "1201");
// {
//     id: "1201",
//     pid: "1200",
//     label: "朝阳区"
// };

getValueByReference

根据对象的引用获取值

参数名参数类型参数说明
targetany要设置值的对象
referstring|string[]对象的引用路径

实现

export function getValueByReference(
  target: any,
  refer: string | string[]
): any {
  const refers: string[] =
    typeof refer === "string"
      ? (refer as string).split(".")
      : (refer as string[]);

  return refers.reduce((obj, key) => {
    return obj && obj[key];
  }, target);
}

示例

const obj = {
  a: {
    b: {
      c: "botaoxy",
    },
  },
};

const val = getValueByReference(obj, "a.b.c"); 
console.log(val) // "botaoxy"

setValueByReference

根据对象的引用设置值

参数名参数类型参数说明
targetany要设置值的对象
referstring|string[]对象的引用路径
valany要设置的值

实现

export function setValueByReference(
  target: any,
  refer: string | string[],
  val: any
): any {
  const refers: string[] =
    typeof refer === "string"
      ? (refer as string).split(".")
      : (refer as string[]);

  return (
    (refers.slice(0, -1).reduce((obj, key) => {
      return (obj[key] = obj[key] || {});
    }, target)[refers.pop() as string] = val),
    target
  );
}

示例

const obj = {};

setValueByReference(obj, "a.b.c", "botaoxy");
console.log(obj);
// {
//   a: {
//     b: {
//       c: "botaoxy"
//     }
//   }
// }

后记

个人博客 | Botaoxy (chengbotao.github.io)
chengbotao (Chengbotao) (github.com)
billows - npm
chengbotao/billows: 使用 TypeScript 收集总结常用的工具函数

感谢阅读,敬请斧正!


我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿