Dec 9, 2016

JavaScript 类型转换

从原理上讲一下 javascript 的隐式类型转换,10分钟学会类型判断原理。

内置七种基本类型

  • null
  • undefined
  • boolean
  • number
  • string
  • object
  • symbol

typeof返回的是类型的字符串值,但是null是例外,typeof null === 'object',这是个bug

typeof function的返回值也不是基本类型的字符串值,是因为functionobject的一个子类型

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

详情见typeof 规范

typeof.png

位运算符

位运算符要求的是32位整数,小数位和超过32位的整数位忽略

  • |按位或运算符可以将小数转换成整数a | 0,不过要注意整数不能超过32位

  • ~按位非运算符将操作数所有位取反,在javascript中相当于-(x+1)

//5 二进制 101,补满 32位
00000000000000000000000000000101
//按位取反
11111111111111111111111111111010
//由于32位开头第一个是1,所以这是一个负数,将二进制转换成负数,需要先反码
00000000000000000000000000000101
//之后,再+1
00000000000000000000000000000110
//转换成十进制为6,加上符号变成负数 -6

一个应用是~-1 == 0可以替换

if(arr.indexOf('a') > -1) {}

if(~arr.indexOf('a')) {
    //有值
}

~~a也可以将a取整

一元运算符

  • +运算符把操作数转换成数字
  • -运算符把操作数转换成数字再改变结果的符号
//涉及到类型转换,下面讲
+[]; //0
+'42'; //42
//++ --会被识别为运算符,所以使用时要加空格
5 + +'3'; //8

类型转换

基础概念就不写了,直接来容易迷糊的and原理,抛几个问题

[] == ![]; //true
"0" == ""; //false
"" == 0; //true
"foo"  == ["foo"]; //true

ToPrimitive

首先介绍一下es内部抽象操作ToPrimitive,它的作用是将输入转换成非对象类型数据,最后的返回值是基本类型值,或者抛错

The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. The abstract operation ToPrimitive converts its input argument to a non-Object type.

Paste_Image.png

ToPrimitive转换对象的时候调用[[DefaultValue]]内部方法,可带一个参数hint,代表如果这个对象可以转成多种基本类型值,我们把它转成哪种,默认是Number

[[DefaultValue]]的转换办法是

  • hintString的话先看有没有toString方法,有调用toString,如果是基本类型值则返回,否则看有没有valueOf方法,有调用valueOf,是基本类型值返回,不是的话抛TypeError错误
  • hintNumber的话则是先看valueOf方法,后看toString方法
  • 没有hint参数则按Number的规则转,Date对象例外,用String

字符串和数字之间的隐式强制类型转换

var a = [1,2];
var b = [3,4];

a + b; //'1,23,4'

+ 加号转换原则简单说就是某个操作数是字符串,或者可以通过ToPrimitive转成字符串,那就执行字符串拼接操作,否则进行数字加法。

[1,2]是个对象,调用内部的[[DefaultValue]]方法,valueOf方法得到的是[1,2]不是基本类型值,调用toString得到字符串'1,2'[3,4]同理,最后执行字符串拼接得到'1,23,4'

疑惑

[] + {}; //"[object Object]"
{} + []; //0

这里先说一下后者,因为{}在javascript里被识别为代码块,自己就执行了,所以{} + []被解析成+ [],这里的+是一元运算符,将[]转换成数字,也就是通过ToPrimitive得到字符串"",转成数字为0 前者被解析成了字符串拼接,""+[object Object]

宽松相等和严格相等

“ ==允许在相等比较中进行强制类型转换,而 === 不允许”

规范里算法很多,挑几个代表性的

一、Number Compare String

Paste_Image.png

二、Boolean Compare with other

Paste_Image.png

三、Object Compare with String or Number

Paste_Image.png

四、null == undefined

现在来看一下之前提出的几个问题

//![]转换成boolean值为0,[] == 0,[]根据规则进行ToPrimitive([]),得到"" == 0,再转成 ToNumber("") == 0
[] == ![]; //true

//字符串比较
"0" == ""; //false

//ToNumber("") == 0
"" == 0; //true

//ToPrimitive(["foo"]) == "foo"
"foo" == ["foo"]; //true

收获

明白了隐式类型转换规则,平时写代码的时候也要注意,不要写容易让别人困惑的代码; 最大的收获是查官方规范,看别人的博客太麻烦了,这里讲的不清楚,那里讲的模模糊糊,直接看规范,一看就懂了。