/**
* 对数字进行补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)
},
},
})