【前端面试系列】:
- 2024前端面试 --
HTML5
+CSS3
篇 - 2024前端面试 --
ES6
篇 - 2024前端面试 --
JavaScript
篇 - 2024前端面试 --
Vue2
篇 - 2024前端面试 --
Vue3
篇 - 2024前端面试 --
Angular
篇 - 2024前端面试 --
Node.js
篇 - 2024前端面试 --
Webpack
篇
1. JavaScript
组成部分:
ECMAScript
:JS
的核心内容。描述了语言的基础语法,比如var
,for
,数据类型(数组、字符串),DOM
(文档对象模型):DOM
把整个HTML
页面规划为元素构成的文档BOM
(浏览器对象模型):对浏览器窗口进行访问和操作
2. 数据类型:
2.1. 内置对象:
JS几种基础数据类型?几种新增?
- 8种基础数据类型:
undefined
、null
、boolean
、number
、string
、object
|symbol
、bigInt
symbol
:独一无二且不可变 => 全局变量冲突、内部变量覆盖bigInt
:任意精度正数 安全的存储和操作大数据,即使超出了number的安全整数范围
2.2. 内置对象:
- String Boolean Number Array Object Function Math Date RegExp...
- Math:abs() sqrt() max() min()
- Date:new Data() getYear()
- Array
- String:concat() length slice() split()
3. 基础数据类型通常如何进行分类?使用起来有什么区别?使用过程中你是如何区分他们的?
(不同的存储形态) = 存储关系
可以分为:原始数据类型 + 引用数据类型
- 原始数据类型:undefined、null、boolean、number、string
- 使用效果:直接赋值后,不存在引用关系;
- 存储位置:栈存储 => 先进后出栈维护结构 => 栈区由编译器自动分配释放 => 临时变量
- 放置在栈中:空间小、大小固定、操作频繁
- 引用数据类型:对象、数组、函数
- 使用效果:属于引用关系;
- 存储位置:堆存在 => 堆内存由开发展进行分配释放 => 长期变量 => 直到应用结束
- 放置于堆中,引用类型数据量大、大小不固定、赋值给的是地址
4. 操作数组的方法有哪些?
push()
、pop()
、sort()
、splice()
、unshift()
、shift()
、reverse()
、concat()
、join()
、map()
、filter()
、ervery()
、some()
、reduce()
、isArray()
、findIndex()
哪些方法会改变原数组?
push()
、pop()
、unshift()
、shift()
、sort()
、reverse()
、splice()
5. 如何进行类型区分判断?
typeof
:只能判断基本数据类型,不能判断引用数据类型
typeof 2 // number
typeof true // boolean
// 问题
typeof {} // object
typeof [] // object
instanceof
:只能判断引用数据类型,不能判断基础数据类型
2 instanceof Number // true
{} instanceof Object // true
*** 手写
instanceof
的原理实现?【通过翻户口本,查家庭信息】
function myInstance(left,right) {
// 获取对象的原型
let _proto = Object.getPrototypeOf(left);
// 构造函数的prototype
let _prototype = right.prototype;
while(true) {
if(!_proto) return false;
if(_proto === _prototype) return true;
_proto = Object.getPrototypeOf(_proto);
}
}
constructor
:可以判断基本类型和引用类型,但是如果声明了一个构造函数,并把他的原型指向了Array,就判断不出
(2).constructor === Number // true
([]).constructor === Array // true
隐患? constructor
代表的是构造函数指向的类型,是可以被修改的。
function Fn() {};
Fn.prototype = new Array();
var f = new Fn();
Object.prototype.toString.call()
:全可以判断
6. 闭包:
什么是闭包?函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包
- 特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存在内存中,不会被垃圾回收机制回收
- 缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,在IE浏览器中才会导致内存泄漏
- 使用场景:防抖,节流,函数嵌套函数避免全局污染的时候
function outer() {
var a = '变量1';
var inner = function () {
console.info(a);
}
return inner; // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
var inner = outer(); // 获得inner闭包函数
inner(); // 输出: "变量1"
7. 防抖和节流
都是应对页面中频繁发事件的优化方法。
- 防抖:避免发生重复触发。
- 【使用场景】:
- 频繁和服务端交互
- 输入框的自动保存时间。
- 优点:
- 减少不必要的操作:触发连续操作时,只执行最后一次操作,避免了频繁执行操作带来的性能浪费。
- 优化用户体验:在一些需要用户等待的操作中,防抖可以避免频繁触发操作导致的界面卡顿,提高用户体验。
- 缺点:
- 可能会错过一些操作:如果事件触发频率过高,防抖可能会忽略掉一些操作。
- 不适合所有场景:只适合连续触发事件时使用,不适合非连续触发的事件。
function debounce(func, wait) {
let timeout;
eturn function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
};
};
// 使用示例
const input = document.querySelector('input');
input.addEventListener('input', debounce(function() {
console.log('Input value:', input.value);
}, 200));
- 节流:把频繁触发的事件减少,每隔一段时间执行。
【使用场景】::
scroll
事件
- 优点:
- 控制操作频率:能够确保在一定时间内执行一次操作,避免了频繁执行操作带来的性能浪费。
- 适合各种场景:无论是连续触发的事件还是非连续触发的事件,都可以是节流来优化性能。
- 缺点:
- 可能会漏掉一些操作:如果事件触发频率过高,防抖可能会漏掉一些操作。
- 需要合理设置时间间隔:节流的时间间隔需要根据实际情况调整,如果时间间隔过长,可能会导致性能问题;如果时间间隔设置过短,则可能会导致频繁触发事件时无法及时处理。
function throttle(func, limit) {
let inThrottle;
return function() {
const context = this;
const args = arguments;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(function() {
inThrottle = false;
}, limit);
}
};
}
// 使用示例
const input = document.querySelector('input');
input.addEventListener('input', throttle(function() {
console.log('Input value:', input.value);
}, 200));
8. 前端的内存泄露怎么理解?
JS里已经分配内存地址的对象,但是由于长时间没有释放或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃的情况。
- 垃圾回收机制(因素):一些为生命直接赋值的变量;一些未清空的定时器;过渡的闭包;一些引用元素没有被清除。
9. 事件委托
又叫事件代理,原理就是利用了事件冒泡的机制来实现,也就是说把子元素的事件绑定到了父元素的身上。如果子元素阻止了事件冒泡,那么委托也就不成立。
- 组织事件冒泡:
event.stopPropagation()
addEventListener('click',函数名,true/false)
默认是false
(事件冒泡),true
(事件捕获) - 好处:提高性能,减少事件的绑定,也就减少了内存的占用。
10. 原型链
- 原型就是一个普通对象,它是为构造函数的实例共享属性和方法;所有实例中引用的原型都是同一个对象。
- 使用
prototype
可以把方法挂在原型上,内存在保存一份 __proto__
可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype
)
11. new
操作符具体做了什么?
- 先创建一个空对象
- 把空对象和构造函数通过原型链进行链接
- 把构造函数的
this
绑定到新的空对象身上 - 根据构造函数分会的类型判断,如果是值类型,则返回对象;如果是引用类型,就要返回这个引用类型。
12. JS
是如何实现继承的?
- 原型链继承
- 特点:继承父类原型上的属性和方法。
- 缺点:创建子类实例时,不能向父类构造函数中传参数,无法实现多继承
// 父类
function Person(name) {
this.name = name;
this.wait = functon() {
console.log(this.name + "正在休息")
}
}
Person.prototype.change = "修改";
Person.prototype.hello = function() {
console.log("Hello!")
}
// 子类
function Child(age) {
this.age = age;
}
Child.prototype = new Person();
Child.prototype.constructor = Child;
let child = new Child(10);
console.dir(child);
- 借用构造函数继承
- 特点:解决了子类构造函数向父类构造函数中传递参数,可以实现多继承(
call
或者apply
多个父类) - 缺点:方法都在构造函数中定义,无法复用,不能继承原型上的属性和方法。
// 父类
function Person(name) {
this.name = name;
this.wait = function() {
console.log(this.name + "等等")
}
}
Person.prototype.change = "父类 change";
Person.prototype.hello = function() {
console.log("Hello!")
}
// 子类
function Child(name, age) {
Person.call(this, name);
this.age = age;
}
let child = new Child("张三", 19)
console.dir(child);
- 组合式继承
- 特点:把
1
和2
的方法相结合,函数可以复用,可以继承属性和方法,并且可以继承原型的属性和方法 - 缺点:会挂载两次父类的属性和方法(有两个
name
和wait
),产生小bug
。
// 父类
function Person(name) {
this.name = name;
this.wait = function() {
console.log(this.name + "等等")
}
}
Person.prototype.change = "父类 change";
Person.prototype.hello = function() {
console.log("Hello!")
}
// 子类
function Child(name, age) {
Person.call(this, name);
// Person.apply(this, [name]) // => apply后面的参数是个数组
this.age = age;
}
Child.prototype = new Person();
Child.prototype.constructor = Child;
let child = new Child("熊大", 14);
console.dir(child);
ES6
的class
类继承
// class 相当于es5中的构造函数
// class中定义方法时,前后不能加function,全部定义在class的prototype属性中
// class中定义的所有方法时不可枚举的
// class中只能定义方法,不能定义对象,变量等
// class和方法内默认都是严格模式
// es5中constructor为隐性属性
class People {
constructor(name, age) {
this.name = name;
this.age = age;
}
wait() {
console.log(`${this.name} ${this.age} 正在等待`)
}
}
// 继承父类
class Child extends People {
constructor(name, age) {
// 继承父类属性
super(name, age)
}
wait() {
console.log(this);
// 继承父类方法
super.wait();
}
}
let child = new Child("小王",22)
console.log(child);
child.wait();
- 寄生组合继承
- 特点:通过寄生的方式来修复组合式继承的不足,完美的实现继承。
Object.create()
方法创建一个空对象,让属性和方法继承到对象__proto__
上
// 父类
function Person(name) {
this.name = name;
this.wait = function() {
console.log(this.name + "等等")
}
}
Person.prototype.change = "父类 change";
Person.prototype.hello = function() {
console.log("Hello!")
}
// 子类
function Child(name, age) {
Person.call(this, name);
// Person.apply(this, [name]) // => apply后面的参数是个数组
this.age = age;
}
Child.prototype = Object.create(Person.prototype);
Child.prototype.constructor = Child;
let child = new Child("熊二", 13);
console.dir(child);
13. JS
设计原理:
JS
引擎、运行上下文、调用栈、事件循环、回调
14. JS
中的this
指向问题
- 全局对象中的
this
指向:指向的是window
; - 全局作用域或者普通函数中的
this
:指向全局window
; this
永远指向最后调用它的那个对象:在不是箭头函数的情况下;new
关键词改变了this
的指向;apply,call,bind
:可以改变this
指向,不是箭头函数;- 箭头函数中的
this
:它的指向在定义的时候就已经确定了。箭头函数它没有this
,看外层是否有函数,有就是外层城函数的this
,没有就是window
; - 匿名函数中的
this
:永远指向了window
,匿名函数的执行环境具有全局性,因此this
指向window
。
15. script
标签里的 async
和 defer
有什么区别?
- 当没有async和defer这两个属性的时候,
- 浏览器会立刻加载并执行指定的脚本
- 有async
- 加载和渲染后面元素的过程将和script的加载和执行并行进行(异步)
- 有defer
- 加载和渲染后面元素的过程将和script的加载并行进行(异步),但是它的执行事件要等
- 所有元素解析完成之后才会执行
16. setTimeout
最小执行时间是多少?
HTML5
规定的内容:
setTimeout
最小执行时间是4ms
;setInterval
最小执行时间是10ms
。
17. ES6的新特性有哪些?
- 新增块级作用域(
let
,const
)- 不存在变量提升
- 存在暂时性死去的问题
- 块级作用域的内容
- 不能在同一个作用域内重复声明
- 新增了定义类的语法糖(
class
) - 新增了一种基本数据类型(
symbol
) - 新增了解构赋值
- 从数组或者对象中取值,然后给变量赋值
- 新增了函数参数的默认值
- 给数组新增了
API
- 对象和数组新增了扩展运算符
- Promise
- 解决回调地狱的问题。
- 自身有
all
,reject
,resolve
,race
方法 - 原型上有
then
,catch
- 把异步操作队列化
- 三种状态:
pending
初始状态,fulfilled
操作成功,rejected
操作失败 - 状态:
pending
->fulfilled
;pending
->rejected
一旦发生,状态就会凝固,不会再变
- 三种状态:
async
和await
- 同步代码做异步的操作,两者必须搭配使用
async
表明函数内有异步操作,调用函数会返回promise
await
是组成async
的表达式,结果是取决于它等待的内容,如果是promise
那就是promise
的结果,如果是普通函数就进行链式调用await
后的promise
如果是reject
状态,那么整个async
函数都会中断,后面的代码不执行
- 新增了模块化(
import
,export
) - 新增了
set
和map
数据结构set
就是不重复map
的key
的类型不受限制
- 新增了
generator
- 新增了箭头函数
- 不能作为构造函数使用,不能用
new
- 箭头函数就没有原型
- 箭头函数没有
arguments
- 箭头函数不能用
call
,apply
,bind
去改变this
的执行 this
指向外层第一个函数的this
- 不能作为构造函数使用,不能用
18. call
,aply
,bind
三者有什么区别?
- 都是改变
this
指向和函数的调用,call
和apply
的功能类似,只是传参的方法不同call
方法传的是一个参数列表apply
传递的是一个数组bind
传参后不会立刻执行,会返回一个改变了this
指向的函数,这个函数还是可以传参的,bind()()
call
方法的性能要比apply
好一些,所以call
用的更多一点
19. 深拷贝和浅拷贝
- 浅拷贝:
- 是指在复制对象或数组时,只复制一层对象的引用而不是实际的对象本身。也就是说,新对象与原对象共享同一个内存地址,其中一个对象的修改会影响到另一个对象。
- 使用场景:只需要复制一层对象引用的情况,比如克隆一个对象用于修改而不影响原始对象
// 对象的赋值运算
Object.assign(target,key,value);
Object.created();
// 数组的解构
Array.concat();
Array.slice();
Array.toReversed();
Array.reverse();
let a = {age:"2"};
let b = a;
// b.age会随着a.age的改变而改变,这就叫浅拷贝
b.age = 14;
console.log(b.age); // 14
- 深拷贝:
- 是指在复制对象或数组时,不仅复制对象本身,还要递归地复制对象的所有子对象,使得新对象与原对象完全对立,互不影响。
- 使用场景:适用于完全独立的对象副本的情况,比如避免修改原始对象或者将对象传递给其他函数时产生副作用。
let obj = {
name: "张三",
age: "18"
}
let newObj = JSON.parse(JSON.stringify(obj));
// 缺点:
// 会忽略 undefined、Symbol 和函数等特殊值,导致无法拷贝。
// 若尝试拷贝 bigInt 类型的值或者是循环引用的数据,会直接报错。
let obj = {
name: '你好啊',
like: {
type: 'coding',
a: undefined,
b: null,
c: function () {
console.log('hello');
},
e: Symbol('hello'), \
// acef都拷贝不了
}
}
let newObj= JSON.parse(JSON.stringify(obj)) console.log(newObj);// {name:'你好啊',like:{ type: 'coding', b: null}}
20. 并发模型与事件循环(EventLoop
)
名称 | 内容 | 优点 | 缺点 | 拓展/备注 |
---|---|---|---|---|
进程 | (操作系统中的基本概念之一)指的是一个正在运行中的程序,包括了程序的执行代码、数据、资源等。操作系统为每个进程分配一定的系统资源,例如内存空间、文件和设备等,以便进程能够正常运行。 | 进程是自愿分配和管理的最小单位 | ||
线程 | (进程中的一个执行流程)可以看做是进程中的一个独立执行单元。每个进程中可以包含多个线程,这些线程共享进程的资源 | 线程通过利用进程的资源来执行任务,并可以在进程内进行切换 | 线程是无法独立存在的,必须依存于进程而存在 | 线程是程序执行的最小单位。 |
单线程 | 指的是程序只有一个执行线程,只能串行执行任务,即每个任务都得等前一个任务执行完毕后才能进行下一个任务。 | 这种模式的好处是逻辑简单,开发维护成本低,但执行效率较低 | 特别是处理大量数据时容易出现阻塞等问题 | 适用于简单的应用程序 |
多线程 | 指的是程序同时拥有多个执行线程,每个线程可以独立运行,可以同时处理多个任务 | 可以大大提高程序的执行效率和响应能力,特别是在需要同时处理多个任务或大量数据时非常有用 | 复杂度高,容易出现并发问题,需要耗费更多的系统资源和开发成本 | 适用于复杂的应用程序 |
在多任务环境下,多个进程可以同时执行,同一个进程中的多个线程也可以同时执行。线程的切换比进程的切换开销小,因此多线程的程序更加高效。
- 渲染线程:指用于渲染图形的计算机进程线程。
- 渲染线程负责将
HTML
、CSS
和JavaScript
代码转化为网页上的可视化内容- 主要任务:解析HTML文档,确定渲染树和绘制图像。还负责检查和处理
JavaScript
代码,以便在网页上进行交互式操作。- 微任务:指的是一些比较短且需要立即执行的任务,例如
Promise
的then
方法,MutationObserver
等等。这些任务执行时,会在当前宏任务执行完毕后立即执行。- 宏任务:包括一些比较耗时的任务,例如
setTimeout
、setInterval
、Ajax
请求、DOM
事件等等。
- 先选取队列中最先进入队列的任务执行,直到队列为空。
微任务具有更高的优先级,会比宏任务优先执行。因此在编写异步代码时,应当注意使用合适的任务类型,以保证代码的正确性和效率。
- 同步任务:代码按照顺序执行,每完成一项任务后再进行下一项任务。
- 异步任务:代码在执行过程中,不必等待前一个任务完成后再执行下一个任务。
- 调用栈:用来跟踪程序执行顺序的一种内存结构。
- 事件循环:
- 先执行所有的微任务队列中的任务,直到队列为空。
- 然后再执行宏任务队列中的任务。
- 然后再执行微任务队列中的任务,直到队列为空。
- 这个过程一直重复下去,直到任务队列中和微任务队列中同时为空。
21. ajax
是什么?怎么实现的?
创建交互式网页应用的网页开发技术:在不重新加载整个网页的前提下,与服务器交换数据并更新部分内容
通过 XMLHttpRequest
对象向服务器发送异步请求,然后从服务器拿到数据,最后通过JS
操作DOM
更新页面
- 创建
XMLHttpRequest
对象xmh
- 通过
xmh
对象里的open()
方法和服务器建立连接 - 构建请求所需的数据,并通过
xmh
对象的send()
发送给服务器 - 通过
xmh
对象的onreadystate change
事件监听服务器和你的通信状态 - 接收并处理服务器响应的数据结果
- 把处理的数据更新到
HTML
页面上
21.1. get
和post
有什么区别?
- get一般是获取数据,post一般是提交数据
- get参数会放在url上,所以安全性比较差,post是放在body中
- get请求刷新服务器或退回是没有影响的,post请求退回时会重新提交数据
- get请求时会被缓存,post请求不会被缓存
- get请求会被保存在浏览器历史记录中,post不会
- get请求只能进行url编码,post请求支持很多种
22. 浏览器的存储方式有哪些?
- cookies
- H5标准前的本地存储方式
- 兼容性好,请求头自带cookie
- 存储量小,资源浪费,使用麻烦(封装)
- localstorage
- H5加入的以键值对为标准的方式
- 操作方便,永久存储,兼容性较好
- 保存值的类型被限定,浏览器在隐私模式下不可读取,不能被爬虫
- sessionstorage
- 当前页面关闭后就会立刻清理,会话级别的存储方式
- indexedDB
- H5标准的存储方式,,他是以键值对进行存储,可以快速读取,适合WEB场景
- token存在sessionstorage还是loaclstorage?
- token:验证身份的令牌,一般就是用户通过账号密码登录后,服务端把这些凭证通过加密等一系列操作后得到的字符串
- 存loaclstorage里,后期每次请求接口都需要把它当作一个字段传给后台
- 存cookie中,会自动发送,缺点就是不能跨域
- 如果存在localstorage中,容易被XSS攻击,但是如果做好了对应的措施,那么是利大于弊
- 如果存在cookie中会有CSRF攻击
23. token的登录流程。
- 客户端用账号密码请求登录
- 服务端收到请求后,需要去验证账号密码
- 验证成功之后,服务端会签发一个token,把这个token发送给客户端
- 客户端收到token后保存起来,可以放在cookie也可以是localstorage
- 客户端每次向服务端发送请求资源的时候,都需要携带这个token
- 服务端收到请求,接着去验证客户端里的token,验证成功才会返回客户端请求的数据
24. 页面渲染的过程是怎样的?
- DNS解析
- 建立TCP连接
- 发送HTTP请求
- 服务器处理请求
- 渲染页面
- 浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树
- 再把CSS解析成CSSOM
- 把DOM和CSSOM合并为渲染树
- 布局
- 把渲染树的每个节点渲染到屏幕上(绘制)
- 断开TCP连接
25. 了解过JWT
吗?
JSON Web Token
:通过 JSON
形式作为在 web
应用中的令牌,可以在各方之间安全的把信息作为 JSON
对象传输。在数据传输过程中还可以完成数据加密,签名等相关处理。
- 信息传输、授权
25.1. 基于传统的 Session
认证
http
协议是一种无状态的协议,也就意味着当用户第一次通过用户名和密码登录成功以后,下一次再请求的时候,用户还需要再进行登录才行,因为根据 http
协议,我们不知道是哪个用户发出的请求。
- 弊端:
- 占用大量服务器内存:每个用户经过认证之后都会在服务器的内存中记录一次,随着用户的增加,服务器的内存开销户明显增加。
- 限制分布式架构的应用:用户认证之后会将
sessionId
保存到内存中,这就意味着用户下次请求必须要请求到这台服务器上,才完成能授权,在分布式系统中,一定程度上增加了配置负载均衡的复杂度(指定请求打到记录sessionId
的不同服务器)。 - 如果后端需要实现
session
共享机制,还需要进行redis
集群的部署,加大了部署难度。 - 因为是基于
cookie
来进行用户识别的(set-cookie
),cookie
如果被捕获,用户很容易受到CSRF
(跨网站请求伪造的攻击)。
25.2. JWT
的认证流程:
- 前端把账号密码发送给后端的接口
- 后端核对账号密码成功后,把用户
id
等其他信息作为JWT
负载,把它和头部分别进行base64
编码拼接后签名,形成一个JWT
(token
)。 - 前端每日请求时都会把JWT放在HTTP请求头的
Authorization
字段内 - 后端检查是否存在,如果存在就验证
JWT
的有效性(签名是否正确,token
是否过期) - 验证通过后后端使用
JWT
中包含的用户信息进行其他的操作,并返回对应结果
简洁、包含性、因为Token
是JSON
加密的形式保存在客户端,所以JWT
是跨语言的,原则上是任何web
形式都支持。
25.3. JWT
的优势:
- 不占用服务器资源:服务器只需要通过
JWT
工具进行校验即可; - 适用于分布式微服务:不需要通过负载均衡找到指定服务器上的会话,只需要在网关中对请求头中的
JWT
令牌进行统一校验即可; - 信息安全:由于签名是使用
标头
和有效负载
计算的,因此还可以验证内容是否遭到篡改; - 支持多种语言:
Token
时以JSON
加密的形式保存在客户端,原则上任何web
都支持。
25.4. JWT
的结构:
JWT
令牌由三个部分组成,分别是标头(Header
)、有效载荷(Payload
)、签名(Signature
),并有“.
”分割。即:Header.Payload.Signature
.
- 第一部分:标头(
Header
):
通常由两个部分组成,分别是令牌的类型(例如:JWT)和所使用的签名算法(例如:HMAC、SHA256、RSA,一般是用HS256即可)。
{
"alg": "HS256",
"typ": "JWT"
}
// 最后,使用 Base64 编码构造出 JWT 结构中的第一部分(Header)。
- 第二部分:有效载荷(
Payload
):
自定义传输的信息,通常是一些用户信息。
// 例如:
{
"id": "11",
"username": "wang",
"admin": true,
}
// 最后,使用 Base64 编码构造出 JWT 结构中的第二部分(Payload)
- 第三部分:签名(
Signature
):防篡改
需要使用
Base64
编码后的header
和payload
以及提供的密钥(私钥),然后使用header
中指定的签名算法(HS256
)构建一个签名,保证JWT
,没有被篡改过。
26. HTTP
协议规定的协议头和请求头有什么?
- 请求头信息:
Accept
:浏览器告诉服务器所支持的数据类型Host
:浏览器告诉服务器我想访问服务器的哪台主机Referer
:浏览器告诉服务器我是从哪里来的(防盗链)User-Agent
:浏览器类型、版本信息Date
:浏览器告诉服务器我是什么时候访问的Connection
:连接方式Cookie
:X-Request-With
:请求方式
- 响应头信息:
-
Location
:告诉浏览器你要去找谁 -
Server
:告诉浏览器,服务器的类型 -
Content-Type
:告诉浏览器,返回的数据类型 -
Refresh
:控制了的定时刷新
-