skip to content
天真笔录

百题复习

/ 17 min read

基础

ajax fetch axios

三者都属于网络请求,基于不同维度

  1. Ajax 属于技术统称
  2. Fetch,是一个具体的 API
  3. Axios,第三方库

XMLHttpRequest

function ajax(url) {
	const xhr = new XMLHttpRequest();
	xhr.open("GET", url, false);
	xhr.onreadystatechange = function () {
		if (xhr.readyState === 4) {
			if (xhr.status === 200) {
				console.log(xhr.responseText);
			}
		}
	};

	xhr.send(null);
}

ajax("http://tzhen.vip");

fetch

ES6 新增 API,支持 Promise

fetch("http://127.0.0.1:8888/user/login", {
	method: "post",
	headers: {
		"Content-Type": "application/x-www-form-urlencoded",
	},
	body: Qs.stringify({
		account: "18310612838",
		password: md5("1234567890"),
	}),
}).then((response) => {
	console.log(response.json());
});

fetch("http://127.0.0.1:8888/user/list").then((response) => {
	console.log(response.json());
});

axios

Axios 是一个库,对 ajax 进行封装,支持 Promise

防抖节流

  1. 节流:限制执行频率,有节奏的执行
  2. 防抖:限制执行次数,多次的密集执行只执行最后一次
  3. 节流关注过程,防抖关注结果

px % em rem vw/vh

  1. px 绝对单位
  2. % 相对于父元素
  3. em: 如果当前元素有设置 font-size,则为当前 font-size 大小,否则根据父元素
  4. rem:
  5. vw
  6. vh

什么时候不能用箭头函数

箭头函数的缺点

  1. 没有 arguments
  2. 不能用 apply、call、bind 改变 this

不适用场景

  1. 对象方法
  2. 对象原型
  3. 构造函数(没有显示原型-prototype)
  4. 动态上下文
  5. Vue 生命周期,method

TCP 三次握手和四次挥手

for in , for of, for await of

相同点

  1. 都可用于迭代数据
  2. for in 遍历的到的是 key
  3. for of 遍历的到的 value
  4. for of 是 es6API

不同点

  • 遍历对象
  1. for in ✅
  2. for of ❌
  • Map,Set
  1. for of ✅
  2. for in ❌
  • generator
  1. for of ✅
  2. for in ❌

本质

  1. for in 用于可枚举数据
  2. for of 用于可迭代数据

offsetHeight/clientHeight/scrollHeight

  1. offsetHeight: border + padding + content
  2. clientHeight: padding + content
  3. scrollHeight: padding + 实际内容尺寸

HTMLCollection 和 NodeList

  • Dom 是一颗树,所有节点都是 Node
  • Node 是 Element 的基类
  • Element 是其他 HTML 元素的基类,如 HTMLDivElement

Node 和 Element 的关系

class Node {}

class CharacterData extends Node {}
class Text extends CharacterData {}
class Comment extends CharacterData {}

class Element extends Node {}
class HTMLElement extends Element {}
class HTMLDivElement extends HTMLElement {}
<div id="d1">
	<p>haha</p>
	<!--注释-->>
</div>

<script>
	const d1 = document.querySelector("#d1");
	console.log(d1.children instanceof HTMLCollection);
	console.log(d1.childNodes instanceof NodeList);
</script>

结论

  1. HTMLCollection 是 Element 的集合
  2. NodeList 是 Node 的集合

computed vs watch

Vue 通信的几种方式

  1. props/emit(父子)
  2. 自定义事件(事件总线)
  3. $attrs/$listeners
    • $attrs: 容器对象,存放父组件传过来且子组件未使用 props 声明接收的数据
    • $listeners: 实现跨组件数据传递 中间组件v-on=“$listeners”
    • inheritAttrs: false; 禁止在 html 标签上显示传过来的数据
  4. $parent/$children|$refs
  5. provide 和 inject(跨层级)
// 父组件
provide: {
  info: 'hello'
}
// 使用data中的数据
provide() {
  return {
    info: computed(() => ***.value)
  }
}
// 子组件
inject: ['info']
  1. vuex

vuex action mutation

严格模式

  1. 全局变量必须先声明
  2. 禁止使用 width
  3. 禁止创建 eval 作用域
  4. 禁止 this 指向 window
  5. 函数参数不能重名

HTTP 跨域请求时为什么会发送 options 请求

跨域请求时,浏览器自动发出 options 请求(预检测),以检测服务端是否支持

知识深度

JS 内存泄漏

垃圾回收

  1. 什么是垃圾回收
  2. 引用计数
  3. 标记清除

内存泄漏场景

  1. 被全局变量、函数引用,组件销毁时未清除
  2. 被全局事件、定时器引用,组件销毁时未清除
  3. 被自定义事件引用,组件销毁时未清除

WeakMap WeakSet

见 coder 高级 JS

vdom 真的快吗

  1. vdom 与 js 直接操作 dom 相比,并没有原生操作快
  2. 数据驱动视图要有合适的方案,不能全部 DOM 重建
  3. vdom 是目前最合适的技术方案,不是因为快,而是合适
  • 扩展:svelet,没有使用 vdom

for vs forEach 哪个速度更快

  • for 更快
  • forEach 需要创建函数,函数有独立的作用域,开销更大

Node 如何开启多进程,进程如何通信

为什么要开启多进程

  1. 开启多进程,能够充分利用多核 CPU 处理能力
  2. 单进程的内存存储有上限,多进程能够分担压力

开启多进程

  1. child_process.fork
  2. cluster.fork
  3. send 和 on 传递消息

JsBridge 原理

webview

是系统渲染网页的一个控件,可执行 javascript,可与页面 JavaScript 交互,实现混合开发

  • Android 的 webview 内核
    1. <4.4: androidWebkit
    2. >=4.4: 采用了 chrome
  • IOS 的 webview
    1. <ios8: UIWebView
    2. >=ios8: WKWebView

Native 与 Webview 通信

Native -> Webview
  • Android
    1. <4.4: loadUrl('javascript:' + jsCode);不支持回调
    2. >=4.4: evaluateJavascript('javascript:' + jsCode, new ValueCallback<String>() {}); 可以获取返回值,支持回调
  • IOS
    1. UIWebview: stringByEvaluatingJavascriptFromString
    2. WKWebView: evaluateJavascript:
WevView -> Native

URL Schema

  1. web 发送的请求都会经过 webview 组件,Native 重写 webview 里的方法,拦截 web 发送的请求,对请求格式进行判断
  2. 如果如何自定义 URL Schema 格式,则进行解析,调用原生 Native 方法
  3. 如果不符合自定义 URL Schema,则直接转发,请求真正的服务
  • web 发起请求的方式
  1. a 标签: 需用户主动操作
  2. location.href:可能会引起页面跳转,丢失调用
  3. ajax:Android 没有相应的拦截方法
  4. iframe.src:推荐
  • 拦截请求
  1. Android
    • shouldOverrideUrlLoading
  2. IOS - UIWebview: shouldStartLoadWithRequest - WKWebView: decidePolicyForNavigationAction

    优点:兼容好;缺点:URL 长度限制且不直观,数据格式限制,发起请求耗时

JS 注入

  1. Android
    • 4.2+: addJavascriptInterface
  2. IOS
    • UIWebview: JavaScriptCore
    • WKWebView: WKScriptMessageHandler

requestIdleCallback 和 requestAnimationFrame

  • requestAnimationFrame: 每次渲染完都会执行(帧变化), 高优先级
  • requestIdleCallback: 空闲时执行, 低级先级

Vue 的每个生命周期都做了什么

  1. beforeCreate
  • new Vue 一个空白的 vue 实例
  • data method 尚未初始化
  1. crated
  • 实例初始化完成,完成响应式绑定
  • data method 已初始化完成
  1. beforeMount
  • 编译模版,调用render生成 vDom
  • 还没有开始渲染 Dom
  1. mounted
  • 完成Dom渲染
  • 组件创建完成
  1. beforeUpdate(更新阶段)
  • data发生变化之后,准备更新 Dom(尚未更新 Dom)
  1. updated(更新阶段)
  • data发生变化,且Dom更新完成
  • 不要在updated中修改data,否则可能导致死循环
  1. beforeUnmount (销毁阶段)
  • 组件进入销毁阶段(尚未销毁)
  • 可移除、解绑一些全局事件、自定义事件
  1. mounted (销毁阶段)
  • 组件已销毁
  • 所有子组件已销毁

keep-alive

  1. activated
  • 缓存组件被激活
  1. deactivated
  • 缓存组件被隐藏

Vue3 Composition API 生命周期与 vue2 有何区别

  • setup代替了beforeCreatedcreated
  • 使用Hooks函数形式,如mounted -> onMounted

Vue2 diff 和 Vue3 diff

  1. 同层比较
  2. tag 不同,则删除重建
  3. 子节点通过 key 区分

vue2

双端比较

vue3

最长递增子序列

react

仅右移

Vue Router 的 MemoryHistory

路由的三种模式

  1. hash
  2. history
  3. abstract(node 中使用,路由地址不会发生变化)
    • vue2 中是 mode: abstract
    • vue3 中 history: createMemoryHistory()

知识广度

移动端H5 click 300ms延迟

背景

  1. double tap to zoom
  2. android: < chrome 32
  3. ios: < 9.3

解决

FastClick

window.addEventListener("load", () => {
	FactClick.attach(document.body);
});

FastClick原理

  1. 监听touchend事件(touchstart/touchend先于click触发)
  2. 使用DOM自定义事件模拟click
  3. 禁用默认的click事件

现代浏览器

<meta name="viewport" content="width=device-width"></meta>

cookie/session/token

cookie

  1. http无状态,通过cookie识别用户身份
  2. 服务端通过set-cookie自动设置,有4kb上限
  3. 默认有跨域限制:不可跨域共享、传递cookie
  4. 跨域传递:
    • 前端设置 withCredentials=true
    • 后端:Access-Control-Allow-Origin:源url(不能为*)

第三方cookie

  1. 现代浏览器禁止第三方JS设置cookie
  2. 其目的是打击第三方广告,保护用户隐私
  3. 新增属性:SameSite: Strict/Lax/None
    • Strict: 严格模式;跨站点时任何情况都不发送cookie
    • Lax: 宽松模式;a链接/preload/get请求发送
    • None: 关闭

session

  1. cookie通常与session相辅相成
  2. 服务端保存session(如redis), set-cookie:sessionId=***
  3. 浏览器下次自动携带
  4. cookie + session 是常见的登录验证解决方案

token

  1. 需手动存储
  2. 自定义header,手动发送
  3. 无大小限制

session vs JWT 哪个更好

session

  1. 优点:
    • 原理简单,容易上手
    • 用户信息存储在服务端,可快速封禁用户
  2. 缺点:
    • 占用服务端内存,硬件成本高
    • 多进程,多服务器,不好同步(需使用第三方缓存:redis)

JWT

  1. 优点:
    • 客户端存储,不占服务端内存
    • 多进程,多服务器不受影响
    • 没有跨域限制
    • 跨端(cookie只适用浏览器)
  2. 缺点:
    • 用户信息存储在客户端,无法快速封禁用户(需要服务端设置黑名单,在访问时拦掉)
    • 服务端私钥泄漏,客户信息全部丢失
    • token体积大于cookie,增加请求的数据量

如何选择

  1. 如需严格管理用户信息(保密,快速封禁);推荐:session
  2. 无特殊要求,使用JWT(如初创企业)

单点登录(SSO)

单点登录就是登录了一个网站,另一个网站也自动登录(如:登录百度网盘,则百度账号,百度贴吧等自动登录)

如何实现

  1. cookie
    • 设置domain=baidu.com,则主域名下的cookie可以共享
  2. token(第三方登录)
    • 客户端访问a.com, 无token,重定向到c.com
    • 在c.com登录之后,返回token,并在c.com设置token
    • 返回至客户端, 存储token,携带token再次访问a.com
    • a.com重定向到c.com, 校验token,返回用户信息至a.com
    • -----------------------------
    • 访问b.com, 无token,重定向至c.com
    • 因为之前c.com已经存储token,则直接使用(没有过期)
    • 返回用户信息至b.com, b.com存储token

原理

  1. 使用第三方c.com作为中间商
  2. 所有网站都重定向至c.com,在c.com完成验证, 并存储token
  3. 之后的a,b..其他网站都能在c.com中使用之前生成的token,并在之后自行存储

oAuth2.0

TCP 和 UDP

TCP

  1. 连接:三次握手
  2. 断开:四次挥手
  3. 稳定传输

UDP

  1. 无连接,无断开
  2. 不稳定传输,但效率高
  3. 应用场景:视频会议,语音通话

http1.0/http1.1/http2.0

http1.0

  1. 只支持GET

http1.1

  1. 支持GET、POST、PUT、DELETE、HEAD
  2. 缓存策略:catch-control, e-tag
  3. 支持长连接:connection: keep-alive
  4. 断点续传:状态码206

http2.0

  1. header头压缩
  2. 多路复用,一个TCP连接支持多个HTTP并行请求
  3. 服务器推送

https中间人攻击?如何防御?

https加密过程

  1. 中间人攻击
    • 比如DNS劫持,冒充目标服务器
  2. 防御
    • 购买正规CA证书

defer vs async

  1. <script src="***"></script>
    • 暂停解析,下载JS,执行,继续解析
  2. <script src="***" defer></script>
    • 继续解析,并行下载JS,解析完成,执行JS
  3. <script src="***" async></script>
    • 继续解析,并行下载JS,下载完立即执行JS,再继续解析
  4. <script type="module""></script>
    • 默认defer

prefetch vs dns-prefetch

prefetch vs preload

  1. preload: 资源在当前页面使用,优先加载
  2. prefetch: 资源在未来页面使用,空闲时加载

preconnect vs dns-prefetch

  1. preconnect: DNS预连接(TCP)
  2. dns-prefetch: DNS预查询

前端攻击手段

xss

  1. cross site script 跨站脚本攻击
  2. 将JS代码插入网页中,渲染时执行JS(比如评论区)
  3. 预防:标签替换
  • vue/react
    • vue: v-html
    • react: dangerouslySetInnerHTML

CSRF

  1. cross site request forgery 伪造跨站请求
  2. 诱导用户访问另一个网站,伪造请求
    • 基于用户在之前的网站上已登录,有了cookie
    • 再访问一个网站时自动携带cookie
  3. 预防
    • 严格的跨域限制,判断referrer来源
    • 为cookie设置SameSite,禁止跨域传递
    • 关键接口使用短信验证码再次验证

点击劫持

  1. 诱导界面上蒙一个透明的iframe,诱导用户点击
  2. 预防:X-Frame-Options: sameorigin (同域下允许加载iframe)

DDos

  1. 分布式拒绝服务
  2. 手段:分布式、大规模的流量访问,使服务器瘫痪
  3. 预防:硬件预防(阿里云WAF)

sql 注入

  1. 输入内容提交时,注入sql语句
  2. 预防:处理输入时,特殊字符替换

websocket vs http

websocket的连接过程

  1. 先发起一个http请求
  2. 成功之后升级到websocket,再进行通信
    • Status Code: Switching Protocols
    • Connection: Upgradge
    • Upgradge: Websocket

区别

  1. WebSocket协议:ws:// ,可双端发起请求
  2. WebSocket没有跨域限制
  3. 通过send和onmessage通信(http,req, res)

websocket vs http长轮询

输入URL到页面展示的完整过程

  1. 网络请求

    • DNS解析,得到IP
    • TCP三次握手
    • 发起HTTP请求
    • 得到响应内容(HTML字符串)
  2. 解析

    • DOM树
    • CSSOM树
    • 结合生成Render tree
  3. 渲染、计算、绘制

重绘repaint 和 重排reflow

重绘

  1. 元素外观改变,如:颜色,背景色
  2. 元素尺寸、定位不变,不会影响其他元素位置

重排

  1. 重新计算尺寸和布局,可能会影响其他元素的位置
  2. 如新增、删除元素,改变宽高等

区别

  1. 重排比重绘影响更大,消耗更大
  2. 尽量避免无意义重排

减少重排

  1. 统一修改样式,或切换class
  2. 修改之前修改 display: none, 脱离文档流
  3. 利用BFC
  4. 使用防抖/节流
  5. createDocumentFragment 批量操作DOM
  6. 使用css3和requestAnimationFrame

多标签通信

  1. websocket
    • 支持跨域
  2. localStorage
    • 同域限制
    • window.addEventListener(‘storage’, event => { console.log(event.key, event.newValue) })
  3. sharedworker

iframe 通信

  1. 发送:
    • postMessage
  2. 接受
    • window.addEventListener(‘message’, event => event.origin)

koa2 洋葱模型

  1. nodejs 流行框架
  2. 中间件组织代码
  3. 多个中间件以洋葱模型运行

手写题

  1. 数组拍平
function flatten(arr) {
	if (!Array.isArray(arr)) return arr;
	let result = [];

	arr.forEach((item) => {
		if (Array.isArray(item)) {
			const child = flatten(item);
			result = result.concat(child);
		} else {
			result.push(item);
		}
	});

	return result;
}

const arr = [1, [2, 3, [4]]];
const result = flatten(arr);
console.log(result);
  1. 数据类型
Object.prototype.toString.
  1. 深度优先
// 遍历如下DOM
<div class="box">
	<ul>
		<li>item</li>
	</ul>
	<h1>hello</h1>
	<!--please say hello-->
	<h2>welcome hello</h2>
</div>
function visitNode(node) {
	if (node instanceof Comment) {
		console.log("comment-", node);
	}
	if (node instanceof Text) {
		console.log("Text-", node);
	}
	if (node instanceof HTMLElement) {
		console.log("Element-", node);
	}
}

function depthFirstTraverse1(root) {
	visitNode(root);
	const childNodes = root.childNodes;
	if (childNodes.length > 0) {
		childNodes.forEach((child) => {
			depthFirstTraverse1(root);
		});
	}
}

不使用递归如何实现

  1. 广度优先
function breakFirstTraverse1(root) {
	const queue = [];

	// 根节点入队
	queue.unshift(root);
	while (queue.length > 0) {
		const currentNode = queue.pop();
		if (currentNode === null) return;

		if (currentNode.length > 0) {
			currentNode.forEach((child) => {
				queue.unshift(child);
			});
		}
	}
}