2019-07 中旬 ✅
- 创建于:2019-07-11
- 更新于:2023-03-16

# 2019-07-20: 什么是 XSS 攻击?如何预防? ✅✅
好好想想先 😌
- 参考链接
- 一句话
- 跨站脚本攻击(Cross Site Scripting)
- 存储型
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
- 反射型
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
- DOM 型
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
- xss 预防
- 输入过滤
- 慎用 innerHTML, outerHTML, document.write()
- cookie 使用 http-only
- 使用 csp 禁用外域脚本
# 2019-07-19: 什么是 Http 中间人攻击?如何预防? ✅✅
好好想想先 😌
- 参考链接
- 一句话
- DNS 欺骗
- 攻击者通过入侵 DNS 服务器、控制路由器等方法把受害者要访问的目标机器域名对应的 IP 解析为攻击者所控制的机器,这样受害者原本要发送给目标机器的数据就发到了攻击者的机器上
- 会话劫持
- 代理服务器
- DNS 欺骗
# 2019-07-18: Set 和 WeakSet 如何理解?有何区别? ✅✅
好好想想先 😌
参考链接
一句话
Set
- 初始化
// 例一 const set = new Set([1, 2, 3, 4, 4]) ;[...set] // [1, 2, 3, 4] // 例二 const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]) items.size // 5 // 例三 const set = new Set(document.querySelectorAll('div')) set.size // 56 // 类似于 const set = new Set() document.querySelectorAll('div').forEach((div) => set.add(div)) set.size // 56prototype
- Set.prototype.constructor:构造函数,默认就是 Set 函数。
- Set.prototype.size:返回 Set 实例的成员总数。
- Set.prototype.add(value):添加某个值,返回 Set 结构本身。
- Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
- Set.prototype.has(value):返回一个布尔值,表示该值是否为 Set 的成员。
- Set.prototype.clear():清除所有成员,没有返回值。
遍历
- Set.prototype.keys():返回键名的遍历器
- Set.prototype.values():返回键值的遍历器
- Set.prototype.entries():返回键值对的遍历器
- Set.prototype.forEach():使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']) for (let item of set.keys()) { console.log(item) } // red // green // blue for (let item of set.values()) { console.log(item) } // red // green // blue for (let item of set.entries()) { console.log(item) } // ["red", "red"] // ["green", "green"] // ["blue", "blue"]
WeakSet
- WeakSet 的成员只能是对象,而不能是其他类型的值
- WeakSet 只有 delete, add, has 方法
- WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
# 2019-07-17: Map 和 WeakMap 如何理解?有何区别? ✅✅
好好想想先 😌
参考链接
一句话
Map
- 初始化
const map = new Map([['name', '张三'], ['title', 'Author']]) map.size // 2 map.has('name') // true map.get('name') // "张三" map.has('title') // true map.get('title') // "Author" map.delete('title) map.clear()- 遍历
- Map.prototype.keys():返回键名的遍历器。
- Map.prototype.values():返回键值的遍历器。
- Map.prototype.entries():返回所有成员的遍历器。
- Map.prototype.forEach():遍历 Map 的所有成员。
const map = new Map([ ['F', 'no'], ['T', 'yes'] ]) for (let key of map.keys()) { console.log(key) } // "F" // "T" for (let value of map.values()) { console.log(value) } // "no" // "yes" for (let item of map.entries()) { console.log(item[0], item[1]) } // "F" "no" // "T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value) } // "F" "no" // "T" "yes" // 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value) } // "F" "no" // "T" "yes"
WeakMap
- WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
- WeakMap 的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap 结构有助于防止内存泄漏
- WeakMap 只有四个方法可用:get()、set()、has()、delete()
# 2019-07-16: Node 的特性是什么?如何理解? ✅✅
好好想想先 😌
- 参考链接
- 一句话
- 事件驱动
- 非阻塞式 IO
# 2019-07-15: 什么是深度优先(DFS)和广度优先(BFS)遍历,如何实现 ✅✅
好好想想先 😌
参考链接
一句话
DFS: 递归遍历,有 children 直接遍历
BFS: 一级级遍历,将 children 暂存,上一级遍历完了再遍历 children 合集
const tree = [ { id: '1', children: [ { id: '1_1', children: [ { id: '1_1_1' } ] }, { id: '1_2', children: [ { id: '1_2_1' } ] } ] } ] function bfs(tree) { const nodes = [] const queue = [] queue.push(...tree) while (queue.length) { const item = queue.shift() nodes.push(item) if (item.children) { for (const v of item.children) { queue.push(v) } } } return nodes }
# 2019-07-14: 实现一个 Promise ✅✅
好好想想先 😌
参考链接
一句话
Promises/A+ 规范
- 为实现者提供一个健全的、可互操作的 JavaScript promise 的开放标准。
- 解决 (fulfill) : 指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
- 拒绝(reject) : 指一个 promise 失败时进行的一系列操作。
- 拒因 (reason) : 也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
- 终值(eventual value) : 所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
- Promise : promise 是一个拥有 then 方法的对象或函数,其行为符合本规范。
- thenable : 是一个定义了 then 方法的对象或函数,文中译作“拥有 then 方法”。
- 异常(exception) : 是使用 throw 语句抛出的一个值。
最简单的实现,仅实现
then方法//Promise 的三种状态 (满足要求 -> Promise的状态) const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class AjPromise { constructor(fn) { //当前状态 this.state = PENDING //终值 this.value = null //拒因 this.reason = null //成功态回调队列 this.onFulfilledCallbacks = [] //拒绝态回调队列 this.onRejectedCallbacks = [] //成功态回调 const resolve = (value) => { // 使用macro-task机制(setTimeout),确保onFulfilled异步执行,且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。 setTimeout(() => { if (this.state === PENDING) { // pending(等待态)迁移至 fulfilled(执行态),保证调用次数不超过一次。 this.state = FULFILLED // 终值 this.value = value this.onFulfilledCallbacks.map((cb) => { this.value = cb(this.value) }) } }) } //拒绝态回调 const reject = (reason) => { // 使用macro-task机制(setTimeout),确保onRejected异步执行,且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。 (满足要求 -> 调用时机) setTimeout(() => { if (this.state === PENDING) { // pending(等待态)迁移至 fulfilled(拒绝态),保证调用次数不超过一次。 this.state = REJECTED //拒因 this.reason = reason this.onRejectedCallbacks.map((cb) => { this.reason = cb(this.reason) }) } }) } try { //执行promise fn(resolve, reject) } catch (e) { reject(e) } } then(onFulfilled, onRejected) { typeof onFulfilled === 'function' && this.onFulfilledCallbacks.push(onFulfilled) typeof onRejected === 'function' && this.onRejectedCallbacks.push(onRejected) // 返回this支持then 方法可以被同一个 promise 调用多次 return this } }
# 2019-07-13: 执行上下文和作用域链,词法作用域,动态作用域 💊✅✅
好好想想先 😌
- 参考链接
- 一句话
- 执行上下文
- 全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
- 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
- Eval 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。
- 执行栈
- 执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)数据结构的栈,被用来存储代码运行时创建的所有执行上下文。
- 当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。
- 引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。
- 作用域
- 块级作用域
- 函数作用域
- 全局作用域
- 作用域链
- 再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是 作用域链
- js 为词法作用域,bash 为动态作用域
- 解释阶段:
- 词法分析
- 语法分析
- 作用域规则确定
- 执行阶段:
- 创建执行上下文
- 执行函数代码
- 垃圾回收
- 执行上下文
# 2019-07-12: defer 和 async 有什么区别,prefetch, preload, preconnect,dns-prefetch 的作用 ✅✅
好好想想先 😌
- 参考链接
- 一句话
- defer:此布尔属性被设置为向浏览器指示脚本在文档被解析后执行。
- 对于 defer,我们可以认为是将外链的 js 放在了页面底部。js 的加载不会阻塞页面的渲染和资源的加载。不过 defer 会按照原本的 js 的顺序执行,所以如果前后有依赖关系的 js 可以放心使用。
- async:设置此布尔属性,以指示浏览器如果可能的话,应异步执行脚本。
- 对于 async,这个是 html5 中新增的属性,它的作用是能够异步的加载和执行脚本,不因为加载脚本而阻塞页面的加载。一旦加载到就会立刻执行在有 async 的情况下,js 一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。
- preload: 允许浏览器来设定资源加载的优先级因此可以允许前端开发者来优化指定资源的加载
- prefetch: Prefetch 是一个低优先级的资源提示,允许浏览器在后台(空闲时)获取将来可能用得到的资源,并且将他们存储在浏览器的缓存中。
- preconnect: preconnect 允许浏览器在一个 HTTP 请求正式发给服务器前预先执行一些操作, DNS+TCP for HTTP, and DNS+TCP+TLS for HTTPS origins。
- dns-prefetch: 预加载 dns
- defer:此布尔属性被设置为向浏览器指示脚本在文档被解析后执行。
# 2019-07-11: Javascript 中常见的设计模式 ✅✅
好好想想先 😌
- 参考链接
- 一句话
- 单例模式
- 确保只有一个实例
- 可以全局访问
- 发布订阅模式
- Event Center
- 观察者模式
- Object.defineProperty
- Proxy
- 工厂模式
- 即为面向对象
- 迭代器模式
const iterator = function (arr) { let current = 0 const next = function () { current = current + 1 } const done = function () { return current >= arr.length } const value = function () { return arr[current] } return { next, done, value } }
- 单例模式