初衷

写这篇文章的初衷是来源于JS基础问题-类型判断,仅当作巩固下知识。

我们常说学习一个知识点要学会延伸思考,点画线,线绘面,形成自己的知识体系。

一个小小的类型判断其实也可以拓展出很多知识点。

What: 什么是类型?

计算机程序的运行需要对值进行操作,而在编程语言中,能够表示并操作的值的类型称为数据类型。

编程语言最基本的特征就是支持多种数据类型。

Who:JS有哪几类数据类型,以及分别是什么?

JS有两类数据类型:原始类型(primitive type)和对象类型(object type)。

原始类型

  • number
  • string
  • boolean
  • undefined
  • null
  • symbol
  • Bigint

指的注意的是null确实属于基本类型,因为有时它容易被误认为是对象类型。


typeof null === 'object' // 这应该算语言的一个bug
复制代码

typeof null 感兴趣的可以看这个讨论

对象类型

  • object

实际上JS还有很多特殊的对象子类型,我们可以称之为复杂基本类型。

比如说:

  • String
  • Number
  • Boolean
  • Function
  • Array
  • Date
  • Regexp
  • Error
  • Object
  • Buffer
  • Map
  • Set
  • WeakSet
  • WeakMap

这里又会有一个思考:基本数据类型与引用数据类型的区别,常见于函数的入参。

基本数据类型它是按值访问。


const a = 10;
let b = a;
b = 20;
复制代码

引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址(“指针”)。通过这个引用地址可以快速查找到保存中堆内存中的对象。


const obj1 = new Object();

const obj2 = obj1;

obj2.name = "我有名字了";
复制代码

Why:为什么我们需要自己实现JS类型判断?

因为JS自带的类型判断方法不能满足我们的需求。

JS自带的类型判断方法

  • typeof

console.log(typeof '11') // string
console.log(typeof 11) // number
console.log(typeof true) // boolean
console.log(typeof undefined) // undefined
console.log(typeof null) // object, 算语言的bug,实际上该方式并不能判断出null
console.log(typeof Symbol('test')) // symbol
console.log(typeof /.?/gi) // object
console.log(typeof []) // object
console.log(typeof new Date()) // object
复制代码

你会发现typeof只能判断基本数据类型(null还是一个特例,需要特殊处理),不适用于判断对象类型,因为它无法判断出具体的子类型。

下面几个简单题目测试下是否掌握了typeof用法。


var y = 1, x = y = typeof x;
console.log(x) // undefined
复制代码

  var foo = {
    bar: function() { return this.baz; },
    baz: 1
  };
  (function(){
    return typeof arguments[0]();//"undefined"
  })(foo.bar);
复制代码
  • instanceof

instanceof 主要的作用就是判断一个实例是否是其父类型或者祖先类型的实例。它是基于原型链的查找。


let person = function () {
}
let nicole = new person()
nicole instanceof person // true
复制代码

我们看下面这个demo你就知道为什么有些场景下不能用instanceof来判断数据类型了。

const arr = []
const obj = {}
const func = function() {}

console.log(arr instanceof Array)  // true
console.log(arr instanceof Object) // true
console.log(obj instanceof Object) // true
console.log(func instanceof Object) // true
复制代码

How:如何实现JS类型判断?

下面我们看看lodash是如何进行JS类型判断的。

  • _.isObjectLike(value) 类对象

/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * isObjectLike({})
 * // => true
 *
 * isObjectLike([1, 2, 3])
 * // => true
 *
 * isObjectLike(Function)
 * // => false
 *
 * isObjectLike(null)
 * // => false
 */
function isObjectLike(value) {
  return typeof value === 'object' && value !== null
}

export default isObjectLike
复制代码
  • _.isArguments(value) 类 arguments 对象

const toString = Object.prototype.toString

// Gets the `toStringTag` of `value`.
// 这个方法很重要
function getTag(value) {
  if (value == null) {
    return value === undefined ? '[object Undefined]' : '[object Null]'
  }
  return toString.call(value)
}

function isArguments(value) {
  return isObjectLike(value) && getTag(value) == '[object Arguments]'
}
复制代码
  • _.isArrayLike(value)

// 检查 value 是否为有效的类数组长度。
function isLength(value) {
  return typeof value === 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER
}


function isArrayLike(value) {
  return value != null && typeof value !== 'function' && isLength(value.length)
}
复制代码
  • _isBoolean()

// isBoolean(null) false
function isBoolean(value) {
  return value === true || value === false ||
    (isObjectLike(value) && getTag(value) == '[object Boolean]')
}
复制代码

这里就不详细列举,感兴趣强烈推荐阅读lodash源码

关于类型还涉及的知识点有

ps: 感兴趣的关注我的公众号。