/** * 对数字进行补0操作 * @param value 要补0的数值 * @param length 要补的总长度 * @return 补0之后的字符串 */ function zeroize(value: number | string, length: number = 2): string { let str = '' + value let zeros = '' for (let i = 0, len = length - str.length; i < len; i++) { zeros += '0' } return zeros + str } /****************************************扩展Object****************************************/ interface Object { /** * 返回一个浅副本的对象 * 此对象会拷贝key value * * @memberOf Object */ clone?(): Object /** * 将数据拷贝到 to * @param to 目标 */ copyto?(to: Object): void /** * 获取指定属性的描述,会查找当前数据和原型数据 * @param property 指定的属性名字 */ getPropertyDescriptor?(property: string): PropertyDescriptor zssign?(target: any): any } Object.defineProperties(Object.prototype, { clone: { value: function () { let o = {} for (let n in this) { // @ts-ignore o[n] = this[n] } return o }, writable: true, }, getPropertyDescriptor: { value: function (property: string): any { let data = Object.getOwnPropertyDescriptor(this, property) if (data) { return data } let prototype = Object.getPrototypeOf(this) if (prototype) { return prototype.getPropertyDescriptor(property) } return }, writable: true, }, copyto: { value: function (to: Object) { for (let p in this) { if (!(p in to)) { // 本身没有这个属性 // @ts-ignore to[p] = this[p] } else { let data: PropertyDescriptor = to.getPropertyDescriptor(p) if (data) { if (data.set || data.writable) { // 可进行赋值 // @ts-ignore to[p] = this[p] } } } } }, writable: true, }, zssign: { value: function (target: Object) { if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object') } let output = Object(target) for (let nextKey in this) { if (!(nextKey in output)) { // 本身没有这个属性 output[nextKey] = this[nextKey] } // else { // let data: PropertyDescriptor = output.getPropertyDescriptor(nextKey); // if (data) { // if (data.set || data.writable) {// 可进行赋值 // output[nextKey] = this[nextKey]; // } // } // } } return output }, writable: true, }, }) /****************************************扩展Math****************************************/ interface Math { /** * 角度转弧度的乘数 * Math.PI / 180 * @type {number} * @memberOf Math */ DEG_TO_RAD: number /** * 弧度转角度的乘数 * 180 / Math.PI */ RAD_TO_DEG: number /** * 整圆的弧度 */ PI2: number /** * 90°的弧度 * * @type {number} * @memberOf Math */ PI_1_2: number /** * 让数值处于指定的最大值和最小值之间,低于最小值取最小值,高于最大值取最大值 * @param value 要处理的数值 * @param min 最小值 * @param max 最大值 */ clamp?(value: number, min: number, max: number): number /** * 从最小值到最大值之间随机[min,max) */ random2?(min: number, max: number): number } Math.DEG_TO_RAD = Math.PI / 180 Math.RAD_TO_DEG = 180 / Math.PI Math.PI2 = 2 * Math.PI Math.PI_1_2 = Math.PI * 0.5 Math.clamp = (value, min, max) => { if (value < min) { value = min } if (value > max) { value = max } return value } Math.random2 = (min, max) => { return min + Math.random() * (max - min) } /****************************************扩展Number********************************************/ interface Number { /** * 对数字进行补0操作 * @param length 要补的总长度 * @return 补0之后的字符串 */ zeroize?(length: number): string /** * 数值介于,`min` `max`直接,包含min,max * 即:[min,max] * * @param {number} min * @param {number} max * @returns {boolean} */ between?(min: number, max: number): boolean } Object.defineProperties(Number.prototype, { zeroize: { value: function (this: number, length: number) { return zeroize(this, length) }, writable: true, }, between: { value: function (this: number, min: number, max: number) { return min <= this && max >= this }, writable: true, }, }) /****************************************扩展String****************************************/ interface String { /** * 替换字符串中{0}{1}{2}{a} {b}这样的数据,用obj对应key替换,或者是数组中对应key的数据替换 */ substitute?(args: any[]): string /** * 对数字进行补0操作 * @param length 要补的总长度 * @return 补0之后的字符串 */ zeroize?(length: number): string /** * 将一个字符串转换成一个很小几率重复的数值 * 此方法hash的字符串并不一定唯一,慎用 */ hash?(): number /** * 获取字符串长度,中文方块字符算两个长度 */ trueLength?(): number /** * 中文字符个数 * */ cnLength?(): number /** * 比较版本号 * */ versionCompare?(target: string): number } Object.defineProperties(String.prototype, { zeroize: { value: function (length: number) { return zeroize(this, length) }, writable: true, }, substitute: { value: function (this: string) { let len = arguments.length if (len > 0) { let obj: IArguments if (len == 1) { obj = arguments[0] if (typeof obj !== 'object') { obj = arguments } } else { obj = arguments } if (obj instanceof Object && !(obj instanceof RegExp)) { return this.replace(/\{(?:%([^{}]+)%)?([^{}]+)\}/g, function (match: string, handler: string, key: string) { //检查key中,是否为%开头,如果是,则尝试按方法处理 // @ts-ignore let value = obj[key] if (handler) { //如果有处理器,拆分处理器 let func = String.subHandler[handler] if (func) { value = func(value) } } return value !== undefined ? '' + value : match }) } } return this.toString() //防止生成String对象,ios反射String对象会当成一个NSDictionary处理 }, writable: true, }, hash: { value: function () { let len = this.length let hash = 5381 for (let i = 0; i < len; i++) { hash += (hash << 5) + this.charCodeAt(i) } return hash & 0x7fffffff }, writable: true, }, trueLength: { value: function () { let arr: string[] = this.match(/[^x00-xff]/gi) return this.length + (arr ? arr.length : 0) }, writable: true, }, cnLength: { value: function () { // /[\u2E80-\u9FBF] let arr: string[] = this.match(/[^x00-xff]/gi) return arr ? arr.length : 0 }, writable: true, }, versionCompare: { value: function (target: string): number { const GTR = 1 //大于 const LSS = -1 //小于 const EQU = 0 //等于 if (!target) { return GTR } let v1arr = String(this) .split('.') .map(function (a) { return parseInt(a) }) let v2arr = String(target) .split('.') .map(function (a) { return parseInt(a) }) let arrLen = Math.max(v1arr.length, v2arr.length) let result //排除错误调用 if (this == undefined || target == undefined) { throw new Error() } //检查空字符串,任何非空字符串都大于空字符串 if (this.length == 0 && target.length == 0) { return EQU } else if (this.length == 0) { return LSS } else if (target.length == 0) { return GTR } //循环比较版本号 for (let i = 0; i < arrLen; i++) { result = versionComp(v1arr[i], v2arr[i]) if (result == EQU) { continue } else { break } } return result function versionComp(n1: number, n2: number) { if (typeof n1 != 'number') { n1 = 0 } if (typeof n2 != 'number') { n2 = 0 } if (n1 > n2) { return GTR } else if (n1 < n2) { return LSS } else { return EQU } } }, writable: true, }, }) interface StringConstructor { /** * 对数字进行补0操作 * @param value 要补0的数值 * @param length 要补的总长度 * @return 补0之后的字符串 */ zeroize?: (value: number, length: number) => string /** * substitute的回调函数 * * @type {Readonly<{ [index: string]: { (input: any): string } }>} * @memberOf StringConstructor */ subHandler?: Readonly<{ [index: string]: { (input: any): string } }> } String.zeroize = zeroize /****************************************扩展Date****************************************/ interface Date { /** * 格式化日期 * * @param {string} mask 时间字符串 * @param {boolean} [local] 是否基于本地时间显示,目前项目,除了报错信息,其他时间都用UTC时间显示 * @returns {string} 格式化后的时间 */ format(mask: string, local?: boolean): string /** * 增加n天 * @param {number} days * @return {Date} */ addDays(days: number): Date } Object.defineProperties(Date.prototype, { format: { value: function (mask: string, local?: boolean) { let d: Date = this // @ts-ignore return mask.replace(/"[^"]*"|'[^']*'|(?:d{1,2}|m{1,2}|yy(?:yy)?|([hHMs])\1?)/g, function ($0: string) { switch ($0) { case 'd': return gd() case 'dd': return zeroize(gd()) case 'M': return gM() + 1 case 'MM': return zeroize(gM() + 1) case 'yy': return (gy() + '').substr(2) case 'yyyy': return gy() case 'h': return gH() % 12 || 12 case 'hh': return zeroize(gH() % 12 || 12) case 'H': return gH() case 'HH': return zeroize(gH()) case 'm': return gm() case 'mm': return zeroize(gm()) case 's': return gs() case 'ss': return zeroize(gs()) default: return $0.substr(1, $0.length - 2) } }) function gd() { return local ? d.getDate() : d.getUTCDate() } function gM() { return local ? d.getMonth() : d.getUTCMonth() } function gy() { return local ? d.getFullYear() : d.getUTCFullYear() } function gH() { return local ? d.getHours() : d.getUTCHours() } function gm() { return local ? d.getMinutes() : d.getUTCMinutes() } function gs() { return local ? d.getSeconds() : d.getUTCSeconds() } }, writable: true, }, addDays: { value: function (days: number) { this.setDate(this.getDate() + days) return this }, writable: true, }, }) /****************************************扩展Array****************************************/ const enum ArraySort { /** * 升序 */ ASC = 0, /** * 降序 */ DESC = 1, } interface ArrayConstructor { // binaryInsert(partArr: T[], item: T, filter: { (tester: T, ...args): boolean }, ...args); SORT_DEFAULT: { number: 0; string: ''; boolean: false } } /** * 用于对Array排序时,处理undefined */ Array.SORT_DEFAULT = { number: 0, string: '', boolean: false, } Object.freeze(Array.SORT_DEFAULT) interface Array { /** * 如果数组中没有要放入的对象,则将对象放入数组 * * @param {T} t 要放入的对象 * @returns {number} 放入的对象,在数组中的索引 * * @member Array */ pushOnce?(t: T): number /** * * 删除某个数据 * @param {T} t * @returns {boolean} true 有这个数据并且删除成功 * false 没有这个数据 */ zremove?(t: T): boolean /** * 排序 支持多重排序 * 降序, 升序 * @param {(keyof T)[]} kArr 参数属性列表 * @param {(boolean[] | ArraySort[])} [dArr] 是否降序,默认升序 * @returns {this} * * @member Array */ multiSort?(kArr: (keyof T)[], dArr?: boolean[] | ArraySort[]): this /** * 默认排序 * * @param {string} [key] * @param {boolean} [descend] * * @member Array */ doSort?(key?: keyof T, descend?: boolean | ArraySort): this doSort?(descend?: boolean | ArraySort, key?: keyof T): this /** * 将数组克隆到to * to的数组长度会和当前数组一致 * * @template T * @param {Array} to */ cloneTo?(to: Array): void /** * 将数组附加到to中 * * @template T * @param {Array} to * * @member ArrayConstructor */ appendTo?(to: Array): void /** * 移除数组index位置的元素, 比slice效率高 * @param index */ spliceOne?(index: number): boolean /** * 随机排序 */ randomSort?(): void /** * 检查数组中是否含有另外一个object * @param obj 与数组同类型的obj | 同类型的数组 | 指定child字段的值 | 指定child字段的数组 * @param child 比较字段 */ contains?(obj: T | T[] | {} | {}[], child?: string): boolean /** * 将数组随机插入当前数组中 * @param arr */ randomInsert?(arr: Array): void /** * 随机获取n个元素 * @param count */ randomGet?(count?: number): T[] /** * 随机获取1个元素 */ randomOne?(): T /** * 随机移除n个元素 * @param count */ randomRemove?(count?: number): T[] /** * 数组移动n位 * @param n n > 0 右移, n<0 左移 */ moveElement?(n: number): T[] /** * 两个数组并集 * @param arr */ union?(arr: T[]): T[] /** * 两个数组交集 * @param arr */ intersect?(arr: T[]): T[] /** * 相对于arr的差集 * @param arr */ difference?(arr: T[]): T[] /** * 转换成Map * @param {string} key 用于生成map的key字段名 * @return {Map} */ toMap?(key: string): Map /** * 将数组分块 * @param chunkSize */ chunkArray?(chunkSize: number): T[][] } Object.defineProperties(Array.prototype, { cloneTo: { value: function (this: T[], b: any[]) { b.length = this.length let len = this.length b.length = len for (let i = 0; i < len; i++) { b[i] = this[i] } }, writable: true, }, appendTo: { value: function (this: T[], b: any[]) { let len = this.length for (let i = 0; i < len; i++) { b.push(this[i]) } }, writable: true, }, pushOnce: { value: function (this: T[], t: T) { let idx = this.indexOf(t) if (!~idx) { idx = this.length this.push(t) } return idx }, writable: true, }, zremove: { value: function (this: T[], t: T) { let idx = this.indexOf(t) if (~idx) { this.splice(idx, 1) return true } return false }, writable: true, }, doSort: { value: function () { let key: string, descend: boolean let len = arguments.length for (let i = 0; i < len; i++) { let arg = arguments[i] let t = typeof arg if (t === 'string') { key = arg } else { descend = !!arg } } if (key) { return this.sort((a: any, b: any) => (descend ? b[key] - a[key] : a[key] - b[key])) } else { return this.sort((a: any, b: any) => (descend ? b - a : a - b)) } }, writable: true, }, multiSort: { value: function (kArr: string[], dArr?: boolean[] | boolean) { let isArr = Array.isArray(dArr) return this.sort((a: any, b: any): number => { const def = Array.SORT_DEFAULT for (let idx = 0, len = kArr.length; idx < len; idx++) { let key = kArr[idx] // @ts-ignore let mode = isArr ? !!dArr[idx] : !!dArr let av = a[key] let bv = b[key] let typea = typeof av let typeb = typeof bv if (typea == 'object' || typeb == 'object') { return 0 } else if (typea != typeb) { if (typea == 'undefined') { // @ts-ignore bv = def[typeb] } else if (typeb == 'undefined') { // @ts-ignore av = def[typea] } else { return 0 } } if (av < bv) { return mode ? 1 : -1 } else if (av > bv) { return mode ? -1 : 1 } else { continue } } return 0 }) }, writable: true, }, spliceOne: { value: function (index: number): boolean { if (index === -1 || index >= this.length) { return false } const len = this.length - 1 for (let i = index; i < len; i++) { this[i] = this[i + 1] } this.length = len return true }, writable: true, }, randomSort: { value: function () { for (let j, x, i = this.length; i; j = (Math.random() * i) | 0, x = this[--i], this[i] = this[j], this[j] = x) {} }, writable: true, }, contains: { value: function (obj: T | T[] | {} | {}[], child?: string): boolean { let result = false if (child) { const isArr = Array.isArray(obj) if (isArr) { // @ts-ignore if (obj[0].hasOwnProperty(child)) { let set0 = new Set() // @ts-ignore for (let s of obj) { // @ts-ignore set0.add(s[child]) } // @ts-ignore let set1 = new Set(this.filter(x => set0.has(x))) return set0.size === set1.size } else { // @ts-ignore let set0 = new Set(obj) let set1 = new Set(this.filter((x: {}) => set0.has(x))) return set1.size === set0.size } } else { if (obj.hasOwnProperty(child)) { for (let sub of this) { if (sub.hasOwnProperty(child)) { // @ts-ignore if (sub[child] === obj[child]) { result = true break } } } } else { for (let sub of this) { if (sub.hasOwnProperty(child)) { // @ts-ignore if (sub[child] === obj) { result = true break } } } } } } else { // 不指定 比较字段 的话, 只处理2种情况 // 1: obj 为数组 // 2: obj 不是数组 if (Array.isArray(obj)) { let set0 = new Set(obj) // @ts-ignore let set1 = new Set(this.filter(x => set0.has(x))) return set1.size === set0.size } else { let idx = this.indexOf(obj) return !!~idx } } return result }, writable: true, }, randomInsert: { value: function (arr: Array) { const length = this.length arr.forEach(value => { this.splice(Math.random() * length, 0, value) }) }, writable: true, }, randomGet: { value: function (count: number = 1): T[] { let shuffled: T[] = this.slice(0), i = this.length, min = i - count, temp, index if (min < 0) { return shuffled } while (i-- > min) { index = Math.floor((i + 1) * Math.random()) temp = shuffled[index] shuffled[index] = shuffled[i] shuffled[i] = temp } return shuffled.slice(min) }, writable: true, }, randomOne: { value: function (): T { let results = this.randomGet(1) if (results.length > 0) { return results[0] } else { return null } }, writable: true, }, randomRemove: { value: function (count: number = 1): T[] { let result = [] while (count-- > 0 && this.length > 0) { let index = (Math.random() * this.length) | 0 result.push(...this.splice(index, 1)) } return result }, writable: true, }, moveElement: { value: function (n: number): T[] { if (Math.abs(n) > this.length) n = n % this.length return this.slice(-n).concat(this.slice(0, -n)) }, writable: true, }, union: { value: function (this: T[], b: any[]): T[] { let a = this.concat(b) return [...new Set(a)] }, writable: true, }, intersect: { value: function (this: T[], b: any[]): T[] { let set0 = new Set(b) let set1 = new Set(this.filter(x => set0.has(x))) return [...set1] }, writable: true, }, difference: { value: function (this: T[], b: any[]): T[] { let set0 = new Set(b) let set1 = new Set(this.filter(x => !set0.has(x))) return [...set1] }, writable: true, }, toMap: { value: function (this: T[], key: string) { let result: Map = new Map() for (const o of this) { // @ts-ignore result.set(o[key], o) } return result }, writable: true, }, chunkArray: { value: function (this: T[], chunkSize: number): T[][] { const chunks: T[][] = [] for (let i = 0; i < this.length; i += chunkSize) { const chunk = this.slice(i, i + chunkSize) chunks.push(chunk) } return chunks }, writable: true, }, }) interface Map { /** * 只针对V为number的Map, 有值的话, 加上V, 没值则直接set * V为其他类型时, 直接set * @param key * @param value */ inc?(key: K, value: V): number } Object.defineProperties(Map.prototype, { inc: { value: function (key: K, value: V) { if (typeof value == 'number') { this.set(key, (this.get(key) || 0) + value) } else { this.set(key, value) } return this.get(key) }, }, })