✍Vue源码——学习如何判断数据类型

6,292 阅读5分钟

前言

Vue 框架作为一个成熟的框架,里面有很多值得去学习、借鉴、使用。本专栏主要学习 Vue 中如何判断数据类型。

一、数据类型

到 ECMAScript 10 为止,规定了 8 种 数据类型,又把数据类型分为原始类型和对象类型。

原始类型

名称描述
Null只包含一个值:null。
Undefined只包含一个值:undefined。
Boolean布尔值包含两个值:true和false。
Number数值,例如整数或浮点数,还有一些特殊值(-Infinity、+Infinity、NaN)。
String字符串。
Symbol一种实例是唯一且不可改变的数据类型,ES6引入。
BigInt用于当整数值大于Number数据类型支持的范围时,ES10引入。

引用类型

名称描述
ObjectObject 对象、Array 数组、Function 函数、Date 日期、RegExp 正则 等等

二、Vue中如何判断原始类型

function isPrimitive(value) {
    return (
        typeof value === 'string' ||
        typeof value === 'number' ||
        typeof value === 'symbol' ||
        typeof value === 'boolean'
    )
}

原因

typeof operand,参数 operand 一个表示对象或原始值的表达式,其类型将被返回。

  • typeof undefined === 'undefined'
  • typeof true === 'boolean'
  • typeof 3.14 === 'number'
  • typeof 'bla' === 'string'
  • typeof Symbol('foo') === 'symbol
  • typeof 42n === 'bigint'
  • typeof null === 'object'

从以上可以总结,原始类型的数据除了 null 其它都可以用 typeof 准确判断出来。

三、Vue中如何判断引用类型

function isObject(obj) {
	return obj !== null && typeof obj === 'object'
}

其实 Vue 漏掉了对 Function 函数的判断,完整的判断,如下所示:

function isObject(obj) {
	return obj !== null && ( typeof obj === 'object' || typeof obj === 'function' )
}

原因

typeof operand,参数 operand 一个表示对象或原始值的表达式,其类型将被返回。

  • typeof function() {} === 'function'
  • typeof null === 'object'
  • typeof {a: 1} === 'object'
  • typeof [1, 2, 4] === 'object'
  • typeof new Date() === 'object'
  • typeof /regex/ === 'object'
  • typeof new Boolean(true) === 'object'
  • typeof new Number(1) === 'object'

从以上可以总结,原始类型的数据除了 null 其它都可以用 typeof 准确判断出来。引用类型的数据就无法用 typeof 准确判断出来,返回的值都是 object

四、Vue中如何判断对象

const _toString = Object.prototype.toString;
function isPlainObject(obj){
    return _toString.call(obj) === '[object Object]'
}

原因

toString 方法是 Object 的一个实例方法,Object.prototype.toString。在JavaScript中,几乎所有的对象都是 Object 类型的实例,它们都会从 Object.prototype 继承属性和方法。而 Object 是个引用类型。所以每个引用类型都有 toString 方法,都从 Object.prototype 继承属性和方法,而且在引用类型上若这个方法未被自定义,那么使用 toString 方法,其返回 [object type],其中 type 是对象的类型,这样就可以用 toString 方法来判断引用类型的数据的类型。

因为引用类型上的 toString 方法,可能会被自定义。比如Array、Date、RegExp等都自定义了toString 方法。

故要调用 Object.prototype.toString,并使用 call来将 this 指向要判断的数据,例如:

  • Object.prototype.toString.call([]) === "[object Array]"
  • Object.prototype.toString.call({}) === "[object Object]"
  • Object.prototype.toString.call(function (){}) === "[object Function]"
  • Object.prototype.toString.call(/df/) === "[object RegExp]"
  • Object.prototype.toString.call(new Date()) === "[object Date]"

五、Vue中如何判断正则表达式

const _toString = Object.prototype.toString
function isRegExp(v){
    return _toString.call(v) === '[object RegExp]'
}

原因

如第四点的原因所示。

六、补充

toString

从上面得知使用 Object.prototype.toString 可以准确地判断出引用类型的数据的类型。那对于原始类型的数据呢?

会发现以下等式也成立:

  • Object.prototype.toString.call(true) === "[object Boolean]"
  • Object.prototype.toString.call(123) === "[object Number]"
  • Object.prototype.toString.call('123') === "[object String]"

这是为什么呢?不是说 toString 是引用类型的数据的方法。

这是 call 方法在起作用。我们来看一下在 MDN上对 call 方法的用法介绍。

function.call(thisArg, arg1, arg2, ...)

  • 参数 thisArg :可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。
  • 参数 arg1, arg2, ... :指定的参数列表。

按上述所介绍,在非严格模式下,若 thisArg 是原始值会被包装。那什么是包装。现有的JS中有三个包装器 new Number()new String()new Boolean()

例如,参数 thisArg123 时,在 call 方法内部会执行 new Number(123)123 包装一下,而 new Number(123) 是个引用类型,可以调用 toString 方法。故使用 Object.prototype.toString 来判断 NumberStringBoolean 这三个原始类型的数据类型。

对于 Symbol 类型的原始数据,其是用 Symbol() 函数创建的,返回的值上面也有 toString 方法,故Object.prototype.toString.call(Symbol()) === "[object Symbol]"

对于 BigInt 类型的原始数据,其 BigInt 是一种内置对象,属性上面也有 toString 方法,故Object.prototype.toString.call(42n) === "[object BigInt]"

至于对 nullundefined 类型的原始数据,可能是在 Object.prototype.toString 源码中做了处理,也可以判断出来。

  • Object.prototype.toString.call(null) === "[object Null]"
  • Object.prototype.toString.call(undefined) === "[object Undefined]"

可以看出使用Object.prototype.toString能最全面的判断数据类型。

instanceof

object instanceof constructor:instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。

  • object 某个实例对象
  • constructor 某个构造函数

从 instanceof 运算符的定义来看,可以弥补 typeof 无法判断引用类型的数据。例如:

  • new Date() instanceof Date === true
  • new RegExp() instanceof RegExp === true

看上去还真可以判断引用类型的数据,其实不会很准确,这不是它设计的初衷。例如:

  • [] instanceof Array === true
  • [] instanceof Object === true
  • const a =function(){}; a instanceof Object === true

所以 Vue 中很少用 instanceof 运算符去判断数据的类型,只用来判断实例化对象是不是某个类实例化出来的。例如:

if ( !(this instanceof Vue) ){}
if( vnode instanceof VNode ){}