高频前端js面试题总结 对应的视频讲解位置
2023前端高频面试题-JS高频面试题(上)_哔哩哔哩_bilibili
目录
1. var let const 的区别?
2. javascript 有哪些基础数据类型?
3. null和undefined区别
4. == 与 === 的区别?
5. js中那些数据在 if 判断时是 false
6. 判断数据类型的方法有哪些,有什么区别
7. JS垃圾回收
8. this指向
9. call、apply、bind
10. 闭包、闭包的使用场景
11. 什么是事件代理/事件委托?
12. 如何阻止事件冒泡?
13. new操作符的实现原理
14. 回流与重绘
15. Javascript原型链
16. 深拷贝浅拷贝
17. 数组去重
18. 数组的方法
19. 箭头函数和普通函数的区别
20. forEach和map方法有什么区别
21. for...in和for...of的区别
1. var let const 的区别?
- const 定义常量, 不可以重复赋值 块级作用域 不存在变量提升
- var 定义变量,可以重复声明 var 全局作用域或函数作用域 有变量提升
- let 定义变量,不可以重复声明 , 块级作用域 不存在变量提升
2. javascript 有哪些基础数据类型?
- 基础数据类型String、Number、Boolean、Undefined、Null
- 引用数据类型: Object、Array、Function、Date等
- Symbol、BigInt是ES6之后新增的 属于基本数据类型
- Symbol
指的是独一无二的值
-
- BigInt
是一种数字类型的数据,它可以表示任意精度格式的整数
3. null和undefined区别
- 首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
- undefined 代表的含义是
未定义,一般变量声明了但还没有定义的时候会返回 undefined
- null 代表的含义是
空对象。null主要用于赋值给一些可能会返回对象的变量,作为初始化。
4. == 与 === 的区别?
- 双等号(==)进行相等判断时,如果两边的类型不一致,会强制类型转化再进行比较。
- 三等号(===)进行相等判断时,如果两边的类型不一致,不强制类型准换,直接返回 false
Object.is()
[]==[]
5. js中那些数据在 if 判断时是 false
- 0、“”、false、null、undefined、NaN 判断为false (6个)
- 其他皆为true
[]==false 和 ![]==false true
第一个 []==false 转为数字 0==0
第二个 ![]==false 转为布尔 false==false
[]==[] -- >false
js中布尔值为false的六种情况
下面6种值转化为布尔值时为false,其他转化都为true
1、undefined(未定义,找不到值时出现)
2、null(代表空值)
3、false(布尔值的false,字符串"false"布尔值为true)
4、0(数字0,字符串"0"布尔值为true)
5、NaN(无法计算结果时出现,表示"非数值";但是typeof NaN===“number”)
6、""(双引号)或’’(单引号) (空字符串,中间有空格时也是true)
注意空数组空对象,负值转的布尔值时都为true
6. 判断数据类型的方法有哪些,有什么区别
判断数据类型的方法 | 描述 |
typeof | 判断基础数据类型(数组、对象、null都会被判断为object) |
instanceof | 判断引用数据类型,不能判断基本数据类型 |
constructor | 判断数据的类型 |
Object.prototype.toString.call() | 使用 Object 对象的原型方法 toString 来判断数据类型 |
JavaScript类型判断的四种方法
一、typeof
typeof是一个操作符而不是函数,其右侧跟一个一元表达式,并返回这个表达式的数据类型。
返回的结果用该类型的字符串(全小写字母)
用于判断数据类型,返回值为6个[字符串],分别为string、Boolean、number、function、object、undefined。
console.log(typeof undefined) // undefind
console.log(typeof null) // object
console.log(typeof true) // boolean
console.log(typeof 43) // number
console.log(typeof '21') // string
console.log(typeof {a:1}) // object
console.log(typeof Symbol()) // symbol
console.log(typeof 123n) // bigint
function a() {}
console.log(typeof a) // function
var date = new Date()
var error = new Error()
console.log(typeof date) // object
console.log(typeof error) // object
二、instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型
instanceof用来判断对象,代码形式为obj1 instanceof obj2(obj1是否是obj2的实例),obj2必须为对象,否则会报错!其返回值为布尔值
通俗一些讲,instanceof 用来比较一个对象是否为某一个构造函数的实例。注意,instanceof可以准确的判断复杂数据类型,但是不能正确判断基本数据类型
console.log(12 instanceof Number) // false
console.log('22' instanceof String) // false
console.log(true instanceof Boolean) // false
console.log(null instanceof Object) // false
console.log(undefined instanceof Object) // false
console.log([] instanceof Array) // true
console.log({a: 1} instanceof Object) // true
console.log(json instanceof Object) // true
function a() { }
console.log(a instanceof Function) // true
console.log(new Date() instanceof Date) //true
let reg=new RegExp()
console.log(reg instanceof RegExp) //true
let error=new Error()
console.log(error instanceof Error) // true
三、Object.prototype.toString.call()
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
console.log(Object.prototype.toString.call(1)) // [object Number]
console.log(Object.prototype.toString.call(1n)) // [object BigInt]
console.log(Object.prototype.toString.call('123')) // [object String]
console.log(Object.prototype.toString.call(true)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(function a() {})) // [object Function]
console.log(Object.prototype.toString.call(Symbol())) // [object Symbol]
console.log(Object.prototype.toString.call(Math)) // [object Math]
console.log(Object.prototype.toString.call(JSON)) // [object JSON]
console.log(Object.prototype.toString.call(new Date())) // [object Date]
console.log(Object.prototype.toString.call(new RegExp())) // [object RegExp]
console.log(Object.prototype.toString.call(new Error)) // [object Error]
console.log(Object.prototype.toString.call(window) // [object Window]
console.log(Object.prototype.toString.call(document) // [object HTMLD
四、constructor
constructor属性,可以得知某个实例对象,到底是哪一个构造函数产生的。 constructor属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor属性,防止引用的时候出错。所以,修改原型对象时,一般要同时修改constructor属性的指向。
constructor属性,可以得知某个实例对象,到底是哪一个构造函数产生的。
constructor属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor属性,防止引用的时候出错。所以,修改原型对象时,一般要同时修改constructor属性的指向。
console.log('22'.constructor === String) // true
console.log(true.constructor === Boolean) // true
console.log([].constructor === Array) // true
console.log(document.constructor === HTMLDocument) // true
console.log(window.constructor === Window) // true
console.log(new Number(22).constructor === Number) // true
console.log(new Function().constructor === Function) // true
console.log((new Date()).constructor === Date) // true
console.log(new RegExp().constructor === RegExp) // true
console.log(new Error().constructor === Error) // true
注意:
1、null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
2、函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
7. JS垃圾回收
有两种垃圾回收策略:
- 标记清除:
标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。
- 引用计数:
它把对象是否不再需要简化定义为对象:有没有其他对象引用到它。如果没有引用指向该对象(引用计数为 0),对象将被垃圾回收机制回收。
8. this指向
9. call、apply、bind
三者区别
- 三者都可以改变函数的this对象指向
- 三者第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined或null,则默认指向全局window
- 三者都可以传参,
- call是参数列表,apply是数组,call和apply是一次性传入参数,
- bind是参数列表,但可以分为多次传入参数
- bind是返回绑定this之后的函数,apply、call 则是立即执行
应用场景
- call 经常做继承。
- apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值。
- bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。
10. 闭包、闭包的使用场景
闭包的概念:
闭包:是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
闭包的特性
1.函数内再嵌套函数 2.内部函数可以引用外层的参数和变量 3.参数和变量不会被垃圾回收机制回收
闭包的优缺点
- 优点:延长变量生命周期、私有化变量
- 缺点:过多的闭包可能会导致内存泄漏
闭包的应用场景
- ajax请求的成功回调
- 事件绑定的回调方法
- setTimeout的延时回调
- 函数内部返回另一个匿名函数
- 函数节流、防抖 封装模块
11. 什么是事件代理/事件委托?
事件代理/事件委托是利用事件冒泡的特性,将本应该绑定在多个元素上的事件绑定在他们的祖先元素上,尤其在动态添加子元素的时候,可以非常方便的提高程序性能,减小内存空间。
12. 如何阻止事件冒泡?
w3c的方法是e.stopPropagation(),
e.stopPropagation();
如何阻止默认事件?
w3c的方法是e.preventDefault()
function stopDefault( e ) { e.preventDefault(); }
13. new操作符的实现原理
- 首先创建了一个新的空对象
- 设置原型,将对象的原型设置为函数的 prototype 对象。
- 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
- 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
14. 回流与重绘
当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树。完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘
15. Javascript原型链
首先理解三个概念:
- prototype:原型对象,每个函数都有一个 prototype 属性,再通过 new 命令实例对象时,该属性会成为该实例的原型对象。
- constructor:构造函数。指向原型对象的 constructor
- __proto__:实例对象的原型
在javascript中,实例对象与原型之间的链接,叫做原型链。Javascript解析引擎在读取一个Object的属性的值时,会沿着原型链向上寻找,如果最终没有找到,则该属性值为undefined;如果最终找到该属性的值,则返回结果。
16. 深拷贝浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝
// 第一层为深拷贝 Object.assign() Array.prototype.slice() //扩展运算符 ...
深拷贝
JSON.parse(JSON.stringify())
递归函数
let obj = {
a: 'hello',
b: [10, 20],
c: { name: 'admin',age:{xx:''} }
}
//定义函数实现深层拷贝
function deepCopy(obj) {
//1. 先判断obj是否存在 是否是对象格式
if (obj === null || typeof obj !== 'object') {
return obj;
}
//2. 创建一个新的容器存储拷贝的数据
let newObj = Array.isArray(obj) ? [] : {};
//3. 遍历对象for循环可以 获取所有的下标keys
let temp=null;// temp键值
//遍历下标 --获取元素的值
for(let key in obj){
temp=obj[key];//键值
//获取键值 存储新的容器里--需要判断当前的键值的类型是不是对象
if(temp && typeof temp ==='object'){
newObj[key]=deepCopy(temp)
//newObject={a:hello,b}
}else{
//键值是基本数据类型可以直接存储
newObj[key]=temp;
}
}
return newObj;
}
let obj2=deepCopy(obj)
17. 数组去重
- 使用es6 set方法
[...new Set(arr)]
let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique =(arr)=>[...newSet(arr); unique(arr);//[1,2,3,4,6,7]
- 利用新数组indexOf查找indexOf()方法可返回某个指定的元素在数组中首次出现的位置。如果没有就返回-1。
- for双重循环通过判断第二层循环,去重的数组中是否含有该元素,如果有就退出第二层循环,如果没有j==result.length就相等,然后把对应的元素添加到最后的数组里面。
18. 数组的方法
数组的方法 | 描述 |
map | 遍历数组,返回回调返回值组成的新数组 |
forEach | 无法break,可以用try/catch中throw new Error来停止 |
filter | 过滤 |
some | 有一项返回true,则整体为`true |
every | 有一项返回false,则整体为`false |
join | 通过指定连接符生成字符串 |
push / pop | 末尾推入和弹出,改变原数组。push 返回数组长度, pop 返回原数组最后一项; |
unshift / shift | 头部推入和弹出,改变原数组,unshift 返回数组长度,shift 返回原数组第一项 ; |
sort(fn) / reverse | 排序与反转,改变原数组 |
concat | 连接数组,不影响原数组, 浅拷贝 |
slice(start, end) | 返回截断后的新数组,不改变原数组 |
splice(start, number, value...) | 返回删除元素组成的数组,value 为插入项,改变原数组 |
indexOf / lastIndexOf(value, fromIndex) | 查找数组项,返回对应的下标 |
reduce / reduceRight(fn(prev, cur), defaultPrev) | 两两执行,prev 为上次化简函数的return值,cur 为当前值 当传入 defaultPrev 时,从第一项开始; 当未传入时,则为第二项 |
19. 箭头函数和普通函数的区别
- 箭头函数比普通函数更加简洁
- 箭头函数没有自己的this
- 箭头函数继承来的this指向永远不会改变
- 箭头函数不能作为构造函数使用
- 箭头函数没有自己的arguments
- 箭头函数没有prototype
- call()、apply()、bind()等方法不能改变箭头函数中this的指向
20. forEach和map方法有什么区别
- forEach()方法会针对每一个元素执行提供的函数,对数据的操作会改变原数组,该方法没有返回值;
- map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值;
注意:
- forEach()会改变原数组的方法
- 参数:item数组中的当前项,index当前项的索引,array原始数组;
- 数组中有几项,那么传递进去的匿名回调函数就需要执行几次
使用场景:当我们对数组的元素进行处理时(例如:增加元素,元素值改变),可以使用这个函数
注意:
item数组中的当前项,index当前项的索引,array原始数组
map的回调函数中支持return返回值,return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆了一份,把克隆这一份的数组中的对应项改变了 );
map会改变原数组的方法,
map的执行速度更快,比forEach的执行速度快了70%;
使用场景:
map适用于你要改变数据值的时候,不仅在于它更快,而且返回一个新的数组,这样可以提高复用性(map(),filter(),reduce())等组合使用。
var arr = [1, 2, 3, 4, 5]; var newArr = arr.map(num => num * 2).filter(num => num > 5); // newArr = [6, 8, 10]
总结:
- 能用forEach()做到的,map()同样可以。反之亦成立
- map()会分配内存空间存储新数组并返回,forEach()不会返回数组
- forEach()允许callback更改原始数组的元素。而map()返回新的数组
21. for...in和for...of的区别
- for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
- for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
- 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;