文末
篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
-
通过 transform 实现垂直居中
-
使用弹性盒子居中
BFC
所谓 BFC,指的是一个独立的布局环境,BFC 内部的元素布局与外部互不影响。
触发 BFC 的方式有很多,常见的有:
-
设置浮动
-
overflow 设置为 auto、scroll、hidden
-
positon 设置为 absolute、fixed
常见BFC的应用
-
解决浮动元素令父元素高度坍塌的问题
-
解决非浮动元素被浮动元素覆盖问题
-
解决外边距垂直方向重合问题
JS 的基本数据类型有哪些?基本数据类型和引用数据类型的区别
基本数据类型
JavaScript共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt
这些数据可以分为原始数据类型和引用数据类型:
-
栈:原始数据类型(Undefined、Null、Boolean、Number、String)
-
堆:引用数据类型(对象、数组和函数)
基本数据类型和引用数据类型的区别
-
访问方式:
-
原始值:访问到的是值
-
引用值:访问到的是引用地址 (js不允许直接访问保存在堆中的对象、首先得到在堆中的地址,然后按照这个地址去获得对象的值)
-
比较方式:
-
原始值:比较的是值
-
引用值: 比较的是引用的地址
-
变量赋值:
-
原始值赋值:赋值的是新值,与原来互不影响
-
引用值赋值:赋值的是地址,指向原值所在堆内存中的地址
-
动态属性:
-
原始值:赋值的是值
-
引用值:赋值的是地址
ES6 新增哪些东西
-
箭头函数
-
字符串模板
-
支持模块化(import、export)
-
类(class、constructor、extends)
-
let、const 关键字
-
新增一些数组、字符串等内置构造函数方法,例如 Array.from、Array.of 、Math.sign、Math.trunc 等
-
新增一些语法,例如扩展操作符、解构、函数默认参数等
-
新增一种基本数据类型 Symbol
-
新增元编程相关,例如 proxy、Reflect
-
Set 和 Map 数据结构
-
Promise
-
Generator 生成器
let const var 的区别?什么是块级作用域?如何用?
-
var 定义的变量,是函数作用域,没有块的概念,可以跨块访问, 不能跨函数访问,有变量提升。
-
let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明。
-
const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改,无变量提升,不可以重复声明。
-
var不存在暂时性死区,let和const存在暂时性死区
-
let和const创建的全局变量没有给window设置相应的属性
暂时性死区:使用
let / const 命令声明变量之前,该变量都是不可用的
箭头函数与普通函数的区别
-
箭头函数没有自己的this、会捕获其所在的上下文的this值,作为自己的this值
-
箭头函数继承来的this指向永远不会改变
-
call()、apply()、bind()等方法不能改变箭头函数中this的指向
-
箭头函数是匿名函数,不能作为构造函数,不能使用new
-
箭头函数没有自己的arguments
-
箭头函数没有prototype(原型)
-
箭头函数不能用作Generator函数,不能使用yeild关键字
bind,apply,call三者的区别
-
三者都可以改变函数的this对象指向
-
三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
-
三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
-
bind是返回绑定this之后的函数,apply、call 则是立即执行
是否了解 JavaScript 中的包装类型?
包装对象 : 就是当基本类型以对象的方式去使用时,JavaScript 会转换成对应的包装类型,相当于 new 一个对象,内容和基本类型的内容一样,然后当操作完成再去访问的时候,这个临时对象会被销毁,然后再访问时候就是 undefined
代码理解:
var str = 'hello';
str.number = 10; //假设我们想给字符串添加一个属性 number ,后台会有如下步骤
(
var _str = newString('hello'); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象
_str.number = 10; // 2 通过这个对象调用包装对象下的方法 但结果并没有被任何东西保存
_str =null; // 3 这个对象又被销毁
)
console.log(str.number); // undefined 当执行到这一句的时候,因为基本类型本来没有属性,后台又会重新重复上面的步骤
(
var str = newString('hello');// 1 找到基本包装对象,然后又新开辟一个内存,创建一个值为 hello 对象
str.number = undefined;// 2 因为包装对象下面没有 number 这个属性,所以又会重新添加,因为没有值,所以值是未定义;然后弹出结果
str =null; // 3 这个对象又被销毁
)
复制代码
JS 中如何进行数据类型的转换?
类型转换可以分为两种,隐性转换和显性转换
显性转换
主要分为三大类:数值类型、字符串类型、布尔类型
三大类的原始类型值的转换规则我就不一一列举了,更多详情文章链接
👉
juejin.cn/post/695617…
数值类型(引用类型转换)
Number({a: 1}) // NaNNumber([1, 2, 3]) // NaNNumber([5]) // 5
第一步,调用对象自身的`valueOf`方法。如果返回原始类型的值,则直接对该值使用`Number`函数,不再进行后续步骤。
第二步,如果`valueOf`方法返回的还是对象,则改为调用对象自身的`toString`方法。如果`toString`方法返回原始类型的值,
则对该值使用`Number`函数,不再进行后续步骤。
第三步,如果`toString`方法返回的是对象,就报错。
补充一点:`valueOf`和`toString`方法,都是可以自定义的
复制代码
字符串类型(引用类型转换)
String({a: 1}) // "[object Object]"String([1, 2, 3]) // "1,2,3"`String`方法背后的转换规则,与`Number`方法基本相同,只是互换了`valueOf`方法和`toString`方法的执行顺序。
复制代码
隐性转换
类型 | 转换前 | 转换后 |
number | 4 | 4 |
string | “1” | 1 |
string | “” | 0 |
boolean | true | 1 |
boolean | false | 0 |
undefined | undefined | NaN |
null | null | 0 |
详情看上方👆文章链接
闭包
闭包是指有权访问另一个函数作用域中的变量的函数 ———— 《JavaScript高级程序设计》
- 闭包用途:
-
能够访问函数定义时所在的词法作用域(阻止其被回收)
-
私有变量化
-
模拟块级作用域
-
创建模块
-
闭包缺点:闭包调用函数的变量,并且这个变量在函数执行完之后,不能释放,会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏。
-
解决:变量设置成null
原型和原型链
原型
-
每个对象都有一个 proto 属性,该属性指向自己的原型对象
-
每个构造函数都有一个 prototype 属性,该属性指向实例对象的原型对象
-
原型对象里的 constructor 指向构造函数本身
原型链
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾null
作用域和作用域链的理解
作用域
(1)全局作用域
-
最外层函数和最外层函数外面定义的变量拥有全局作用域
-
所有未定义直接赋值的变量自动声明为全局作用域
-
所有window对象的属性拥有全局作用域
-
全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突。
(2)函数作用域
-
函数作用域声明在函数内部的变零,一般只有固定的代码片段可以访问到
-
作用域是分层的,内层作用域可以访问外层作用域,反之不行
(3)块级作用域
-
使用ES6中新增的let和const指令可以声明块级作用域,块级作用域可以在函数中创建也可以在一个代码块中的创建(由{ }包裹的代码片段)
-
let和const声明的变量不会有变量提升,也不可以重复声明
-
在循环中比较适合绑定块级作用域,这样就可以把声明的计数器变量限制在循环内部。
作用域链
-
在当前作用域中查找所需变量,但是该作用域没有这个变量,那这个变量就是自由变量。
-
如果在自己作用域找不到该变量就去父级作用域查找,依次向上级作用域查找,直到访问到window对象就被终止,这一层层的关系就是作用域链。
作用域链有一个非常重要的特性,那就是作用域中的值是在函数创建的时候,就已经被存储了,是静态的。
防抖和节流
我们在平时开发的时候,会有很多场景会频繁触发事件,比如说搜索框实时发请求,onmousemove、resize、onscroll 等,有些时候,我们并不能或者不想频繁触发事件,这时候就应该用到函数防抖和函数节流。
函数防抖(debounce),指的是短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行。
函数节流(throttle),指连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次… 。节流如字面意思,会稀释函数的执行频率。
区别在于:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行
宏任务和微任务
-
常见的宏任务有:setTimeout、setInterval、requestAnimationFrame、script等。
-
常见的微任务有:new Promise( ).then(回调)、MutationObserver 等。
宏任务和微任务的执行流程,总结起来就是:
js在调用时,优先取出微任务,并且在执行过程中如果创建了新的作业,则放在本次执行完后紧接着调用,微任务执行完成后,再取出宏任务执行
哪些情况会导致内存泄漏
以下四种情况会造成内存的泄漏:
-
意外的全局变量: 由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
-
被遗忘的计时器或回调函数: 设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
-
脱离 DOM 的引用: 获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
-
闭包: 不合理的使用闭包,从而导致某些变量一直被留在内存当中。
请介绍一下 JavaScript 中的垃圾回收站机制
JavaScript 具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行。
JavaScript 常见的垃圾回收方式:标记清除、引用计数方式。
标记清除方式:
-
工作原理:当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
-
工作流程:
-
垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记;
-
去掉环境中的变量以及被环境中的变量引用的变量的标记;
-
被加上标记的会被视为准备删除的变量;
-
垃圾回收器完成内存清理工作,销毁那些带标记的值并回收他们所占用的内存空间。
引用计数方式:
-
工作原理:跟踪记录每个值被引用的次数。
-
工作流程:
-
声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是 1;
-
同一个值又被赋值给另一个变量,这个引用类型值的引用次数加 1;
-
当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减 1;
-
当引用次数变成 0 时,说明没办法访问这个值了;
-
当垃圾收集器下一次运行时,它就会释放引用次数是 0 的值所占的内存。
防抖,节流是什么
-
函数防抖(debounce),指的是短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行
-
函数节流(throttle),指连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次…
深拷贝和浅拷贝
-
浅拷贝:只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做浅拷贝(浅复制) 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
-
深拷贝:在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响。
web浏览器中的javascript
- 客户端javascript
- 在html里嵌入javascript
- javascript程序的执行
- 兼容性和互用性
- 可访问性
- 安全性
- 客户端框架
- 开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
window对象
-
计时器
-
浏览器定位和导航
-
浏览历史
-
浏览器和屏幕信息
-
对话框
-
错误处理
-
作为window对象属性的文档元素