JavaScript类型检测和转换

388 阅读4分钟

数据类型检测

  • 基本数据类型和函数类型检测使用typeof
typeof variable === 'string'
typeof variable === 'number'
typeof variable === 'boolean'
typeof variable === 'undefined'
typeof variable === 'function'
  • 判断是否是null或undefined
variable == null
  • 判断是否是null
variable === null
// typeof null会返回"object"
  • 引用类型检测
variable instanceof Object
variable instanceof RegExp
variable instanceof Array
// instanceof操作符会返回布尔值

instanceof 操作符,左边必须是引用类型值(对象),右边必须是函数(构造函数),判断的原则是右侧的构造函数的prototype属性是否在左侧对象的原型链上,如是则返回true,否则返回false。注意不同的window之间不能用instanceof检测对象。

对于原始类型来说,想直接通过 instanceof 来判断类型是不行的,当然还是有办法让 instanceof 判断原始类型

class PrimitiveString {
  static [Symbol.hasInstance](x) {
    return typeof x === 'string'
  }
}
console.log('hello world' instanceof PrimitiveString) // true

Symbol.hasInstance是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。

Symbol.hasInstance不仅可以直接挂在对象或类的静态属性上,也可以挂在对象的原型上(查找对象属性时对象本身找不到会沿原型链向上查)

var obj3 = {};
// undefined

1 instanceof obj3;
// Uncaught TypeError: Right-hand side of 'instanceof' is not callable

obj3.__proto__ = {
  [Symbol.hasInstance](x) {
    return typeof x === 'string';
  }
};
1 instanceof obj3;
// false

'1' instanceof obj3;
// true

var obj4 = {};
// undefined

Object.prototype[Symbol.hasInstance] = function (x) {
  return typeof x === 'string';
}

1 instanceof obj4;
// false
  • 还有一个相当好用的检测类型方法Object.prototype.toString.apply()Object.prototype.toString.call()
	Object.prototype.toString.apply(undefined); // "[object Undefined]"
	Object.prototype.toString.apply(null); // "[object Null]"
	Object.prototype.toString.apply(3); // "[object Number]"
	Object.prototype.toString.apply(''); // "[object String]"
	Object.prototype.toString.apply([]); // "[object Array]"
	Object.prototype.toString.apply(function(){}); // "[object Function]"
	Object.prototype.toString.apply(/a/); // "[object RegExp]"
	Object.prototype.toString.apply(new Date); // "[object Date]"
	Object.prototype.toString.apply({}; // "[object Object]"

注意es5以下的引擎,null和undefined会返回"[object Object]"

在es6中Object.prototype.toString的返回值可能被修改

({[Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"

类型转换

image.png

转布尔值

在js中会被转换为false的有undefined, null, false, NaN, '', 0, -0

转字符串

布尔值/数字/undefined/null/symbol/bigInt,转字符串都是直接变成字符串类型。

String('false') === 'false'
String('null') === 'null'
String('undefined') === 'undefined'
String(12) === '12'
String(Symbol('xxx')) === 'Symbol(xxx)'

数组转字符串相当于调用join(',')函数

String([1, 2]) === '1,2'

转number

Number(false) === 0;
Number(true) === 1;
Number(null) === 0;
Number.isNaN(Number(undefined)) // true;
Number(Symbol('xxx')) // Uncaught TypeError:Cannot convert a Symbol value to a number
Number(9007199254740993n) // 9007199254740992(2^53)

// 字符串
Number('123') === 123;
Number.isNaN('123a') // true

// 数组
Number([]) === 0;
Number([1]) === 1;
Number(['1']) === 1;
Number.isNaN(Number([1, 2])) // true

// 对象
Number.isNaN(Number({})); // true

// 注意在chrome中 {} + 1 // 1,这是因为大括号会被认为是代码段

对象转原始类型

对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:

  • 如果已经是原始类型了,那就不需要转换了
  • 如果需要转字符串类型就调用 x.toString(),转换为基础类型的话就返回转换的值。不是字符串类型的话就先调用 valueOf,结果不是基础类型的话再调用 toString
  • 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
  • 如果都没有返回原始类型,就会报错

Symbol.toPrimitive方法在转原始类型时调用优先级最高。

let a = {
  valueOf() {
    return 0
  },
  toString() {
    return '1'
  },
  [Symbol.toPrimitive]() {
    return 2
  }
}
1 + a // => 3

四则运算符

加号运算符

  • 运算中其中一方为字符串,那么就会把另一方也转换为字符串
  • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串

注意优先级,一方不是字符串或数字的话会先转为数字,其次是字符串

1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"

其他四则运算符

  • 只要其中一方是数字,那么另一方就会被转为数字

关系操作符

image.png

相等操作符

image.png

基本比较流程如上图,对于==操作符,最常用的就是判断一个变量是否为null或undefined。

[] == ![]
// true

按运算符优先级,![]为false。转为比较[] == false,按照流程,布尔值转为Number。转为比较[] == 0,对象转原始类型,转为比较'' == 0,字符串转Number,转为比较0 == 0