面试之自我问答

98 阅读12分钟
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等相关资源, 多页面跳转, 需要整个页面的资源刷新

image.png image.png

21、渲染进程内有什么线程
  • GUI渲染线程:负责渲染浏览器界面,解析HTML,CSS,构建DOMRenderObject树,布局和绘制等, 当界面需要重绘或由于某种操作引发回流时,该线程就会执行
  • js引擎线程:负责解析JavaScript脚本,运行代码
  • 事件触发线程:用来控制事件循环,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理
  • 定时触发器线程:setTimeoutsetInterval所在的线程,计时完毕后,添加到事件队列中,等待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头部。