JavaScript - 数据类型及隐式转换

# 数据类型

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

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

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

# 隐式转换

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

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

# ToPrimitive(input, PreferredType?)

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

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

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

PreferredType = Number:

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

PreferredType = String: 类似于 Number

1
2
3
4
  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

Licensed under CC BY-NC-SA 4.0