前言
做为一名前端小白,终于迎来了个人的第一次面试,内心的起伏可谓波澜壮阔,是紧张,是激动,是忐忑,亦或是胆怯,带着这些,我开启了面试之旅,接受了面试官的灵魂拷问!......一个多小时下来,体验感还好,面试官挺好的,挺温柔的,中途卡住了提醒,遇到不会的会说没事的,没关系。就是自己拉跨了,哈哈哈,很多问题都没有答全,更有直接卡住的,在电话面前更是不知如何是好,尴尬至极。但这个过程却是学习成长+战胜自我。
面试题
1. 如何实现水平垂直居中
水平居中
-
对于行内元素: text-align: center;
-
对于
确定宽度
的块级元素:1.width和margin实现。margin: 0 auto;
2.绝对定位和margin-left: -width/2, 前提是父元素position: relative
-
对于
宽度未知
的块级元素:1.table标签配合margin左右auto实现水平居中。使用table标签(或直接将块级元素设值为display:table),再通过给该标签添加左右margin为auto。
2.inline-block实现水平居中方法。display:inline-block和text-align:center实现水平居中。
3.绝对定位+transform,translateX可以移动本身元素的50%。
4.flex布局使用justify-content:center
垂直居中
1.利用line-height实现居中,这种方法适合纯文字类
2.通过设置父容器相对定位,子级设置绝对定位,标签通过margin实现自适应居中
3.弹性布局flex:父级设置display: flex; 子级设置margin为auto实现自适应居中
4.父级设置相对定位,子级设置绝对定位,并且通过位移transform实现
5.table布局,父级通过转换成表格形式,然后子级设置vertical-align实现。(需要注意的是:vertical-align: middle使用的前提条件是内联元素以及display值为table-cell的元素)。
6.flex方案: align-items: center;
2. 重绘和重排
重排(回流):
GPU拿到渲染树后,进行布局绘制叫做重排
怎样触发重排:
-
页面首次渲染
-
添加/删除可见的DOM元素
-
改变元素的位置
-
改变元素的尺寸
-
改变窗口大小
-
offsetWidth offset ...... clientTop client...
重绘:
当页面上的DOM节点发生非布局变更时GPU需要再次绘制叫做重绘
怎样触发重排:
- 当页面上的DOM改变了非几何信息的属性时
注意: 重排一定重绘,重绘不一定重排
3. 盒模型
盒模型分为IE盒模型和W3C标准盒模型
W3C 标准盒模型:
属性width,height只包含内容content,不包含border和padding。
IE 盒模型:
属性width,height包含border和padding,指的是content+padding+border。
width = border + padding + 内容的宽度
height = border + padding + 内容的高度
注意:当前W3C标准中盒模型是可以通过box-sizing自由的进行切换的。(笔者在这吃了亏)
4. ES6的新特性
-
不一样的变量声明:const和let
-
模板字符串: 基本的字符串格式化,将表达式嵌入字符串中进行拼接。用${}来界定; ES6反引号(``)直接搞定;
-
箭头函数: 箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;
-
函数的参数默认值
// ES6之前,当未传入参数时,text = 'default';
function printText(text) {
text = text || 'default';
console.log(text);
}
// ES6;
function printText(text = 'default') {
console.log(text);
}
printText('hello'); // hello
printText();// default
- 二进制和八进制字变量:ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:
let oValue = 0o11;
console.log(oValue); // 9
let bValue = 0b11; // 二进制使用 `0b` 或者 `0B`
console.log(bValue); // 3
- 对象和数组解构
// 对象
const student = {
name: 'Jack',
age: 20,
sex: '男'
}
// 数组
// const student = ['Jack', 20, '男'];
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + age + sex);
// ES6
const { name, age, sex } = student;
console.log(name + age + sex);
-
对象超类
-
for...of和for...in for...of 用于遍历一个迭代器,如数组:
let letter = ['a', 'b', 'c'];
letter.size = 3;
for (let letter of letters) {
console.log(letter);
}
// 结果: a, b, c
for...in 用来遍历对象中的属性:
stu.size = 3;
for (let stu in stus) {
console.log(stu);
}
// 结果: Jack, 20, 男
-
ES6中的类:ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。
-
Spread/Rest操作符
5. 暂时性死区
let
和 const
声明的变量不存在变量提升,其作用域都是块级作用域,凡是在声明变量之前使用变量就会报错,所以,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
if (true) {
// 死区开始
z = 'lut'; // ReferenceError
console.log(z); // ReferenceError
// 开始声明变量,死区结束
let z;
console.log(z); // undefined
z = 520;
console.log(z); // 520
}
这里就不得不深入到let
、const
和var
的区别了:
-
变量提升方面:var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined。
let和const不存在变量提升问题(注意这个‘问题’后缀,其实是有提升的,只不过是let和const具有一个暂时性死区的概念,即没有到其赋值时,之前就不能用),即它们所声明的变量一定要在声明后使用,否则报错。
-
块级作用域方面:var不存在块级作用域,let和const存在块级作用域
-
声明方面:var允许重复声明变量,let和const在同一作用域不允许重复声明变量。其中const声明一个只读的常量一旦声明,常量的值就不能改变。
6. 对防抖和节流的理解和认识
防抖
防抖这个技术点允许我们将多个相似的调用分成一组,或者可以理解为多个相同的事件最后只执行一次。 防抖函数的作用就是控制函数在一定时间内的执行次数。防抖意味着 N 秒内函数只会被执行一次(最后一次),如果 N 秒内再次被触发,则重新计算延迟时间。
<button id="btn">防抖提交</button><!-- 设置表单 -->
<script>
function success(e){
console.log('提交成功')//设置success函数,并且打印结果
}
// 防抖函数
const debounce=(fn,delay)=>{// 1 创建防抖函数debounce
let timer=null // 创建一个标记用来存放定时器的返回值
return (...args)=>{//4 进行解构
clearTimeout(timer)//5 每当用户输入的时候把前一个setTimeout 清除掉
timer = setTimeout(()=>{//3 然后又创建一个新的setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行fn 函数
fn.apply(this,args)//6 绑定this作用域和接受success参数
},delay)
}
}
const oDebounce=debounce(success,2000)//2 设置间隔2秒执行一次 success被debounce修饰为oDebounce 然后调用
let btn=document.getElementById('btn')
btn.addEventListener('click',oDebounce) // 防抖 click调用oDebounce
</script>
节流
节流函数的实现原理是,将即将被执行的函数用定时器延时一段时间后执行。如果本次延时执行还没有完成,则忽略调用函数的请求。节流函数接受两个参数,第一个是 需要被延时执行的函数,第二个是需要延迟的时间。 节流函数的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。
<button id="btn">节流提交</button> <!-- 设置表单 -->
<script>
function success(e){
console.log('提交成功')//设置success函数,并且打印结果
}
//节流函数
const throttle =(fn,delay)=>{//1 创建节流函数throttle
let flag=true//5 通过闭包保存一个标记
return (...args)=>{//4
if(!flag) return // 在函数开头判断标记是否为true,不为true则return
flag=false//6 立即设置为false
setTimeout(()=>{//3 将外部传入的函数的执行放在setTimeout中
fn.apply(this,args) // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。
flag=true //当定时器没有执行的时候标记永远是false,在开头被return掉
},delay)
}
}
const oThrottle=throttle(success,2000)//2 设置间隔2秒执行一次 success被throttle修饰为oThrottle 然后调用
let btn=document.getElementById('btn')
btn.addEventListener('click',oThrottle) // 节流 click调用oThrottle
</script>
7.谈谈垃圾回收机制
JavaScript 具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。
原理:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。
通常有以下两个策略:
-
标记清除(最常用):当变量进入环境(例如,在函 数中声明一个变量)时,就将这个变量标记为“进入环境”。 而当变量离开环境时,则将其 标记为“离开环境”。
过程:
- 给存储在内存中的所有变量都加上标记;
- 去掉环境中的变量以及被环境中的变量引用的变量的标记;
- 之后再被加上标记的变量将被视为准备删除的变量;
- 销毁那些带标记的值并回收它们所占用的内存空间。
-
引用计数(不常见):当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就加1;如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成 0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。
缺点:有循环引用的问题。
笔者在被问到时直接懵圈了,尴尬至极
8. OSI体系结构
OSI是Open System Interconnect的缩写,意为开放式系统互联。其各个层次的划分遵循下列原则:
-
一层中的各网络节点都有相同的层次结构,具有同样的功能。
-
同一节点内相邻层之间通过接口进行通信。
-
七层结构中的每一层使用下一层提供的服务,并且向其上层提供服务。
-
不同节点的同等层按照协议实现对等层之间的通信。
熟记OSI模型结构的顺序,笔者被问到该知识点支支吾吾的,不知如何是好........
9.TCP和UDP的区别
TCP是一个面向连接的、可靠的、基于字节流的传输层协议。
UDP面向无连接,UDP协议只是数据报文的搬运工,不保证有序且不丢失的传递,UDP没有任何流量控制算法,相对TCP来说比较轻便
-
面向无连接
在发送端,应用层将数据传递给传输层的UDP协议,UDP只负责给数据只增加一个UDP标识,然后就传递给网络层
在接收端,网络层将数据传给传输层,UPD去除IP报文头就传给应用层,不会进行任何拼接操作
-
不可靠性
接收到什么数据就传递什么数据,并且不会备份数据,发送数据也不会关系对象是否接收到
UDP没有拥塞控制,一直会以恒定的速度发送数据,即使网络不好,也不会进行调整(会导致包丢失)
-
高效
比TCP轻量,头部开销小(只有8字节),传输数据很快
10. 从输入url到页面的渲染过程
-
首先做 DNS 查询,进行DNS 解析的,会提供访问速度最快的 IP 地址回来
-
接下来是 TCP 握手
-
TCP 握手结束后会进行 TLS 握手,然后就开始正式的传输数据
-
数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用就是将请求合理的分发到多台服务器上,这时假设服务端会响应一个 HTML 文件
-
浏览器开始解析文件,如果是 gzip 格式的话会先解压一下,然后通过文件的编码格式知道该如何去解码文件
-
文件解码成功后会正式开始渲染流程,先会根据 HTML 构建 DOM 树,有 CSS 的话会去构建 CSSOM 树。
-
初始的 HTML 被完全加载和解析后会触发 DOMContentLoaded 事件
-
CSSOM 树和 DOM 树构建完成后会开始生成 Render 树,这一步就是确定页面元素的布局、样式等等诸多方面的东西
-
在生成 Render 树的过程中,浏览器就开始调用 GPU 绘制,合成图层,将内容显示在屏幕上了
一套流程下来,如能一气呵成,那是相当完美
你会发现在面试当中TTTP的知识必会灵魂拷问,推荐神三元的文章
11. cookie、sessionstorage和localstorage的区别
生命周期
cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后失效
localStorage:除非被手动清除,否则将会永久保存。
sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。
存放数据大小
cookie:4KB左右
localStorage和sessionStorage:可以保存5MB的信息。
http请求
cookie:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
localStorage和sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性
cookie:需要程序员自己封装,源生的Cookie接口不友好
localStorage和sessionStorage:源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
12. git常见命令及回滚操纵
初学者对于git只知道git add
.、 git commit -m 'lianxi'
、git push origin main
这三个招式,其实git的招式还有很多,奉上下图
git通用流程图
当被问到git回滚操作时,我脑海里搜索着点滴的记忆,硬是想不起来.........
回滚操作:
git reset origin/master --soft
13. Koa和Express的区别
因为之前用这两个框架配置过环境,但未曾想上来就碰到了......
请看:
koa2
-
基于node的一个web开发框架,利用co作为底层运行框架,利用Generator的特性,实现“无回调”的异步处理;
-
更小、更富有表现力、更健壮的基石;
-
利用async函数、Koa丢弃回调函数,增强错误处理;
-
很小的体积,因为没有捆绑任何中间件;
-
类似堆栈的方式组织和执行;
-
低级中间件层中提供高级“语法糖”,提高了互操性、稳健性;
Express
-
Node的基础框架,基础Connect中间件,自身封装了路由、视图处理等功能;
-
线性逻辑,路由和中间件完美融合,清晰明了;
-
弊端是callback回调方式,不可组合、异常不可捕获;
-
connect的执行流程:connect的中间件模型是线性的,即一个一个往下执行;
对于这两个开发框架,笔者用的也不是很多,日后还得深入学习,详细可见
14. 谈谈数据库中的事务
事务就是要保证一组数据库操作要么全部成功 要么全部失败。不是所有的数据库引擎都支持事务,MyISM 不支持事务,InnoDB 支持事务
事务具有四个特性 ACID
-
A 原子性 要么全部成功要么全部失败
-
C 一致性 确保一个事务执行之前和执行之后必须处于一致的状态
-
I 隔离性 一个事务在提交之前是否能够被其他事务可见
-
D 持久性 一旦一个事务提交了,那么这个改变就是永久性的
事务隔离具有四种
-
读未提交 一个事务还没有提交,其他事务能够看到它做的变更
-
读提交 一个事务提交之后,其他事务才能看到变更
-
可重复读 一个事务在执行过程中,看到的数据总是跟这个事务在启动时看到的数据一致
-
串行化 事务不可并行执行,后访问的事务必须等前一个事务完成,才能继续执行
事务的启动方式
-
显示启动事务 begin 启动, commit 提交, rollback 回滚
-
程序会自动提交事务
15. 谈谈对Set的认识
ES6中的Set主要的应用场景在于数组去重和数据存储,Set是一种叫做集合
的数据结构
集合
-
集合是由一组无序且唯一(即不能重复)的项组成的,可以想象成集合是一个既没有重复元素,也没有顺序概念的数组
-
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
-
Set 本身是一个构造函数,用来生成 Set 数据结构
Set实例的属性和方法
Set的属性:
- size:返回集合所包含元素的数量
Set的方法:
-
操作方法
-
add(value):向集合添加一个新的项
-
delete(value):从集合中移除一个值
-
has(value):如果值在集合中存在,返回true,否则false
-
clear(): 移除集合里所有的项
-
-
遍历方法
-
keys():返回一个包含集合中所有键的数组
-
values():返回一个包含集合中所有值的数组
-
entries:返回一个包含集合中所有键值对的数组(感觉没什么用就不实现了)
-
forEach():用于对集合成员执行某种操作,没有返回值
-
总结
面试当中还问了有关React
生命周期和React-hooks
的问题以及数个排序算法的原理,在这里给大家推荐一波: React全家桶以及十大排序算法
面试面试即为当面展示自我,可能我此次面试展示的并不是很好,但对我这个小白而言,却是一次成长的体验,是一次学习成长的经历,更是一次战胜自我的过程。
希望各位指出其中的错误(翻看各种资料,只能算偏完整),我会不断学习,未来可期。