作者碎碎念:
经历过一场场面试,然后又一场场被挂,小编就算是个铁人也会流泪滴!!!继续受虐虐虐虐,在此记录面试的高频考点,巩固基础,屡败屡战!!!!(SOS什么毒鸡汤???!!!)
BFC(1次)
定义:
MDN:区块格式化上下文(Block Formatting Context,BFC)是 Web 页面的可视 CSS 渲染的一部分,是块级盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
通俗来讲:BFC是一个独立的布局环境,可以理解为一个容器,在这个容器中按照一定规则进行物品摆放,并且不会影响其它环境中的物品。如果一个元素符合触发BFC的条件,则BFC中的元素布局不受外部影响。
解决布局问题:浮动、外边距重叠
构成BFC方法:
- HTML根元素
- float: left | right(除none)
- position: absolute | fixed
- display: inline-block | flex | inline-flex | grid | inline-grid | table-cell |table-caption | flow-root(专门用来创建BFC属性)
- overflow: 除了visible
- contain: layout | content | paint(现代浏览器支持)
清除浮动
浮动会造成父元素高度塌陷(没有设置height)
方法:BFC、clear: both、伪元素(不影响布局复杂度)
像素单位(1次)
px:绝对单位
em:相对单位,相对父元素的字体大小
rem:相对单位,相对根元素(html)的字体大小
- vw/vh:视口宽高的百分比
CSS选择器以及优先级(2次)
选择器 | 格式 | 优先级权重 |
id | #id | 100 |
类 | .class | 010 |
伪类 注意区分 | li:last-child | 010 |
属性 | a[class="refValue"] | 010 |
元素 | div | 001 |
伪元素 注意区分 | div::after | 001 |
相邻兄弟 | h1+p | 000 |
子 | ul>li | 000 |
后代 | li a | 000 |
* | *] | 000 |
提示:内联样式的优先级是1000(比id还高) ,!important声明的样式的优先级最高
伪类和伪元素的区别(字节一面)
伪类
将特殊效果添加到特定选择器上,在已有元素上添加类别,不产生新元素
a:hover {color: #FF00FF}
p:first-child {color: red}
p:nth-child(odd) {color: pink}
伪元素
在内容元素的前后插入额外元素/样式,插入的元素实际上并不在文档中生成。它们只在外部显示可见,但不会在文档的源代码中找到它们,因此,称为“伪”元素。
p::before {content:"第一章:";}
p::after {content:"Hot!";}
p::first-line {background:red;}
p::first-letter {font-size:30px;}
H5C3新特性
H5
- 语义化标签:header footer article section main nav
好处:1)便于搜索引擎爬虫准确抓取页面,提高页面在搜索结果中的排名。2)增加代码易读性,便于代码维护和更新
- 多媒体:video audio
- canvas绘图(画布)
- 本地存储:localStorage sessionStorage cookie
扩展:三者区别
- 地理位置的API
- 拖放
CSS3
- 弹性布局: display: flex 重要
- 网格布局: display: grid
- 渐变、阴影、过渡、动画
- 媒体查询:依据设备的屏幕尺寸、分辨率 使用不同的样式规则,实现响应式设计
- 自定义字体:@font-face
如何画一个三角形(基础)
把一个元素的宽高设置为0,然后给不同方向的边框设置颜色和宽度,接着想要哪个方向的三角就留下颜色,其余改为透明
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.traingle {
width: 0;
height: 0;
border: 10px solid transparent;
border-top-color: pink;
}
</style>
</head>
<body>
<!-- 画一个三角形 -->
<div class="traingle"></div>
</body>
</html>
自适应布局和响应式布局(1次)
自适应:随着视口变化,元素进行放大缩小 (淘宝无限适配方案:可以动态调整根元素font-size的值 -> rem)
响应式:通过媒体查询来设置特定屏幕尺寸的样式规则(750px 1080px 2000px)
<style>
/* 小屏幕设备样式 */
@media (max-width: 767px) {
body {
background-color: lightblue;
font-size: 14px;
}
.container {
padding: 10px;
}
}
/* 大屏幕设备样式 */
@media (min-width: 768px) {
body {
background-color: lightgreen;
font-size: 16px;
}
.container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
}
</style>
如何实现垂直居中对齐(5次,高频,唯品会一面,字节一面)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 绝对定位 1.*/
/* #parent {
position: relative;
width: 500px;
height: 500px;
background-color: aqua;
}
#center {
position: absolute;
width: 100px;
height: 100px;
line-height: 100px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
background-color: brown;
} */
/* 绝对定位 2.*/
/* #parent {
position: relative;
width: 500px;
height: 500px;
background-color: aqua;
}
#center {
position: absolute;
width: 100px;
height: 100px;
line-height: 100px;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
text-align: center;
background-color: brown;
} */
/* flex布局 */
/* #parent {
display: flex;
justify-content: center;
align-items: center;
width: 500px;
height: 500px;
background-color: aqua;
}
#center {
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
background-color: brown;
} */
/* flex中用margin布局 */
/* #parent {
display: flex;
place-items: center;
width: 500px;
height: 500px;
background-color: aqua;
} */
/* #center {
width: 100px;
height: 100px;
margin:auto;
line-height: 100px;
text-align: center;
background-color: brown;
} */
/*grid布局*/
/* #parent {
display: grid;
place-items: center;
width: 500px;
height: 500px;
background-color: aqua;
}
#center {
width: 100px;
height: 100px;
background-color: brown;
}*/
</style>
</head>
<body>
<!-- 实现三栏布局 -->
<div id="parent">
<div id="center">center</div>
</div>
</body>
</html>
效果图如下:
延伸:说一下flex布局(6次,超高频)
说一下flex怎么布局的,然后说一下常用属性的用法,让面试官觉得你会;
如何实现三栏布局(笔试3次,面试3次,CSS基础)
flex、绝对定位、浮动、双飞翼、圣杯(后两者布局还没理解到)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* flex布局 */
/* #parent {
width: 1000px;
height: 100px;
display: flex;
}
#left {
width: 100px;
text-align: center;
background-color: palegoldenrod;
}
#right {
width: 200px;
text-align: center;
background-color: blue;
}
#center {
flex: 1;
text-align: center;
background-color: brown;
} */
/* 绝对定位 */
#parent {
position: relative;
width: 1000px;
height: 100px;
}
#left {
position: absolute;
height: 100px;
width: 100px;
text-align: center;
background-color: palegoldenrod;
}
#right {
position: absolute;
width: 200px;
height: 100px;
top: 0;
right: 0;
text-align: center;
background-color: blue;
}
#center {
/* position: absolute;
left: 100px;
right: 200px;*/
height: 100%;
margin-left: 100px;
margin-right: 200px;
text-align: center;
background-color: brown;
}
/* 浮动:注意中间元素center要在末尾 */
/* #parent {
width: 1000px;
height: 100px;
}
#left {
float: left;
height: 100px;
width: 100px;
text-align: center;
background-color: palegoldenrod;
}
#right {
float: right;
width: 200px;
height: 100px;
text-align: center;
background-color: blue;
}
#center {
height: 100%;
margin-left: 100px;
margin-right: 200px;
text-align: center;
background-color: brown;
} */
</style>
</head>
<body>
<!-- 实现三栏布局 -->
<div id="parent">
<div id="left">left</div>
<div id="center">center</div>
<div id="right">right</div>
</div>
</body>
</html>
效果图都是这样,最好自己动手实操,加深印象~
盒子模型(4次)
width和height属性的范围:
- box-sizing: content-box,标准盒子模型:content + padding + border + margin
- box-sizing: border-box,IE(怪异)盒子模型: margin + content (content + padding + border)
面向对象(纪传体)vs面向过程(编年体)
面向过程(ex:C语言):负责完成某个具体任务的代码(可以理解为函数),核心:将要完成的事情拆分成一个个步骤,依次完成
面向对象(Object Oriented Programming ex:JAVA):以对象为核心,考虑各个对象有什么性质、能做什么事情;把事务先分解到对象身上,描述各个对象的作用,然后才是它们之间的交互
提取性质定义类创建对象(类是创建对象的模板,对象是类的实例),用对象绑定相关属性(放在类里面的变量)有利于让程序逻辑更加清晰,数据流动更加清晰
用对象绑定对象能实现的方法(放在类里面的函数)
结合方法和属性,能更优雅地处理逻辑
面向对象特性:
封装:写类的人将内部实现细节隐藏起来,使用类的人只通过外部接口(方法)访问和使用
继承:面向对象编程允许创建有层次的类
多态:同样的接口,因为对象具体类不同而有不同的表现
new操作符做了什么
- 创建了一个空对象
- 将空对象的原型指向构造函数的原型
- 将空对象作为构造函数的上下文(改变this指向)
- 对构造函数有返回值的处理判断
function Fun(age, name){
this.age = age;
this.name = name;
}
function create(fn, ...args){
// 1.创建了一个空对象
let obj = {};
// 2.将空对象的原型指向构造函数的原型
Object.setPrototypeOf(obj, fn.prototype);
// 3.将空对象作为构造函数的上下文(改变this指向)
let res = fn.apply(obj, args)
// 4.对构造函数有返回值的处理判断
return res instanceof Object ? res : obj;
}
console.log(create(Fun, 18, '王五'));
原型和原型链(2次,重点!)
原型:每一个函数都有prototype显式属性,称为原型(原型对象),原型有属性和方法与实例对象共享,可以继承
原型链:对象都有隐式__proto__属性,指向它的原型对象,原型对象也是对象,也有__proto__属性,指向原型对象的原型对象,这样一层一层形成的链式结构称原型链(最顶层为null),原型链是通过对象间的原型链接形成的属性查找路径
画图加深记忆(理解理解才能吸收!!)
继承
原型链继承:父类的实例赋值给子类的原型
优:子类可以共享父类的方法
劣:不能给父类传参、父类的引用数据类型可能被共享
<script>
function Person(name) {
this.name = name
}
Person.prototype.say = function () {
console.log(123);
}
function Son(age) {
this.age = age
}
Son.prototype = new Person()
console.log(new Son())
</script>
构造函数继承:在子类构造函数中调用父类构造函数
优:可以给父类传递属性
劣:无法共享父类方法
<script>
function Person(name) {
this.name = name
}
Person.prototype.say = function () {
console.log(123);
}
function Son(age) {
Person.call(this, age)
this.age = age
}
console.log(new Son(18)) // {name:18, age:18}
</script>
组合式继承:结合原型链继承和构造函数继承的优点
劣:复杂度增加、增加了父类对象的创建
<script>
function Person(name) {
this.name = name
}
Person.prototype.say = function () {
console.log(123);
}
function Son(age) {
Person.call(this, age) // 1
this.age = age
}
Son.prototype = new Person() // 2
console.log(new Son(18)) // {name:18, age:18}
</script>
class继承:本质基于原型链和构造函数继承
劣:低版本浏览器不支持
<script>
// 父类
class Person {
constructor(name) {
this.name = name
}
say() {
console.log(123)
}
}
// 子类
class Son extends Person {
constructor(name, age) {
super(name)
this.age = age
}
write() {
console.log(456)
}
}
const son = new Son('fu', 'zi')
console.log(son) // {name: 'fu', age: 'zi'}
son.say() // 123
son.write() // 456
</script>
ES6新特性(n次,必背必背!!!)
1、Symbol、BigInt数据类型
2、模版字符串:`${}`
3、新增let、const关键字
var、let、const区别:
- var没有块级作用域,let、const有(块级作用域 : { } )
- var存在变量提升(变量只能在声明之后使用,否在会报错),let(暂时性死区)、const没有
- var可以重复声明,let、const不可以
- var、let不用设置初始值,const必须设置(初始化)
- let创建的变量可以重新赋值,const不可以
4、箭头函数()=>{}
箭头函数和普通函数的区别:
- 箭头函数更简洁
- 箭头函数不绑定this,会捕获其所在上下文的this,作为自己的this。箭头函数中this的指向在它在定义时已经确定了,不会改变。普通函数的this指向调用者(谁调用就指向谁)。
- 箭头函数不能作为构造函数使用(如上因为不能绑定this)
- call()、apply()、bind()等方法不能改变箭头函数中this的指向
- 箭头函数没有prototype,当然就不存在原型;没有自己的arguments对象
- 箭头函数不能用作Generator函数,不能使用yeild关键字(作者自己标红,还不懂呜呜呜呜)
5、扩展运算符
6、class类
7、解构赋值
如何在不添加第三个变量的情况下交换两个变量的值?
let a = 1;
let b = 2;
[a, b] = [b, a]
console.log(a, b) // 2 1
8、新增Map、Set数据结构
Map、Set、json区别:
- JSON 对象在 JavaScript 中以键值对的形式表示,但键必须是字符串,且整个 JSON 对象必须是一个字符串。
- Map存储键值对的集合,键和值都能是任意类型,并且键具有唯一性,可迭代(用于
for...of
循环)。 - Set存储唯一的值的集合,可迭代(用于
for...of
循环)。
Map和weakMap区别:待续...
Set和weakSet区别:待续...
9、默认参数
即给函数设置默认参数
function greet(name, message = "Hello") {
console.log(`${message}, ${name}!`);
}
10、Promise:处理异步操作的对象
11、async/await
扩展:promise和async/await区别
async/await基于promise的语法糖(语法糖:简化,语法盐:复杂化)
12、模块导入导出:import export
13、??:空值合并运算符
- 若左侧操作数为
null
或者undefined
时,返回右侧操作数 - 若左侧操作数不为
null
或undefined
,则返回左侧操作数
14、?. :可选链操作符
const uer = {
name: 'dili',
address:{
street: 'Main Street'
}
}
console.log(user.address?.street)
// 若address属性存在,则继续访问其street属性并输出值
// 若address属性不存在,则返回undefined避免引用错误
15、replaceAll()(String的api)
let str = 'hello world, hello agin'
console.log(str.replaceAll('h', 'H')) // Hello world, Hello agin
16、顶层await
<script type="module">
// 模拟一个异步操作
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data fetched successfully');
}, 1000);
});
}
// 顶层使用 await
const data = await fetchData();
console.log(data);
</script>
JS的组成部分
ECMAScript、DOM(文档对象模型)、BOM(浏览器对象模型)
async和defer区别
作用:控制外部脚本的加载和执行顺序
- async:异步加载脚本,脚本文件会并行下载,不会阻塞页面的解析,下载完就直接执行
- defer:延迟加载脚本,脚本文件会并行下载,在文档完全解析之后,按顺序执行
javascript执行过程
预编译:代码执行前,先进行编译
JS代码执行过程:
- 通篇检查代码是否有语法错误
- 预编译
- 解释一行执行一行
变量声明和函数声明
- var存在变量提升(声明提升,赋值不会提升)
- 函数声明的提升是提升整个函数,包括里面的代码
注意:如果给一个没有进行声明的变量赋值,那么这个变量默认为全局变量
函数执行期上下文
活跃对象AO:activation object
- 创建AO对象 AO = {}
- 找函数的形参和变量声明,并赋值为undefined
- 把实参赋值给形参
- 找函数声明,并赋值函数体
- 执行代码 在预编译阶段执行过的代码,在函数执行期不再执行
遍历数组的方式有哪些?(1次)
map forEach filter reduce some/every find/findIndex flatMap
操作数组常用的方法有哪些?哪些方法会改变原数组?
不改变原数组,方法会返回一个新的数组或某个结果,不会修改原来数组的内容
slice、reduce、filter、map
改变原数组
push、pop、uhshift(+)、shift(-)、splice、sort、reverse
深浅拷贝(2次)
- 浅拷贝:只复制对象/数组的一层属性/元素,如果对象/数组的属性/元素是基本数据类型复制其值;但如果属性/元素是引用数据类型,则只会复制其引用,(即新对象/数组和原对象/数组的该属性/元素会指向同一个内存地址)
实现方式:Object.assign()、展开运算符
- 深拷贝:递归地复制对象/数组的所有属性,包括嵌套的对象和数组,创建一个完全独立的新对象/数组,新对象/数组和原对象/数组在内存中占用不同的地址,修改新对象/数组不会影响原对象/数组,反之亦然
实现方式:
1、JSON的序列化+反序列化(JSON.parse(JSON.stringfy()))
缺点:
- 不能识别BigInt类型
- 不能拷贝undefined、sysmbol、函数类型的值
- 不能处理循环引用(对象的属性直接或间接地引用了对象本身)
2、手搓递归
<script>
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj
}
let copy = Array.isArray(obj) ? [] : {}
for (let key in obj) {
copy[key] = deepCopy(obj[key])
}
return copy
}
let obj = { a: 1, b: { c: 2 } }
let copy = deepCopy(obj)
copy.b.c = 4
console.log(obj, copy)
// { a: 1, b: { c: 2 } } { a: 1, b: { c: 4 } }
</script>
Promise
Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,改善了回调地狱
如何捕获Promise中的错误信息(1次)
try/catch,Promise.then(,reject), Promise.catch(),Promise.finally()(无论成功失败都会执行)
Promise实例的三个状态(1次)
- Pending(进行中)
- Fulfilled(已完成)
- Rejected(已拒绝)
两个过程(pending起手):状态凝固
- pending -> fulfilled : Resolved(已完成)
- pending -> rejected:Rejected(已拒绝)
注意:一旦从进行状态变成为其他状态就永远不能更改状态了。
处理成功、失败
.then() 处理成功的情况 .then(onResolve,onRejected) 接受两个函数,第一个处理成功,第二个处理失败
.catch() 处理失败的情况 本质:.then(null,onRejected)语法糖
***Promise.all()和.race()(1次 美团)
.all()接受一个数组,返回一个新的promise,只有所有的promise都成功才会成功,如果有一个失败,那么立刻返回失败
场景题:我要上传多张图片,但是我要等所有图片上传成功后,再去调提交,应该怎么做?
.race()接受一个数组,返回一个新的promise,只要有一个promsie完成(成功/失败),就立刻决定整个promsie的结果
***原理
- 初始化promise:创建promise对象,构造函数会立刻调用一个函数,该函数有两个参数resolve(函数,把promise状态从pending -> fullfilled),reject((函数,把promise状态从pending -> rejected))
- 执行异步操作:在执行函数的时候,它一般是个异步任务(发送请求),异步任务的结果决定了promise的状态(任务成功则调用resolve函数,失败则调用reject函数,改变promise状态),一旦执行状态不可变
如何实现Promis功能(要看Promis源码)(1次)
this/call/apply/bind区别(4次,背背背!!!)
这三个方法都显式指定调用函数的 this 指向
- call,第一个参数this绑定的对象,其余参数需要依次列举出来,立即执行
- apply,第一个参数this绑定的对象,第二个传参数数组,立即执行
- bind,第一个参数this绑定的对象,仅定义(创建新函数)未执行
// bind
<script>
function say(arg, msg) {
return `${arg}, ${this.name}, ${msg}`
}
const obj = { name: 'dili' }
const newSay = say.bind(obj, 'Hello')
console.log(newSay('I\'m liuyifei'))
</script>
this指向
全局this --> window
普通函数中this --> 谁调用指向谁(默认window)
对象中this --> 函数作为对象的方法被调用,this指向调用该方法的对象
构造函数this --> 新创建的实例对象
箭头函数this --> 外层普通函数/外层作用域的this
定时器中this -> window(定义在window对象下)
事件循环(必要)
JS是单线程的(why?设计为多线程如果同时添加删除同一DOM节点会出问题),为了防止阻塞,将代码分为同步and异步
常见宏任务:script(代码块),setTimeout/setInterval,setImmediate定时器、事件、ajax
常见微任务:Promise.then()/catch(),ASync/Await,Object.observe,process.nextTick(node)
执行顺序:执行栈中的同步代码 -> 微任务队列(直到没有微任务) -> 宏任务队列
说完概念面试官一般会让你做一道关于时间循环的题(穿插seetTimeout、Promise),我的建议是多看多练,就会对事件循环理解更深刻。
先自己做一下哦~
setTimeout(() => {
console.log('setTimeout');
Promise.resolve().then(() => {
console.log('setPro1');
}).then(() => {
console.log('setPro2');
})
}, 0);
setTimeout(() => {
console.log('set');
}, 0)
Promise.resolve().then(() => {
console.log('then1');
}).then(() => {
console.log('then2');
}).then(() => {
console.log('then3');
})
公布答案啦~
下面出一个纯Promise的,很绕很绕,直接晕厥
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then(res => {
console.log(res);
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
})
闭包(3次)
MDN:闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。
内部函数可以访问外部函数作用域且被外部函数返回就形成闭包,闭包可以使变量私有(不会造成变量污染)
经典面试题:循环中使用闭包解决 var 定义函数的问题
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
首先使用了立即执行函数(5次)将 i
传入函数内部,这个时候值就被固定在了参数 j
上面不会改变(j取到的值都是对应循环中的i值),当下次执行 timer
这个闭包的时候,就可以使用外部函数的变量 j
,从而达到目的。
弊端以及注意点:
-
避免内存泄漏:确保在不需要闭包时,手动释放捕获的变量。
-
优化性能:避免在嵌套很深的函数中使用闭包,减少作用域链的长度。
-
保持代码清晰:尽量避免复杂的闭包逻辑,确保代码易于理解和维护。
注意:内存泄漏可能会延伸垃圾回收机制(V8)
浏览器的跨域问题
跨域问题其实就是浏览器的同源策略造成的。同源策略是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。同源指的是:协议、域名、端口号必须一致。
如何解决跨域问题
跨域问题涉及后端,建议找视频看看,文字内容比较抽象实践一下印象更深;
(1)CORS(跨域资源共享),CORS需要浏览器和服务器同时支持,整个CORS过程都是浏览器完成的,无需用户参与。因此实现CORS的关键就是服务器,只要服务器实现了CORS请求,就可以跨源通信了。
浏览器将CORS分为简单请求和非简单请求:
简单请求(不会触发预检请求OPTIONS):请求方法:HEAD/GET/POST,请求头信息默认不改变
浏览器会直接发出CORS请求,它会在请求的头信息中增加一个Origin(协议+域名+端口)字段, 如果Orign指定的域名在许可范围之内,服务器返回的响应头字段中至少有Access-Control-Allow-Origin(和Origin字段存储的数据相等)
非简单请求:简单请求的要求之外的情况,非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求。预检请求使用的请求方法是OPTIONS,他的头信息中的关键字段有Origin,Access-Control-Request-Method,Access-Control-Request-Headers,服务器在收到浏览器的预检请求之后,会根据头信息的三个字段来进行判断,如果返回的头信息在中有Access-Control-Allow-Origin这个字段就是允许跨域请求,如果没有,就是不同意这个预检请求,就会报错。
(2)nginx反向代理接口跨域;
(3)JSONP(已过时):利用<script>
标签没有跨域限制,通过<script>
标签src属性,发送带有callback参数的GET(只能是GET)请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
XSS攻击(3次)
定义:跨站脚本注入攻击,攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。
本质:网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
攻击类型:
- 存储型:攻击者将恶意脚本存储在服务端数据库中,当有请求发送过来,跟随响应数据一起返回,浏览器解析并执行,完成攻击
- DOM型:通过修改页面DOM节点形成xss
- 反射型:攻击者诱导用户访问带有恶意代码的URL,服务端处理请求并返回带有恶意代码的数据,浏览器把这段数据当脚本解析并执行,完成攻击
如何预防?(2次,待更新...)
1.对用户输入进行严格验证
明确规定用户输入的格式、长度和允许的字符范围。(脚本通常需要很长的字段)
2.过滤危险字符
对于用户输入的内容,过滤掉可能会被用于 XSS 攻击的危险字符,如 <
、>
、&
等
3.设置HTTP头Content-Security-Policy(CSP)
Content-Security-Policy: default-src'self'; script-src'self' example.com
4.对敏感的 Cookie 设置 HttpOnly 标志,这样javascript脚本就无法访问(服务端)
setcookie('session_id', '123456', time() + 3600, '/', '', false, true);
什么是CSRF(Cross-site request )攻击
定义:跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求(和被攻击网站同源)。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
本质:利用 cookie 会在同源请求中携带发送给服务器的特点(服务器根据域名判断),以此来实现用户的冒充。
常见攻击类型:
- GET型:ex: 在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
- POST型:ex: 构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
- 链接型:ex: 在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。
当输入一个网址按下Enter键会发生什么(美团一面,2次)
重排一定会触发重绘,重绘不一定触发重排
布局没有发生变化,跳过layout+layer阶段,直接进行paint(绘制)阶段
触发回流(重排):
- 首次渲染页面
- 添加/删除元素
- 改变元素大小/位置/内容、字体大小
- 调整浏览器窗口大小
- 查询某些属性或调用某些方法(clientWidth/clientLeft/clientHeight..., getcomputerdStyle(),getBoundingClientReact())
如何避免回流:避免频繁操作样式、DOM(脱离文档流,隐藏修改再显示)、transform/opacity/filters/will-change不会引起回流重绘
触发重绘:没有出现几何图形的变化的CSS样式
浏览器缓存机制(百度一面,浏览器原理必需知道,2次)
我将缓存机制分为四种类型
浏览器首次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header(响应头),以供下次加载时对比使用;
- cache-control: max-age;请求数据时,响应头中的cache-control字段,表示有效时间内的重复请求浏览器无需再次访问服务器,直接使用缓存资源;
- expires:指定过期时间,同样是服务端的响应头字段,在有效的时间点之前,浏览器无需再次访问服务端,直接使用缓存资源;
- Etag(服务端)/If-None-Match(客户端):首次请求资源时,服务端将缓存结果签名并设置在响应头的Etag字段中发送给客户端,客户端缓存Etag和结果数据,并在下一次请求时将Etag设置在请求头的If-None-Match字段中,服务端接收请求会比较Etag和If-None-Match是否一致, 若一致,返回304告诉客户端资源没有发生变化,客户端接收304状态码直接访问之前缓存的结果数据;
- Last-Modified(服务端)/If-Modified-Since(客户端):首次请求资源时,服务端将缓存Last-Modified(结果数据最新的更改时间)并设置在响应头的Last-Modified字段中发送给客户端,客户端缓存Last-Modified和结果数据,并在下一次请求时将Last-Modified设置在请求头的If-Modified-Since字段中,服务端接收请求比较Last-Modified和If-Modified-Since,如果Last-Modified < If-Modified-Since(请求资源的时间在结果数据变化之后,资源是最新的),说明结果数据并没有发生变化,返回304告诉客户端资源没有发生变化,客户端接收304状态码直接访问之前缓存的结果数据;
下一次加载时,强缓存优先级更高,先开始强缓存,判断cahe-control中的max-age(时间点)是否过期(若没有max-age,则判断expirse(时间段)是否过期);
若没过期,命中强缓存,直接使用本地资源;
若过期,开始协商缓存,在请求头中携带If-None-Match/If-Modified-Since向服务端发送请求,服务端判断If-None-Match中的etag值(若没有etag,比较If-Modified-Since是否在Last-Modified之后)是否改变;
如果没有改变(在后面),命中协商缓存,返回304;
如果改变(在之前),返回200+新的资源文件+新etag(Last-Modified)
总的来说,就是设置资源过期时间和判断结果数据是否发生变化两种方式,理解记忆哦~
OSI七层模型(字节一面)
应用层 | 为应用程序提供服务(http发生在这一层) |
表示层 | 数据格式转化、数据加密 |
会话层 | 建立、管理和维护会话 |
传输层 | 建立、管理和维护端到端的连接(TCP、UDP在这一层,规定了数据包的传输方式) |
网络层 | IP选址以及路由选择(规定了数据包的传输路线) |
数据链路层 | 提供介质访问和链路管理(传输路线) |
物理层 | 物理层(通过物理介质传输比特流) |
REACT的diff算法(2次,待更新...)
核心思想:
REACT的Fiber
问题:REACTv15在渲染时会递归对比虚拟DOM,找到需要更新的节点然后同步更新他们,一气呵成。整个过程REACT会占用浏览器资源,,这会导致用户触发的事件得不到响应,并且会导致掉帧,导致用户感觉到卡顿
解决:
为了给用户制造一种应用很快的“假象”,不能让一个任务长期霸占着资源。 可以将浏览器的渲染、布局、绘制、资源加载(例如 HTML 解析)、事件响应、脚本执行视作操作系统的“进程”,需要通过某些调度策略合理地分配 CPU 资源,从而提高浏览器的用户响应速率, 同时兼顾任务执行效率。
所以 React 通过Fiber 架构,让这个执行过程变成可被中断。“适时”地让出 CPU 执行权,除了可以让浏览器及时地响应用户的交互,还有其他好处:
- 分批延时对DOM进行操作,避免一次性操作大量 DOM 节点,可以得到更好的用户体验;
- 给浏览器一点喘息的机会,它会对代码进行编译优化(JIT)及进行热代码优化,或者对 reflow 进行修正。
核心思想:Fiber也称协程或纤程,它和线程不一样,本身不具有并发或并行的能力(需要线程配合),它是一种控制流程让出机制。让出CPU的执行权,让CPU先执行优先级高的任务(如与用户交互)。渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。
V8的垃圾回收机制——GC算法分代式垃圾回收机制
V8将内存(堆)分为新生代和老生代。
新生代中的对象一般存活时间较短,使用 Scavenge GC 算法。在新生代空间中,内存空间分为两部分,分别为 From 空间和 To 空间。在这两个空间中,必定有一个空间是使用的,另一个空间是空闲的。新分配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了。算法会检查 From 空间中存活的对象并复制到 To 空间中,如果有失活的对象就会销毁。当复制完成后将 From 空间和 To 空间互换,这样 GC 就结束了。
老生代中的对象一般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法和标记压缩算法。
对象会出现在老生代空间中的情况:
- 新生代中的对象是否已经经历过一次 Scavenge 算法,如果经历过的话,会将对象从新生代空间移到老生代空间中。
- To 空间的对象占比大小超过 25 %。在这种情况下,为了不影响到内存分配,会将对象从新生代空间移到老生代空间中。
在这个阶段中,会遍历堆中所有的对象,然后标记活的对象,在标记完成后,销毁所有没有被标记的对象。在标记大型对内存时,可能需要几百毫秒才能完成一次标记。这就会导致一些性能上的问题。为了解决这个问题,2011 年,V8 从 stop-the-world 标记切换到增量标志。在增量标记期间,GC 将标记工作分解为更小的模块,可以让 JS 应用逻辑在模块间隙执行一会,从而不至于让应用出现停顿情况。但在 2018 年,GC 技术又有了一个重大突破,这项技术名为并发标记。该技术可以让 GC 扫描和标记对象时,同时允许 JS 运行。
清除对象后会造成堆内存出现碎片的情况,当碎片超过一定限制后会启动压缩算法。在压缩过程中,将活的对象向一端移动,直到所有对象都移动完成然后清理掉不需要的内存。
防抖和节流
先介绍具体含义和可能使用的场景(一定一定一定不要游戏回城技能起手,典型的B站大学选手)
- 防抖:确保在指定时间间隔内,仅最后一次触发的事件才会执行函数
应用场景:输入框输入、窗口大小调整
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 使用示例
const inputHandler = debounce(() => {
console.log('Input event triggered');
}, 300);
document.getElementById('myInput').addEventListener('input', inputHandler);
- 节流:防止高频触发,在一段时间内仅触发一次
应用场景:滚动、鼠标移动事件
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// 使用示例
window.addEventListener('resize', throttle(() => {
console.log('Window resized');
}, 1000));
Webpack热更新原理(唯品会一面,字节一面,待更新...)
TS篇
interface和type区别(3次 待更新...)
如何实现子接口仅继承部分父接口且具有额外属性
使用typescript的工具类型Pick筛选出子接口想要继承的变量
Pick属于 TypeScript 内置的工具类型,其用途是从某个类型里选取特定的属性,进而构建出一个新类型
interface Parent {
P1: string;
P2: number;
P3: boolean;
}
type PartialParent = Pick<Parent, "P1" | "P2">;
// 添加额外属性1.
interface Child extends PartialParent {
C1: string;
}
let children: Child = {
P1: "hello",
P2: 123,
C1: "world",
};
console.log(children);
// 添加额外属性2.添加一个字符串索引签名 可自定义添加0-n个属性
interface Child {
[propName: string]: any;
}
let children1: Child = {
P1: "hello",
P2: 123,
C1: "world",
extra: "extra",
name:"name"
}
console.log(children1);
手写代码篇
数组去重(3次)
// 数组去重的方法
const equal = function (list) {
// 1.利用Set特性
// let _list = [...new Set(list)];
// 2.数组indexOf、includes方法
// for (let i = 0; i < list.length; i++) {
// // if (_list.indexOf(list[i]) < 0) {
// if (!_list.includes(list[i])) {
// _list.push(list[i]);
// }
// }
// 3.数组filter方法和indexOf组合技
// let _list = list.filter((item, index, array) => {
// return array.indexOf(item) === index;
// })
// 4.数组reduce和indexOf/includes组合技
let _list = list.reduce((pre, cur) => {
if (!pre.includes(cur)) {
pre.push(cur);
}
return pre;
}, [])
return _list;
}
console.log(equal([1, 2, 3, 4, 5, 23, 1, 2, 3, 4, 5]));
//4. 利用Map映射+循环 Map.has()的时间复杂度是O(1)!!!!!!
//总体时间复杂度为O(n)
function uniqueArray(arr: number[]) {
const map = new Map<number, boolean>();
const uniArr: number[] = [];
for (let index in arr) {
if (!map.has(arr[index])) {
map.set(arr[index], true);
uniArr.push(arr[index]);
}
}
return uniArr;
}
console.log(uniqueArray([1, 1, 1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 7, 8, 8]));
对象扁平化
// 处理对象,使得 { a: {b: { c: 1, d:2 } } } 变成 { abc: 1, abd:2 }
function flatObj(obj: any, prefix: string = "", result: object = {}) {
for (let key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
flatObj(obj[key], prefix + key, result);
} else {
result[prefix + key] = obj[key];
}
}
return result;
}
const obj = { a: { b: { c: 1, d: 2 }, e: 3 } }; // { abc: 1, abd: 2, ae: 3 }
console.log(flatObj(obj));
先这样吧,洗洗睡咯...嘻嘻天天都在洗洗睡睡~略略略,就喜欢平平淡淡的生活