JS中的数据类型

187 阅读5分钟

数据分类

原始数据类型:string、number、boolean、null、undefined、symbol、bigInt
引用数据类型:object

原始数据类型无函数可以调用,例如 undefined.toString()这个是会报错的。

image.png

此时你肯定回疑惑,为啥我们写(如下)不会报错呢,这是因为运行机制,在调用toString()是,已经将num转化为了Number类型。

var num = 12;
num.toString();

原始数据类型

基本数据类型按值访问,操作的是变量的实际值 如:

let a = 1;
let b = a;
b = 2;
console.log(a); // 输出 1

上面b拷贝了a的值,虽然a和b相等,但是ab保存的两个不同基本类型的值,所以当b改变的时候也不会影响到a

引用数据类型

js中的引用类型是保存在堆内存中的对象,可操作在栈中存的引用地址

const obj = {
    name: '我的第一个名字'
}
const obj2 = obj;
obj2.name = '我的第二个名字'
console.log(obj.name); // 输出:我的第二个名字

两个引用类型指向同一个堆内存中的对象,所以obj2修改name的值的时候,通过obj去访问name时,name为我的第二个名字。说明实际改变的堆内存中的对象。

两者的区别

声明变量时不同的内存分配
基础数据类型,存储在栈中简单数据,可以通过变量直接访问,例如 const a = 1, a存储的是值。
引用类型,存储在堆中的对象,存储在变量处的是一个指针,指向存储对象的内存地址,例如 const obj = {},obj存储的是指针

不同的内存分配也带来了不同的访问机制
基础数据类型在拷贝后完全独立,互不影响。
引用数据类型在拷贝后改动互相影响,不独立

typeof

typeof对于原始数据类型来说,除了null,其他的都可以判断正确

typeof 1; // number
typeof NaN; // number
typeof '1'; // string
typeof true; // boolean
typeof Symbol(1) // symbol

typeof对于对象数据类型来说,除了Function,其余的都会显示object

typeof []; // object
typeof {}; // object
typeof console.log // function

instanceof

如果想正确的判断一个对象类型,可以利用instanceof,因为其内部时通过原型链来判断的。但 instanceof 返回的结果不是 百分之百可信的。

const Person = function () {};
const p1 = new Persion();
p1 instanceof Persion; // true

let str = 'Hello World';
str instanceof String; // false

let str1 = new String('hello');
str1 instanceof String; // true

类型转换

js中的类型转换主要有三种

  • 转换为布尔值
  • 转换为字符串
  • 转换为数字
原始值转换目标结果
number布尔值除了0、NaN都是true
string布尔值除了空字符串都为true
null、undefined布尔值false
引用类型布尔值true
number字符串1 => '1'
数组字符串[1, 2] => '1,2'
对象字符串'[object Object]'
string数字'1' => 1, 'a' => NaN
数组数字空数组为0,[10] => 10, [9, 10] => NaN
null数字0
除了数组的对象类型数字NaN
Symbol数字报错

转boolean

在判断条件时,除了 0, -0, null, undefined, NaN, false其他所有值都转换为true

对象转原始类型

对象在转换时,会调用内置的[[toPrimitive]]函数,这个函数算法逻辑一般如下

  • 如果已经是原始类型,就不转换
  • x.valueOf(),如果转换为原始类型,则返回值
  • x.toString(),如果转换为原始类型,则返回值
  • 如果都没有返回值,则报错

当然也可以修改Symbol.toPrimitived优先级

let a = {
    // 首先
    [Symbol.toPrimitive]() {
        return 3
    },
    
    // 其次
    valueOf() {
        return 1;
    },
    
    //最后
    toString() {
        return '2'
    }
    
}
1 + a; // 4

四则运算

加法运算特点:

  • 其中一方为字符串,则会将另一方也转换为子都串,此时 +是拼接的作用
  • 如果一方不是字符串 或 数字,则会转换为数字 或 字符串进行运算
1 + '1' // '11'
false + true // 1
4 + [1, 2, 4] // '4,1,2,4'
‘a’ + +'b' // 'aNaN'

4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN

比较运算符

特点:

  • 如果是对象,则通过[[toPrimitive]]转换为原始类型
  • 如果是字符串,则通过unicode字符索引来比较大小
const s = {
    valueOf() {
        return 0
    },
    toString() {
        return '1'
    }
}
a > -1 // true

== VS ===

== 对于数据类型不一致时,会进行转换

x == y判断过程如下

  1. 首先判断数据类型是否一致,如果一致就判断大小
  2. 类型不同,则进行转换
  3. 会先判断 是否在对比 null 和 undefined,是则返回true
  4. 判断两者类型是否为 stringnumber,是则转换为 number
1 == '1'
// 转换为
1 == 1
  1. 判断一方是否为boolean,若是则将boolean转换为 number
1 == true
// 转换为
1 == 1
  1. 判断一方是否为object且另一方为stringnumbersymbol,是则将object转换为原始类型
{test: 'hh'} == '1'
// 转换为
'[object Object]' == '1' // false

[] == ![]

比较过程:

  1. ![] 对应的值是false
[] == false
  1. 则进入到上面的第五步,将false值转换为 数字 0
[] == 0
  1. 此时一方为 number,则需要将[]转换为 原始类型,
  • 调用valueOf方法,没有得到原始值
  • 调用toString方法,toString方法内部调用join方法转换为空字符串,
'' == 0
  1. 此时进入上面的第四步,将空字符串转换为数字的0
0 == 0
  1. 所以最终结果是 true

练习

对象形式的函数参数

const person = {
    name: 'll',
    age: 18
}
function foo(p) {
    p.age = 26;
    p = {
        name: 'hh',
        age: 28;
    }
    return p;
}
consr p = foo(persion);
console.log(person.age);
console.log(p.age);

输出:

  • 26
  • 28 解析: 首先,函数传参是传递的对象指针的副本。到函数内部修改属性是,person的值也修改了。当重新为p分配一个对象时,p就拥有了一个新的指针地址,也就和person没有关系了,所以最后两个输出的结果不一样。

参考资料