1. TCP 的流模式
流模式: 提供
有序的
,安全的
,双向的
,基于连接的字节流传输服务,字节流传输服务是以字节
作为最小传输单位
的传输方式
2. 邮箱验证的正则表达式
^ [a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(.[a-zA-Z0-9-]+)*.[a-zA-Z0-9]{2,6}$
需满足的验证逻辑:
- @之前必须有内容且只能是字母(大小写)、数字、下划线(_)、减号(-)、点(.)
- @和最后一个点(.)之间必须有内容且只能是字母(大小写)、数字、点(.)、减号(-),且两个点不能挨着
- 最后一个点(.)之后必须有内容且内容只能是字母(大小写)、数字且长度为大于等于2个字节,小于等于6个字节
3. token 过期的处理办法
使用
refresh_token
解决token
过期问题 服务器生成 token 的过程中,会有两个事件,一个是 token失效时间
,一个是 token刷新时间
,刷新时间肯定比失效时间长,当用户的 token 过期时,你可以拿着过期的 token 去换取新的tokens,来保持用户的登录状态,当然你这个过期 token 的过期期间必须在刷新时间之内,如果超出了刷新时间,那么返回的依旧是 401。
处理流程:
- 在 axios 的拦截器中加入 token 刷新逻辑
- 当用户 token 过期时,去向服务器请求新的 token
- 把旧的 token 替换新的 token
- 然后继续用户当前的请求
4. 如何注册一个自定义指令(全局注册和局部注册)
注册一个自定义指令有
全局注册
与局部注册
全局注册主要是通过
Vue.directive
方法进行注册
Vue.directive
第一个参数是指令的名字
(不需要写上v-
前缀),第二个参数可以是对象数据
,也可以是一个指令函数
//注册一个全局自定义指令 'v-focus'
Vue.directive('focus',{
// 当被绑定的元素插入到 DOM 中时 ...
inserted: function (el) {
// 聚焦元素
el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
}
})
局部注册通过在组件
options
选项中设置 ‘directive’ 属性
directive:{
focus:{
// 指令的定义
inserted:function(el){
el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
}
}
}
然后你可以在模板中任何元素上使用新的
v-foucs
property,如下:
<input v-focus />
5. Vue常用修饰符
一、表单修饰符
lazy
(当光标离开标签时,才会将赋值赋value)trim
(过滤掉两边的空格)number
(自动将用户的输入值转数值类型) 二、事件修饰符stop
(阻止事件的冒泡)prevent
(阻止默认行为)self
(只当在 event.target 是当前元素自身时触发处理函数)once
(只能触发一次)capture
(事件捕获,从顶层往下触发)passive
(用于提升移动端 scroll 事件的性能)native
(在自定义组件标签上绑定原生事件时使用)三、鼠标按钮修饰符
left
(左键)right
(右键)middle
(中键)四、键盘修饰符
- 普通键:
enter
、tab
、delete
、space
…- 系统修饰键:
ctrl
、alt
、meta
、shift
…五、v-bind 修饰符
sync
(实现子组件props双向绑定)props
(设置自定义标签属性,避免暴露数据,防止污染)camel
(将命名变为驼峰命名法)
6. JS 数据类型隐式转换
三种情况:
- 转换为 boolean 类型
- 转换为 number 类型
- 转换为 string 类型
一、Boolean类型
数据类型 | 转换为 true 的值 | 转换为 false 的值 |
---|---|---|
String | 任何非空字符串 | 空字符串(“” 或 ‘’) |
Number | 任何非零数字 | 0 和 NaN |
Object | 任何对象 | null |
Undefined | 不适用 | undefined |
二、Number类型
原始数据类型 | 转换之后的值 |
---|---|
空字符 ‘’ 或 “” | 0 |
非空字符串 | 将字符内的数据内容变为数据,如果还有其他符号中文等转为NaN |
true | 1 |
false | 0 |
null | 0 |
undefined | NaN |
三、String类型
原始数据类型 | 转换之后的值 |
---|---|
数字类型 | 数字类型的字符表示 |
null | ‘null’ |
undefined | ‘undefined’ |
布尔类型 | true 变 ‘true’, false 变 ‘false’ |
四、复杂数据类型 复杂数据类型会在隐式转换时先转成String,然后再转成 Number 运算
7.Blob / File / base64 的区别
Blob
:在一些图片处理插件(如 vue-copper),将图片处理完毕后都将使用 Blob 对象返回处理好的图片,供开发者用于上传服务器
File
:它继承于 Blob 的属性,使得它能够支持用户系统上的文件,可以理解为更“高级”的文件对象,所以被广泛用于各种上传组件
Base64
:就算脱离JS语言,也普遍应用于需要通过被设计为处理文本数据的媒介上储存和传输二进制数据而需要编码该二进制数据的场景,就连电子邮件也需要它储存复杂数据
8. 获取屏幕宽高及分辨率等信息
- 网页可见区域宽:
document.body.clientWidth
- 网页可见区域高:
document.body.clientHeight
- 网页可见区域宽:
document.body.offsetWidth
(包括边线的宽)- 网页可见区域高:
document.body.offsetHeight
(包括边线的高)- 网页正文全文宽:
document.body.scrollWidth
- 网页正文全文高:
document.body.scrollHeight
- 网页被卷去的高:
document.body.scrollTop
- 网页被卷去的左:
document.body.scrollLeft
- 网页正文部分上:
window.screenTop
- 网页正文部分左:
window.screenLeft
- 屏幕分辨率的高:
window.screen.height
- 屏幕分辨率的宽:
window.screen.width
- 屏幕可用工作区高度:
window.screen.availHeight
- 屏幕可用工作区宽度:
window.screen.availWidth
9. 为什么 typeof 不能满足所有数据类型的判断?
typeof 在判断 null、array、object 以及函数实例(new+函数)时,得到的都是“ object ”
10. forEach 和 for 循环的区别
for 循环可以中断循环;forEach 不可以中断循环 (forEach通过抛出异常实现break,用 return 实现continue)
11. AMD 和 CMD 的区别
AMD | CMD |
---|---|
预加载 | 懒加载 |
提前执行 | 延后执行 |
在对应的加载之前导入 | 在用的时候导入 |
12. 代码题
//加入输入数字5,输出如下形式的字符串:
*****
****
***
**
*
function printStar(n:number){
if(Number.isNaN(n) || n <= 0) {
throw new Error('传入的n必须是一个 > 0 的整数');
}
n = Math.floor(n,0); // 把小数转成整数
for( let i=0; i < n ; i++){
console.log(`${' '.repeat(i)}${'*'.repeat(n-i)}`);
}
}
printStar(5)
13. JavaScript 会有死锁吗?
不可能在 JavaScript 中产生死锁,因为不能有多个线程访问数据
14. vite 为什么比 webpack 快
两者的打包过程以及原理
Webpack
- Webpack 叫做
bundler
,将所有文件打包成一个文件。- Webpack 先识别入口文件,启动服务后,最后直接给出打包结果。Webpack 做的是分析代码,转换代码,最后形成打包后的代码。
Vite
- Vite 又叫做
no bundler
,顾名思义,就是不打包,支持 ES Module 加载。- Vite 启动服务器后,会按需加载,当请求哪个模块时才会对该模块进行编译,按需加载的方式,极大的缩减了编译时间。
这样的打包过程使得 Webpack 在启动服务器的过程中比 Vite 慢很多
实现方式 Webpack 是基于
Node.js
实现的,而 Vite 是使用Esbuild
预构建依赖,Esbuild 使用 Go 语言编译,比以 Node.js 编写的打包器预构建依赖快10-100
倍
15. 手写图片懒加载
<script>
function lazyload(){
const imgs = document.querySelectorAll('img')
const len = imgs.length
const viewHeight = document.documentElement.clientHeight
const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop
for(let i=0; i < len; i++){
const offsetHeight = imgs[i].offsetTop
if(offsetHeight < viewHeight + scrollHeight){
const src = imgs[i].dataset.src
imgs[i].src = src
}
}
}
window.addEventListener('scroll',lazyload)
</script>
16. tree shaking 的原理
- ES Module 引入进行静态分析,故而编译的时候正确判断到底加载了哪些模块
- 静态分析程序流,判断哪些模块和变量未被使用或者引用,进而删除对应代码
17. HTTP 常见的请求头
HTTP最常见的请求头如下:
Accept
:浏览器可接受的MIME类型;Accept Charset
:浏览器可接受的字符集;Accept-Encoding
:浏览器能够进行解码的数据编码方式,比如 gzip。Servlet 能够向支持 gzip的浏览器返回经 gzip 编码的 HTML 页面。许多情形下这可以减少 5 到 10 倍的下载时间;Accept-Language
:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到;Authorization
:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中;Connection
:表示是否需要持久连接。如果Servlet看到这里的值为Keep-Alive",或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接)它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet 需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream ,然后在正式写出内容之前计算它的大小;Content-Length
:表示请求消息正文的长度;Cookie
:这是最重要的请求头信息之一;From
:请求发送者的 email 地址,由一些特殊的 Web 客户程序使用,浏览器不会用到它;Host
:初始URL中的主机和端口;lf-Modified-Since
:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304 “Not Modified” 应答;Pragma
:指定 “no-cache” 值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝;Referer
:包含一个 URL,用户从该URL代表的页面出发访问当前请求的页面。User-Agent
:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用;UA-Pixels
,UA-Color
,UA-OS
,UA-CPU
:由某些版本的旧浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和 CPU 类型。
18. 缓存的4种策略
Cache-Aside
:最常用的,应用程序(Application)会与缓存(Cache)和数据源(Data Source)进行通信,应用程序会在命中数据之前先检查缓存Read-Through
:应用程序无需管理数据和缓存,只需要将数据源的同步委托给缓存提供程序 Cache Provider 即可Write-Through
Write-Behind
19. CDN 加速
CDN(Content Delivery Nerwork),即
内容分发网络
。CDN加速主要是加速静态资源
,需要依靠各个网络节点。
20. 正向代理,反向代理,及作用
一、正向代理
定义: 客户端通过代理服务器向目标服务器发送请求,代理服务器获取数据并返回给客户端;客户端清楚目标服务器,而目标服务器不清楚来自客户端,所以正向代理可以隐藏客户端信息。作用:
- 突破自身 IP 访问界限
- 提高访问速度
- 隐藏用户真实IP
二、反向代理
定义: 与正向代理相反,反向代理站在服务器那边作用:
- 隐藏服务器的 IP
- 负载均衡
- 提高访问速度
- 提供安全保障
21. 打包工具 webpack 性能优化
从打包构建速度、代码调试、代码运行这几个方面优化
1. HMR(模块热替换)
开启 HMR 后,webpack 就会监听哪些文件发生了变化,一旦文件发生变化,就只会打包被修改的那个文件,其他文件不会重新打包
2. source-map
一种提供源代码到构建后代码映射技术,即如果代码出现报错,是否会指向源代码中错误的那一行
3. oneOf
放在 oneOf 中的 loader,一个文件只会匹配一次,如果被匹配到了就会跳出,相当于 js 中的 switch
4. 缓存
设置文件资源缓存,浏览器刷新时不会重新向服务器请求资源,而是直接从缓存中进行读取
5. 代码分割
6. 懒加载与预加载
7. 多进程打包
8. resolve(引入更方便)
22. 操作系统的物理地址和虚拟地址
物理地址
- 指的是真实物理内存中地址,更具体一点就是内存地址寄存器中的地址。物理地址是内存单元真正的地址。
- 我们32位的系统的地址空间Ω就是我们的2 ^ 32字节(4GB)而64位的地址空间大小就是 2 ^ 64个字节。
虚拟地址
现代处理器增加一个中间层,利用一种间接的地址访问方法访问物理内存。按照这种方法,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址(Virtual
Addressing),然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。实际上完成虚拟地址转换为物理地址转换的硬件是CPU中含有一个被称为内存管理单元(Memory Management Unit,MMU)的硬件。
通过虚拟地址访问内存有以下优势:
- 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
- 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
- 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
- 当不同的进程使用同样的代码时,比如库文件的代码,物理内存中可以值存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存。
23. 数组和链表的特点和区别
- 数组的元素个数是固定的,而组成链表的结点个数可按需要增减;
- 数组元素的存诸单元在数组定义时分配,链表结点的存储单元在程序执行时动态向系统申请;
- 数组中的元素顺序关系由元素在数组中的位置(即下标)确定,链表中的结点顺序关系由结点所包含的指针来体现。
- 对于不是固定长度的列表,用可能最大长度的数组来描述,会浪费许多内存空间。
- 对于元素的插人、删除操作非常频繁的列表处理场合,用数组表示列表也是不适宜的。若用链表实现,会使程序结构清晰,处理的方 法也较为简便。
例如在一个列表中间要插人一个新元素,如用数组表示列表,为完成插人工作,插人处之后的全部元素必须向后移动一个位置空出的位置用于存储新元素。
对于在一个列表中删除一个元素情况,为保持数组中元素相对位置连续递增,删除处之后的元素都得向前移一个位置。如用链表实现列表链表结点的插人或删除操作不再需要移动结点,只需改变相关的结点中的后继结点指针的值即可,与结点的实际存储位置无关。
24. 进程间通信
管道 | System V IPC | POSIX IPC |
---|---|---|
匿名管道 | System V 消息队列 | 消息队列 |
命名管道 | System V 共享内存 | 共享内存 |
System V 信号量 | 信号量 | |
互斥量 | ||
条件变量 | ||
读写锁 |
1. 匿名管道
只能用于具有亲缘关系的进程之间进行通信,常用于父子
2. 命名管道
一种特殊类型的文件,两个进程通过命名管道的文件名打开同一个管道文件,此时这两个进程也就看到了同一份资源,进而可以进行通信
3.共享内存
让不同进程看到同一份资源,开辟物理空间,开启虚拟空间,建立映射
4. 信号量
共享内存解决了通信问题,但出现了新问题,进程间的临界资源;信号量就是用来保护临界资源,信号量本质是一个计数器
25. HTML常用标签
- 标题标签(h1 - h5)
- 字体标签(font)
- 段落标签(p)
- 换行标签(br)
- 水平线标签(hr)
- 图片标签(img)
- 背景音乐(audio)
- 视频(video)
- 超链接标签(a)
- 列表标签
- 文本设置标签
26. 前端鉴权的10种方式
HTTP 鉴权
允许客户在请求时通过 用户名+密码 的方式,实现对用户身份的认证Session-Cookie 鉴权
利用服务端的 Session (会话)和浏览器(客户端)的 cookie 来实现的前后端通信认证模式Token 鉴权
解决了服务器存储 session 的压力JWT 鉴权
使用 token时,每次都要查询和验证用户信息,会产生延迟等性能问题;JWT 在登陆成功后将相关信息组成 JSON 对象 ,并对它进行某种方式的加密返回给客户端,并在下次请求时带上这个token,服务端会校验其合法性单击登录
只需要登录一次,就可以访问其它相互信任的应用系统;同域下的 SSO (主域名相同)OAuth 2.0
第三方登陆方式(例如:QQ、微信、支付宝); OAuth 是一种授权机制,数据的所有者告诉系统,同意授权第三方应用进入系统。系统从而产生了一个短期的进入令牌 Token,用来代替密码,供第三方使用联合登陆
和信任登陆
唯一登陆
扫码登陆
27. 手写快排
第一种写法:
public static void sort1(int a[], int low, int high) {
// 结束的条件
if (low >= high) {
return;
}
int i, j, index;
i = low;
j = high;
index = a[i]; // 此处暂用第一个元素作为基准值
while (i < j) {
// 先从右边进行扫描,找到大于基准值的元素
// 注意,此处必须先从右进行扫描,否则会影响结果正确性
while (i < j && a[j] >= index)
j--;
//找到之后交换
if (i < j)
// 注意此处相当于 a[i]=a[j]; i++;
// 补充:a[++i]=a[j] 相当于 i++; a[i]=a[j];
a[i++] = a[j];
//然后从左边扫描,找到小于基准值的元素
while (i < j && a[i] < index)
i++;
//找到之后交换
if (i < j)
a[j--] = a[i];
}
a[i] = index;
// 对基准值左边的所有元素进行递归排序
sort1(a, low, i - 1);
// 对基准值右边的所有元素进行递归排序
sort1(a, i + 1, high);
}
第二种写法:
public static void sort2(int a[],int low,int high){
// 结束的条件
if (low >= high) {
return;
}
int i, j, index;
i = low;
j = high;
index = a[i]; // 此处暂用第一个元素作为基准值
while(i != j) {
//从右向左寻找第一个小于基准元素的数
while(i < j && a[j] >= index) j--;
//从左向右寻找第一个大于基准元素的数
while(i < j && a[i] <= index) i++;
// 上面的循环结束表示找到了位置或者 i >= j 了,交换两个数在数组中的位置
if(i < j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
//将 i 和 j 共同指向的元素与基准元素交换
a[low] = a[i];
a[i] = index;
//对左边进行快速排序
sort2(a, low, i - 1);
//对右边进行快速排序
sort2(a, i + 1, high);
}
28. 高阶组件
高阶组件
:接收一个组件并返回一个组件,这个组件具有原组件的功能,并拥有了新定义的功能
组件最重要的三个功能就是
事件
、属性
以及插槽
,如果能完全复制这三个能力,那么就是高阶组件思路:通过组件的render函数基于参数组件的模板进行属性、事件乃至插槽的捆绑
29. 多个接口并发请求,并返回结果
function promiseAll(arr) {
let count = 0
let res = []
return new Promise((resolve, reject) => {
arr.forEach((el, i) => {
getApi(el).then(r => {
count++
// 按照顺序依次输出结果
res[i] = r // // --------promiseAll:[2000,1000,500]
// 按照接口返回的顺序输出结果,没啥作用
// res.push(r) // --------promiseAll:[500,1000,2000]
if (count === arr.length) {
resolve(res)
}
}).catch(err => reject(err))
})
})
}
// 模拟 api
function getApi(t) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(t)
}, t)
})
}
promiseAll(time).then(res => {
console.log('--------promiseAll:', res)
// -------promiseAll:[2000,1000,500]
})
30. 微前端和小程序
什么是微前端?
摘取了两种自己比较认可的说法:
- 一种是将多个可独立交付的小型前端应用聚合为一个整体的架构风格
- 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术及方法策略
通俗点来讲就是多个不限制技术栈的前端应用可以组合在一起构建最终需要的系统(类似多个组件组成页面一样)
为什么会出现微前端?
- 中心化的配置有可能会 “牵一发而动全身” ,例如 route.js、webpack 配置等
- 以企业级Web 应用为典型的长周期项目,最终会演变成为一个巨大且杂乱的项目,打包体积大,启动时间长
- 传统应用对多个团队之间的配合要求度较高,团队之间的耦合度较高。例如需要使用相同的技术栈,一个团队的开发可能受限于其他团队的进度
微前端的有点?
- 技术栈无关主框架不限制接入应用的技术栈,子应用具备完全自主权(important)
- 独立开发、独立部署 子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
- 增量升级在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
- 独立运行时,每个应用之间状态隔离,运行时状态不共享
微信小程序
定义:
微信小程序,是小程序的一种,英文名 Wechat Mini
Program,是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用
微信小程序有四大特性
- 无需安装随时可用
- 触手可及
- 用完即走
- 无需卸载
微信小程序的优势
便捷性:
小程序是不需要下载安装的,即用即走,非常方便用户的运用,而且不占用手机的内存,很便捷唯一性:
小程序的名称是具有唯一性的,谁先注册便是谁的,当别人已经注册成功了,你是没有办法再注册的本钱低:
小程序的本钱是比较低的,是能够大大的降低运营本钱的用户体验好:
小程序的体验是非常好的,在微信生态里面,小程序的在体会与功能上是能够秒杀H5页面的,H5页面会常常出现卡顿、加载慢等现象,可是小程序却不会出现这些问题,小程序归于应用程序,H5归于网页的页面效率高:
在同样的一个广告链接中,在公众号中插入链接,文末广告以及小程序广告所获得的翻开率是彻底不同的,小程序翻开率比正常的多了20倍左右用户精准:
小程序是依赖微信中生存的,是有自己的后台系统的,小程序能够经过用户的画像、数据分析、来完善自己的产品,而且提高品牌的口碑
31. react 组件之间的数据传递
- 正向传值用 props
逆向传值用函数传值
,通过事件调用函数传递- 同级传值用 pubsub-js
跨组件传递用context
,使用 createContext() ,有两个对象 —— provider / consumer
32. Vue 和 React 有什么不同
相同点
- 都支持服务器渲染
- 都有虚拟 dom,组件化开发,通过 props 参数进行父子组件数据的传递
- 都实现 webcomponent 规范
- 都是数据驱动视图
- 都有状态管理,react 有 redux,vue 有 vuex
- 都有支持 native 的方案,react 有 react native native,vue 有 weex
不同点
- react严格上只针对 mvc 的 view 层,vue 是 mvvm 模式
- 虚拟dom不一样,vue 会跟踪每一个组件的依赖关系,不需要重新渲染整个dom组件树,而 react 不同,当应用的状态被改变时,全部组件都会重新渲染,所以 react 中用 shouldcomponentupdate
这个生命周期的钩子函数来控制- 组件写法不一样,react 是 jsx 和 inline style,就是把 html 和 css 全写进 js 中,vue 则是 html,css,js 在同一个文件
- 数据绑定不一样,vue 实现了数据双向绑定,react 数据流动是单向的
- 在 react 中,state 对象需要用 setstate 方法更新状态,在 vue 中,state 对象不是必须的,数据由 data 属性在 vue 对象中管理
33. BEM
BEM 代表 block、element 和 modifier
block:
独立的块element:
块下面的元素modifier:
表示样式大小 一种有用且功能强大的命名约定,可确保参与网站开发的每个人都使用单一代码库,并且使用相同的语言
写法:
block_element-modifier
- 单词之间用(-)分隔
- 元素名称通过双下划线(__)与块名称分隔
- 修饰符名称通过单个下划线(_)与块元素名称分隔
34. 什么情况下会发送 options 请求
当一个请求跨域且不失简单请求时就会发送 OPTIONS 请求
不是简单请求:
- 请求方法不是 GET、POST、HEAD
- 自定义头部
- POST 请求的 Content-Type 异常(application / x-www-form-urlencoded、multipart / form-data 或 text / plain 之外的)
35. OPTIONS 请求会携带哪些头部信息
请求头
Request Header | 作用 |
---|---|
Access-Control-Request-Method | 告诉服务器实际请求所使用的 HTTP 方法 |
Access-Control-Request-Headers | 告诉服务器实际请求所携带的自定义首部字段 |
Origin | 发起请求的域名(协议、域名、端口号) |
响应头
Response Header | 作用 |
---|---|
Access-Control-Allow-Methods | 返回了服务端允许的请求,包含 GET / HEAD / PUT / PATCH / POST / DELETE |
Access-Control-Allow-Credentials | 允许跨域携带 cookie (跨域请求携带 cookie 必须设置为true) |
Access-Control-Allow-Origin | 允许跨域请求的域名,这个可以在服务端配置一些信任的域名白名单 |
Access-Control-Allow-Headers | 客户端请求所携带的自定义首部字段 |
36. ETag 的缺点
ETag:强 ETag 值 & 弱 ETag 值
强 ETag:
无论实体发生多么细微的变化都会改变其值 弱
弱 ETag:
只用于提示资源是否相同,只有资源发生了根本改变,产生差异时才会改变 ETag 值
缺点: 计算 ETag 只需要性能损耗;分布式服务器时依赖算法:分布式服务器存储的情况下,计算 ETag的算法不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时出现 ETag 不匹配的情况
37. http 强制跳转 https
- 登陆到主机控制面板,进入[文件管理器]
- 进入到网站根目录 public_html
- 在网站根目录 public_html 里面,创建 .htaccess
- 在 .htaccess 文件中输入一下代码:
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.)$ https://%{SERVER_NAME}/$1[R,L]
- 完成一下操作,即可强制跳转
38. MVVM框架 与 MVC框架 的区别
- mvc 中 Controller 演变成 mvvm 中的 viewmodel
- mvvm 通过数据来驱动视图层而不是节点操作
- mvc 中 View 和 model 可以直接打交道,造成 model 层 和 view 层之间的耦合度高;而 mvvm 中 model 和 view 不打交道,通过 vm 中的桥梁 viewModel 来同步
- mvvc 解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢
39. 分析比较 opacity:0 / visibility:hidden / display:none
opacity:0 ——
重建图层,性能较高
visibility:hidden ——重绘操作,比回流操作性能高一点
dispaly:none ——回流操作,性能开销大
40. 在不修改代码的情况下,让图片宽度为 300 px
- css 方法
max-width:300px
;覆盖其样式- transform:
scake(0.625)
缩放- js 方法
document.getElementByTagName('img')[0].setAttribute('style','width:300px !important;')
41. 如何用 CSS 或 js 实现多行文本溢出省略效果
- 使用 split + 正则表达式将单词与单个文字切割出来存入 words
- 加上 ‘…’
- 判断 scrollHeight 与 clientHeight ,超出的话就从 words 中 pop 一个出来
<p> 这是一段测试文字, this is some test text, 测试文字,测试文字测 </p>
const p = document.querySelector('p')
let words = p.innerHTML.split(/(?<=[\u4e00-\u9fa5])|(?<=\w*?\b)/g)
while(p.scrollHeight > p.clientHeight){
words.pop()
p.innerHTML = words.join('') + '...'
}
42. [‘1’,‘2’,‘3’].map(parseInt) => 1, NaN, NaN
... .map(item,index)=>{
return parseInt(item,index)
})
//....................//
parseINt('1',0) // 1
parseINt('2',1) // NaN
parseINt('3',2) // NaN, 3不是二进制
43. 介绍下 Set,WeakSet,Map 和 WeakMap 的区别
- Set
- 成员唯一,无序且不重复
- [value,value],键值与键名是一致的(或者说只有键值,没有键名)
- 可以遍历,方法有:add,delete,has
- WeakSet
- 成员都是对象
- 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
- 不能遍历,方法有add,delete,has
- Map
- 本质上是键值对的集合,类似集合
- 可以遍历,方法很多可以跟各种数据格式转换
- WeakMap
- 只接受对象作为键名(null除外),不接受其他类型的值作为键名
- 键名是弱引用,键值可以是任意的,键名所指的对象可以被垃圾回收,此时键名是无效的
- 不能遍历,方法有get,has,set,delete
44. ES5 和 ES6 继承上的差异
ES5 和 ES6 子类 this 生成顺序不同
ES5
的继承先生成子类实例
,再调用父类的构造函数修饰子类实例
ES6
的继承先生成父类实例
,再调用子类的构造函数修饰父类实例
(这个差别使得 ES6 可以继承内置对象)
45. 介绍下模块化发展历史
- IIFE
- 使用自执行函数编写模块化
- 特点:在一个单独的函数作用域中执行代码,避免变量冲突
- AMD
- 使用 requireJS 来编写模块化
- 特点:依赖必须提前声明好
- CMD
- 使用 SeaJS 来编写模块化
- 特点:支持动态引入依赖文件
- CommonJS
- nodejs 中自带的模块化
- UMD
- 兼容 AMD,CommonJS 模块化语法
- ES Module
- ES 6 引入的模块化,支持 import 来引入另一个 js
46. a、b、c、d 和 a[‘b’][‘c’][‘d’] 哪个性能更高
a、b、c、d 比 a[‘b’][‘c’][‘d’] 性能更高
从 AST 的角度考虑,后者要比前者多解析 [ ] 的部分
47. 为什么普通 for 循环性能远高于 forEach
因为 for 循环 没有任何额外的函数调用栈和上下文 forEach 函数签名实际上是:
array.forEach(function(currentValue, index, arr), thisValue)
它不是普通的 for 循环语法糖,还有诸多参数和执行上下文需要在执行的时候考虑进来,这里可能拖慢性能
48. var, let, const 区别的实现原理
- var的话,会直接在栈内存里分配内存空间,然后等到实际语句执行的时候,再存储对应的变量;如果传的是引用类型,那么会在堆内存中开辟一个内存空间存储实际内容,栈内容会存储一个指针指向堆内存
- let 的话,不会在栈内存里预分配内存空间,而且在栈内存分配变量时,做一个检查,如果已经有相同变量名存在就会报错
- const的话,不会预分配内存空间,在栈内存分配变量时会做同样的检查,不过 const 存储的变量不可修改,对于基本类型来说无法修改定义的值,对于引用类型来说你无法修改内存里分配的指针,但是你可以需改指针指向的对象里的属性
其实三者都存在变量提升,let 只是创建过程提升,初始化过程并没有提升,所以会产生暂时性死区;var 的创建和初始化过程都提升了,所以赋值前访问会得到 undefined function 的创建、初始化、赋值都被提升了
49. 设计并实现 Promise.race()
Promise._race = promise => ((resolve, reject)=>{
promise.forEach(promise => {
promise。then(resolve,reject)
})
})
50. 多路复用(http)的解释
在一个连接里,客户端和浏览器都可以同时发送多个请求,而且不用按照顺序一一对应