数据类型

基础数据类型: undefined, null, Number, String, Boolean, Symbol (ES6+)
引用数据类型: Object(Object, Array, Date, RegExp, Function)

基础数据类型 值直接存储在相应 栈内存 上的, 使用时是按值访问;
引用数据类型 地址存储在 占内存上, 内容存储在 堆内存上, 使用时是按 引用访问;

注:
Number, Boolean, String 三种类型 也可以通过 new 关键字创建对应的 基本包装类型, 包装类型的实例

隐式转换

涉及到隐式转换的运算符最多的是 + 和 ==, -*/等运算符只会针对 number 类型, 结果也只转换为 number 类型;

  • 将值转换为原始值 ToPrimitive()
  • 将值转换为数字 ToNumber();
  • 将值转化为字符串 ToString();

ToPrimitive(input, PreferredType?)

input是要转换的值,PreferredType是可选参数,可以是Number或String类型, 只是一个转换标志,转化后的结果并不一定是这个参数所值的类型,但是转换结果一定是一个原始值(或者报错), 也就是说设置 Number 时也可能转换为 string, 设置为 String 时 也可能转化为 Number;

  • 当 PreferredType 未设置时, 按照下面规则设置:

    1. input 是 Date 类型时, PreferredType = String;
    2. 否则, PreferredType = Number;
  • PreferredType = Number:

    1. input 是原始值, 返回原始值;
    2. input 不是原始值, 调用 input.valueOf(); 如果返回值是原始值则返回原始值结果;
    3. 如果 2 的结果不是原始值, 调用 input.toString(), 如果返回值是原始值则返回原始值结果;
    4. 如果 3 的结果不是原始值, 抛出 TypeError 异常;
  • PreferredType = String: 类似于 Number :

    1. input 是原始值, 返回原始值;
    2. input 不是原始值, 调用 input.toString(); 如果返回值是原始值则返回原始值结果;
    3. 如果 2 的结果不是原始值, 调用 input.valueOf(), 如果返回值是原始值则返回原始值结果;
    4. 如果 3 的结果不是原始值, 抛出 TypeError 异常;

ToNumber

1
2
3
4
5
6
undefined NaN
null +0
boolean true: 1/ false: +0
number number
string '123': 123 / 'qwer': NaN
object ToPrimitive(input, Number).ToNumber()

ToString

1
2
3
4
5
6
undefined "undefined"
null "null"
boolean true: "true"/ false: "false"
number "number"
string string
object ToPrimitive(input, String).ToString()
valueOf方法和toString方法详解

首先 valueOf() 和 toString() 是 Object.prototype 的方法, 也就是所有的对象都有这两个方法;

  • valueOf() 总的原则是能转化为原始值则转化为原始值, 不能转化为原始值的返回 this, 也就是对象本身, Date 对象转化为 毫秒级数值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Number('123').valueOf() // 123
    String('123fs').valueOf() // '123fs'
    Boolean(true).valueOf() // true
    new Date().valueOf() // 1530706938289
    var arr = ['1', '2'];
    arr.valueOf() // [ '1', '2' ] === arr => true 等于 自身
    var obj = {};
    obj.valueOf() // {} === obj => true 等于自身
  • toString() 将对象转化成字符串形式, 重写了 toString() 的调用自己的 toString() 方法, 其余的调用 Object.prototype.toString()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    Number.prototype.hasOwnProperty('toString'); // true
    Boolean.prototype.hasOwnProperty('toString'); // true
    String.prototype.hasOwnProperty('toString'); // true
    Array.prototype.hasOwnProperty('toString'); // true
    Date.prototype.hasOwnProperty('toString'); // true
    RegExp.prototype.hasOwnProperty('toString'); // true
    Function.prototype.hasOwnProperty('toString'); //
    var num = new Number('123sd');
    num.toString(); // 'NaN'
    var str = new String('12df');
    str.toString(); // '12df'
    var bool = new Boolean('fd');
    bool.toString(); // 'true'
    var arr = new Array(1,2);
    arr.toString(); // '1,2'
    var d = new Date();
    d.toString(); // "Wed Jul 04 2018 20:22:09 GMT+0800 (CST)"
    var func = function () {}
    func.toString(); // "function () {}"
    var obj = new Object({});
    obj.toString(); // "[object Object]"
    Math.toString(); // "[object Math]"

隐式转换的例子

1
2
3
4
5
6
7
'' == 0 // true
'0' == 0 // true
'0' == '' // false类型相同 不进行隐式转换
[] == !{} // []==false => ''==0 => 0==0 => true
({} + {}) = ? // "[object Object]" + "[object Object]" = "[object Object][object Object]"
2 * {} = ? // 2 * NaN = NaN
2 * [] = ? // 2 * ''.toString() = 2 * 0 = 0
1
2
3
4
5
6
7
8
9
const a = {
i: 1,
toString: function () {
return a.i++;
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('hello world!');
}

分析:

  1. 当执行a == 1 && a == 2 && a == 3 时,会从左到右一步一步解析,首先 a == 1,会进行上面第9步转换。ToPrimitive(a, Number) == 1。
  2. ToPrimitive(a, Number),按照上面原始类型转换规则,会先调用 valueOf 方法,a 的 valueOf 方法继承自 Object.prototype。返回a本身,而非原始类型,故会调用 toString 方法。
  3. 因为 toString 被重写,所以会调用重写的 toString 方法,故返回1,注意这里是 i++,而不是 ++i,它会先返回i,在将i+1。故 ToPrimitive(a, Number) = 1。也就是 1 == 1,此时 i = 1 + 1 = 2。
  4. 执行完 a == 1返回 true,会执行 a == 2,同理,会调用 ToPrimitive(a, Number),同上先调用 valueOf 方法,在调用 toString 方法,由于第一步,i = 2 此时,ToPrimitive(a, Number) = 2, 也就是2 == 2, 此时i = 2 + 1。
  5. 同上可以推导 a == 3 也返回 true。故最终结果 a == 1 && a == 2 && a == 3 返回 true