一、JS面试题
Q1. JS的数据类型都有哪些?
A1: javascript有11种数据类型
- 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol、BigInt。
- 引用数据类型:对象(Object)、数组(Array)、函数(Function)、正则(RegExp)、日期(Date)。
注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。注:BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数ID等等,而不需要使用库(数字后面+n结尾)
Q2. 怎么判断JS的数据类型
A2:
方法一:变量的数据类型可以使用 typeof 操作符来查看,typeof返回的都是string类型的参数:
typeof "John" // 返回 string
typeof 3.14 // 返回 number
typeof false // 返回 boolean
typeof 42n // 返回 bigint
typeof Symbol() // 返回 symbol
typeof Symbol('foo') // 返回 symbol
typeof undefined // 返回 undefined
typeof ttttttt // 返回 undefined
typeof func // 返回 'function'
typeof class cs {} // 返回 'function'
typeof String // 返回 'function'
typeof RegExp // 返回 'function'
typeof new Function() // 返回 'function'
typeof {} // 返回 'object' 对象
typeof [] // 返回 'object' 数组
typeof null // 返回 'object' null
typeof /d/ // 返回 'object' 正则
typeof 的局限性,在于无法精确判断出 null、数组、对象、正则 的类型。所以如果要精准判断,还需要使用其他技术手段,或组合判断。如下,判断数组类型:
Object.prototype.toString.call([]) // '[object Array]'
[] instanceof Array // true
[].constructor === Array // true
方法二:instanceof(instanceof 是用来判断 A 是否为 B 的实例)
console.log({} instanceof Object); // true
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
console.log(new Date() instanceof Date); // true
console.log(function () { } instanceof Function); // true
console.log('123' instanceof String); // false
由上述代码看出instanceof对于引用类型的类型检测支持很好,但是无法对基本类型数据进行类型检测。
方法三:Object.prototype.toString.call()
//1、基本数据类型
var num1 = 1;
var num2 = new Number(1);
console.log(Object.prototype.toString.call(num1) == '[Object Number]'); // true
console.log(Object.prototype.toString.call(num2) == '[Object Number]'); // true
//2、数组
console.log(Object.prototype.toString.call([]) == '[Object Array]') // true
//3、函数
console.log(Object.prototype.toString.call(function(){})=='[Object Function]')// true
//4、自定义对象
function P() { }
console.log(Object.prototype.toString.call(new P()) == '[Object Object]') // true
Object.prototype.toString.call()对于基本类型和引用类型都可以判断(除了自定义的类)
目前最推荐方法三 👆
Q3: 堆和栈存储机制有什么区别?
A3:程序运行的时候,需要内存空间存放数据。一般来说,系统会划分出两种不同的内存空间:一种叫做栈(stack),另一种叫做堆(heap)。
堆是无序的,栈是有序的(先进后出)。
堆存放对象类型数据,栈存放基本类型数据,栈存放对象数据的地址,存在栈中的数据大小与生存期必须是确定的。可以明确知道每个区块的大小,因此,栈stack的寻址速度要快于堆heap
栈是会自动释放的,堆需要手动释放或者垃圾回收机制回收。如obj = null
Q4:深拷贝与浅拷贝
A4:
- 浅拷贝是指引用类型的数据,在拷贝的数据变化时,也会引起原本数据改变。多个栈中地址对应同一个引用数据(基本数据类型默认是深拷贝的)。
- 深拷贝是指,拷贝的数据变化,不会引起原本数据的改变。多个栈中地址对应多个引用数据
浅拷贝方法一:
Object.assign()
var obj = {
a: {
b: 1
},
c: 3
};
var newObj = Object.assign({}, obj); // Object对象浅拷贝
newObj.a.b = 2; // 修改的newObj.a是引用类型,会影响原对象obj.a
console.log(obj.a.b); // 2
newObj.c = 4; // 修改的newObj.c是基本类型,不会影响原对象obj.c
console.log(obj.c); // 3
浅拷贝方法二(只使用与数组Array):
slice()、concat()、Array.from()
var arr = [1, 2, [3, 4]];
var newArr = arr.slice(); // Array数组浅拷贝
// var newArr = arr.concat(); // 同理
// var newArr = Array.from(arr); // 同理
newArr[2][1] = 5; // 修改的newArr[2]是引用类型,会影响原数组arr[2]
console.log(arr[2][1]); // 5
newArr[0]=6; // 修改的newArr[0]是基本类型,不会影响原数组arr[0]
console.log(arr[0]); // 1
浅拷贝方法三(适用于Object对象和Array数组):
扩展运算符 ...
var obj = {
a: {
b: 1
}
};
var newObj = { ...obj }; // Object对象浅拷贝
newObj.a.b = 2;
console.log(obj.a.b); // 2var arr = [1, 2, [3, 4]];
var newArr = [...arr]; // Array数组浅拷贝
newArr[2][1] = 5;
console.log(arr[2][1]); // 5
深拷贝方法一:
JSON.parse(JSON.stringify(…)) (简单但有缺陷)
- 原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
- 缺点:不能深拷贝含有undefined、function、symbol值的对象。
- 优点:用法简单粗暴,已经满足90%的使用场景了。
var obj = {
a: {
b: 1
},
c: 3
};
var newObj = JSON.parse(JSON.stringify(obj));
newObj.a.b = 2;
console.log(obj.a.b); // 1
newObj.c = 4;
console.log(obj.c); // 3
/** -------------------------- **/
var arr = [1, 2, [3, 4]];
var newArr = JSON.parse(JSON.stringify(arr));
newArr[2][1] = 5;
console.log(arr[2][1]); // 4
newArr[0] = 6;
console.log(arr[0]); // 1
深拷贝方法二:
手写递归方法 (复杂但很完善)
// 定义检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1);
}
// 实现深度克隆---对象/数组
function deepCopy(target) {
var result; // 初始化变量result 成为最终克隆的数据
var targetType = checkedType(target); // 判断拷贝的数据类型
if (targetType === 'Object') {
result = {};
} else if (targetType === 'Array') {
result = [];
} else {
return target;
}
// 遍历目标数据
for (let key in target) {
// 获取遍历数据结构的每一项值。
let value = target[key];
// 判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
// 继续递归遍历获取到value值
result[key] = deepCopy(value);
} else {
// 获取到value值是基本的数据类型或者是函数。
result[key] = value;
}
}
return result;
}
var obj1 = {
'name': 'zhangsan',
'age': 18,
'language': [1, [2, 3],
[4, 5]
]
};
var obj2 = deepCopy(obj1);
obj2.name = "lisi";
obj2.language[1] = ["二", "三"];
console.log('obj1', obj1); // {name: "zhangsan", age: 18, language: [1, [2, 3], [4, 5]]}
console.log('obj2', obj2); // {name: "lisi", age: 18, language: [1, ["二", "三"], [4, 5]]}