数据类型
ECMAScript数据类型分为俩大类:
值类型:String
、Number
、Boolean
、Undefined
、Null
、Symbol
以及Bigint
。
引用类型:Array
、Object
、Function
、Set
、WeakSet
、Map
、WeakMap
。
TIP
值类型的值直接存储在栈内、引用类型的值存储在堆中,并有相对应的引用地址存储在栈内。
故变量赋值时,引用类型变量赋值的是引用地址,指向的是存储在堆中的同一份数据,需要深拷贝。
深拷贝
// 深拷贝方式:
// 无法拷贝内置方法、函数和循环引用
JSON.parse(JSON.stringify(data))
// 浅层深拷贝
Object.assign({}, data)
{...data}
[...data]
/**
* 以上方式都不是最优解,但是项目中使用最多的是JSON.parse(JSON.stringify())
* 只需确认是否含有函数和循环引用即可
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
手写深拷贝
// 深拷贝 需考虑内置对象、循环引用等问题
function deepClone(data, map = new WeakMap()) {
// 循环引用直接返回
if (map.has(data)) return data
// 值类型 和 null 直接返回
if (typeof data !== 'object' || data === null) return data
// 记录 已设置的引用类型 避免循环引用
map.set(data, true)
// 内置对象 返回其实例化
if (/Date|Math|RegExp/.test(data.constructor.name)) {
return new data.constructor(data)
}
const result = Array.isArray(data) ? [] : {}
// 遍历递归
for (const key in data) {
result[key] = deepClone(data[key], map)
}
return result
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
思考
以下p1 p2分别输出什么
function test(person) { person.age = 26 person = { name: 'hzj', age: 18 } return person } const p1 = { name: 'fyq', age: 19 } const p2 = test(p1) console.log(p1) console.log(p2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
答案解析
// p1 { name: 'fyq', age: 26 }
// p2 { name: 'hzj', age: 18 }
2
- 函数参数传递->引用地址传递,当person.age = 26,修改了p1引用地址引用的值
- person = {name: 'hzj', age: 18}, 直接用新的引用地址替换了函数参数引用地址 返回的是个新的引* 用地址 即p2
判断数据类型
判断数据类型方法有:typeof
、instanceof
、Object.prototype.toString.call()
等
- typeof
typeof 可以判断值类型中除Null以外的所有类型,引用类型中Function,其它无法判断
typeof null === 'object'? 为什么?
因为 JavaScript中不同对象在底层都表示成二进制,而typeof将前三位为零的都判断为object
null的二进制全是0,所以返回 object
2
3
4
5
- instanceof
instanceof是根据原型链判断,如果是在同一原型链上则相等
function myInstanceof(left, right) { if ( (typeof right !== 'object' && typeof right !== 'function') || right === null) { throw new TypeError("Right-hand side of 'instanceof' is not an object") } // 获取right的原型 const rP = right.prototype // 获取left的隐式原型 let lP = left.__proto__ while (true) { if (lP === null) return false if (lP === rP) return true lP = lP.__proto__ } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- Object.prototype.toString.call()
TIP
此方法是将值 调用Object方法 转字符串来判断
examples:
Object.prototype.toString.call({}) === '[object Object]'
Object.prototype.toString.call([]) === '[object Array]'
Object.prototype.toString.call(1) === '[object Number]'
相等运算符
ECMAScript中判断相等有以下三种:==
、===
、Object.is()
注意
==
不完全相等,会先进行隐式转换再判断是否相等,不校验数据类型
1 == '1' || false == 0 || null == void 0 等等
===
完全相等,校验数据类型
但 NaN === NaN 和 -0 === +0 判断不严格
Object.is
修复了以上俩种情况
Object.is(NaN, NaN) // true
Object.is(-0, +0) // false
题目
- 0.1 + 0.2 === 0.3 ?
答案
false
0.1 和0.2在转化二进制的时候丢失精度 , 故 在转换回来的时候 不为 0.3
- 对象转原始类型是根据什么流程运行的?
答案
调用Symbol.toPrimitive()方法,优先调用再返回
调用valueOf(),如果转换为原始类型,则返回
调用toString(),如果转换为原始类型,则返回
如果都没有返回原始类型,会报错
const obj = {
value: 3,
valueOf() {
return 4;
},
toString() {
return '5'
},
[Symbol.toPrimitive]() {
return 6
}
}
console.log(obj + 1); // 输出7
2
3
4
5
6
7
8
9
10
11
12
13
// 定义一个变量 a, 让a == 1 && a == 2 条件成立
const a = {
value: 1,
valueOf() {
return this.value++
}
}
2
3
4
5
6
7