数据分类
原始数据类型:string、number、boolean、null、undefined、symbol、bigInt
引用数据类型:object
原始数据类型无函数可以调用,例如 undefined.toString()这个是会报错的。
此时你肯定回疑惑,为啥我们写(如下)不会报错呢,这是因为运行机制,在调用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判断过程如下
- 首先判断数据类型是否一致,如果一致就判断大小
- 类型不同,则进行转换
- 会先判断 是否在对比 null 和 undefined,是则返回
true - 判断两者类型是否为
string和number,是则转换为number
1 == '1'
// 转换为
1 == 1
- 判断一方是否为
boolean,若是则将boolean转换为 number
1 == true
// 转换为
1 == 1
- 判断一方是否为
object且另一方为string、number、symbol,是则将object转换为原始类型
{test: 'hh'} == '1'
// 转换为
'[object Object]' == '1' // false
[] == ![]
比较过程:
- ![] 对应的值是false
[] == false
- 则进入到上面的第五步,将
false值转换为 数字0
[] == 0
- 此时一方为 number,则需要将
[]转换为 原始类型,
- 调用valueOf方法,没有得到原始值
- 调用toString方法,toString方法内部调用join方法转换为空字符串,
'' == 0
- 此时进入上面的第四步,将空字符串转换为数字的0
0 == 0
- 所以最终结果是 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没有关系了,所以最后两个输出的结果不一样。