文章目录
前言
这里总结的所有面试题都是从牛客网找的各种大厂真实场景的面试题,并且做了汇总,各位看官看后做好总结,绝对可以应对90%JS相关的面试题。
1、javascript有几种数据类型?分别是什么?
JS数据类型分为两类:一类是基本数据类型
,包含7
种类型,分别是Number
、String
、Boolean
、BigInt
、Symbol
、Null
、Undefined
。另一类是引用数据类型
,通常用Object
代表,普通对象,数组,正则,日期,Math数学函数都属于Object。
本质区别:基本数据类型和引用数据类型它们在内存中的存储方式不同
。
基本数据类型是直接存储在栈
中的简单数据段,占据空间小,属于被频繁使用的数据。
引用数据类型是存储在堆内存
中,占据空间大。引用数据类型在栈中存储了指针
,该指针指向堆中该实体的起始地址
,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
Symbol
是ES6新出的一种数据类型,这种数据类型的特点就是没有重复的数据
,可以作为object的key
。
BigInt
也是ES6新出的一种数据类型,这种数据类型的特点就是数据涵盖的范围大
,能够解决超出普通数据类型范围报错的问题
。
2、let,const和var有什么区别?
1.变量提升:var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined。let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
2.重复声明:var允许重复声明变量。let和const在同一作用域不允许重复声明变量
3.暂时性死区:var不存在暂时性死区。let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
4.变量修改:const 声明的变量不能被重新赋值,而 let 和 var 可以。但是,使用 const 声明的对象或数组可以修改它们的属性或元素。
5.块级作用域:let 和 const 声明的变量具有块级作用域,可以在不同的块中重新定义变量。而使用 var 声明的变量在不同的块中被重新定义,会影响整个函数的作用域。
3、说说你了解的暂时性死区
如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。
4,箭头函数
- 箭头函数声明的全都是匿名函数,不能用于构造函数,并且不具有arguments对象,
- 箭头函数中的this指向与普通函数不同
- 在普通函数中,this总是指向调用它的对象,如果用作构造函数,他指向创建的对象实例。
- 箭头函数没有自己的this,他的this是继承而来的,默认指向在定义时他所在的对象,而不是执行时的对象。
5. 常用的数组方法
- push:向数组末尾添加一个或多个元素,并返回新数组的长度
- pop:从数组末尾删除一个元素,并返回该元素的值
- shift:从数组开头删除一个元素,并返回该元素的值
- unshift:向数组开头添加一个或多个元素,并返回新数组的长度
- splice:从数组中删除或替换一个或多个元素,可以传入三个参数,分别是
开始位置、要删除的元素数量、插入的元素
(一直与slice记混,重点记一下) - slice:返回数组的一个片段或子数组,可以传入两个参数,分别是
开始索引位置、截取到的索引位置
- concat:将两个或多个数组合并成一个新数组
- join:将数组元素连接成一个字符串
- indexOf():返回要查找的元素在数组中的位置,如果没找到则返回 -1
- includes():返回要查找的元素在数组中的位置,找到返回true,否则false
- find():返回第一个匹配的元素
- reverse:将数组元素反转
- sort:对数组元素进行排序
- map:对数组中的每个元素执行指定的函数,并返回一个新数组
- filter:对数组中的每个元素执行指定的测试函数,并返回一个新数组,其中包含所有通过测试的元素
- reduce:对数组中的每个元素执行指定的累加器函数,并返回一个累加器值。(
这个函数在实际开发中是一个特别好用的方法,如果能用好可以大大提高开发效率,具体的使用方式不在这里展开,如果想进一步了解这个方法,可以到我的另一篇文章:
reduce()方法详解) - forEach:对数组中的每个元素执行指定的函数,无返回值
需要留意的是哪些方法会对原数组产生影响,哪些方法不会,这也是一个比较重要的特性
会改变原数组的方法:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
6. js数据类型判断的方式
JavaScript有4种方法判断变量的类型,分别是typeof
、instanceof
、Object.prototype.toString.call()
(对象原型链判断方法)、 constructor
(用于引用数据类型)
1.typeof
:常用于判断基本数据类型
,对于引用数据类型除了function返回function,其余全部返回object
。
2.instanceof
:主要用于区分引用数据类型
,检测方法是检测的类型在当前实例的原型链上
,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。
原理:验证当前类的原型prototype
是否会出现在实例
的原型链__proto__
上,只要在它的原型链上,则结果都为true。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,找到返回true,未找到返回false。
3.constructor
:用于检测引用数据类型
,检测方法是获取实例的构造函数判断和某个类是否相同
,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
4.Object.prototype.toString.call()
:适用于所有类型的判断检测
,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。
原理:Object.prototype.toString 表示一个返回对象类型的字符串
,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果
这四种判断数据类型的方法中,各种数据类型都能检测且检测精准的就是Object.prototype.toString.call()这种方法。
7. 垃圾回收机制
js引擎会定期找出那些不在使用的变量,然后释放其内存,通常由两种方式,标记清除法和引用计数法。
标记清除法:
- 垃圾收集器在运行时会给
所有的变量都加上一个标记
,假设内存中所有的对象都是垃圾,全部标记为0
- 然后从各个跟对象开始遍历把不是垃圾的节点改成1
- 再清除所有标记为0的垃圾,销毁并回收他们所占用的内存空间
- 最后再把内存中的对象标记修改为0,等待下一轮垃圾回收。
优点是简单,只有标记和不标记两种状态,使得使用二进制位0和1就可以为其标记。
缺点是在清除之后,由于剩余对象内存位置是不变的,会导致空闲内存空间是不连续的,会出现内存碎片的情况。
引用计数法:他的策略是记录每个变量被使用的次数。
- 当声明了一个变量并且将一个引用类型赋值给改变量的时候这个值的引用次数就为1
- 如果同一个值又被赋给了另一个变量,那么引用数加一
- 如果该变量被其他的值覆盖了,则引用次数减一
- 当这个值的引用次数变为0的时候,说明没有变量在使用,垃圾回收器在运行的时候清理掉引用次数为0的值占用的内存。
优点:比标记清楚更加清晰,在引用值为0时就回收。
缺点:需要一个计数器,因为不知道引用数量的上限,所以计数器需要很大的位置,还有就是无法解决循环引用无法回收的问题。