json-treeify
Version:
json-treeify: Get tree string(├└│─) via json, support browser|node, browser none dependencies!
302 lines (293 loc) • 9.03 kB
JavaScript
/**
* Type check
*
* You'd better do not init a primitive type variable via 'new Object'
* is would make somme confused in program!!!
*/
/*
* 获取typeString,结果里会带有'['和']'字符
* @param obj
* @returns {string}
* @private
*/
function _typeString(obj){
return Object.prototype.toString.call(obj);
}
/*
* 获取Type
* @param obj
* @returns {string}
* _getType(1);//"number"
* _getType([]);//"array"
* _getType({});//"object"
* let p1 = new Person("petter");//custom type
* _getType(p1);//"person"
*/
function _getType(obj){
if(obj === null || obj === undefined){
return obj === undefined ? 'undefined' : 'null';// String(obj)
}
let typeName = _typeString(obj).slice(8, -1).toLowerCase();
//将object类型再细化一下,以区分出自定义类型以及细化类型
let tpOf = typeof obj;// 这里要用typeof关键字而非typeName这个结果,不然error的子类会不准确
if(typeName!=='arguments' && (tpOf==='object' || tpOf==='function')){
typeName = (obj.constructor && obj.constructor.name) ? obj.constructor.name.toLowerCase() : typeName;
}
return typeName;
}
/*
* 判断类型函数
* @param type
* @returns {Function}
*/
function _isTypeOf(type){
type = ((typeof type === 'string' || type instanceof String) ? type : '').toLowerCase();
return function(obj){
return type === _getType(obj);
};
}
/*
* 判断类型isXxx(obj)
* @param obj
* @returns {Boolean}
*/
let _Type_ = {};
[
'arguments', 'array', 'date',
'error', 'syntaxError', 'typeError', 'rangeError',
'regExp', 'symbol',
'set', 'weakSet',
'map', 'weakMap'
].forEach((t) => {
_Type_[`is${t[0].toUpperCase()}${t.substr(1)}`] = _isTypeOf(t);
});
let isArguments = _Type_.isArguments;
let isSymbol = _Type_.isSymbol;
let isSet = _Type_.isSet;
let isWeakSet = _Type_.isWeakSet;
let isMap = _Type_.isMap;
let isWeakMap = _Type_.isWeakMap;
function isArray(obj){
return _Type_.isArray(obj) || (typeof Array.isArray !=='undefined' && Array.isArray(obj));
}
// date 和 RegExp的判断必须要在 toString函数执行之前
function isDate(obj){
return obj instanceof Date || _Type_.isDate(obj);
}
function isRegExp(obj){
return obj instanceof RegExp || _Type_.isRegExp(obj);
}
function isError(obj){
return _Type_.isError(obj) || obj instanceof Error;
}
function isSyntaxError(obj){
return _Type_.isSyntaxError(obj) || obj instanceof SyntaxError;
}
function isTypeError(obj){
return _Type_.isTypeError(obj) || obj instanceof TypeError;
}
function isRangeError(obj){
return _Type_.isRangeError(obj) || obj instanceof RangeError;
}
// isObject:这里采用原生关键字typeof判定,即:数组,任何自定义类型,都属于Object
/* eg:
function Person(){};
let p1 = new Person();
isObject(p1);//true
isObject(Person);//false, function
isObject("12aa");//false, string is primitive type
isObject([1,2,3]);//true, array is object via 'typeof'
*/
function isObject(obj) {
return (typeof obj === 'object' || obj instanceof Object) && obj !==null;
}
function isFunction(obj) {
return typeof obj === 'function' || obj instanceof Function;
}
function isNull(obj){
return obj === null;
}
function isUndefined(obj){
return obj === undefined;
}
function isNill(obj){
return (obj === null || obj === undefined);
}
// isUndefinedOrNull
// isNullOrUndefined
function isBoolean(obj){
return obj === true || obj === false || obj instanceof Boolean;
}
function isString(obj) {
return typeof obj === 'string' || obj instanceof String;
}
function isChar(obj){
return isString(obj) && obj.length === 1;
}
function isNumber(obj, warn) {
warn = warn===undefined ? true : !!warn;//default true
// warning when NaN
if(warn && obj !== obj){ // eslint-disable-line
console.warn('obj is NaN. Using \'isRealNumber(obj)\' instead of \'isNumber(obj)\'\nOr using \'isNumber(obj,false)\' to stop warning out\n');
}
return typeof obj === 'number' || obj instanceof Number;
}
function isNaN(obj){
return obj !== obj; // eslint-disable-line
}
function isRealNumber(obj){
return !isNaN(obj) && isNumber(obj);
}
// 检查是否为简单类型:除开function和object之外的所有类型均为简单类型
function isPrimitive(obj){
return !isObject(obj) && !isFunction(obj);
}
// 是否还可以再展开: Object,Array,以及某些Function
function isSpreadable(obj){
if(isArray(obj)){
return !!obj.length;
}else if(isObject(obj) || isFunction(obj)){
for(let j in obj){
if(obj.hasOwnProperty(j)){
return true;
}
}
}
return false;
}
// 递归函数判断是否为json
function _isJSON(value, visited) {
(visited || (visited = [])).push(value);
return isPrimitive(value) ||
(isArray(value) && value.every(element => _isJSON(element, visited))) ||
(isObject.isFlat(value) && Object.keys(value).every((key) => {
let $ = Object.getOwnPropertyDescriptor(value, key);
return ((!isObject($.value) || !~visited.indexOf($.value))
&& !('get' in $) && !('set' in $)
&& _isJSON($.value, visited));
}));
}
// isJSON
function isJSON(obj){
return _isJSON(obj);
}
// isObject.isEmpty
isObject.isEmpty = function(obj, ownCheck){
if(!isObject(obj) && !isArray(obj)){
return false;
}
ownCheck = ownCheck || false;
for(let k in obj){
if(ownCheck){
if(obj.hasOwnProperty(k)){
return false;
}
}else{
return false;
}
}
return true;
};
// isObject.isEmptyOwn: return true if has none own property
isObject.isEmptyOwn = function(obj){
return isObject.isEmpty(obj, true);
};
/*
flat (对象的直接来源 `Object.prototype` or `null`).
eg:
isFlat({}) // true
isFlat(new Type()) // false
*/
isObject.isFlat = function(obj) {
if(isNull(obj)){
return true;
}else if(isObject(obj)){
return null===Object.getPrototypeOf(obj)
|| (null===Object.getPrototypeOf(Object.getPrototypeOf(obj)));
}
return false;
};
// decimal 浮点
isNumber.decimal = function(obj){
return !isNaN(obj) && isNumber(obj) && (obj % 1 !== 0);
};
// integer 整型
isNumber.integer = function(obj){
return !isNaN(obj) && isNumber(obj) && (obj % 1 === 0);
};
// odd 奇数
isNumber.odd = function(obj){
return !isNaN(obj) && isNumber(obj) && (obj % 2 !== 0);//这里判断不使用位操作符&,因为会使得一些特殊数如Infinity的结果不对
};
// even 偶数
isNumber.even = function(obj){
return !isNaN(obj) && isNumber(obj) && (obj % 2 === 0);
};
/*
ECMAScript当对数值应用位操作符时会发生如下转换过程:
64位的数值被转换成32位数值,然后执行位操作,最后再将32位的结果转换回64位数值。这样,表面上看起来就好像是在操作32位数值。
但这个转换过程也导致了一个严重的负效应,即在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0来处理。
如果对非数值进行位操作,会先使用Number()函数将该数值转换成一个数值(自动完成),然后再应用位操作,得到的结果是一个数值。
*/
/*
* query prototype chain to check instanceof
* @param value
* @param Type
* @returns {boolean}
*
* 用法:
* 余原生的instanceof有区别,原生的instanceof对于简单类型的判定都是false
* 而本例中会递归去查询原型链
* isInstanceOf(NaN,Number);// false
*/
// isInstanceOf
function _instOf(value, type) {
// 先用原生instanceof判断
let isInstanceOf = value instanceof type;
// 对于非undefined和非null的value,需要再用构造器名称判断,以避免原生instanceof的判定
let isConstructorNameSame, isConstructorSourceSame;
if (!isInstanceOf && undefined!==value && null!==value) {
isConstructorNameSame = (value.constructor && value.constructor.name === type.name);
isConstructorSourceSame = (value.constructor && String(value.constructor) == String(type));
isInstanceOf = (isConstructorNameSame && isConstructorSourceSame);
isInstanceOf = isInstanceOf || _instOf(Object.getPrototypeOf(value), type);
}
return isInstanceOf;
}
// exports
module.exports = {
typeStr: _typeString,
getTypeOf: _getType,
isTypeOf: _isTypeOf,
isInstanceOf: _instOf,
//
isArguments,
isSymbol,
isSet,
isWeakSet,
isMap,
isWeakMap,
isArray,
isDate,
isRegExp,
isError,
isSyntaxError,
isTypeError,
isRangeError,
isObject,
isFunction,
isNull,
isUndefined,
isNill,
isNullOrUndefined: isNill,
isUndefinedOrNull: isNill,
isBoolean,
isString,
isChar,
isNumber,
isNaN,
isRealNumber,
isPrimitive,
isSpreadable,
isJSON
};