typeof

typeof 对于判断简单数据类型简单有效,包装类型的简单数据类型除外

基本数据类型的判断(OK)

  1. // 数值
  2. typeof 37 === 'number';
  3. typeof 3.14 === 'number';
  4. typeof(42) === 'number';
  5. typeof Math.LN2 === 'number';
  6. typeof Infinity === 'number';
  7. typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
  8. typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值
  9. typeof 42n === 'bigint';
  10. // 字符串
  11. typeof '' === 'string';
  12. typeof 'bla' === 'string';
  13. typeof `template literal` === 'string';
  14. typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
  15. typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
  16. typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全
  17. // 布尔值
  18. typeof true === 'boolean';
  19. typeof false === 'boolean';
  20. typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
  21. typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean()
  22. // Symbols
  23. typeof Symbol() === 'symbol';
  24. typeof Symbol('foo') === 'symbol';
  25. typeof Symbol.iterator === 'symbol';
  26. // Undefined
  27. typeof undefined === 'undefined';
  28. typeof declaredButUndefinedVariable === 'undefined';
  29. typeof undeclaredVariable === 'undefined';
  30. // 对象
  31. typeof {a: 1} === 'object';

引用数据类型的判断

但是对引用数据类型的判断作用不大,除 function外,其他都返回 object,业务中我们通常需要知道它具体的类型

  1. typeof {a: 1} === 'object';
  2. typeof [1, 2, 4] === 'object';
  3. typeof new Date() === 'object';
  4. typeof /regex/ === 'object';
  1. typeof function() {} === 'function';
  2. typeof class C {} === 'function'
  3. typeof Math.sin === 'function';

包装类型的基本数据类型

迷惑吧!

  1. typeof new Boolean(true) === 'object';
  2. typeof new Number(1) === 'object';
  3. typeof new String('abc') === 'object';

在lodash中 在判断基础数据类型前 会判断是否是包装数据类型

  1. function isObjectLike(value) {
  2. return typeof value === 'object' && value !== null
  3. }

typeof null

  1. typeof null === 'object';

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 “object”。

instanceof

对比实例原型链上的基类
实例.proto == 基类.prototype

  1. function Car(make, model, year) {
  2. this.make = make;
  3. this.model = model;
  4. this.year = year;
  5. }
  6. const auto = new Car('Honda', 'Accord', 1998);
  7. console.log(auto instanceof Car);
  8. // expected output: true
  9. auto.__proto__ == Car.prototype
  10. console.log(auto instanceof Object);
  11. // expected output: true
  12. auto.__proto__ == Object.prototype

验证原理

验证一下确实是对比的proto 而不是构造方法里的prototype 有人说是这样的

  1. const auto1 = new Car('Honda', 'Accord', 1998);
  2. const auto2 = new Car('Honda', 'Accord', 1998);
  1. auto1 instanceof Car
  2. true
  3. auto2 instanceof Car
  4. true

删掉

  1. auto1.__proto__ = null
  2. null
  3. auto2.constructor = null
  4. null

结果

  1. auto1 instanceof Car
  2. false
  3. auto2 instanceof Car
  4. true

结论

instanceof原理
实例.proto == 基类.prototype

prototype给instanceof带来的风险

  1. auto2 instanceof Car
  2. true
  3. auto2.__proto__ == Car.prototype
  4. true

操作

  1. Car.prototype = null
  1. auto2.__proto__ == Car.prototype
  2. false
  3. auto2 instanceof Car
  4. VM1487:1 Uncaught TypeError: Function has non-object prototype 'null' in instanceof check
  5. at Function.[Symbol.hasInstance] (<anonymous>)
  6. at <anonymous>:1:7

由于基类的prototype 可改变,所以 instanceof这种对比 是不安全的

Object.prototype.toString()

每个对象都有一个 toString() 方法,默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回一个字符串 “[object type]”,其中 type 是对象的类型。

  1. var o = new Object();
  2. o.toString(); // [object Object]

为什么不能直接使用toString()

内置对象或者自定义类型的对象可能会实现自己的 toString 方法或者根本没有定义该方法,导致返回的不再是 “[object type]” 或者直接报错

  1. var str = new String('hello')
  2. str.toString() // "hello"
  3. 123..toString() // "123"
  4. null.toString() // Uncaught TypeError: Cannot read property 'toString' of null
  5. undefined.toString() // Uncaught TypeError: Cannot read property 'toString' of undefined

所以需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用(不陌生吧,改变 this 指向)

  1. var toString = Object.prototype.toString;
  2. toString.call(new String); // "[object String]"
  3. toString.call('hello'); // "[object String]"
  4. toString.call(Math); // "[object Math]"
  5. toString.call(123); // "[object Number]"
  6. toString.call(true); // "[object Boolean]"
  7. toString.call(123n); // "[object BigInt]"
  8. toString.call(new Date); // "[object Date]"
  9. toString.call(Symbol()) // "[object Symbol]"
  10. toString.call(() => {}) // "[object Function]"
  11. toString.call([1, 2]); // "[object Array]"
  12. toString.call(new Map) // "[object Map]"
  13. toString.call(function* () {}); // "[object GeneratorFunction]"
  14. toString.call(Promise.resolve()); // "[object Promise]"
  15. // ES5 中才实现下面的功能
  16. toString.call(undefined); // "[object Undefined]"
  17. toString.call(null); // "[object Null]"

ECMAScript 内置的针对某个类型的判断方法

Array.isArray()

  1. Array.isArray([1]); // true
  2. Array.isArray(new Array('a', 'b', 'c', 'd')); // true

总结

基本数据类型的判断使用 typeof 足够,instanceof由于判断的是对象的prototype,具有可变性,所以相对不安全。
引用数据类型使用Object.prototype.tosString.call(obj) 最为安全可靠