import { unionArray } from './array.js'
/**
* @param {any} value
* @returns {boolean}
*/
export function isUndefined(value) {
return typeof value === 'undefined'
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNull(value) {
return value === null
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNullish(value) {
return isUndefined(value) || isNull(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNone(value) {
return isNullish(value) || isNaN(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isArray(value) {
return Array.isArray(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isObject(value) {
return value && typeof value === 'object' && value.constructor === Object
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isDate(value) {
return isInstanceOf(value, Date)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isString(value) {
return typeof value === 'string'
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNumber(value) {
return typeof value === 'number' && !isNaN(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNaN(value) {
return typeof value === 'number' && Number.isNaN(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isSymbol(value) {
return typeof value === 'symbol'
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isFinite(value) {
return typeof value === 'number' && Number.isFinite(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isInfinite(value) {
return typeof value === 'number' && !Number.isNaN(value) && !Number.isFinite(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isBoolean(value) {
return value === true || value === false
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isNumeric(value) {
return isString(value) && /^\-{0,1}[0-9]+\.{0,1}([0-9]+){0,1}$/.test(value)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isBlob(value) {
return isInstanceOf(value, Blob)
|| (
value
&& typeof value.size === 'number'
&& typeof value.type === 'string'
)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isFile(value) {
return isInstanceOf(value, File)
|| (
isBlob(value)
&& (typeof value.lastModifiedDate === 'object' || typeof value.lastModified === 'number')
&& typeof value.name === 'string'
)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isFormData(value) {
return isInstanceOf(value, FormData)
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isEmpty(value) {
if (isNone(value) || value === '' || isNaN(value)) {
return true
}
else if (isArray(value)) {
return value.length === 0
}
else if (isObject(value)) {
return Object.getOwnPropertyNames(value).length === 0
}
else {
return false
}
}
/**
* @param {any} value
* @param {boolean} [isStrict] where Constructor is to return false
* @returns {boolean}
*/
export function isFunction(value, isStrict) {
if (typeof value !== 'function') {
return false
}
return isStrict ? !isConstructor(value, 2) : true
}
/**
* judge whether a value is a Constructor
* @param {any} f
* @param {number} [strict] strict level
* - 4: should must be one of native code, native class
* - 3: can be babel transformed class
* - 2: can be some function whose prototype has more than one properties
* - 1: can be some function which has this inside
* - 0: can be some function which has prototype
* @returns {boolean}
*/
export function isConstructor(f, strict) {
if (typeof f !== 'function') {
return false
}
if (f === Symbol) {
return false
}
// bond function && arrow function
if (!f.prototype) {
return false
}
const entire = f + ''
const fnBody = entire.slice(entire.indexOf("{") + 1, entire.lastIndexOf("}")).trim()
// native class definition
const isNativeClass = entire.indexOf('class ') === 0
// std lib: String, Number...
const isNativeSTD = fnBody === `[native code]`
const level4 = isNativeClass || isNativeSTD
if (strict >= 4) {
return level4
}
const topCtx = fnBody.replace(/function.*?\{.*?\}/gm, '')
.replace(/return/gm, '')
.replace(/\n+/gm, ';')
.replace(/\s+/gm, '')
.replace(/;;/gm, ';')
// babel transformed class, begin with '_classCallCheck(this,', may by minified by compile tool
const isBabelTransformedClass = /^_classCallCheck\(this,/.test(topCtx)
// @babel/plugin-transform-runtime '(0, _classCallCheck2["default"])(this,'
const isBabelRuntimeTransformedClass = /^\(.*?_classCallCheck.*?\)\(this,/.test(topCtx)
// webpack minified
const isBabelTransformedMinifiedClass = /^[0-9a-zA-Z_;!?:]*?\(this,/.test(topCtx)
const level3 = level4 || isBabelTransformedClass || isBabelRuntimeTransformedClass || isBabelTransformedMinifiedClass
if (strict == 3) {
return level3
}
// there are some properties on f.prototype
const protos = Object.getOwnPropertyDescriptors(f.prototype)
const keys = Object.keys(protos).filter(item => item !== 'constructor')
const hasProtos = !!keys.length
const level2 = level3 || hasProtos
if (strict == 2) {
return level2
}
// function() { this.name = 'xx' }
const hasThisInside = topCtx.indexOf('this.') === 0 || topCtx.indexOf(';this.') > -1 || topCtx.indexOf('=this;') > -1
const level1 = level2 || hasThisInside
if (strict == 1) {
return level1
}
return true
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isTruthy(value) {
return !!value
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isFalsy(value) {
return !value
}
/**
* @param {any} val1
* @param {any} val2
* @returns {boolean}
*/
export function isEqual(val1, val2) {
const equal = (obj1, obj2) => {
const keys1 = Object.getOwnPropertyNames(obj1)
const keys2 = Object.getOwnPropertyNames(obj2)
const keys = unionArray(keys1, keys2)
for (let i = 0, len = keys.length; i < len; i ++) {
const key = keys[i]
if (!inArray(key, keys1)) {
return false
}
if (!inArray(key, keys2)) {
return false
}
const value1 = obj1[key]
const value2 = obj2[key]
if (!isEqual(value1, value2)) {
return false
}
}
return true
}
if (isObject(val1) && isObject(val2)) {
return equal(val1, val2)
}
else if (isArray(val1) && isArray(val2)) {
return equal(val1, val2)
}
else {
return val1 === val2
}
}
/**
* @param {any} a
* @param {any} b
* @returns {boolean}
*/
export function isLt(a, b) {
return a < b
}
/**
* @param {any} a
* @param {any} b
* @returns {boolean}
*/
export function isLte(a, b) {
return a <= b
}
/**
* @param {any} a
* @param {any} b
* @returns {boolean}
*/
export function isGt(a, b) {
return a > b
}
/**
* @param {any} a
* @param {any} b
* @returns {boolean}
*/
export function isGte(a, b) {
return a >= b
}
/**
* @param {any} value
* @returns {boolean}
*/
export function isPromise(value) {
return isInstanceOf(value, Promise)
|| (
value
&& (typeof value === 'object' || typeof value === 'function')
&& typeof value.then === 'function'
)
}
/**
* @param {any} value
* @param {any} Constructor
* @param {boolean} [isStrict]
* @returns {boolean}
*/
export function isInstanceOf(value, Constructor, isStrict) {
if (!value || typeof value !== 'object') {
return false
}
if (isStrict) {
return value.constructor === Constructor
}
else {
return value instanceof Constructor
}
}
/**
* @param {any} SubConstructor
* @param {any} Constructor
* @param {boolean} [isStrict]
* @returns {boolean}
*/
export function isInheritedOf(SubConstructor, Constructor, isStrict) {
if (typeof SubConstructor !== 'function') {
return false
}
const ins = SubConstructor.prototype
if (!ins) {
return false
}
return isInstanceOf(ins, Constructor, isStrict)
}
/**
* check wether a property is the given object's own property,
* it will check:
* - only string properties (except symbol properties, different from hasOwnKey),
* - only enumerable properties;
* @param {string} key
* @param {object} obj
* @param {boolean} [own] use hasOwnKey to check
* @returns {boolean}
*/
export function inObject(key, obj, own) {
if (!obj || typeof obj !== 'object') {
return false
}
if (own) {
return hasOwnKey(obj, key)
}
return typeof key !== 'symbol' && Object.prototype.propertyIsEnumerable.call(obj, key)
}
/**
* check wether a property is the given object's own property,
* as default, it will check:
* - both string and symbol properties (different from inObject),
* - both enumerable and non-enumerable properties;
* @param {object|array} obj
* @param {string} key
* @param {boolean} [enumerable]
* @returns {boolean}
*/
export function hasOwnKey(obj, key) {
if (!obj || typeof obj !== 'object') {
return false
}
return Object.prototype.hasOwnProperty.call(obj, key)
}
/**
* @param {any} item
* @param {array} arr
* @returns {boolean}
*/
export function inArray(item, arr) {
return isArray(arr) && arr.includes(item)
}
/**
* @param {object} objA
* @param {object} objB
* @param {number} [deepth] how many deepth to check
* @returns {boolean}
*/
export function isShallowEqual(objA, objB, deepth){
if(objA === objB) {
return true
}
// not object. number, string, boolean, null
if(!(typeof objA === 'object' && objA !== null) || !(typeof objB === 'object' && objB !== null)) {
return false
}
// object vs. array
if ([objA, objB].filter(item => isArray(item)).length === 1) {
return false
}
const keysA = Object.keys(objA).sort()
const keysB = Object.keys(objB).sort()
if (keysA.length !== keysB.length) {
return false
}
for (let i = 0; i < keysA.length; i ++) {
const keyA = keysA[i]
const keyB = keysB[i]
if (keyA !== keyB) {
return false
}
const key = keyA
if (objA[key] !== objB[key]) {
if (deepth && typeof objA[key] === 'object' && typeof objB[key] === 'object') {
if (!isShallowEqual(objA[key], objB[key], deepth - 1)) {
return false
}
}
else {
return false
}
}
}
return true
}
/**
* is one of items in array arr
* @param {any[]} items
* @param {any[]} arr
* @returns {boolean}
*/
export function isOneInArray(items, arr) {
return arr.some(one => items.includes(one))
}
/**
* is all items in array arr
* @param {any[]} items
* @param {any[]} arr
* @returns {boolean}
*/
export function isAllInArray(items, arr) {
return !arr.some(one => !items.includes(one))
}
/**
* all items in shortArr are in longArr
* may be the same with isAllInArray
* @param {*} shortArr
* @param {*} longArr
* @returns {boolean}
*/
export function isArrayInArray(shortArr, longArr) {
return shortArr.every(item => longArr.includes(item))
}