1、强缓存和协商缓存
强缓存:
- cache-control (HTTP/1.1):如 max-age=3600 表示缓存 3600 秒; no-cache 不直接用缓存,需验证; no-store 不缓存。
- expires (HTTP/1.0):如 expires: Wed, 29 Feb 2024 12:00:00 GMT ,但逐渐被 cache-control 替代,可同时设置以兼容旧版。
协商缓存:
- etag 和 if-none-match :服务器生成 etag ,浏览器携带 if-none-match 验证。
- last-modified 和 if-modified-since :服务器设置 last-modified ,浏览器携带 if-modified-since 验证。
设置方式因服务器和语言而异。通常结合使用以优化性能,静态资源设强缓存,常更新资源用协商缓存。
2、CSRF(跨站请求伪造)和(XSS跨站脚本攻击)
CSRF(跨站请求伪造)和 XSS(跨站脚本攻击)是常见的 Web 安全漏洞,以下是一些常见的防范措施:
CSRF 攻击防范:
1. 验证请求来源:通过在请求中添加不可预测的令牌(token),并在服务器端验证该令牌,确保请求来自合法的源。
2. 限制请求方法:对于敏感操作,只允许使用安全的请求方法(如 POST),而不允许使用容易被伪造的请求方法(如 GET)。
3. 检查 Referer 头:虽然不可完全依赖,但可以检查请求的 Referer 头来判断请求是否来自预期的源。
4. 双重认证:例如结合密码和短信验证码等方式进行重要操作的认证。
XSS 攻击防范:
1. 输入验证和过滤:对用户输入的数据进行严格的验证和过滤,去除可能的恶意脚本代码。
2. 输出编码:在将用户输入的数据输出到页面时,进行适当的编码,如 HTML 编码,以防止脚本被执行。
3. 设置 HTTP 响应头:设置 Content-Security-Policy 等 HTTP 响应头来限制页面能够加载的资源和执行的脚本。
4. 避免直接使用用户输入拼接 HTML:尽量使用模板引擎或安全的方法来构建页面。
这些防范措施需要综合运用,以最大程度地降低 CSRF 和 XSS 攻击的风险,保障 Web 应用的安全性。
3、TS type 和 interface区别
在 TypeScript 中, type 和 interface 有以下一些区别:
1. 扩展方式:
-
interface 通过继承来扩展,使用 extends 关键字。
-
type 使用交叉类型( & )来组合类型实现类似扩展的效果。 2. 重复定义:
-
多次定义同名的 interface 会自动合并其成员。
-
重复定义同名的 type 会导致错误。 3. 定义联合类型和交叉类型:
-
type 更适合定义联合类型和交叉类型。 4. 计算属性:
-
type 可以使用计算属性来创建新类型。 5. 声明合并:
-
interface 支持声明合并,即可以多次声明同一个接口来添加新的成员。
-
type 不支持声明合并。
总的来说, interface 更常用于定义对象的形状,而 type 在定义复杂类型和类型运算时更加灵活。具体使用哪种方式取决于具体的场景和需求。
4、手写题
解析urlparams
// '?a=1&a=2&b=3&c=4'
function formatQuery(query = '') {
const searchParams = new URLSearchParams(query.slice(1))
const result = {}
for (const [key, value] of searchParams) {
if (result[key]) {
if (Array.isArray(result[key])) {
result[key].push(value)
} else {
result[key] = [result[key], value]
}
} else {
result[key] = value
}
}
return result
}
// 合并两个有序数组并排序,不能先合并在排序
// [1,3,5,7]
// [2,4,6,8]
function mergeSort(arr1, arr2) {
let i = 0, j = 0, mergeArr = []
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
mergeArr.push(arr1[i++])
} else {
mergeArr.push(arr2[j++])
}
}
while (i < arr1.length) {
mergeArr.push(arr1[i++])
}
while (j < arr2.length) {
mergeArr.push(arr2[j++])
}
return mergeArr
}
// 重写Array.filter()
Array.prototype.myFilter = function (callback) {
const arr = this
const result = []
for (let i = 0; i < arr.length; i++) {
if (callback(this[i], i, this)) {
result.push(this[i])
}
}
return result
}
// 每隔3位加逗号
function joinSymbol(num, symbol = ',') {
const stringNum = String(num)
let result = '', count = 0
for (let i = stringNum.length - 1; i >= 0; i--) {
count++
if (count % 3 == 0 && i !== 0) {
result = `${symbol}${stringNum[i]}${result}`
} else {
result = `${stringNum[i]}${result}`
}
}
return result
}
// 数组扁平化
function myFlat(arr) {
let result = []
for (const item of arr) {
if (Array.isArray(item)) {
if (item.length) result = result.concat(myFlat(item))
} else {
result.push(item)
}
}
return result
}
function myFlat2(arr) {
return arr.reduce((acc, cur) => acc.concat((Array.isArray(cur) ? myFlat2(cur) : cur), []))
}
function myFlat3(arr) {
return arr.flat(Infinity)
}
快速排序:
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[arr.length - 1];
const left = [];
const right = [];
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
冒泡排序:
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
归并排序:
function merge(left, right) {
let result = [], i = 0, j = 0;
while (i < left.length && j < right.length) {
result.push(left[i] < right[j] ? left[i++] : right[j++]);
}
return result.concat(i < left.length ? left.slice(i) : right.slice(j));
}
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
return merge(mergeSort(arr.slice(0, mid)), mergeSort(arr.slice(mid)));
}
深比较对象是否相等
function isEqual(obj1, obj2) {
// 如果两个对象不是对象类型(如基本数据类型),直接比较值
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
return obj1 === obj2;
}
// 获取对象的键集合
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
// 键的数量不同,对象不等
if (keys1.length !== keys2.length) {
return false;
}
// 遍历键
for (const key of keys1) {
// 如果对应键的值不相等,对象不等
if (!isEqual(obj1[key], obj2[key])) {
return false;
}
}
// 所有键对应的值都相等,对象相等
return true;
}