原文: https://fatge.github.io/2019/...
欢迎Star: https://github.com/FatGe/FatG...
JS 的基本数据类型有 Number
,String
,Boolean
,Symbol
,Null
,Undefined
,六种数据类型。一种引用类型 object
。html
数值,根据 ECMAScript 标准,JavaScript 中只有一种数字类型:基于 IEEE 754 标准的双精度 64 位二进制格式的值(-(263 -1) 到 263 -1)。它并无为整数给出一种特定的类型。java
除了可以表示浮点数外,还有一些带符号的值:+Infinity
,-Infinity
和 NaN
(非数值,Not-a-Number)。node
对应 lodash 中的检测函数有git
isNumber
检查 value
是不是原始 Number
数值型 或者 对象;isInteger
检查 value
是否为一个整数;isNaN
检测 value
是否为 NaN
;isFinite
检测 value
是不是原始有限数值。isNumber
function isNumber(value) { return typeof value == 'number' || (isObjectLike(value) && getTag(value) == '[object Number]') }
typeof
操做符能够返回一个字符串,表示未经计算的操做数的类型。对于 Number、String、Boolean、Undefined、String 能够很明确的获得它的类型。github
那么 lodash 为何还要添加 (isObjectLike(value) && getTag(value) == '[object Number]')
?web
缘由在于,JS 中也容许咱们以以下形式建立一个数值数组
const value = new Number(1) console.log(value) // log 1 console.log(typeof value) // log "object"
这时,单单只是使用 typeof
操做符就无法判断 value
的类型是否为数值。因此要结合如下两个函数来判断,value
是否为 object
而后再经过过 toString()
来获取每一个对象的类型。数据结构
function getTag(value) { if (value == null) { return value === undefined ? '[object Undefined]' : '[object Null]' } return Object.prototype.toString.call(value) } function isObjectLike(value) { return typeof value == 'object' && value !== null }
Object.prototype.toString.call
每一个对象都有一个toString()
方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。
isInteger
function isInteger(value) { return typeof value == 'number' && value == toInteger(value); }
检查 value
是否为一个整数,判断是否 value
的类型是否为数值,而且是否与 Int 型相同。其取整过程以下函数
function toInteger(value) { var result = toFinite(value), remainder = result % 1; return result === result ? (remainder ? result - remainder : result) : 0; }
isNaN
检查 value
是不是 NaN
。spa
function isNaN(value) { return isNumber(value) && value != +value; }
与 ES 2015 的 isNaN
不一样的是,对于 undefined
,{}
,原生的结果是 true
,而 lodash 为 false
。这是由于若是isNaN
函数的参数不是Number
类型, isNaN
函数会首先尝试将这个参数转换为数值,而后才会对转换后的结果是不是NaN
进行判断。
// js native isNaN var isNaN = function(value) { var n = Number(value); return n !== n; };
可是不管是 ES 2015 仍是 lodash,它们本质上都是利用 x != x
来判断 NaN
。
isFinite
检查 value
是不是原始有限数值。
function isFinite(value) { return typeof value == 'number' && nativeIsFinite(value); }
利用原生的 isFinite
结合 typeof
判断数字是否为有限值。
String 类型用于表示由零或多个16 位Unicode 字符组成的字符序列,即字符串。用于保存能够以文本形式表示的数据很是有用。
值得注意的是,不仅仅要注意基本字符串,还须要注意字符串对象,字符串字面量 (经过单引号或双引号定义) 和 直接调用 String 方法(没有经过 new 生成字符串对象实例)的字符串都是基本字符串。
JavaScript会自动将基本字符串转换为字符串对象,只有将基本字符串转化为字符串对象以后才可使用字符串对象的方法。
与以前的 number 相似,利用构造函数 String
建立的字符串是一个 object
const s_prim = "foo"; const s_obj = new String(s_prim); console.log(typeof s_prim); // Logs "string" console.log(typeof s_obj); // Logs "object"
因此检测字符串,除了基本字符串之外还要注意字符串对象。
function isString(value) { const type = typeof value return type == 'string' || (type == 'object' && value != null && !Array.isArray(value) && getTag(value) == '[object String]') }
能够利用 typeof
检测基本字符串,对于模板字符串采用了以前介绍的方案 getTag
来获取 value
的类型。
Boolean 类型是ECMAScript 中使用得最多的一种类型,该类型只有两个字面值:true
和 false
。一样也须要区分基本的 Boolean 类型以及 Boolean 对象。
function isBoolean(value) { return value === true || value === false || (isObjectLike(value) && getTag(value) == '[object Boolean]') }
大部分在以前都已经涉及到了,这里出现了 isObjectLike
,那么它是作什么的。
function isObjectLike(value) { return typeof value == 'object' && value !== null }
原来只是检测是不是一个非 null
的对象。
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值。Symbol 值经过Symbol
函数生成。
function isSymbol(value) { const type = typeof value return type == 'symbol' || (isObjectLike(value) && getTag(value) == '[object Symbol]') }
会发现 (isObjectLike(value) && getTag(value) == '[object Symbol]')
,也对 Symbol 对象进行检测,可是若是直接 new Symbol
会 log 出 TypeError
。
那么 lodash 为何要对其进行检测,原来是建立一个显式包装器对象从 ECMAScript 6 开始再也不被支持,如今能够利用以下代码来模拟,虽然没什么用。
const sym = Symbol("foo"); typeof sym; // "symbol" const symObj = Object(sym); typeof symObj; // "object"
Undefined 类型只有一个值,即特殊的 undefined
。在使用 let
或 var
声明变量但未对其加以初始化时,这个变量的值就是 undefined
。
function isUndefined(value) { return value === undefined; }
Null 类型是只有一个值的数据类型,这个特殊的值是 null
。与 undefined
不一样的是,它是一个字面量,而 undefined
是全局对象的一个属性。
从逻辑角度来看,null 值表示一个空对象指针,null
是表示缺乏的标识,指示变量未指向任何对象。而这也正是使用typeof 操做符检测null 值时会返回"object"的缘由。
对其的判断也很是的简单,只须要
function isNull(value) { return value === null }
固然你也可使用
console.log(Object.prototype.toString.call(null)) // [object Null]
以上是基本数据类型的判断,总结一下,主要是利用 typeOf
以及 Object.prototype.toString
,还有一些特殊值的特性。下面开始分析引用类型 Object
引用类型的值(对象)是引用类型的一个实例。在ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一块儿。具体的有 Object
、Array
、Date
、Error
、RegExp
、Function
,还有ES2015 引入 Set
、Map
、WeakSet
、WeakMap
。
ECMAScript 中的对象其实就是一组数据和功能的集合。它有一个很重要的用途,就是在 JavaScript 中的全部对象都来自 Object
;全部对象从Object.prototype
继承方法和属性,尽管它们可能被覆盖。即在ECMAScript 中,Object 类型是全部它的实例的基础。
因此 Lodash 去判断 value
是否为 Object
时,只使用了 typeOf
操做便可。
function isObject(value) { const type = typeof value return value != null && (type == 'object' || type == 'function') }
Function 构造函数 建立一个新的Function对象。 在 JavaScript 中, 每一个函数实际上都是一个Function对象。
function isFunction(value) { if (!isObject(value)) { return false } const tag = getTag(value) return tag == '[object Function]' || tag == '[object AsyncFunction]' || tag == '[object GeneratorFunction]' || tag == '[object Proxy]' }
有个问题,typeOf
能够检测 Function对象的类型为 Function
, 那为何还须要 Object.prototype.toString
呢?
// in Safari 9 which returns 'object' for typed arrays and other constructors.
Array 在 ECMAScript 中表明数组,它的每一项能够保存任何类型的数据。
对它的常规检测就是 Array.isArray
,Lodash 也是使用这个 API,若是须要 Polyfill 方案的话,可使用
// plan 1 Object.prototype.toString.call(value) === '[object Array]' // plan 2 value.constructor === Array
以前还有 value instanceof Array
会什么问题么?
在存在不一样全局变量的环境,经过语义instanceof
检测数组的时候,value instanceof Array
只有当value
是由该页面的原始Array
构造函数建立的数组时才能正常工做。
ECMAScript 中的 Date 类型是在早期Java 中的java.util.Date 类基础上构建的。
const nodeIsDate = nodeTypes && nodeTypes.isDate const isDate = nodeIsDate ? (value) => nodeIsDate(value) : (value) => isObjectLike(value) && getTag(value) == '[object Date]'
Lodash 分为两个环境来处理这个问题,若是是 Node 就利用 util.types.isDate(value)
来检测,若是是在游览器,就仍是经过 Object.prototype.toString
来判断。
ES2015 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。
const isSet = nodeIsSet ? (value) => nodeIsSet(value) : (value) => isObjectLike(value) && getTag(value) == '[object Set]'
一样的还有 Map
const isMap = nodeIsMap ? (value) => nodeIsMap(value) : (value) => isObjectLike(value) && getTag(value) == '[object Map]'
WeakSet 结构与 Set 相似,也是不重复的值的集合。可是,它与 Set 有两个区别。
function isWeakSet(value) { return isObjectLike(value) && getTag(value) == '[object WeakSet]' }
也是利用 Object.prototype.toString
,一样还有 WeakMap
function isWeakMap(value) { return isObjectLike(value) && getTag(value) == '[object WeakMap]' }
当运行时错误产生时,Error的实例对象会被抛出。
function isError(value) { if (!isObjectLike(value)) { return false } const tag = getTag(value) return tag == '[object Error]' || tag == '[object DOMException]' || (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)) }
有以前一致的 Object.prototype.toString
依然能够用来判断对象是不是一个 Error,除此以外,若是对象知足如下条件,也能够被视为一个 Error
message
、name
属性,且值为 string
;Object
构造函数建立,或者 [[Prototype]]
为 null
。那么如何检测普通对象呢?
function isPlainObject(value) { if (!isObjectLike(value) || getTag(value) != '[object Object]') { return false } if (Object.getPrototypeOf(value) === null) { return true } let proto = value while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto) } return Object.getPrototypeOf(value) === proto }
主要是利用 Object.getPrototypeOf()
方法返回指定对象的原型(内部[[Prototype]]
属性的值),同时和 value
自己的 [[Prototype]]
作判断。