1、对于模块化的理解
模块化的好处
- 解决命名冲突
- 提供复用性
- 提高代码可维护性
实现模块化的方式
- 立即执行函数
- AMD/CMD
- Commonjs / ES Module
commonjs 和 es module 的区别
- commonjs一般用于服务端, 是同步导入, es用于浏览器,是异步导入
- commonjs支持动态导入, es不支持
- commonjs导出的是值, es导出的是指针
- es最后编译出来也是require/exports格式
2、http的状态码
- 1xx 服务器接收到请求
- 2xx 请求成功
- 3xx 重定向
- 4xx 客户端错误
- 5xx 服务器错误
说一下常用的状态码
- 301 永久重定向
- 302 临时重定向
- 401 未授权
- 403 服务器拒绝请求
- 404 服务器找不到请求资源
- 500 服务器内部错误
- 502 服务器网关错误
- 503 服务器不可用
- 504 网关超时
301和302的区别
- 永久性: 301表示永久重定向, 资源被永久移动到新位置 302表示临时重定向, 资源被临时移动到新位置
- seo处理: 301重定向会传递原URL的权重到新URL,有利于SEO, 302重定向不会传递权重,因为搜索引擎认为原URL仍然有效
- 客户端行为:301会记住新的url, 下次访问旧的url会直接访问新的url, 302不会记住新的url
307、303、302的区别
- 302是http1.0的协议状态码,1.1将302细化为303和307
- 303明确表示客户端应当采用get方法获取资源,会把post请求变为get请求进行重定向
- 307会遵照浏览器标准,不会从post变为get
3、http1.0/1.1/2.0
-
http1.0: http1.0是基础, 增加的特性有状态码、缓存等, 它是短连接, 一次请求一次完整的tcp连接,并发性差
-
http1.1:
- 持久连接: tcp默认不关闭,可以被多个请求复用
- 管道机制: 一个tcp中可以同时发起多个请求, 以前一个tcp只能发起一个请求, 等响应后再发起第二个, 管道机制允许同时并发多个请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。(会出现队头堵塞)
- 缓存控制: 增加了Cache-Control
- 增加了host请求头字段, 为了虚拟主机打基础
http1.1解决队头阻塞的方法:1、减少请求 2、多开持久化连接, 所以浏览器为每个域名同时维护最多6个tcp连接
-
http2.0
- 传输不是文本格式, 而是二进制格式
- 多路复用: 解决1.1的队头堵塞, 只开一个tcp连接,在请求上增加一个二进制分帧层, 数据经过二进制分帧层处理之后,会被转换为一个个带有请求 ID 编号的帧,通过协议栈将这些帧发送给服务器。- 服务器接收到所有帧之后,会将所有相同 ID 的帧合并为一条完整的请求信息。
- 头部压缩:使用HPACK算法对头部信息进行压缩,减少了传输的数据量
- 服务器推送:服务器可以主动向客户端推送资源,而无需等待客户端请求,减少了延迟。
- 强制使用https
4、TCP三次握手,为什么要握手三次,两次不行
-
双方确认连接:双方都能确认对方的存在,并且可以接收和发送数据。
-
防止重复连接:避免因为网络延迟等原因导致的重复连接。
-
初始序列号同步:确保双方的序列号同步,保证数据的有序传输和完整性。
5、js如何取消请求
- xhr和jquery都是 xhr.abort()来取消请求
- Axios自带API, axios.CancelToken来取消请求
- fetch采用webworker的const controller = new AbortController(), controller.abort();取消请求
6、React的setState是同步还是异步
- react18之前, 除了合成事件是异步的, 其他都是同步
- react18之后, 都是异步, 为了批处理
7、什么是闭包, 闭包的优缺点
-
闭包就是变量的集合, 是内部函数调用了外部函数的变量, 即使外部函数执行完成后, 内部函数引用的变量也还在内存中, 这些变量集合就是闭包
-
优点:
- 可以保存函数内部执行状态
- 创建私有变量
- 模块化代码
-
缺点:
- 内存泄漏
-
闭包的垃圾回收
- 引用全局变量, 内存只有被关闭页面才释放, 因为变量全局对象的一部分, 全部可访问, 所以不回收
- 函数变量, 当再没有调用闭包调用, 调用链取消时候就会被回收
8、for in 和 for of的区别
- for in 性能差, 因为会循环原型上的属性,循环时先循环数字, 然后其他, 不能循环symbol
- for of 循环具有迭代属性的对象和数组
9、react的组件渲染流程
- 初始化阶段: 创建元素和虚拟dom
- 初始渲染阶段: 执行函数组件、初始化状态, 注册副作用
- 更新阶段: 调和阶段对比新旧dom,映射到真实的dom
- 副作用处理: 处理
useEffect中定义的副作用,并在必要时清理上次渲染的副作用 - 卸载阶段: 组件卸载时执行
useEffect的清理函数
10、说一下this对象
this就是个动态绑定的上下文, 它的值取决于函数被调用的方式
- 默认情况下,this指向全局window
- 函数作为对象, 指向的是函数
- 通过bind, call , apply来显性指定
- new关键词调用构造函数,thisb绑定到新创建的函数上
- 箭头函数中的this,this捕获上下文的this
11、call、apply、bind
- call和apply是立即调用函数,区别在于传参方式, call是逗号隔开, apply是数组
- bind是创建一个新函数, 参数传递是逗号隔开
- call、apply、bind的实现方式如下
1、call的实现方式
Function.prototype.myCall = function (context = window, ...args) {
if (typeof context !== "object") {
context = new Object(context);
}
let fnKey = Symbol();
context[fnKey] = this;
let result = context[fnKey](...args);
delete context[fnKey];
return result;
};
2、apply的实现方式
Function.prototype.myApply = function (context = window, args) {
if (typeof context !== "object") {
context = new Object(context);
}
let fnKey = Symbol();
context[fnKey] = this;
let result = context[fnKey](...args);
delete context[fnKey];
return result;
};
3、bind的实现方式
Function.prototype.myBind = function (context = window, ...args) {
let self = this;
let innerFun = function (...innerargs) {
return self.apply(
this instanceof innerFun ? this : context,
args.concat(innerargs)
);
};
innerFun.prototype = Object.create(this.prototype);
return innerFun;
};
12、首页白屏优化, 前端性能优化
对于前端性能优化实际上想达到的就是加载速度更快, 交互更流畅,白屏时间短等 对于白屏优化,首页要知道url输入浏览器发生了什么
- DNS解析优化
- DNS缓存优化
- DNS预加载策略
- 稳定可靠的DNS服务
- TCP网络链路优化 (多花点钱)
- 服务端处理优化
- 前端渲染优化
- 代码相关的文件打包优化
- 写法优化 1、首屏css有条件可以采用内联 2、图片和可后置的相关js,图片等后置引入 3、重点关注样式表和js文件
13、如何防御 XSS 攻击?
- 服务端对输入脚本进行过滤和转码
- 充分利用 CSP
- 使用 HttpOnly 属性
14、JavaScript为什么要进行变量提升,它导致了什么问题?
变量提升是因为js执行分两步: 先编译后执行
- 编译阶段: 要创建全局执行上下文, 解析代码, 将变量和函数提升到变量环境中, 初始化定义
- 执行阶段: 按照从上到下的顺序依次执行代码
存在的问题
- 变量的覆盖
- 变量的污染
15、SSL的传输数据原理
采用对称密钥和非对称密钥相结合, 随机数字+非对称密钥的公钥相结合生成对称密钥的密钥进行传输
16、说一下react hooks,解决了什么问题, 有哪些常用的hooks
react hooks是react16.8引入的新特性, 自此函数组件才开始大量使用
具体好处,解决了什么问题, 需要对比class组件来分析
- 写法更简单, 拆分组件更简单, 不需要定义实例,没那么多复杂的生命周期函数
- 没有类组件的this指向问题, 类组件可能会用到call, apply等指定this
- 可以自定义hooks, 丰富的hooks能解决更多的场景需求
常用的hooks useEffect、 useState、useRef、useReducer、useContext、userMemo、useCallback
17、介绍一下useEffect
useEffect是react16.8中引入的一个hook, 用于在函数组件中执行副作用, 副作用是指那些影响组件外部系统或需要与外部系统交互的的操作, 例如数据获取, 订阅, 手动dom操作
接收一个函数和一个数组, 依赖数组中的变量变化时,传入的函数会执行,返回一个函数可以用来清除副作用
18、什么是副作用?
在React中, 副作用是指那些与纯渲染逻辑无关的操作, 这些操作可能会影响组件外部的状态或状态, 常见的副作用包括:
- 数据获取
- 订阅
- 手动dom操作
- 计时器
- 日志记录
18、介绍一下Redux
Redux是一个状态管理库, Redux 的设计理念基于 Flux 架构,并且强调单一状态树、纯函数和不可变性。为了解决非父子传输数据的问题
19、介绍一下箭头函数
箭头函数是ES6中新引入的一种的新的定义函数的方式
- 不绑定this, 会捕获上下文的this
- 没有arguments
- 不能作为构造函数
20、SPA和MPA的区别
SPA(Single Page Application)单页应用: 单个html完成多个页面的切换和功能的应用, 一个html作为入口,初始加载页面时js、css相关资源都会被一次性加载, 所有首次渲染很慢, 切换其他的时候只刷新局部资源, 后续会很快
MPA(Multi Page Application)多页应用:有多个独立页面的应用, 每个页面必须重复加载js、css等相关资源, 多页面跳转, 需要整个页面的资源刷新
21、渲染进程内有什么线程
- GUI渲染线程:负责渲染浏览器界面,解析
HTML,CSS,构建DOM树RenderObject树,布局和绘制等, 当界面需要重绘或由于某种操作引发回流时,该线程就会执行 - js引擎线程:负责解析
JavaScript脚本,运行代码 - 事件触发线程:用来控制事件循环,由于
JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理 - 定时触发器线程:
setTimeout和setInterval所在的线程,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行 - 异步
http请求线程:- 在XMLHttpRequest在连接后是通过浏览器新型一个线程请求
GUI渲染线程和js引擎线程是互斥的,JS引擎执行时GUI线程会被挂起(相当于冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
22、JavaScript原型,原型链 ? 有什么特点?
原型: 原型就是对象的一个隐藏属性, 也是一个对象, 一般 [__proto__] 指向的就是该对象的原型
原型链: 当一个对象调用的属性/方法自身不存在时,就会去自己 [__proto__] 关联的前辈 prototype 对象上去找
原型特点: JavaScript对象是通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
a.constructor.prototype = a.__proto__都是指向该对象的原型对象
23、判断数组和对象的方法
let obj = { a: 1 };
let arr = [1, 2, 3];
1. `typeof` 运算符
console.log(typeof obj); // 输出 "object"
console.log(typeof arr); // 输出 "object"
2. `Array.isArray` 方法
console.log(Array.isArray(obj)); // 输出 false
console.log(Array.isArray(arr)); // 输出 true
3. `instanceof` 运算符
console.log(obj instanceof Object); // 输出 true
console.log(arr instanceof Array); // 输出 true
console.log(arr instanceof Object); // 输出 true,因为 Array 是 Object 的子类
4. `Object.prototype.toString` 方法
console.log(Object.prototype.toString.call(obj)); // 输出 "[object Object]"
console.log(Object.prototype.toString.call(arr)); // 输出 "[object Array]"
总结
- 使用
typeof只能判断基本类型,对于对象和数组都会返回'object'。 - 使用
Array.isArray可以直接判断一个变量是否为数组。 - 使用
instanceof可以判断一个变量是否为特定构造函数的实例。 - 使用
Object.prototype.toString.call可以区分具体的对象类型,通过返回字符串精确判断变量类型。
24、vw和100%的区别
-
vw: 视口范围, 桌面端指的是浏览器可视范围,移动端是布局视口
- 无论父元素的尺寸多少, vw都是相对于视口的
- 适应视口变化, 视口缩小,他也缩小
-
%: 相对于父元素的宽度比例
- 相对于父元素
- 依赖父元素
25、post为啥发送两次请求
对于option的预请求两种情况下发生1、复杂请求 2、跨域请求
简单请求和复杂请求: 简单请求是get、head、post, 当post时候, content-type 必须是application/x-www-form-urlencoded,multipart/form-data或text/plain中的⼀个值, 没有自定义请求头, 复杂请求是剩余其他
跨域请求的option原因: 检查请求头和请求源是否符合跨域返回要求, 通过request-header将 Access-Control-Request-Headers与Access-Control-Request-Method发送给后台,另外浏览器会⾃⾏加上⼀个Origin请求地址。服务端在接收到预检请求后,根据资源权限配置,在response-header头部加⼊access-control-allow-headers(允许跨域请求的请求头)、access-control-allow-methods(允许跨域请求的请求⽅式)、access-control-allow-origin(允许跨域请求的域)。另外,服务端还可以通过Access-Control-Max-Age来设置⼀定时间内⽆须再进⾏预检请求,直接⽤之前的预检请求的协商结果即可。浏览器再根据服务端返回的信息,进⾏决定是否再进⾏真实的跨域请求。这个过程对于⽤户来说,也是透明的。
options请求如何避免
1:使⽤代理,避开跨域。
2:将复杂跨域请求更改为简单跨域请求。
3:不使⽤带⾃定义配置的header头部。