JavaScript的kind-of库类型判断解读

125 阅读3分钟

说明

该库是用于js类型判断的。

使用

const kindOf = require('kind-of');

kindOf(undefined);
//=> 'undefined'

kindOf(null);
//=> 'null'

kindOf(true);
//=> 'boolean'

kindOf(false);
//=> 'boolean'

kindOf(new Buffer(''));
//=> 'buffer'

kindOf(42);
//=> 'number'

kindOf('str');
//=> 'string'

kindOf(arguments);
//=> 'arguments'

kindOf({});
//=> 'object'

kindOf(Object.create(null));
//=> 'object'

kindOf(new Test());
//=> 'object'

kindOf(new Date());
//=> 'date'

kindOf([1, 2, 3]);
//=> 'array'

kindOf(/foo/);
//=> 'regexp'

kindOf(new RegExp('foo'));
//=> 'regexp'

kindOf(new Error('error'));
//=> 'error'

kindOf(function () {});
//=> 'function'

kindOf(function * () {});
//=> 'generatorfunction'

kindOf(Symbol('str'));
//=> 'symbol'

kindOf(new Map());
//=> 'map'

kindOf(new WeakMap());
//=> 'weakmap'

kindOf(new Set());
//=> 'set'

kindOf(new WeakSet());
//=> 'weakset'

kindOf(new Int8Array());
//=> 'int8array'

kindOf(new Uint8Array());
//=> 'uint8array'

kindOf(new Uint8ClampedArray());
//=> 'uint8clampedarray'

kindOf(new Int16Array());
//=> 'int16array'

kindOf(new Uint16Array());
//=> 'uint16array'

kindOf(new Int32Array());
//=> 'int32array'

kindOf(new Uint32Array());
//=> 'uint32array'

kindOf(new Float32Array());
//=> 'float32array'

kindOf(new Float64Array());
//=> 'float64array'

源码解读

值类型(基本类型)(7) :字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol、bigint。

引用数据类型(对象类型) :对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。

基础类型

  if (val === void 0) return 'undefined';
  if (val === null) return 'null';

  var type = typeof val;
  // typeof 可以 undefined、string、boolean、number、symbol,bigint、function
  if (type === 'boolean') return 'boolean';
  if (type === 'string') return 'string';
  if (type === 'number') return 'number';
  if (type === 'symbol') return 'symbol';
  if (type === 'bigint') return 'bigint';

function

函数分为普通函数与generator函数;

function ctorName(val) {
  return typeof val.constructor === 'function' ? val.constructor.name : null;
}

function  isGeneratorFn(name, val) {
  return ctorName(name) === 'GeneratorFunction';
}

if (type === 'function') {
  
  return isGeneratorFn(val) ? 'generatorfunction' : 'function';
}

generotor

var getGeneratorFunc = function () { // eslint-disable-line consistent-return
	if (!hasToStringTag) {
		return false;
	}
	try {
		return Function('return function*() {}')();
	} catch (e) {
	}
};
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = getProto && generatorFunc ? getProto(generatorFunc) : false;
var getProto = Object.getPrototypeOf;
var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';

var isFnRegex = /^\s*(?:function)?*/;

function isGeneratorFunction(fn) {
	if (typeof fn !== 'function') {
		return false;
	}
  // 方式1
	if (isFnRegex.test(fnToStr.call(fn))) {
		return true;
	}
	if (!hasToStringTag) {
		var str = toStr.call(fn);
    // 方式2
		return str === '[object GeneratorFunction]';
	}
  // 方式3
	return getProto && getProto(fn) === GeneratorFunction;
};

生成器


// 1.
function isGeneratorObj(val) {
  return typeof val.throw === 'function'
    && typeof val.return === 'function'
    && typeof val.next === 'function';
}

// 2. Object.prototype.toString.call(g) '[object Generator]'


var gen = function* gen(){
  try {
    yield console.log('a');
  } catch (e) {
    // ...
  }
  yield console.log('b');
  yield console.log('c');
}

var g = gen();
g.next() // a
g.throw() // b
g.next() // c

数组

function isArray(val) {
  // 1.
  if (Array.isArray) return Array.isArray(val);
  //2.
  return val instanceof Array;
}

//3. 
[].constructor === Array

//4.
Object.prototype.toString.call([]) // '[object Array]'

arguments

// 1. 
function isArguments(val) {
  try {
    if (typeof val.length === 'number' && typeof val.callee === 'function') {
      return true;
    }
  } catch (err) {
    if (err.message.indexOf('callee') !== -1) {
      return true;
    }
  }
  return false;
}



// 2.
function test(){
    // [object Arguments]
    console.log('kindOf(arguments)', Object.prototype.toString.call(arguments))

}

test()

Date

function isDate(val) {
  // 1. 
  if (val instanceof Date) return true;
  // 2.
  return typeof val.toDateString === 'function'
    && typeof val.getDate === 'function'
    && typeof val.setDate === 'function';
}

// 3.
Object.prototype.toString.call(new Date) // '[object Date]'

// 4. 
data = new Date()
data.constructor.name === 'Date'

 

Error

function isError(val) {
  return 
  // 1.
  val instanceof Error 
  // 2. 
  || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number');
}

// 3. Object.prototype.toString.call(err)

RegExp

function isRegexp(val) {
  if (val instanceof RegExp) return true;
  return typeof val.flags === 'string'
    && typeof val.ignoreCase === 'boolean'
    && typeof val.multiline === 'boolean'
    && typeof val.global === 'boolean';
}

// 3. Object.prototype.toString.call(/123/g)  // '[object RegExp]'

constructor类型判断


function ctorName(val) {
  return typeof val.constructor === 'function' ? val.constructor.name : null;
}

switch (ctorName(val)) {
    case 'Symbol': return 'symbol';
    case 'Promise': return 'promise';

    // Set, Map, WeakSet, WeakMap
    case 'WeakMap': return 'weakmap';
    case 'WeakSet': return 'weakset';
    case 'Map': return 'map';
    case 'Set': return 'set';

    // 8-bit typed arrays
    case 'Int8Array': return 'int8array';
    case 'Uint8Array': return 'uint8array';
    case 'Uint8ClampedArray': return 'uint8clampedarray';

    // 16-bit typed arrays
    case 'Int16Array': return 'int16array';
    case 'Uint16Array': return 'uint16array';

    // 32-bit typed arrays
    case 'Int32Array': return 'int32array';
    case 'Uint32Array': return 'uint32array';
    case 'Float32Array': return 'float32array';
    case 'Float64Array': return 'float64array';
  }

tostring类型判断

  // Non-plain objects
  type = toString.call(val);
  switch (type) {
    case '[object Object]': return 'object';
    // iterators
    case '[object Map Iterator]': return 'mapiterator';
    case '[object Set Iterator]': return 'setiterator';
    case '[object String Iterator]': return 'stringiterator';
    case '[object Array Iterator]': return 'arrayiterator';
  }

map = new Map()
Object.prototype.toString.call(map.keys()) // '[object Map Iterator]'

  // other
  return type.slice(8, -1).toLowerCase().replace(/\s/g, '');