# JS 数据类型
# 原始数据类型
在 JS 中,存在着 6 种原始数据类型,分别是:
- number
- string
- boolean
- null
- undefined
- symbol
# 引用数据类型:
对象Object
- 包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function
# null是对象吗
- 回答: null不是对象。
- 解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。
- 在 JS 的最初版本中,为了性能,使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。
# 类型判断的方法
# typeof
typeof xxx
得到的值有以下几种类型:undefined
boolean
number
string
object
function
、symbol
这里需要注意几点:
typeof null
结果是object
,实际这是typeof
的一个bug,null是原始值,非引用类型typeof [1, 2]
结果是object
,结果中没有array
这一项,引用类型除了function
其他的全部都是object
typeof Symbol()
用typeof
获取symbol
类型的值得到的是symbol
,这是 ES6 新增的知识点
对于原始类型来说,除了 null 都可以调用typeof显示正确的类型。
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
1
2
3
4
5
2
3
4
5
但对于引用数据类型,除了函数之外,都会显示"object"。
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
1
2
3
2
3
因此采用 typeof
判断对象数据类型是不合适的
# instanceof
instanceof
运算符用于检测某个构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
- 语法
object instanceof constructor
- 参数
- object 某个实例对象
- constructor 某个构造函数
# 原始类型 vs 引用类型
# 对象类型和原始类型的不同之处
- 原始类型存储的是值,
- 对象类型存储的是地址(指针)。
- 当创建一个对象类型的时候,将它分配到堆空间里面,分配后该对象会有一个在“堆”中的地址,然后再将该数据的地址写 进 该对象 的变量值。
- 如果将变量赋值给另外一个变量时,复制的是原本变量的地址(指针),当进行数据修改的时候,就会修改存放在地址(指针)上的值,也就导致了两个变量的值都发生了改变。
# 具体分析
function foo(a){
a = a * 10;
}
function bar(b){
b.value = 'new';
}
var a = 1;
var b = {value: 'old'};
foo(a);
bar(b);
console.log(a); // 1
console.log(b); // value: new
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
通过代码执行,会发现:
a
的值没有发生改变- 而
b
的值发生了改变
这就是因为Number
类型的a
是按值传递的,而Object
类型的b
是按共享传递的。
JS 中这种设计的原因是:按值传递的类型,复制一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保证了其访问速度。按共享传递的类型,是复制其引用,而不是整个复制其值(C 语言中的指针),保证过大的对象等不会因为不停复制内容而造成内存的浪费。
// 说出下面运行的结果,并解释原因。
function test(person) {
person.age = 26
person = {
name: lin,
age: 18
}
return person
}
const p1 = {
name: 'cherry',
age: 19
}
const p2 = test(p1)
console.log(p1)
console.log(p2)
/* 结果:
p1:{name: “cherry”, age: 26}
p2:{name: “lin”, age: 18} */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
原因:
- 在函数传参的时候传递的是对象在堆中的内存地址值,
- test 函数中的实参 person 是 p1 对象的内存地址,通过调用
person.age = 26
确实改变了 p1 的值, - 但随后 person 变成了另一块内存空间的地址,并且在最后将这另外一份内存空间的地址返回,赋给了 p2 。
# 面试题
# 0.1+0.2为什么不等于0.3?
- 0.1和0.2在转换成二进制后会无限循环,
- 由于标准位数的限制 后面 多余的位数会被截掉,此时就已经出现了精度的损失,
- 相加后 再将其转换为 十进制 就会变成0.30000000000000004。
# '1'.toString()为什么可以调用?
其实在这个语句运行的过程中做了这样几件事情:
var s = new Object('1');
s.toString();
s = null;
1
2
3
2
3
- 第一步: 创建Object类实例。
- 注意为什么不是String ? 由于Symbol和BigInt的出现,对它们调用new都会报错,目前ES6规范也不建议用new来创建基本类型的包装类。
- 第二步: 调用实例方法。
- 第三步: 执行完方法立即销毁这个实例。
【回答】
- 在这种情况下,'1' 已经不是原始类型了,而是被强制转换成了 String 类型也就是对象类型,所以可以调用 toString 函数。
【什么是基本包装类型】
- 三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”(wrapper)。
- 所谓“包装对象”,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。
- 这三个原生对象可以把原始类型的值变成(包装成)对象。
# JS数据类型转换
# JS中类型转换有哪几种?
在 JS 中类型转换有三种情况,分别是:
- 转换为布尔值
- 转换为数字
- 转换为字符串
# == 和 ===有什么区别?
- ===叫做严格相等,是指:左右两边不仅值要相等,类型也要相等,例如'1'===1的结果是false,因为一边是string,另一边是number。
- ==不像===那样严格,对于一般情况,只要值相等,就返回true,
- ==涉及一些类型转换,它的转换规则如下:
- 首先会判断两者类型是否相同。相同的话就是比大小了
- 类型不相同的话,那么就会进行类型转换
- 会先判断是否在对比 null 和 undefined,是的话就会返回 true
- 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
- 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
- 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
# 判断 [] == ![] 的结果
- 结果为true
- 解析:
- == 中,左右两边都需要转换为数字然后进行比较。
- []转换为数字为0。
- ![] 首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true,
- 因此![]为false,进而在转换成数字,变为0。
- 0 == 0 , 结果为true
# 对象转原始类型是根据什么流程运行的?
对象转原始类型,会调用内置的[ToPrimitive]函数,对于该函数而言,其逻辑如下:
- 如果Symbol.toPrimitive()方法,优先调用再返回
- 调用valueOf(),如果转换为原始类型,则返回
- 调用toString(),如果转换为原始类型,则返回
- 如果都没有返回原始类型,会报错
var obj = {
value: 3,
valueOf() {
return 4;
},
toString() {
return '5'
},
[Symbol.toPrimitive]() {
return 6
}
}
console.log(obj + 1); // 输出7
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 如何让if(a == 1 && a == 2)条件成立?
var a = {
value: 1,
valueOf: function() {
return this.value++
}
}
console.log(a == 1 && a == 2) //true
1
2
3
4
5
6
7
2
3
4
5
6
7
← 知识体系 2.JS 语法 & API →