前端JS面试题-基础-变量类型和计算
写在前面:本文内容主要根据慕课网双越老师的付费课程“一天时间迅速准备前端面试 快速构建初级前端知识体系 ”进行编写,主要是为了自己在面试前总结学习,欢迎留言指教。
本系列包括如下内容:
每一部分包括题目和知识点两部分。
变量类型和计算
题目:
- typeof能判断哪些类型
- 何时使用 === 何时使用 ==
- 值类型和引用类型的区别
- 手写深拷贝
1. typeof能判断哪些类型
- 识别所有值类型
- 识别函数
- 判断是否是引用类型,但无法进一步判断具体引用类型
2. 何时使用 ===, 何时使用 ==
除了 判断 == null 之外,其他都一律用 ===
因为 == 在尽量地对要比较的变量进行类型转换,很多不相等的变量通过类型转换会相等,这不是我们预期的结果,因此我们要尽量使用 === 进行比较
3. 值类型和引用类型的区别
- 首先,什么是值类型,什么是引用类型
在JS中值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。
值类型变量声明后,不管是否已经赋值,编译器为其分配内存;当声明一个引用类型变量时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间,当使用该变量时,才会分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。 - 其次,常见的值类型变量和引用类型变量
常见的值类型变量:String,Number,Boolean,undefined,Symbol
常见的引用类型变量:Array, Object, null, function
4. 手写深拷贝
参考知识点3中给出的代码。
注意:
- 注意判断值类型和引用类型,指对引用类型进行深拷贝
- 主语判断是数组还是对象,影响返回结果的初始化
- 递归
知识点:
1. 值类型 引用类型
首先给出常见值类型和常见引用类型的举例:
- 常见值类型
let a //undefined 未定义
const s = 'abc' //String 字符串
const n = 100 //Number 数值
const b = true //Boolean 布尔值
const s = Symbol('s)
- 常见引用类型
const obj = {x: 100} //Object 对象
const arr = {'a', 'b', 'c'} //Array 数组
const n = null //特殊引用类型,指针指向为空地址
function fn(){} //特殊引用类型,但不用于存储数据,所有没有“拷贝、赋值函数”一说
接下来给出两个关于值类型和引用类型的赋值的代码,分析其原理:
// 值类型
let a = 100
let b = a
a = 200
console.log(b) // 100
// 引用类型
let a = { age: 20}
let b = a
b.age = 21
console.log(a.age) // 21
对于值类型的数据,其存储情况如下:
考虑到性能问题,值类型数据的存储和复制都比较快速,不耗性能,因此它被存储到栈里,key下的value直接存储值。
对于上面给出的代码,新建a,并赋值,其存储结果如第一个框所示;新建b,并将a赋值给b,其存储结果如第二个框所示;所以再a变化时,b不受影响,如第三个框所示。
对于引用类型的数据,其存储情况如下:
在栈中,value内只存储key所在的内存地址,通过内存地址到堆里进行查看具体值。
因此,当新建一个引用类型a,再新建一个引用类型b,并将a赋值给b,那b下存储的只是a的内存地址。当b对age进行修改时,堆内的值被修改,也就是a的age也被修改,因为a、b指向同一个内存地址。
2. typeof运算符
- 识别所有值类型
- 识别函数
- 判断是否是引用类型,不能识别到具体的引用类型
3. 深拷贝
在知识点1中展示并讲解了值类型数据和引用类型数据的拷贝问题。值类型数据可以利用浅拷贝来完成数据的拷贝,但引用类型数据无法利用浅拷贝来完成数据的拷贝,因此我们需要写一个深拷贝的方法来解决上述问题。
接下来给出手写深拷贝的代码:
/**
* @param {type} 要拷贝的对象
*/
function deepClone(obj = {}) {
//针对的是对象和数组
if(typeof obj != 'object' || obj == null) {
return obj
}
//初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
//保证key不是原型的属性
//递归
result[key] = deepClone(obj[key])
}
}
//返回结果
return result
}
给出如下的代码来演示浅拷贝和深拷贝的区别,和深拷贝的作用:
const obj1 = {
age: 20,
name: 'xxx',
address: {
city: 'beijing'
},
arr: ['a', 'b', 'c']
}
const obj2 = obj1
obj2.address.city = 'shanghai'
console.log(obj1.address.city)
const obj3 = deepClone(obj1)
obj3.address.city = 'shenzhen'
console.log(obj1.address.city)
运行,可以在控制器内看到如下结果:
我们创建了一个obj1,其中city的值为beijing,将obj1浅拷贝给obj2,并obj2.address.city = 'shanghai'
,打印obj1.address.city
,发现beijing被修改为shanghai;将obj1深拷贝给obj3,并obj3.address.city = 'shenzhen'
,打印obj1.address.city
,发现没有被修改。这样我们就完成了深拷贝操作。
4. 类型转换
有3种情况会发生隐式类型转换,分别为:
- 字符串拼接
==
==
会尽量地通过类型转换使其相等,所以我们应该尽可能地使用 ===
- if语句和逻辑判断
在这里要介绍两种变量,分别为:- truely变量: !!a === true 的变量
- falsely变量: !!a === false 的变量
在进行if语句和逻辑判断时,会将变量转换为truely变量和falsely变量。