JS基础,从概念、检测方法、转换方法讲解js数据类型。

概念

image.png

基础类型和引用类型

基础类型:存储在内存中,被引用或拷贝时,会创建一个完全相等的变量

引用类型:存储在内存中,存储的是地址,多个引用指向同一个地址,这里涉及一个“共享”的概念

let a = {
    name: 'lee',
    age: 18
}
let b = a;
console.log(a.name);
b.name = 'son';
console.log(a.name);
console.log(b.name);
let a = {
    name: 'Julia',
    age: 20
}
function change(o) { // 函数传参的对象,传递的是对象在堆中的地址
    o.age = 24; // 确实改变了o的属性值
    o = {
        name: 'Kath',
        age: 30
    }
    return o; // 但是此处又把o变成了另一个内存地址,并存入数据,没有return,则返回一个undefined
}
let b = change(a);
console.log(b.age);
console.log(a.age);

检测

typeof:可以判断基本数据类型(null除外),但是引用数据类型中,除了function类型以外,其他的无法判断;

instanceof:可以准确判断复杂引用数据类型,但是不能准确判断基础数据类型;

Object.prototype.toString:可以准确判断,但是返回格式是[object Xxx];

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object',js历史悠久的bug,建议用===null判断
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
let Car = function(){}
let benz = new Car()
benz instanceof Car // true

let car = new String('Mercedes Benz')
car instanceof String // true

let str = 'Covid-9' // str只是一个以string为数据类型的值,但并不属于String对象的实例
str instanceof String // false,可以使用原型链判断
Object.prototype.toString({}) // '[object Object]'
Object.prototype.toString.call({}) // 结果同上加上call也ok
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call('1') // '[object String]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(function(){}) // '[object Function]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(/123/g) // '[object RegExp]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(document) // '[object HTMLDocument]'
Object.prototype.toString.call(window) // '[object Window]'

数据类型检测通用方法

function  getType(obj) {
    let type = typeof obj;
    if (type !== 'object') {
        return type;
    }
    return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');
    // 注意正则中间有个空格
}

数据类型的转换

强制类型转换:Number()、parseInt()、parseFloat()、toString()、String()、Boolean()

'123' == 123 // 
'' == null
'' == 0
[] == 0
[] == ''
[] == ![]
null == undefined // true
Number(null) // 0
Number('') // 0
parseInt('') // NaN
{} + 10

Number()方法的强制转换规则

  • 布尔值:true和false分别被转换成1和0
  • 数字:返回自身
  • null:返回0
  • undefined:返回NaN
  • 字符串:
    • 如果字符串中只包含数字,则将其转换为十进制
    • 如果字符串中包含有效的浮点格式,将其转换为浮点数值
    • 如果是空字符串,将其转换为0
    • 如果不是以上格式的字符串,均返回NaN
  • Symbol:抛出错误
  • 对象,并且部署了[Symbol.toPrimitive]:那么调用此方法,否则调用对象的valueOf()方法,然后根据前端的规则转换返回值,如果返回的是NaN则调用对象的toString()方法再次转换
Number(true) // 1
Number(false) // 0
Number('0111') // 111
Number(null) // 0
Number('') // 0
Number('1a') // NaN
Number('-0X11') // -17
Number('0X11') // 17

Boolean()方法的强制转换规则

  • undefined、null、false、’‘、0(包括+0、-0)、NaN转换出来是false,其他都是true
Boolean(0) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
Boolean(1) // true
Boolean(13) // true
Boolean(12) // true
Boolean([]) // true
Boolean({}) // true

隐式类型转换

  • 逻辑运算符:&&、||、!
  • 运算符:+、-、*、/
  • 关系操作符:>、<、<=、>=
  • 相等运算符:==
  • if/while 条件

’==’ 隐式类型转换

  • 如果类型相同无需进行转换
  • 如果其中一个操作值是undefined或者null,那么另外一个操作值必须为null或者undefined才会返回true,否则都返回false
  • 如果其中一个为Symbol类型那么返回false
  • 两个操作值如果都为string和number类型那么将会将字符串转换为number
  • 如果一个操作值是boolean那么转换为number
  • 如果一个操作值为object且另一方为string、number或者symbol,就会把object转换为原始类型(调用object.valueOf或者toString方法进行转换)再进行判断
null == undefined // true 规则2
null == 0 // false 规则2
'' == null // false 规则2
'' == 0 // // true 规则4
'123' == 123 // true 规则4
0 == false
1 == true

var a = {
    value: 0,
    valueOf: function() {
        this.value++;
        return this.value;
    }
}
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a == 3); // true f规则 Object隐式转换
// 注意执行过3遍之后,再执行a==3或者之前的数字就是false,因为value已经加上去了,这里需要注意一下

’+’ 隐式类型转换

’+’操作符,不仅可以用作数字相加,还可以用作字符串拼接

  • 如果其中有一个是字符串,另外一个是undefined、null或布尔型,则调用toString()方法进行字符串拼接;如果是纯对象、数组、正则等,则默认调用对象的转换方法会存在优先级,然后再进行拼接
  • 如果其中有一个是数字,另外一个是undefined、null、布尔型或数字则会将其转换成数字进行加法运算,对象的情况还是参考上一条
  • 如果其中一个是字符串、一个是数字,则按照字符串规则进行拼接
1 + 2 // 3 常规情况
'1' + '2' // '3' 常规情况

// 特殊情况
'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
1 + 1n // 错误,不能把BigInt和Number类型直接混合相加

'1' + 3 // '13',规则3,字符串拼接

Object的转换规则

  • 如果部署了Symbol.toPrimitive方法优先调用再返回
  • 调用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,5,10",注意[1,2,undefined,4,5]会默认先调用valueOf结果还是这个数组,不是基础数据类型继续转换,
// 也还是调用toString,返回"1,2,,4,5",然后在和10进行运算,还是按照字符拼接规则,参考'+'的第3条规则