typeOf
返回一个 对象或原始值表达式 的类型
参数名 | 参数类型 | 参数说明 |
---|---|---|
operand | any | 一个对象或原始值表达式 |
实现
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
属性是否出现在某个实例对象的原型链上
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any | 检测的构造函数 |
ctor | any | 实例对象 |
实现
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
函数柯里化
参数名 | 参数类型 | 参数说明 |
---|---|---|
fn | any | 需要柯里化函数 |
args | any | fn 函数参数 |
实现
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
偏函数
参数名 | 参数类型 | 参数说明 |
---|---|---|
fn | any | 需要偏函数函数 |
实现
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
深克隆
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any | 要克隆的对象 |
map | WeakMap<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
扁平数据结构转树状结构
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any[] | 扁平化数据结构 |
options? | Partial<Record<'idKey' | 'pidKey' | 'childrenKey' | 'topVal', string>> | 树形结构关键 keys(唯一标志、 父级id 、 子集 key 、 topVal 顶层值) |
实现
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
树状结构扁平化
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any[] & DuckTyping | 树结构对象数组或一个树结构对象 |
subsetKey | string | 树形子集的键名(默认是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
获取树状结构的节点
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any[] & DuckTyping | 树结构对象数组或一个树结构对象 |
mark | any | 要找的节点的标志 |
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
根据对象的引用获取值
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any | 要设置值的对象 |
refer | string |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
根据对象的引用设置值
参数名 | 参数类型 | 参数说明 |
---|---|---|
target | any | 要设置值的对象 |
refer | string |string[] | 对象的引用路径 |
val | any | 要设置的值 |
实现
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 收集总结常用的工具函数
感谢阅读,敬请斧正!
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。