一、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); // 2
var 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]]}
Q5:未完待续……