JS 的数据类型

基本数据类型

  1. 存储在栈中
  2. 无共享概念,赋值后毫无相关

引用数据类型

  1. 数据存储在堆中,对象的引用地址存放在栈中

  2. 存在数据共享,赋值的对象的地址,一个改变另外一个也随之改变

  3. 作为实参,传入的也是对象的引用地址,改变其引用地址的值,共享改引用地址的变量都会跟着改变

数据类型检测

typeof、instanceof、Object.prototype.toString.call()、constructor

typeof

可以识别基本数据类型,null除外

不可以识别引用类型(Function除外)

instanceof

不可以识别基本数据类型

可以识别内置对象类型【object、Date、Array】,可以识别自定义对象数据类型【通过 new 的构造函数】

Object.prototype

可以识别基本数据类型,内置对象类型

不能识别自定义对象数据类型【通过 new 的构造函数】

constructor

可以识别基本数据类型【undefined和null除外】

可以识别内置对象类型和自定义对象类型

function getconstructorType(obj) {
	return obj && obj.constructor && obj.constructor.toString().match(/function\s*([^(]*)/)[1];
}
getconstructorType([])

类型转换

练习

'123' == 123   // false or true?
'' == null    // false or true?
'' == 0        // false or true?
[] == 0        // false or true?
[] == ''       // false or true?
[] == ![]      // false or true?
null == undefined //  false or true?
Number(null)     // 返回什么?
Number('')      // 返回什么?
parseInt('');    // 返回什么?
{}+10           // 返回什么?
let obj = {
    [Symbol.toPrimitive]() {
        return 200;
    },
    valueOf() {
        return 300;
    },
    toString() {
        return 'Hello';
    }
}
console.log(obj + 200); 

强制类型转换

toString()、String()、Number()、parseInt()、parseFloat()、Boolean()

Number()

布尔值返回 0或1

数字返回自身

undefined返回 NaN

null 返回 0

symbol返回 NaN

字符串:纯数字字符串 "1.12" 、或者(0X / 0x) 开头的十六进制字符串转成十进制、允许正负号;空字符串转0,其他字符串格式均为NaN

如果是对象:如果对象部署了[Symbol.toPrimitive],则调用 此方法;否则调用 valueOf() 方法,然后根据上面的规则转换valueOf返回的值;如果转换的结果是NaN,则调用对象的toString() 根据上面的规则对返回的值进行转换

Boolean()

0、-0、+0、’’, false、null、undefined、NaN,其他的都是true

String()

任何类型转的的都是字符串类型

toString()

null、undefined不支持使用toString()、symbol、boolean支持通过值调用toString,number、object不支持值调用,只支持赋值变量后调用

parseInt

整数返回自身,浮点型整数部分4.12 => 4;

boolean、null、undefined、NaN=> NaN

symbol 报错

字符串:空字符串、非纯数字字符串=> NaN;纯数字字符串支持正负号转换为 正负整数;支持-0X / 0x 开头的十六进制字符串 parseFloat(0x11)

如果是对象则跟Number的转换规则是一样的

parseFloat

数字返回自身

boolean、null、undefined 、NaN => NaN

symbol 转换错误

字符串:空字符串、非纯数字的返回NaN,纯数字字符串返回数字,支持正负号,支持-0X / 0x 开头的十六进制字符串 parseFloat(0x11)

隐式类型转换

逻辑运算符(&&、||、!),运算符(+、-、*、/);关系操作符(>、<、>=,<=、),相等操作符(),条件操作符(if / while),类型不一致都会产生隐式类型转换

==符合隐式类型转换

1、操作符两边类型一致,则不进行隐式类型转换

2、其中一个是 null 或 undefined,另外一个必须是undefined或null,否则false

3、symbol 返回 false

4、string 和number进行对比,将字符串转成数字进行对比

5、如果一个值是 boolean ,那么将会转成number

6、如果一个值是 object,另外一个是 number、string、symbol ,就会把 object 转成原始类型(这点非常重要),通过调用(object.valueOf 或者 object.toString):[] == 0 => “” == 0 => 0 == 0 => true

7、!的优先级是大于 ==,会先执行 ![]

null == undefined       // true  符合第二点
null == 0               // false 符合第二点
'' == null              // false 符合第二点
'' == 0                 // true  符合第四点 字符串转隐式转换成Number之后再对比
'123' == 123            // true  符合第四点 字符串转隐式转换成Number之后再对比
0 == false              // true  符合第五点 布尔型隐式转换成Number之后再对比
1 == true               // true  符合第五点布尔型隐式转换成Number之后再对比
var a = {
  value: 0,
  valueOf: function() {
    this.value++;
    return this.value;
  }
};
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a ==3);  //true 通过第六点隐式转换
// 注:但是执行过3遍之后,再重新执行a==3或之前的数字就是false,因为value已经加上去了,这里需要注意一下

“+” 隐式类型转换

两边都是数字则进行加法运算

其中一个是字符串,另外一个是 null、undefined或boolean值则对值进行toString转成字符串再拼接;如果另外一个是对象、数组、正则等;则根据对象的转换方法进行转成后再拼接(转换优先级 Symbol.toPrimitive 再次是 valueOf 其次是toString() )

其中一个是数字,另一个是 undefined、null、boolean,则会将其转成数字进行加法运算,另一个是对象的话参考上一条

其中一个是字符串另一个是数字则进行拼接

其中一个是字符串,另外一个是bigint,将Bigint转成字符串相加 “1” + 1n => “11”

number不能和BigInt进行相加 :1+1n => err

1 + 2        // 3  常规情况
'1' + '2'    // '12' 常规情况
// 下面看一下特殊情况
'1' + undefined   // "1undefined" 规则1,undefined转换字符串
'1' + null        // "1null" 规则1,null转换字符串
'1' + true        // "1true" 规则1,true转换字符串
'1' + 1n          // '11' 比较特殊字符串和BigInt相加,BigInt转换为字符串
1 + undefined     // NaN  规则2,undefined转换数字相加NaN
1 + null          // 1    规则2,null转换为0
1 + true          // 2    规则2,true转换为1,二者相加为2
1 + 1n            // 错误  不能把BigInt和Number类型直接混合相加
'1' + 3           // '13' 规则3,字符串拼接

Object的转换规则

如果部署 Symbol.toPrimitive 方法,则优先调用再返回,如果返回值不是基础类型,则报错,如果是基础类型就返回不报错

调用 valueOf() ,如果转换为基础类型则返回

调用 toString(),如果转换为基础类型则返回

如果以上都没有返回基础类型,都error

注意:默认会调用valueOf(),如果不是基础类型会继续转换,去调用toString() 再返回,注意看下面代码的注释

var obj = {
  value: 1,
  valueOf() {
    return 2;
  },
  toString() {
    return '3'
  },
  [Symbol.toPrimitive]() {
    return 4
  }
}
console.log(obj + 1); // 输出5
// 因为有Symbol.toPrimitive,就优先执行这个;如果Symbol.toPrimitive这段代码删掉,则执行valueOf打印结果为3;如果valueOf也去掉,则调用toString返回'31'(字符串拼接)
// 再看两个特殊的case:
10 + {}
// "10[object Object]",注意:{}会默认调用valueOf是{},不是基础类型继续转换,调用toString,返回结果"[object Object]",于是和10进行'+'运算,按照字符串拼接规则来,参考'+'的规则C
[1,2,undefined,4,5] + 10
// "1,2,,4,510",注意[1,2,undefined,4,5]会默认先调用valueOf结果还是这个数组,不是基础数据类型继续转换,也还是调用toString,返回"1,2,,4,5",然后再和10进行运算,还是按照字符串拼接规则,参考'+'的第3条规则

题目

{} == ! {}  // false
看完object的转换规则:
{} == ! {} =={} == false!优先级比==高先隐式转成boolean:true取反为false => 
{} == Number(0) 【如果是boolan会隐身转成number】 => 
({}).toString() == 0 => 【对象根据转换规范执行转换基础类型】
'[object Object]' == 0 => 【字符串跟数字类型对比会隐式转成number】
Number('[object Object]') == 0 ==> NaN == 0 ==>false  //注意不是:[Object Object] == 0 -> false

//验证注释说明
{} == '[object Object]' ==> 
    ({}).toString() == '[object Object]'
		'[object Object]' == '[object Object]' => true