面试准备
一、vue2
1、生命周期
1)、created(创建)==>data,mounted(挂载)==>dom
2)、执行顺序,有子组件的,注意生命周期中存在异步请求的情况时的执行。会在mouted执行之后才开始执行异步操作
父:beforeCreate created beforeMounte
子:beforeCreate created beforeMounte mounted
父:mounted
3)、Vue2中对象或数组数据更新但视图不刷新问题
数组:置空使用length=0不行,需要设置=[]。通过下标去改变时数据也不会触发响应式。 原因:因为v2是通过object.defineProperty实现响应式的,没办法劫持数组。是通过对数组方法(push、pop等)进行重写,将数组转换为对象后在进行劫持。所以data中定义的数组想要其具有响应式的话需要通过数组的方法进行操作。
对象:无法检测到复杂数据的添加或移除。并且只有存在于data中的preporty才具有响应式
因为vue2只会在数据
初始化劫持数据,会遍历对象的所有属性,对其进行get/set的转化。解决方式:$set,也可以使用$forceUpdate去重新渲染一下。或者用数组自带的方法splice等。vue2是对对象的每个属性进行劫持。vue3是直接劫持整个对象。不用遍历。
Vue3中不会存在这个问题,vue3的响应式是通过new proxy去实现的。Vue2是通过object.defineProperty实现的。
2、vue2的v-model扽双向绑定原理
通过数据劫持和发布订阅模式实现的。首先通过Object.defineProperty对数据进行劫持并监听数据的变化(监听数据的读取和修改),通过get/set方法对数据进行读写。当数据发生变化时,vue会通过发布订阅者模式通知所有订阅者进行更新,这里的订阅者指的是(组件、watcher等)
3、页面渲染的过程是怎样的?
> DNS解析
> 建立TCP连接
> 发送HTTP请求
> 服务器处理请求
> 渲染页面(以下是渲染页面的过程)
浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树
再把CSS解析成CSSOM
把DOM和CSSOM合并为渲染树
布局
把渲染树的每个节点渲染到屏幕上(绘制)
> 断开TCP连接
4、dom树和渲染树的区别
dom树是和html元素一一对应的,并且包括head和隐藏元素
渲染树不包括head和隐藏元素
二、vue源码相关
1、nextTick()的实现
$nextTick(callBack()=>{
return Promise.resolve().then(){
callBack()
}
})
5、v-if和v-show的区别
1、v-show有css的dispaly控制显隐,显隐切换的时候不会触发组件的生命周期,效率更高
2、v-if才是真正意义上的条件渲染,显隐切换的过程是编译和卸载的过程,会涉及到组件的重建和监听等。隐藏时会把整个dom删除。
6、vue的组件通讯
- prop/$emit
- ref/refs
- $chidren/ $parent
- provide/inject
- vuex
- 事件总线
7、vue的模板编译原理,ast(现代语法树)是如何将template转换为js的
整体流程
编译原理的实现
词法分析-->语法分析-->代码的转换-->代码生成
模板(字符串)
=>解析(词法分析、语法分析)- parser
=>模板AST (扁平的队列)
=>转换器 - transformer
=>JS AST (数据结构类型)
=>生成器 - generator
=>render
词法分析:
语法分析:
三、es6+
1、导入导出
注意的点:默认导出不用解构(default), 按需导出就要解构
//默认导出 export default{} //引用 import son from './son.vue'
//按需导出 export const{} //引用解构 import {son} from './son.vue'
2、promise和async await的区别
promise:es6的语法。处理和捕获异常要用到.then和.catch,链式调用,会造成代码重叠,可读性不高。 async await:es8的语法,基于promise实现的,通过try catch捕获异常。异步请求同步实现,遇到await会立刻返回结果。 都是处理异步请求的,具有非阻塞性
3、promise的内部原理及优缺点:
4、ES6的常用语法
扩展运算符···、模板字符串 `` 、let 、const、 promise、Set、Map
var的问题:1、声明提升 2、变量覆盖 3、var没有块级作用域
注意解构时重命名的写法:
5、 for...in 和 for...of的区别
for...in是一般用来循环对象的,返回的是的键名。 for...of用来循环数组,返回的就是数组中的元素。
6、promise
//构造函数同步执行。resolve调用后会继续执行后续的任何代码。
let promise=New promise((reslove,reject)=>{
console.log(1)
resolve()
console.log(2)
})
.then异步执行
promise.then(res=>{
console.log(3)
})
console.log(4)
//1 2 4 3
7、Map与object的区别
map用来存放hash结构的键值对
1、创建的区别(a是键名,1是值)
2、键名的区别:
object的键名必须是string/symbol类型,若是非string类型会将数据进行转换 map的键名可以是任意类型(string、object、array、function),不会进行类型转换,在添加类型的时候需要===严格判断键名是否存在
3、key的顺序
object的key是无序的,不会按照插入的顺序返回(但有一定的规律)
map的key是有序的,按插入的顺序返
4、键值对的size
object只能通过Object.keys().length或者循环去获取长度
map有专门的size属性获取键值对长度 map.size
5、键值对的访问
object:
map:
6、序列化
object可以进行JSON.stringify的序列化,但是map不行。可以采用Array.from将map对象转为object对象后进行序列化。
7、使用场景
8、async/await
1、async
用于定义一个异步函数,总是返回一个promise。能够用更简洁的方式编写异步代码
2、await
只能出现在async内部,用于等待一个promise结果。如果await后面跟的不是promise,则会被隐式的包装成为一个resolved的promise
四、vue3
1、pinia
pinia中store 的的内容不能解构,会使其失去响应式。
import { useCounterStore } from '@/stores/counter'
import { computed } from 'vue'
const store = useCounterStore()
const { name, doubleCount } = store// ❌这将不起作用因为它破坏了响应性
2、vue3的响应式原理
通过Proxy对象实现响应式,proxy可以直接代理整个对象,拦截对象的任意属性访问、修改、添加和删除等操作。无需遍历每一个属性。有更强大的数据观测能力。
4、prop传值和v-model传值的区别
prop是单向数据流的(: v-bind),子组件不能直接修改父组件传递过来的参数,只能通过自定义事件通知父组件去修改。以上只针对基本数据类型,不能进行修改。但如果prop传递过来的变量是一个复杂数据类型则可以修改,由复杂数据类型存储机制决定的,传过去的是栈内存中的地址,改变的是堆内存中的值,所以是可以的。
v-model是数据双向绑定的,子组件中可以直接修改父组件传递过来的参数。v-model是语法糖,可以拆分为v-bind:value和v-on:input(:value。@input)。v-model默认值为value,想要修改时在子组件中:emit('update:modelValue',222)即可。v-model可以绑定多个值,v-model:num,v-model:count等,父组件会自动监听update:num,update:count事件,所以不用手动接收自定义事件。
5、vue3的新特性
- composition API
- 响应式系统重构
- 内置组件
- suspense,异步组件的加载。可提供占位内容
- teleport,保持dom状态的前提下完成传送(配合disabled属性使用)
- fragment,使得template不再只支持一个根节点
- 更好的支持ts
- tree shaking 运行速度更块 打包体积更小
6、ref和reactive的区别
ref接收一个参数,返回的是ref对象,此对象具有一个value属性,通过.value属性可以获得其内部值。在模板中能够自动解构。ref处理基本数据类型是通过defineProperty、处理引用类型是通过proxy。
reactive接收一个对象,返回的是代理对象。不同于源对象。
7、异步组件的加载 suspense和defineAsyncComponent的区别
suspense是为了展示加载过程中的一个占位内容,可以实现延迟加载和错误处理。有两个插槽:default和fallback占位内容(可以放置加载状态效果)。当异步组件加载完成以后会自动替换占位内容。官方原话:在可能的时候都将显示默认槽中的节点。否则将显示后备槽中的节点。 可以结合## errorCaptured或者#onErrorCaptured函数进行错误处理。
<susepense>
<template #default>
<user/>
</template>
<template #fallback>
Loading...
</template>
</susepense>
defineAsyncComponent用以定义异步组件,用户可以控制加载时机。返回一个Promise的工厂函数,并且在调用该函数时才会加载对应的异步组件。
简写:
const asyncComponent=defineAsyncComponent(()=>{
import ('./component')
)
高级配置
const asyncComponent = defineAsyncComponent({
loader: () => import("./component.vue"),
loadingComponent: LoadingComponent, /* 在加载时显示 */
errorComponent: ErrorComponent, /* 显示是否有错误 */
delay: 1000, /* 在显示加载组件之前延迟毫秒 */
timeout: 3000 /* 这个毫秒之后的超时 */
})
8、计算属性的泛型显示注解
//type用于限制的是返回值的类型
//let aa = computed<type>(()=>{})
let arr1=ref([[1,2,3])
let arr2=ref([['1','2','3'])
let test = computed<number[]>(()=>{
return arr1.filter(item => item>1) //可以
return arr2.value //不可以
}
五、typescript
1、类的属性
private:受保护的属性,只能在类的内部去使用,实例也不能访问。
protect:只有当前类和继承类能访问。
一定要在外部(实例中)进行访问的话可以在类中定义一个方法去返回该属性。
2、TS属性的快捷操作
1、pick和omit
//pick:在已经定义的对象中选取一些
interface ZW {
name:string;
age:number;
location:string;
}
type zw1 = Pick<ZW, 'name'|'age'>
let zhaowa1:zw1={
name:'zhaowa',
age:18
}
//omit:在已经定义的对象中删除一些,留下剩下的
interface ZW {
name:string;
age:number;
location:string;
}
type zw1 = Omit<ZW, 'location'>
let zhaowa1:zw1={
name:'zhaowa',
age:18
}
2、ts的部分属性(Partial)
//注意区分可选属性和Partial的应用场景,可选属性占大部分的时候用Partial,占小部分用可选属性。
interface zhaowa {
name:string;
age:18
}
type zhaowa1 = Partial<zhaowa>
let obj:zhaowa1 = {
age:20
}
3、interface和type的区别
type:类型别名,可以定义基本数据类型、联合数据类型和元组。使用$扩展。不能重复定义。
interface:只能用于定义对象类型,可以声明对象上的属性和方法。使用extends扩展。可以重复定义,同名接口会自动合并。
两者可以相互扩展,都可以用来描述对象或函数。
后续:vue3中的ref变量定义,props传值,接口请求参数和返回值类型处理等等
1、function fn(a=1,b:string){} a代表可选,并且默认值是1
2、class的private属性是能在当前类的内部中使用,protect只能在当前类及其子类中使用(也就是继承这个类的子类中),static关键字表示将静态属性,表示将属性设置给类本身而不是实例,调用的时候也是通过类去调用,实例调用不到。class本身是构造函数的语法糖
private static属性只能以以下方式使用:
3、super关键字:
在继承类中如果想使用任何this.的语法时需要先调用super(),super 关键字用于访问和调用父类(基类)的属性和方法。
六、JS
1、数据类型的检测
- typeOf()
- instanceOf()
- constructor
- object.prototype.toString.call()
// 1、typeOf() 对于基本数据类型没问题,引用数据类型就不管用。
console.log(typeOf 666) //number
console.log(typeOf [1,2,3]) //object
// 2、instanceOf() 只能判断复杂数据类型,不能判断基本数据类型
console.log( [] instanceOf Array) //true
console.log( 'abc' instanceOf String) //false
// 3、constructor 可以判断基本数据类型和复杂数据类型,如果声明了一个构造函数并把他的原型指向了Array就不行了
console.log(('abc').constructor===String) //true
// 4、object.prototype.toString.call() //相对完美
let opt=object.prorotype.toString
console.log(opt.call(2)) //[object Number]
console.log(opt.call('abc')) //[object String]
console.log(opt.call(true)) //[object Boolean]
2、对闭包的理解及其特点
闭包:函数嵌套函数,内部函数被外部函数返回并且保存下来,就会产生闭包。不一定要return(如果外部不需要使用的话)。闭包=内部函数+引用外部函数变量
function aa() {
let val = 0
reurn function fn() {
val++
console.log(val)
}
}
let str = aa()
str()//1
str()//2
str()//3
注意:如果不赋值给aa()而是直接执行str函数,那么fn不会调用和打印val。str函数本身执行时只是定义了val变量并返回了内部函数fn,并没有执行fn里的内容。
每次执行一遍str(),由于是闭包,能够记住并访问外部函数str中的变量val,即使在str函数执行完毕后仍然如此。所以val会累加。后续多次进行调用时不会重新声明val,而是直接访问并修改外部已经存在的val。
let val = 0只会在最初aa()被调用时执行一次,闭包会记住外部函数中的变量val
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存再内存中,不会被垃圾回收机制回收
缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,在IE浏览器中才会导致内存泄漏
3、基本数据类型和引用数据类型的区别
基本数据类型:存储在栈内存中,保存的是一个具体的值
引用数据类型:保存在堆内存中,存储的是引用数据类型的地址 假如声明两个引用数据类型同时指向同一个内存地址,那么修改其中一个,另一个也会跟着改变
let obj={
name:'张三',
age:14
}
let obj1 = obj
obj1.name = '李四'
//此时obj.name也会变成李四。
导致以上代码出现的原因是,直接改变的原来地址指向的对象的属性而没有创建一个新对象。通过obj.name的方式拿到的是堆内存地址中的属性
4、原型和原型链
原型:每个构造函数身上都有prototype属性,这个属性是一个对象类型的,包含了一些公用的属性和方法。所以也称原型为原型对象。
原型链:每个对象身上都有_ptroto_属性,这个属性指向构造函数的原型。
原型链的形成:因为原型是对象类型的,所以原型也具有_ptroto_属性。指向原型对象的原型对象。一层一层的结构。
当实例调用某个属性或方法的时候会去找构造函数原型中的属性或方法,当在构造函数中找不到这个属性和方法时会到更上一级的也就是Object这个大对象中去找,这个过程像一个链子。
如何创建一个没有原型的对象?
1、使用Object.create(null) 2、将对象的_proto_属性设置为nullObject.setPrototypeOf(obj,null)或者通过Object.definePropoty({},'_proto_',{value:null})
5、如何实现一个深拷贝
1、...扩展符,缺点:当对象有多层结构时,只能实现第一层的深拷贝,多层的时候内层的数据还是浅拷贝。
2、JSON.parse(JSON.stringify()),缺点:该方法不会拷贝内部函数,会直接忽略掉。
3、利用递归函数实现,可以采用lodash库中的_.cloneDeep(value)方法
手动实现:
let origin={
name:'张三',
age:18,
say(){
console.log('say hello')
},
arr:[[1,2],3,4,5]
}
function exten(origin,deep){
let obj={}
if(origin instanceof Array){
obj=[]
}
for(let key in origin){
let value=origin[key]
obj[key]=(!!deep&&typeof value==='object'&&value!==null)?exten(value,deep):value
}
return obj
}
const oo=exten(origin,true)
6、防抖和节流
防抖:若干时间后才去执行该事件,若在限定时间内被重复触发,则重新计时。最后一次触发生效。应用场景:input框的实时搜索、窗口大小的调整
节流:限定时间内,即便重复触发,也只有一次是生效的。应用场景:按钮的点击、页面的滚动
//1、防抖 为了使得业务代码和防抖函数分开,并且防止变量污染全局。需要用到闭包去实现。
let inputDoc = document.querySelector('input')
inputDoc.onInput=debounce(function(){
console.log(this.value) //this为window===第八行的this
},1000)
const debounce=(fn,delay = 1000)=>{
console.log(this) //window
let timer=null
return function(){
if(timer) clearTimeOut(timer)
timer = setTimeOut(()=>{
fn.call(this) //如果这里不设置this的指向,箭头函数就会向外找,也就是window
},delay)
}
}
//debounce调用会返回一个函数,该函数的this指向为调用者也就是inputDoc。只有返回的函数this才指向inputDoc。箭头函数没有自己的this
//2、节流
let throttle=(fn,delay)=>{
let timer = null
return function(){
if(timer) return
timer = setTimeOut(()=>{
fn.call(this)
timer = null
},delay)
}
}
7、this指向问题及call、apply、bind
1)call:为立即调用函数,改变this指向,且只针对当次的调用有效。
function test(arg1,arg2){}
test.call('zhangsan') //this指向zhangsan。相当于zhangsan.test()
test.call('lisi') //this指向lisi。相当于lisi.test()
//参数
第一个参数是this指向的对象,后续的参数是给函数的参数,正常逗号隔开即可。
test.call('zhangsan',1,2)
2)、apply:也是立即调用函数。与call的区别就在于给函数的传参方式不同,是通过数组传参的而不是逗号隔开
test.aplly('zhangsan',[1,2])
3)bind:不是立即执行函数。可以将函数的this固定起来,这个方法不会对函数本身进行任何改造,而是会生成一个新的函数,所以需要接收一下。
传参同call。
function fn(){
}
let newFn = fn.bind('xiaoming')
newFn() //this永远指向xiaoming
解决this不符合预期的办法:
1、将关键字转为变量,也就是保存外部的this。$this = this
2、使用今箭头函数。箭头函数没有自己的this。会继承外部的this。所以不会对this的指向造成影响
3、bind方法。使用bind固定this的指向。
this指向函数的调用者,如果找不到调用者则指向window(如:作为参数的回调函数。浏览器环境下是window、node环境下是global)
8、js是如何运行在浏览器中的(V8)--原理
- 解析器将JS代码进行解析、生成AST,并创建上下文
- 解释器将AST转为字节码
- JIT(即时解析)运行时逐条解析执行=>高频热点代码=>机器码形式存在内存中
9、深浅拷贝问题
浅拷贝:不会复制对象本身,复制的是指向对象的地址指针。
10、script标签中的async和defer
async:异步执行,解决阻塞的问题。此时保不齐是页面先加载完还是script资源先下载完。但有时候脚本是需要修改dom的,就会造成出错。
defer:推迟,规定不论脚本是否先加载完脚本必须等到html执行完了才会执行。
11、this关键字的指向有哪几种
函数调用时:指向window
对象方法调用时:指向调用方法的对象
构造函数调用时:指向新创建的对象
apply、call、bind:调用时指向指定的对象
箭头函数:继承自父作用域
12、通过new创建一个对象的过程
1、 创建一个空对象
2、将构造函数的作用域赋给新对象(因此this就指向新对象)
3、 指向构造函数中的代码(给新对象添加属性)
4、 如果构造函数没有返回其他对象(注意此处一定要是对象,return 后面跟其他值是不返回的。也就是说如果构造函数返回的是基本类型的值,则忽略),则new表达式就返回这个新对象(new Foo),否则返回构造函数返回的对象
2:
手动实现new的过程
13、js的事件循环,宏任务和微任务
将异步任务分为宏任务和微任务,在es5之后引入了promise,这样,不需要浏览器js引擎自身也能实现异步任务了。
1、宏任务是由宿主发起的(浏览器、node)
setTimeout、setInterval、i/o、script
2、微任务是由js引擎发起
微任务的优先级高于宏任务 promise(promise本身同步,then/catch的回调函数是异步的)、async/await、nextTick
3、执行顺序
1、同步代码(js执行栈/回调栈) 2、微任务(先进先出) 3、宏任务(先进先出)
案例:
输出:11 14 12 15 13
当11和14的同步代码执行完毕后发现微任务队列为空,宏任务队列有一个setTimeout,则执行宏任务,进行一轮新的事件循环。
输出 2 3 6 p2 p1 1 4 5
注意是236而不是623.因为promise本身同步,应该从上往下执行。
注意:js会将await后面的代码推到微任务队列。await fn(); console.log(111),那么console.log(111)会被推入到微队列中。 await相当于promise.then
七、浏览器及网络相关
1、垃圾回收与内存泄漏
垃圾回收:后续不需要在用到的内存,浏览器会在某一时间清除掉
内存泄漏:后续不需要在用到的内存,但是垃圾回收机制清不掉。清不掉是因为浏览器没法判断该内存后续是否会触达,解决办法:人工排除内存泄漏比较大的地方将其进行赋值为null,告诉浏览器后续不会再使用了。比如说清除定时器、闭包中条件判断清除内存、避免创建全局变量。
2、localStorage和sessionStorage的区别
localStorage:持久化存储,除非主动删除或用户清空浏览器数据,否则会一直存在
sessionStorage:会话存储的方式,只存在于当前浏览器窗口或当前标签页,一旦关闭则数据消失。
两者存储空间为5MB左右
3、token存在sessionStorage还是localStorage中
token:验证身份的令牌,当用户通过账密登录成功后,服务器将这些凭证通过一系列加密操作等后得到的字符串
localStorage/sessionStorage:后续每次请求都需要把他当中一个字符串传给后端
cookie:会自动发送,但是不能跨域
由于localstorage除非主动清除否则会一直存在,如果网站被恶意攻击,token存在localstorage中并不安全。推荐存储在sessionstorage中。如果需要浏览器关闭后token依然存在,则可以考虑使用localstorage,但需要定时刷新token或者使用http通信。
4、浏览器的缓存策略
强缓(本地缓存):不需要像后端发送请求,直接使用缓存中的内容。浏览器会把JS、CSS、image等资源存在内存中,下次请求直接从内存中取,提供性能。
协商缓存(弱缓存):需要向服务器发送请求,如果请求内容(比较请求头头部信息,而不是直接比对响应内容)没有发生变化,则返回304,使用缓存中的内容。(解决强制缓存的情况下,资源不更新的问题)
响应头参数:cache-control
- 设置为
max-age=300(或其他数字时)为强缓存 - 设置为
no-cache为协商缓存 - 设置为
no-store,要求浏览器不进行缓存
响应头参数:last-modify(协商缓存的参数)
第一次请求,响应头里面会包含有个last-modify字段用于记录最后一次修改的事件。当第二次请求的时候会把值赋给if-modify-since作为请求头参数传递给服务器,服务器会去比对当前值与最新更新值的时间,如果两个值相等,则返回状态码304,使用本地的数据。如果两值不相等,则返回200,重新取值。(last-modify单位是s,也就是说如果一秒内值被多次修改,则监测不到这个值的更新,会出现bug)
cookie缓存和强缓存的区别
cookie缓存的一般都是用户的信息等,存放的是键值对的数据。
强缓存存的是一些静态资源比如js、css、img等,用以减少网络请求次数。存放的是文件资源
5、浏览器的同源策略
要求协议、域名、端口号三者一致,不一致会出现跨域
三个跨域加载资源的标签:img、script、link
解决跨域的方法:JSONP、core、websocket、反向代理
6、TCP/IP
TCP:传输控制协议。 面向字节、可靠的、基于bit
IP:网络层面、报文包
网络接口层(物理层、数据链路层)、网络层、传输层、应用层
在多个不同网络间实现信息传输的规范
7、get post的内容格式,缓存特性
1、get
是通常用来获取资源,请求参数通常附加在url中作为查询字符串。支持缓存。
2、post
post通常用于提交数据到服务器,请求体中可以包含任意格式的数据(JSON、XML、表单数据)。不支持缓存,因为请求体中可以包含敏感数据,并且post方法一般用于改变服务器上的状态,缓存的话会导致安全问题
8、常见的响应头
- cache-control:用于控制http的响应行为,可以设置为max-age、no-cache,no-store
- content-type:描述了返回内容的媒体数据,包括text/html或者application/json
- Etag:提供一个资源版本的标识符,用于验证资源是否被修改
- Date:服务器生成响应的日期和时间
- expires:指定响应何时过期,并且不应在被缓存
9、前端安全问题XSS和CSRF
1、xss
跨站脚本攻击,用户提交的数据中可以注入恶意脚本,并且执行,从而窃取用户信息的攻击(如cookie)。既涉及到前端也涉及到后端。
分为三种:反射型攻击、存储型攻击、DOM攻击
反射型:url中的内容有些会被反射,展示到页面中。如果此时ur中包含恶意代码,也会被植入到页面并且执行。
输入的内容被标签包裹,b标签中跨域插入别的标签,此时h1会生效
存储型:比如可以发表评论的网站,将恶意代码当作评论进行发送,如果服务器不进行过滤,就会把评论永久得保存起来,也就是存储进数据库。顾客在访问评论区得时候就会自动执行恶意代码。或者用户私信
DOM型:如果源码中存在可以操作得方法和属性,导致网页文档结构被修改
修复方式:
永远不要让外部输入直接被插入文档中,比如url
1、对实体字符进行转义(在html中表示特殊字符的方法,<、>表示< >),后端处理
2、使用http only来禁止JavaScript读取cookie值
3、输入内容得长度限制,加大攻击得难度
3、输入时校验,浏览器与web应用端采用相同字符编码
2、csrf
跨站请求伪造攻击,诱导已登录用户访问恶意网站,利用用户的会话信息发起未经用户许可的操作(xss是实现csrf的一种方式),一个网站访问另一个网站,依赖于cookie自动发送得机制。
修复方式
1、尽量采用post方法而不是get方法,get接口太容易被利用了
2、加入验证码,某一次提交得时候让用户输入验证码,可以确保是用户得行为而非黑客得行为。
3、校验referer:http请求头上得referer会记录当前一次请求得来源地址,如果referer得值不是当前网页,就拒绝这个请求。
4、随机令牌:每次访问目标网站时都携带一个随机令牌,目标网站在处理请求时会校验令牌得有效性,攻击者无法伪造随机令牌。从而避免csrf攻击。每一次请求完毕后就销毁掉
八、CSS
1、display有哪些属性
flex、grid、none、block、inline、inline-block、table ··· ···
2、css高度塌陷原因及解决方案
1、高度塌陷
原因:父元素没有设置高度,其高度是由子元素撑开的。当子元素设置为浮动元素等一系列脱离文档流的定位时,会造成父元素的高度为0。
解决方案:
1、给父元素设置高度
2、开启BFC(格式化上下文,即变成一个独立的布局区域,不会影响到别的元素),给父元素设置overflow:hidden/overflow:auto
3、给最后一个元素下面在添加一个块级元素,设置clear:both
2、外边距塌陷
原因:在两个垂直外边距(margin)相遇时并不会简单的相加,而是合并成一个外边距,取最大的那个值。发送在父子元素或相邻兄弟元素之间。
外边距计算规则:
正数&&正数:取最大的数
负数&&负数:去绝对值最大的数
正数&&负数:取量值相加的和
解决方案
1、使用clear:both
2、设置父元素overflow:hidden
3、使用伪元素:content:'',clear:both,display:block
4、使用负的margin
5、使用flex或者grid布局
十、其他
1、对nodejs的理解
js的运行环境,在没有nodejs的时候js只能运行在浏览器中。有了nodejs后可以运行在浏览器环境之外,操作系统之上。可以操作文件、读取数据库。
node环境和浏览器的区别:
2、svg
基于xml语法格式的图像(其他格式是基于像素的),矢量图,对图像的形状描述,放大不会失帧。文本文件、体积小。
可直接插入页面当中,成为dom的一部分,可以用js和css操作。可作为文件引入。可以转为base64引入页面。
3、网站怎么优化?
1精灵图,2懒加载3减少http请求,,,减少对DOM的操作、减少监听器的数量、一定要答一下 缓存(设置合适的cache control)
4、函数柯里化
固定某个函数的一些参数,得到该函数剩余参数的一个新函数,如果没有剩余参数则调用
也就是说将一个接收多个参数的函数,转换为一系列中接收一个参数的函数。是的函数更具有灵活性,易于组合和复用。
实现:
function curry(fn) {
const arity = fn.length; // 获取原函数需要的参数数量
return function curried(...args) {
if (args.length < arity) {
// 如果传递的参数不足,则返回一个新的函数
return function(...moreArgs) {
return curried(...args, ...moreArgs);
};
} else {
// 如果参数足够,则执行原函数
return fn(...args);
}
};
}
// 使用柯里化包装 add 函数
const add = (a, b) => a + b;
const curriedAdd = curry(add);
// 使用柯里化后的函数
const add5 = curriedAdd(5);
// 返回一个新的函数,等待第二个参数
console.log(add5(3)); // 输出 8
代码解析:fn.length,函数的length返回的是函数期望接收的参数个数。当传入的参数和预期一样多时则直接执行原函数,如果参数不够时则返回一个新函数
5、前端跨域请求的问题、项目中遇到如何解决
1、Jsonp
通过动态插入script标签发起跨域请求,只适用于get请求并且存在安全隐患,且不能捕获到错误。通过script标签实现。不推荐使用
因为有些标签不受同源策略的影响:script、img、link、iframe、video等具有src属性的标签
*2、前端配置代理中转请求
因为跨域是浏览器的保护机制,如果脱离浏览器发送请求,就不会收到跨域的影响。也就是服务器和服务器之间的通信。
利用中转服务器发送请求和接收响应。保持与中转服务器的url一致。
以vite为例
*3、服务器配置cors(资源共享)
Access-control-allow-origin:该属性表示哪些域名跨域访问资源。使用*(通配符)的话则所有ip都能访问
华为od
1、XSS、CSRF
1、xss
跨站脚本攻击,用户提交的数据中可以注入恶意脚本,并且执行,从而窃取用户信息的攻击(如cookie)。既涉及到前端也涉及到后端。
分为三种:反射型攻击、存储型攻击、DOM攻击
反射型:url中的内容有些会被反射,展示到页面中。如果此时ur中包含恶意代码,也会被植入到页面并且执行。
输入的内容被标签包裹,b标签中跨域插入别的标签,此时h1会生效
存储型:比如可以发表评论的网站,将恶意代码当作评论进行发送,如果服务器不进行过滤,就会把评论永久得保存起来,也就是存储进数据库。顾客在访问评论区得时候就会自动执行恶意代码。或者用户私信
DOM型:如果源码中存在可以操作得方法和属性,导致网页文档结构被修改
修复方式:
永远不要让外部输入直接被插入文档中,比如url
1、对实体字符进行转义(在html中表示特殊字符的方法,<、>表示< >),后端处理
2、使用http only来禁止JavaScript读取cookie值
3、输入内容得长度限制,加大攻击得难度
3、输入时校验,浏览器与web应用端采用相同字符编码
2、csrf
跨站请求伪造攻击,诱导已登录用户访问恶意网站,利用用户的会话信息发起未经用户许可的操作(xss是实现csrf的一种方式),一个网站访问另一个网站,依赖于cookie自动发送得机制。
修复方式
1、尽量采用post方法而不是get方法,get接口太容易被利用了
2、加入验证码,某一次提交得时候让用户输入验证码,可以确保是用户得行为而非黑客得行为。
3、校验referer:http请求头上得referer会记录当前一次请求得来源地址,如果referer得值不是当前网页,就拒绝这个请求。
4、随机令牌:每次访问目标网站时都携带一个随机令牌,目标网站在处理请求时会校验令牌得有效性,攻击者无法伪造随机令牌。从而避免csrf攻击。每一次请求完毕后就销毁掉
2、cookie、sessionstorage、localstorage的区别,安全性如何,为什么?
cookie:可以设置过期时间,如果没有设置,则浏览器关闭时清空。存储大小为4KB,一般用于存储登录状态等。会随着每次发送http请求而自动发送,安全性较差,但是可以设置http only属性来限制js读取cookie中得值,也可以设置secure来确保cookie只能通过https进行传输
sessionStorage:会话式存储,值保存在当前窗口或标签页,当窗口或标签页关闭时清空。存储大小为5MB。不会随着http请求自动发送,相对安全
localStorage:持久性存储,除非用户手动清除或清空浏览器缓存,否则数据一直存在。存储大小为5MB
3、普通函数与箭头函数的区别
普通函数:
1、普通函数得this是在执行时动态生成得
箭头函数
1、箭头函数得this是在创建时就确认了,继承自父作用域
2、箭头函数不能作为构造函数使用,因为没有自己的this
3、箭头函数书写更简洁
4、call、apply、bind都无法改变箭头函数中的this指向
nomal.biubiu()为undefined,因为作为参数得回调函数得this指向window,此时得window中没有biubiu属性
4、http和https
http
- HTTP 是一种用于传输超文本(如 HTML 文档)的应用层协议。
1、无状态的:也就是说每次请求都需要登录,不会记录这个登录的状态
2、无连接的:每次请求都需要重新建立连接
3、明文传输:请求和响应不会对通信发进行确认,无法保证数据的完整性
4、简单、快速、灵活
5、基于请求和响应:由客户端发起请求,服务器进行响应
6、默认端口为80
https
1、基于http协议,通过SSL或TLS方式加密。
2、默认端口为443
3、需要SSL/TLS的证书来验证服务器的身份
5、https证书的作用,前后端的工作流程
https证书的作用
1、提升SEO的排名,https协议排名会在http之上
2、用于加密:给服务器和浏览器之间的通信数据进行加密
3、验证身份:验证是否是已登陆的服务器,用于区分合法网站和假冒网站
前后端的工作流程 搭建静态页面-->完善用户交互-->发送请求-->响应请求-->展示响应结果-->错误处理
6、事件循环
将异步任务分为宏任务和微任务,在es5之后引入了promise,这样,不需要浏览器js引擎自身也能实现异步任务了。
1、宏任务是由宿主发起的(浏览器、node)
setTimeout、setInterval、i/o操作、script
2、微任务是由js引擎发起
微任务的优先级高于宏任务 promise(promise本身同步,then/catch的回调函数是异步的)、async/await、nextTick
3、执行栈
所有的同步代码都在执行栈中执行,先进先出
4、任务队列
分为宏任务队列和微任务队列,遇到异步操作后,将异步操作的回调函数放入任务队列。
3、执行顺序
1、同步代码(js执行栈/回调栈) 2、微任务(先进先出) 3、宏任务(先进先出)
案例:
输出:11 14 12 15 13
当11和14的同步代码执行完毕后发现微任务队列为空,宏任务队列有一个setTimeout,则执行宏任务,进行一轮新的事件循环。
输出 2 3 6 p2 p1 1 4 5
注意是236而不是623.因为promise本身同步,应该从上往下执行。
注意:js会将await后面的代码推到微任务队列。await fn(); console.log(111),那么console.log(111)会被推入到微队列中。 await相当于promise.then
7、浏览器输入url回车后的具体流程
1、url解析:主要是检查url是否合法,协议、域名、端口号
2、DNS解析,将url解析成对应的ip地址。DNS就是数据库,存放的是url对应的ip
3、建立TCP连接,在正式发送数据之前需要先建立TCP的连接(建立通道)
4、发送HTTP请求
5、响应HTTP请求
6、页面渲染
8、伪元素和伪类
伪类
1、一个冒号
2、可以链式拼接 div:out-of-range:focus,存在多个伪类
3、基于DOM,产生不同的状态,不产生新对象 伪元素
1、两个冒号
2、只能处于选择符的最后方
3、创建一个不存在于DOM里的新对象
9、清除浮动的几种方法
1、伪元素:::after设置content为'',dispaly:block,clear:both
2、在最后一个元素后面添加一个块级元素,设置clear:both
3、给父元素添加固定的高度
4、给父元素设置overflow:hidden或者overflow:auto
10、promise、async/await(考输出顺序)
11、网络通信
12、工作项目的亮点和难点
13、函数式组件和类组件的区别
14、es6+的新特性
15、TCP三次握手和四次挥手及UDP
1、TCP和UDP的区别
tcp面向连接,是可靠的传输,可以保证数据包的顺序。
udp无连接,不可靠,可以容忍部分数据的丢失,实时性高,可以广播和多播,
2、tcp的三次握手和四次挥手
为了解决网络通道不可靠的问题
三次握手
浏览器发送带有SYN标志的TCP报文——>服务器接收到SYN,并发送一个带有SYN+ACK标志的的报文作为响应——>浏览器收到响应后,发送带有ACK标志的报文作为最终确认
四次挥手
浏览器发送fin包——>服务器接收到fin包之后发送ACK包——>服务器发送fin包——>浏览器发送ACK包
浏览器最后发送ACK包等待一段时间在进入到关闭状态是为了确认发送给服务器的fin包已被收到。