2019-05-11 | JavaScript | UNLOCK

JS类型判断和类型转换

7 种类型

  • String
  • Number
  • Boolean
  • Null
  • undefined
  • Symbol
  • Object

类型判断

使用 typeof 类型判断

typeof undefined // "undefined"
typeof true // "boolean"
typeof 1 // "number"
typeof 's' // "string"
typeof {} // "object"
typeof Symbol() // "symbol"
// 特殊类型
typeof null // "object"

// 检测 null 值的类型
const a = null
!a && typeof a === 'object' // true

object 类型中还包含很多的’子类型’, Array、Function、Date、RegExp、Error, 但是函数例外

typeof function a() {} === 'function' // true

function(函数)也是 JavaScript 的一个内置类型。实际上是 object 的一个“子类型”。具体来说,函数是“可调用对象”,它有一个内部属性 [[Call]],该属性使其可以被调用

//typeof 只有 function 的情况下返回 'function',其他类型都返回 `object`
typeof new Date() // object
typeof new Error() // object

Object.prototype.toString 更详细的检测类型

  • 如果 this 值是 undefined ,就返回 [object Undefined]
  • 如果 this 的值是 null ,就返回 [object Null]
  • 返回由 "[object ", class and "]"三个部分组成的字符串

    至少 17 种类型:Number, String, Boolean, Undefined, Null, Object, Function, Array, Date, Error, RegExp, Math, JSON, Arguments Symbol Set Map

//  demo 返回的 `class` 就是我们期望的类型, 与 `typeof` 不同的是,这里返回的类型,首字母都是大写的
Object.prototype.toString.call(undefined) // [object Object]
Object.prototype.toString.call(null) // [object Object]

Object.prototype.toString.call(Symbol()) //[object Symbol]
Object.prototype.toString.call(new Set()) //[object Set]
Object.prototype.toString.call(new Map()) //[object Map]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call(Math) // [object Math]

a = () => {
  console.log(Object.prototype.toString.call(arguments)) // [object Arguments]
}
a()
function classType() {
  const classType = {}
  // 来自 `冴羽的 JavaScript 专题系列`
  // 生成classType映射
  'Boolean Number String Function Array Date RegExp Object Error Symbol Set Map'
    .split(' ')
    .map((item, index) => {
      classType['[object ' + item + ']'] = item.toLowerCase()
    })

  type = obj => {
    // 一箭双雕
    if (obj == null) {
      return obj + ''
    }
    return typeof obj === 'object' || typeof obj === 'function'
      ? classType[Object.prototype.toString.call(obj)] || 'object'
      : typeof obj
  }
}

类型值简介

Undefinednull

undefined 类型只有一个值,即 undefinednull 类型也只有一个值,即 null。它们的名称既是类型也是值。

  • undefined 指从未赋值, undefined 却是一个标识符,在非严格条件下可以被当作变量来使用和赋值, 在严格模式下可以声明一个名为 undefined 的局部变量
  • null 指曾赋过值,但是目前没有值, null 是一个 JavaScript 关键字,不是标识符,我们不能将其当作变量来使用和赋值

void 运算符: 表达式void ___没有返回值,因此返回结果是undefined
为了避免无意中被篡改,使用void 0 来获取 undefined

String

String 有最大长度是 2^53 - 1String 的意义并非“字符串”,而是字符串的 UTF16 编码,我们字符串的操作 charAtcharCodeAtlength 等方法针对的都是 UTF16 编码, JavaScript 中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串的内容,所以字符串具有值类型的特征

区别于数组:

let a = 'foo'
let b = ['f', 'o', 'o']

a[1] = 'O' // "foo" 字符串是不可变的
// a.charAt(1) 取值是不是更优雅一些
b[1] = 'O' // ["f","O","o"]

// 字符串不能直接反转,数组可以
a.reverse() //undefined
b.reverse() //["o","o","f"]

// 实现字符串反转
a.split('')
  .reverse()
  .join('') // ["o","o","f"]

Number

JavaScript 中的 Number 类型有 18437736874454810627(即 2^64-2^53+3) 个值

JavaScript 为了表达几个额外的语言场景(比如不让除以 0 出错,而引入了无穷大的概念),规定了几个例外情况:

  • NaN,占用了 9007199254740990,这原本是符合 IEEE 规则的数字;
  • Infinity,无穷大;
  • -Infinity,负无穷大

JavaScript 中有 +0-0,在加法类运算中它们没有区别,但是除 法的场合则需要特别留意区分,“忘记检测除以 -0,而得到负无穷大”的情况经常会导致 错误,而区分 +0-0 的方式,正是检测 1/xInfinity 还是 -Infinity

NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不 成立)的值。而 NaN != NaN 为 true,可以使用内建的全局工具函数 Number.isNaN(..) 来判断一个值是否是 NaN。

isNaN(…) 和 Number.isNaN(…)

  • isNaN(): 全局工具函数 isNaN(..) 来判断一个值是否是 NaN, 检查方式就是检查参数是否不是 NaN,也不是数字
const a = 2 / 'foo' //NaN
const b = 'foo'
window.isNaN(a) // true
window.isNaN(b) // true 不是数字也返回 true
  • Number.isNaN(..)

较小的数值

Number.isNaN(a) // true
Number.isNaN(b) // false

JavaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则,二进制浮点数最大的问题是
0.1 + 0.2 === 0.3; // false

二进制浮点数中的 0.1 和 0.2 并不是十分精确,它们相加的结果并非刚好等于 0.3,而是一个比较接近的数字 0.30000000000000004,所以条件判断结果为 false

正确的比较方法是使用 JavaScript 提供的最小精度值:
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);

整数的检测

要检测一个值是否是整数,可以使用 ES6 中的 Number.isInteger(..) 方法:

Number.isInteger(42) // true
Number.isInteger(42.0) // true
Number.isInteger(42.3) // false

Symbol

它是一切非字符串的对象 key 的集合,在 ES6 规范 中,整个对象系统被用 Symbol 重塑。

Symbol 可以具有字符串类型的描述,但是即使描述相同,Symbol 也不相等。
var mySymbol = Symbol("my symbol")

Object

对象的定义是“属性的集合”。属性分为数据属性和访问器属性,二者都是 key-value 结构,key 可以是字符串或者 Symbol 类型。

注意点:
Number、String 和 Boolean,三个构造器是两用的,当跟 new 搭配时,它们产生对象,当直接调用时,它们表示强制类型转换。
3 与 new Number(3) 是完全不同的值,它们一个是 Number 类 型, 一个是对象类型。

Object.is(..)

判断两个值是否绝对相等, Object.is(..) 主要用来处理那些特殊的相等比较

Object.is(0 / 'foo', NaN) // true
Object.is(-3 * 0, -0) // true
Object.is(-3 * 0, 0) // false

简单的 polyfill:

if (!Object.is) {
  Object.is = function(v1, v2) {
    // 判断是否是-0
    if (v1 === 0 && v2 === 0) {
      return 1 / v1 === 1 / v2
    }
    // 判断是否是NaN
    if (v1 !== v1) {
      return v2 !== v2
    }
    // 其他情况
    return v1 === v2
  }
}

类型转换