├── README.md ├── algo ├── 数组_Array.md ├── 栈_stack.md ├── 跳表_skiplist.md ├── 链表_linkedList.md └── 队列_ queue.md ├── demos ├── 01背包问题.md ├── 01背包问题变体.md ├── 二叉树的三序遍历.md ├── 八皇后问题.md ├── 动态规划理论.md ├── 滑动窗口11道.md ├── 爬楼梯变体问题.md ├── 股票6道.md └── 薅羊毛问题.md ├── index.js ├── otherShare ├── 前端知识点总结 │ ├── css篇 │ │ ├── img.md │ │ ├── position.md │ │ ├── 垂直居中.md │ │ ├── 盒模型.md │ │ └── 避免重排和重绘的小技巧.md │ ├── html篇 │ │ ├── html5的优缺点.md │ │ ├── iframe的优缺点.md │ │ └── 如何实现浏览器内多个标签页之间的通信.md │ ├── js篇 │ │ ├── JS编程实现简单模板引擎变量替换.md │ │ ├── JavaScript中this.md │ │ ├── Javascript的继承与多态.md │ │ ├── for in 和 for of的区别.md │ │ ├── json和xml的区别.md │ │ ├── js基本数据类型.md │ │ ├── promise的简单实现.md │ │ ├── 不使用NEW运算符如何创建JS对象.md │ │ ├── 函数防抖与节流.md │ │ ├── 实现ES6的class语法.md │ │ ├── 实现call、apply、bind.md │ │ ├── 实现一个 instanceof.md │ │ ├── 实现一个 new.md │ │ ├── 实现一个 sum(1)(2)(4,1).valueOf() sum(1,2,3,4).valueOf().md │ │ ├── 对象深、浅拷贝.md │ │ ├── 箭头函数简析.md │ │ └── 超大数相加.md │ ├── other │ │ ├── 优化网页手段.md │ │ └── 前端缓存.md │ ├── 工程化 │ │ └── webpack 热更新原理.md │ ├── 框架篇 │ │ └── Vue 的响应式原理中 Object.defineProperty 有什么缺陷?.md │ ├── 浏览器 │ │ ├── cookie 和 token 在什么情况下会被劫持?.md │ │ └── 如何获取浏览器的唯一标识?.md │ └── 网络篇 │ │ └── 从输入URL到页面加载发生了什么?.md ├── 数据结构分享 │ ├── 栈和队列转换.md │ └── 树状数组入门.md ├── 服务器知识点总结 │ ├── DNS.md │ ├── TCP.md │ ├── http1.0、1.1、2.0 协议的区别.md │ ├── 接口如何防刷.md │ └── 请求状态码.md └── 源码之殇 │ ├── Vue Keep-alive原理.md │ ├── Vue-virtual dom & DOM-diff算法.md │ └── new Vue().md └── theoreticalKnowledge ├── AlgorithmTemplate算法模板.md ├── Array数组、LinkedList链表、Stack栈、Queue队列.md ├── BitOperation位运算、Bloom Filter布隆过滤器、LRU Cache缓存、Sorting algorithm排序算法.md ├── CommencementIsJustStart毕业也是开始.md ├── DFS深度优先搜索、BFS广度优先搜索、Greedy algorithm贪心算法、Binary search二分查找.md ├── DynamicProgramming动态规划.md ├── Hash table哈希表、Map映射、Set集合 、Tree 树、Binary tree二叉树、Binary search tree二叉搜索树、图(Graph) 、 recursive递归 、divide分治 、recall 回溯.md ├── HighClassDynamicProgramming高级动态规划、字符串算法.md ├── ProblemClassificationCut习题分类斩.md └── Trie树、Union-Find并查集、search搜索、tree树.md /README.md: -------------------------------------------------------------------------------- 1 | # **算法与数据结构等 - 知识荟萃 「长期更新中」** 2 | ## **摘要** 3 | + ***demos*** 4 | + ***otherShare*** 5 | + ***theoreticalKnowledge*** 6 | + ***algo*** 7 | 8 | ### **demos** 9 | + [一些算法问题求解](https://github.com/Alex660/Algorithms-and-data-structures/tree/master/demos) 10 | 11 | ### **otherShare** 12 | + 知识分享 13 | + [服务器知识点总结](https://github.com/Alex660/Algorithms-and-data-structures/tree/master/otherShare/%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93) 14 | + [前端知识点总结](https://github.com/Alex660/Algorithms-and-data-structures/tree/master/otherShare/%E5%89%8D%E7%AB%AF%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93) 15 | + [数据结构分享](https://github.com/Alex660/Algorithms-and-data-structures/tree/master/otherShare/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%88%86%E4%BA%AB) 16 | + [源码之殇](https://github.com/Alex660/Algorithms-and-data-structures/tree/master/otherShare/%E6%BA%90%E7%A0%81%E4%B9%8B%E6%AE%87) 17 | 18 | ### **theoreticalKnowledge** 19 | + 数据结构大部分知识点讲解 20 | + [Array数组、LinkedList链表、Stack栈、Queue队列](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/Array%E6%95%B0%E7%BB%84%E3%80%81LinkedList%E9%93%BE%E8%A1%A8%E3%80%81Stack%E6%A0%88%E3%80%81Queue%E9%98%9F%E5%88%97.md) 21 | + [Hash table哈希表、Map映射、Set集合 、Tree 树、Binary tree二叉树、Binary search tree二叉搜索树、图(Graph) 、 recursive递归 、divide分治 、recall 回溯](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/Hash%20table%E5%93%88%E5%B8%8C%E8%A1%A8%E3%80%81Map%E6%98%A0%E5%B0%84%E3%80%81Set%E9%9B%86%E5%90%88%20%E3%80%81Tree%20%E6%A0%91%E3%80%81Binary%20tree%E4%BA%8C%E5%8F%89%E6%A0%91%E3%80%81Binary%20search%20tree%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E3%80%81%E5%9B%BE(Graph)%20%E3%80%81%20recursive%E9%80%92%E5%BD%92%20%E3%80%81divide%E5%88%86%E6%B2%BB%20%E3%80%81recall%20%E5%9B%9E%E6%BA%AF.md) 22 | + [DFS深度优先搜索、BFS广度优先搜索、Greedy algorithm贪心算法、Binary search二分查找](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/DFS%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2%E3%80%81BFS%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2%E3%80%81Greedy%20algorithm%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95%E3%80%81Binary%20search%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.md) 23 | + [DynamicProgramming动态规划](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/DynamicProgramming%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92.md) 24 | + [BitOperation位运算、Bloom Filter布隆过滤器、LRU Cache缓存、Sorting algorithm排序算法](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/BitOperation%E4%BD%8D%E8%BF%90%E7%AE%97%E3%80%81Bloom%20Filter%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E3%80%81LRU%20Cache%E7%BC%93%E5%AD%98%E3%80%81Sorting%20algorithm%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) 25 | + [Trie树、Union-Find并查集、search搜索、tree树](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/Trie%E6%A0%91%E3%80%81Union-Find%E5%B9%B6%E6%9F%A5%E9%9B%86%E3%80%81search%E6%90%9C%E7%B4%A2%E3%80%81tree%E6%A0%91.md) 26 | + [HighClassDynamicProgramming高级动态规划、字符串算法](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/HighClassDynamicProgramming%E9%AB%98%E7%BA%A7%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E3%80%81%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%AE%97%E6%B3%95.md) 27 | + [总结 - CommencementIsJustStart毕业也是开始](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/CommencementIsJustStart%E6%AF%95%E4%B8%9A%E4%B9%9F%E6%98%AF%E5%BC%80%E5%A7%8B.md) 28 | + 各类算法模板总结 29 | + [AlgorithmTemplate算法模板](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md) 30 | + leetCode习题分类练习 31 | + [ProblemClassificationCut习题分类斩](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/ProblemClassificationCut%E4%B9%A0%E9%A2%98%E5%88%86%E7%B1%BB%E6%96%A9.md) 32 | + [题解](https://github.com/Alex660/leetcode) 33 | 34 | ### **algo** 35 | + 数据结构和算法必知必会的代码实现 36 | + [数组](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E6%95%B0%E7%BB%84_Array.md) 37 | + [链表](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md) 38 | + [跳表](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E8%B7%B3%E8%A1%A8_skiplist.md) 39 | + [栈](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E6%A0%88_stack.md) 40 | + [队列](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%98%9F%E5%88%97_%20queue.md) 41 | + 递归 42 | + [排序](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/BitOperation%E4%BD%8D%E8%BF%90%E7%AE%97%E3%80%81Bloom%20Filter%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E3%80%81LRU%20Cache%E7%BC%93%E5%AD%98%E3%80%81Sorting%20algorithm%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) 43 | + ![](https://camo.githubusercontent.com/65978e2da4398863ca05d258d51d505a5449904b/68747470733a2f2f696d67323031382e636e626c6f67732e636f6d2f626c6f672f3834393538392f3230313930332f3834393538392d32303139303330363136353235383937302d313738393836303534302e706e67) 44 | + 二分查找 45 | + 散列表 46 | + 字符串 47 | + 二叉树 48 | + 堆 49 | + 图 50 | + 回溯 51 | + 分治 52 | + 动态规划 53 | -------------------------------------------------------------------------------- /algo/数组_Array.md: -------------------------------------------------------------------------------- 1 | ### 定义 2 | + 略 3 | ### 创建 4 | + let arr = new Array() 5 | + let arr = [] 6 | ### 方法 7 | + [Array.from()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 8 | + 从类数组对象或者可迭代对象中创建一个新的数组实例 9 | + [Array.isArray()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) 10 | + 用来判断某个变量是否是一个数组对象。 11 | + [Array.of()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/of) 12 | + 根据一组参数来创建新的数组实例,支持任意的参数数量和类型 13 | + [Array.prototype.copyWithin()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) 14 | + 在数组内部,将一段元素序列拷贝到另一段元素序列上,覆盖原有的值 15 | + [Array.prototype.fill()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/fill) 16 | + 将数组中指定区间的所有元素的值,都替换成某个固定的值 17 | + [Array.prototype.pop()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) 18 | + 删除数组的最后一个元素,并返回这个元素 19 | + [Array.prototype.push()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 20 | + 在数组的末尾增加一个或多个元素,并返回数组的新长度 21 | + [Array.prototype.reverse()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) 22 | + 颠倒数组中元素的排列顺序 23 | + shift() 24 | + sort() 25 | + splice() 26 | + unshift() 27 | + concat() 28 | + includes() 29 | + join() 30 | + slice() 31 | + toString() 32 | + toLocaleString() 33 | + indexOf() 34 | + lastIndexOf() 35 | + forEach() 36 | + entries() 37 | + every() 38 | + some() 39 | + filter() 40 | + find() 41 | + findIndex() 42 | + keys() 43 | + map() 44 | + reduce() 45 | + reduceRight() 46 | + values() 47 | + [更多解释敬请参考MDN官方文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array) -------------------------------------------------------------------------------- /algo/栈_stack.md: -------------------------------------------------------------------------------- 1 | ### 定义 2 | + 它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。 3 | + 特点是 先进后出 4 | ### 应用 5 | + 实现栈结构 6 | + 用数组 7 | + 用链表 8 | + 栗子 9 | + 实现浏览器的前进后退 10 | + 实现表达式求值(四则运算) 11 | + 实现括号匹配 12 | + 实现进制转换 13 | ### code 14 | #### 实现栈结构 15 | + 用数组实现栈 16 | ```javascript 17 | class Stack { 18 | constructor() { 19 | this.items = []; 20 | } 21 | push(el) { 22 | this.items.push(el) 23 | } 24 | pop() { 25 | if(!this.items.length) return -1 26 | return this.items.pop() 27 | } 28 | peek() { 29 | return this.items[this.items.length-1] 30 | } 31 | clear() { 32 | this.items = [] 33 | } 34 | print() { 35 | console.log(this.items.toString()) 36 | } 37 | } 38 | ``` 39 | + 用链表实现栈 40 | ```javascript 41 | class Node { 42 | constructor(el) { 43 | this.el = el 44 | this.next = null 45 | } 46 | } 47 | class Stack { 48 | constructor() { 49 | this.top = null 50 | } 51 | push(val) { 52 | const node = new Node(val) 53 | if(!this.top) { 54 | this.top = node 55 | return 56 | } 57 | node.next = this.top 58 | this.top = node 59 | } 60 | pop() { 61 | if(!this.top) return -1 62 | const val = this.top.el 63 | this.top = this.top.next 64 | return val 65 | } 66 | peek() { 67 | return this.top 68 | } 69 | clear() { 70 | this.top = null 71 | } 72 | print() { 73 | if(this.top) { 74 | let cur = this.top 75 | while(cur) { 76 | console.log(cur.el) 77 | cur = cur.next 78 | } 79 | } 80 | } 81 | } 82 | ``` 83 | #### 栗子 84 | + 实现浏览器的前进后退 85 | + 图解 86 | + 1、顺序浏览了a、b、c三个页面 87 | + 借助左边前进栈 88 | + ![](https://static001.geekbang.org/resource/image/4b/3d/4b579a76ea7ebfc5abae2ad6ae6a3c3d.jpg) 89 | + 2、依次后退c、b两个页面 90 | + 借助右边后退栈 91 | + ![](https://static001.geekbang.org/resource/image/b5/1b/b5e496e2e28fe08f0388958a0e12861b.jpg) 92 | + 3、又前进一个页面,此时应显示b页面 93 | + 借助后退栈后退一个页面、前进栈压进一个页面 94 | + ![](https://static001.geekbang.org/resource/image/ea/bc/ea804125bea25d25ba467a51fb98c4bc.jpg) 95 | + 4、基于b页面,跳转到新的页面d。此时后退栈的所有历史页面将无法再后退访问 96 | + 保留前进栈,清空后退栈 97 | + ![](https://static001.geekbang.org/resource/image/a3/2e/a3c926fe3050d9a741f394f20430692e.jpg) 98 | + code 99 | ```javascript 100 | class browserRouter { 101 | // 初始化前进、后退栈 102 | constructor() { 103 | this.frontStack = new Stack() 104 | this.backStack = new Stack() 105 | } 106 | // 浏览新页面 107 | pushNewPage(url) { 108 | this.frontStack.push(url) 109 | this.backStack.clear() 110 | this.printAll() 111 | } 112 | // 前进 113 | front() { 114 | const url = this.backStack.pop() 115 | if(url !== -1) { 116 | this.backStack.push(url) 117 | this.printAll() 118 | return 119 | } 120 | console.log('无法前进') 121 | } 122 | // 后退 123 | back() { 124 | const url = this.frontStack.pop() 125 | if(url !== -1) { 126 | this.backStack.push(url) 127 | this.printAll() 128 | return 129 | } 130 | console.log('无法后退') 131 | } 132 | // 打印页面栈 133 | printAll() { 134 | console.log(`--前进页面--`) 135 | this.frontStack.print() 136 | console.log(`--后退页面--`) 137 | this.backStack.print() 138 | } 139 | } 140 | // 测试 141 | var browser = new browserRouter() 142 | browser.pushNewPage('url-1') 143 | browser.pushNewPage('url-2') 144 | browser.pushNewPage('url-3') 145 | // 前进/后退 146 | browser.back() 147 | browser.back() 148 | browser.front() 149 | browser.pushNewPage('url-new') 150 | ``` 151 | + 实现表达式求值 152 | ```javascript 153 | // 可提issue 154 | ``` 155 | + 实现括号匹配 156 | ```javascript 157 | // 可提issue 158 | ``` 159 | + 实现进制转换 160 | ```javascript 161 | // 可提issue 162 | ``` -------------------------------------------------------------------------------- /algo/跳表_skiplist.md: -------------------------------------------------------------------------------- 1 | + 定义 2 | + 增加了向前指针的链表叫作跳表。跳表全称叫做跳跃表,简称跳表。 3 | + 跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。 4 | + 跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。 5 | + 跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。 6 | + 时间复杂度 7 | + 查找、插入、删除均为O(logn),最坏为O(n) 8 | + 空间复杂度 9 | + O(n) 10 | + 实践 11 | + Redis、LevelDB 12 | + 性能 13 | + 和红黑树、AVL树不相上下 14 | + 图解实现 15 | + 快速查找 16 | + 跳表在原有的有序链表上面增加了多级索引,通过索引来实现 17 | + 首先在最高级索引上查找最后一个小于当前查找元素的位置, 18 | + 然后再跳到次高级索引继续查找,直到跳到最底层为止 19 | + ![](https://static001.geekbang.org/resource/image/46/a9/46d283cd82c987153b3fe0c76dfba8a9.jpg) 20 | + ![22.png](https://pic.leetcode-cn.com/f32753070674913993353da9e75a9398e97486999b15033f0249b6a47d1af7b1-22.png) 21 | + 高效的动态插入和删除 22 | + 插入 23 | + 查询:二分( O(logn) ) 24 | + ![](https://static001.geekbang.org/resource/image/65/6c/65379f0651bc3a7cfd13ab8694c4d26c.jpg) 25 | + ![](https://img-blog.csdnimg.cn/20190930010608521.jpeg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDEyNTc5,size_16,color_FFFFFF,t_70) 26 | + 删除 27 | + 查询节点,因为是链表,获取它的前驱节点,最后进行链表删除节点操作 28 | + ![](https://img-blog.csdnimg.cn/20190930010608866.jpeg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDEyNTc5,size_16,color_FFFFFF,t_70) 29 | + 跳表索引动态更新 30 | + 随机函数 31 | + 相关文章 32 | + [Redis源码学习之跳表](https://cloud.tencent.com/developer/article/1353762) 33 | + 代码实现 34 | ```javascript 35 | /** 36 | * 跳表索引默认最大级数 37 | */ 38 | const MAX_LEVEL = 16; 39 | /** 40 | * 随机生成索引层数概率因子 41 | */ 42 | const SKIPLIST_P = 0.5; 43 | /** 44 | * Node 类 45 | * @param {*} data - 存放类每个节点的数据 46 | * @param {number} maxLevel - 当前节点处于整个跳表索引的级数 47 | * @param {array} forwards - 当前层当前节点的下一个节点,如链表的 next 指针 48 | */ 49 | class Node { 50 | constructor (data = -1,maxLevel = 0,forwards = new Array(MAX_LEVEL)){ 51 | this.data = data; 52 | this.maxLevel = maxLevel; 53 | this.forwards = forwards; 54 | } 55 | } 56 | /** 57 | * 跳表 类 58 | * 跳表中存储的是正整数,并且存储的是不重复的。 59 | */ 60 | class SkipList { 61 | constructor() { 62 | // 带头链表 63 | this.head = new Node(); 64 | // 当前跳表索引的总级数 65 | this.levelCount = 1; 66 | } 67 | /** 68 | * 静态方法:随机生成 1~MAX_LEVEL 之间的索引级数 69 | * 配合概率因子SKIPLIST_P,使得跳表插入新元素时,每一层索引节点数约等于上一级索引节点数的2倍 70 | */ 71 | static randomLevel() { 72 | let level = 1; 73 | while (Math.random() < SKIPLIST_P && level < MAX_LEVEL) { 74 | level++; 75 | } 76 | return level; 77 | } 78 | /** 79 | * 查找返回跳表里面的某个数据节点,没有返回 null 80 | * @param val - 需要查找的数据 81 | * @returns {*} 82 | */ 83 | find(val) { 84 | if(!val) return null; 85 | let p = this.head; 86 | let levelCount = this.levelCount; 87 | // 从顶层开始查找,找到前一节点 88 | // i--,依次移动到下一层级查找前一节点 89 | for(let i = levelCount - 1;i >= 0;i--){ 90 | while(p.forwards[i] != null && p.forwards[i].data < val){ 91 | // 每层都找到一个当前节点的前一个节点,如链表的pre 指针 92 | p = p.forwards[i]; 93 | } 94 | } 95 | // 回到第一层,即原始链表 96 | if(p.forwards[0] !== undefined && p.forwards[0].data === val) { 97 | return p.forwards[0]; 98 | } 99 | return null; 100 | } 101 | /** 102 | * 向跳表里插入数据 103 | * @param val - 需要插入的数据 104 | */ 105 | insert(val) { 106 | // 随机层数 107 | const level = SkipList.randomLevel(); 108 | // 创建新节点 109 | const newNode = new Node(val,level); 110 | // 每一层小于插入节点的前一个节点数组 111 | const update = new Array(level).fill(new Node()); 112 | // 当前节点默认为头节点 113 | let p = this.head; 114 | // 寻找每一层需要插入节点的前一个节点 115 | for(let i = level - 1;i >= 0;i--){ 116 | while(p.forwards[i] !== undefined && p.forwards[i].data < val) { 117 | p = p.forwards[i]; 118 | } 119 | update[i] = p; 120 | } 121 | // 插入节点 122 | // 将每一层节点和后面节点相关联 123 | // 其实就是链表的插入操作(参考当前目录链表一节) 124 | for(let i = 0;i < level;i++){ 125 | newNode.forwards[i] = update[i].forwards[i]; 126 | update[i].forwards[i] = newNode; 127 | } 128 | // 更新层高[添加后有可能多了n层] 129 | if(this.levelCount < level) this.levelCount = level; 130 | } 131 | /** 132 | * 移除跳表里的某个数据节点 133 | * @param val - 需要删除的数据 134 | * 和插入方法同理,先找到每层需要删除节点数据的前一个节点 135 | * 然后就是执行链表的删除操作(参考当前目录链表一节) 136 | */ 137 | remove(val) { 138 | const update = new Node(); 139 | let p = this.head; 140 | let levelCount = this.levelCount; 141 | for(let i = levelCount - 1;i >= 0;--i){ 142 | while(p.forwards[i] !== undefined && p.forwards[i].data < val) { 143 | p = p.forwards[i]; 144 | } 145 | update[i] = p; 146 | } 147 | if(p.forwards[0] !== undefined && p.forwards[0].data === val) { 148 | for(let i = levelCount - 1;i >= 0;i--){ 149 | if(update[i].forwards[i] !== undefined && update[i].forwards[i].data === val) { 150 | update[i].forwards[i] = update[i].forwards[i].forwards[i]; 151 | } 152 | } 153 | } 154 | // 同样更新层高[删除后有可能少了n层] 155 | while(this.levelCount > 1 && this.head.forwards[this.levelCount] === undefined) { 156 | this.levelCount--; 157 | } 158 | } 159 | // 打印跳表里的所有数据 160 | printRes() { 161 | let p = this.head; 162 | while(p.forwards[0] !== undefined) { 163 | console.log(p.forwards[0].data + ' '); 164 | p = p.forwards[0]; 165 | } 166 | } 167 | } 168 | // 测试 169 | let test = () => { 170 | let list = new SkipList(); 171 | // 顺序插入 172 | for (let i = 1; i <= 10; i++) { 173 | list.insert(i); 174 | } 175 | // 输出 176 | // 1,2,3,4,5,6,7,8,9,10 177 | list.printRes(); 178 | // 查找 179 | // 180 | // { 181 | // data: 8 182 | // maxLevel: 1 183 | // forwards: (16) [Node, empty × 15] 184 | // } 185 | list.find(8) 186 | // 删除 187 | list.remove(8) 188 | // 输出 189 | // 1,2,3,4,5,6,7,9,10 190 | list.printRes() 191 | } 192 | test(); 193 | ``` -------------------------------------------------------------------------------- /algo/队列_ queue.md: -------------------------------------------------------------------------------- 1 | ### 定义 2 | + 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作, 3 | + 和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 4 | ### 应用 5 | + 实现队列结构 6 | + 用数组 7 | + 用链表 8 | + 实现循环队列结构 9 | + 用数组 10 | + 用链表 11 | + 栗子 12 | + 击鼓传花 - 循环队列 13 | + 回文检测 14 | ### code 15 | #### 实现队列结构 16 | + 用数组实现 17 | ```javascript 18 | class Queue { 19 | constructor() { 20 | this.items = [] 21 | } 22 | enqueue(el) { 23 | this.items.push(el) 24 | } 25 | dequeue() { 26 | if(this.items.length) { 27 | return this.items.shift() 28 | } 29 | return -1 30 | } 31 | clear() { 32 | this.items = [] 33 | } 34 | getFront() { 35 | return this.items[0] 36 | } 37 | getRear() { 38 | return this.items[this.items.length - 1] 39 | } 40 | size() { 41 | return this.items.length 42 | } 43 | } 44 | ``` 45 | + 用链表实现 46 | ```javascript 47 | class Node { 48 | constructor(el) { 49 | this.el = el 50 | this.next = null 51 | } 52 | } 53 | class Queue { 54 | constructor() { 55 | this.head = null 56 | this.tail = null 57 | } 58 | enqueue(el) { 59 | if(!this.head) { 60 | this.head = new Node(el) 61 | this.tail = this.head 62 | return 63 | } 64 | this.tail.next = new Node(el) 65 | this.tail = this.tail.next 66 | } 67 | dequeue() { 68 | if(this.head) { 69 | const el = this.head.el 70 | this.head = this.head.next 71 | return el 72 | } 73 | return -1 74 | } 75 | size() { 76 | let n = 0,cur = this.head 77 | while(cur) { 78 | cur = cur.next 79 | n++ 80 | } 81 | return n 82 | } 83 | } 84 | ``` 85 | #### 实现循环队列结构 86 | + 用数组实现 87 | + **解法一(推荐):**[设计循环双端队列-戳看](https://github.com/Alex660/leetcode/blob/master/leetCode-1-0641-design-circular-deque.md) 88 | + 解法二: 89 | ```javascript 90 | class CircularQueue { 91 | constructor(n) { 92 | this.queue = new Array(n) // 初始化循环队列 93 | this.front = 0 // 队头指针 94 | this.rear = 0 // 队尾指针 95 | this.count = 0 // 队列所用容量计数 96 | this.n = n // 队列容量 97 | } 98 | enqueue(el) { 99 | if(this.count === this.n) return false // 队列已满 100 | this.queue[this.rear] = el 101 | this.count++ 102 | this.rear = (this.rear + 1) % this.n 103 | return true 104 | } 105 | dequeue() { 106 | if(!this.count) return false // 队列已空 107 | this.count-- 108 | this.front = (this.front + 1) % this.n 109 | return true 110 | } 111 | } 112 | ``` 113 | + 用链表实现 114 | ```javascript 115 | class Node { 116 | constructor(el) { 117 | this.el = el 118 | this.next = null 119 | } 120 | } 121 | class CircularQueue { 122 | constructor() { 123 | this.head = null 124 | this.tail = null 125 | } 126 | enqueue(el) { 127 | if(!this.head) { 128 | this.head = new Node(el) 129 | this.head.next = this.head 130 | this.tail = this.head 131 | return 132 | } 133 | const flag = this.head === this.tail 134 | this.tail.next = new Node(el) 135 | this.tail.next.next = this.head 136 | this.tail = this.tail.next 137 | if(flag) { 138 | this.head.next = this.tail 139 | } 140 | } 141 | dequeue() { 142 | if(!this.head) return -1 143 | if(this.head === this.tail) { 144 | const el = this.head.el 145 | this.head = null 146 | return el 147 | } 148 | const el =this.head.el 149 | this.head = this.head.next 150 | this.tail.next = this.head 151 | return el 152 | } 153 | printAll() { 154 | let res = 0 155 | console.log(`打印---dequeue---元素`) 156 | while(res !== -1) { 157 | console.log(res = this.dequeue()) 158 | } 159 | } 160 | } 161 | ``` 162 | #### 栗子 163 | + 击鼓传花 - 循环队列 164 | + >在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人。某一时刻传花停止, 165 | 这个时候花在谁手里,谁就退出圆圈结束游戏。重复这个过程,直到只剩一个孩子(胜者)。 166 | ```javascript 167 | function drumFlower (children,k) { 168 | let queue = new Queue() 169 | for(let i = 0;i < children.length;i++) { 170 | queue.enqueue(children[i]) 171 | } 172 | let outer = '' 173 | while(queue.size() > 1) { 174 | // 将第k个人前面的人插入到队尾 175 | for(let j = 0;j < k;j++) { 176 | queue.enqueue(queue.dequeue()) 177 | } 178 | // 轮到谁谁出队 179 | queue.dequeue() 180 | } 181 | // 返回最后的胜利者✌️ 182 | return queue.dequeue() 183 | } 184 | // 或者直接用数组 185 | function drumFlower2(arr,n){ 186 | while(arr.length>1){ 187 | for(var i=0;i 对于 n 个物品来说 总的装法就有 2^n 种, 8 | // 去掉总量超过 W 的,选取总重量最接近 W 的。 9 | // 回溯:n个物品依次排列 => n个阶段 ,每个阶段对应一个物品选择装与不装 && 装则总重必须不大于 W 10 | // 递归处理剩下的物品 11 | 12 | 13 | //cw 已装物品的重量和 14 | // i 表示第 i 个物品 15 | // w 背包重量 16 | // n 物品个数 17 | // items 物品重量数组 18 | 19 | // 存储背包物品中总重量的最大值 20 | var maxW = 0; 21 | var hasScaned = {};//【备忘录】模式 22 | function getMax(i,cw,items,n,w) { 23 | // cw == w 背包装满 ; i ==n 遍历完所有物品 24 | if(cw === w || i == n){ 25 | // 背包装满或扫描完所有物品 26 | if(cw > maxW){ 27 | maxW = cw; 28 | } 29 | // 每一轮要退出当前函数否则会造成堆栈溢出 30 | return; 31 | } 32 | // 【备忘录】中有 重复计算 返回 33 | if(hasScaned){ 34 | return 35 | } 36 | // 记录【备忘录】里面 下次不再计算 37 | hasScaned[i] = cw; 38 | // 选择不把当前物品放入背包直接进入下一个 39 | getMax(i+1,cw,items,n,w); 40 | // 符合背包承受总重时 41 | if(cw+items[i]<=w){ 42 | //选择把当前物品i放入背包 【加当前物品重量】 43 | getMax(i+1,cw+items[i],items,n,w); 44 | } 45 | } 46 | 47 | getMax(0,0,[1,2,4,6,7],5,10); 48 | 49 | 50 | // ==> 优化+增加备忘录模式[很多getMax()重复计算了]]如上题所加 51 | 52 | 53 | // 函数调用栈 从下往上压栈,从上往下出栈执行 54 | 55 | // getMax(5,7) return 56 | // 0+1+2+4+6>w && 终止 57 | 58 | // getMax(4,0+1+2+4) i=3 getMax(4,7) ==> getMax(5,7) return 59 | 60 | // ==> 7+7>w 61 | 62 | 63 | // getMax(3,0+1+2+4) getMax(3,7) ==> getMax(4,7) ==> getMax(5,7) return 64 | // <== 65 | // 0+1+2+4<=w 7+6>w 66 | // ==> 67 | 68 | 69 | // getMax(3,1+2) 70 | // <== getMax(3,3) ==> getMax(4,3+6) 71 | // 0+1+2<=w i=2 <== 72 | // 3+6<=w ==> getMax(5,9) return 73 | 74 | // ==> getMax(4,3) getMax(5,3) return 75 | 76 | 77 | 78 | 79 | // getMax(2,1) getMax(2,1) ==> 5+6>w 80 | 81 | // getMax(4,5) getMax(5,5) return 82 | // <== 83 | // getMax(3,1+4) ==> getMax(3,5) ==> 5+6>w ==> getMax(5,5) return 84 | // <== 85 | // 1+4<=w getMax(4,5) 86 | 87 | // getMax(3,1) 88 | // 7+6>w 89 | 90 | // getMax(4,7) ===> getMax(5,7) return 91 | // <== 92 | // 1+6<=w getMax(5,1) return 93 | 94 | // getMax(4,1) 95 | 96 | 97 | // getMax(1,1) i=1 .......... 98 | // <== 99 | // 0+1<=w 100 | // getMax(1,0) i=0 101 | 102 | // <== 103 | // getMax(0,0) i=0 104 | ``` 105 | #### 解法二:动态规划 106 | ```javascript 107 | // states[n][cw=w+1] n:第n个物品 cw=w+1:递增第背包承受重量 108 | // states[i][j] = true 表示存在 109 | var weight = [2,2,4,6,3];//可选装物品重量数组 110 | var w = 9;//背包可承载重量 111 | function knapsack() { 112 | var n = weight.length; 113 | // 初始化一个二维数组 114 | var result = Array(n) 115 | for(var i = 0;i=0;i--){ 137 | if(result[n-1][i] == true){ 138 | return i; 139 | } 140 | } 141 | return 0; 142 | } 143 | 144 | // 第22行代码 j如果按照从大到小遍历 则会少重复很多 145 | 146 | // 分析代码可知 时间复杂度为O(n*w),空间复杂度为O(n*w)【二维数组】 147 | // 不装背包的动作似乎可以忽略 => 优化为一维数组 148 | 149 | var weight = [2,2,4,6,3];//可选装物品重量数组 150 | var w = 9;//背包可承载重量 151 | function knapsack() { 152 | var n = weight.length; 153 | // 初始化一个一维数组 154 | var result = Array(w) 155 | // 第一行的数据要特殊处理,可以利用哨兵优化 156 | result[0] = true; 157 | // 动态规划状态转移 158 | for(var i = 1;i=0;j--){ 161 | if(result[j]==true){ 162 | result[j+weight[i]] = true 163 | } 164 | } 165 | } 166 | // 输出结果 167 | for(var i = w;i>=0;i--){ 168 | if(result[i]){ 169 | return i; 170 | } 171 | } 172 | return 0 ; 173 | } 174 | 175 | // 分析代码可知 时间复杂度为O(w),空间复杂度为O(w)【一维数组】 176 | 177 | 178 | // 分析: 179 | // 2 2 4 6 3 w=9 180 | 181 | // j 0 1 2 3 4 5 6 7 8 9 j<=9 182 | // i 183 | // 0 1 0 1 0 0 0 0 0 0 0 2 arr[i]<=9 184 | // 1 1 0 1 0 1 0 1 0 0 0 2 arr[i]+cw<=9 185 | // 2 1 0 1 0 1 0 1 0 1 0 4 186 | // 3 1 0 1 0 1 0 1 0 1 0 6 187 | // 4 . . . i<=5 188 | 189 | // w:背包总承受重量 190 | 191 | // states[n][cw=w+1] n:第n个物品 cw=w+1:递增第背包承受重量 192 | 193 | // states[i][j] = true 194 | 195 | // 0,2 => 0+0,0+2 => 0,2 => 196 | // 不装 states[0][0] 197 | // 装 states[0][2] 198 | 199 | // 1,2 => 0+0,0+2 2+0,2+2 => 0,2,4 => 200 | // 不装 states[1][0] states[1][2] 201 | // 装 states[1][4] 202 | 203 | // 2,4 => 0+0,0+4 2+0,2+4 4+0,4+4 => 0,2,4,6,8 => 204 | // 不装 states[2][0] states[2][2] 205 | // 装 states[2][4] states[2][6] states[2][8] 206 | 207 | // 3,6 => 0+0,0+6 2+0,2+6 4+0,4+6 6+0,6+6 8+0,8+6 => 0,2,4,6,8 208 | // 不装 states[3][0] states[3][2] states[3][4] states[3][6] states[3][8] 209 | // 装 states[3][6] states[3][8] states[3][10](不符合不再加6即不再装入) 210 | 211 | // 4,3 => 0+0,0+3 2+0,2+3 4+0,4+3 6+0,6+3 8+0,8+3 => 0,2,3,4,5,6,7,8,9 212 | // 不装 states[4][0] states[4][2] states[4][4] states[4][6] states[4][8] 213 | // 装 states[4][3] states[4][5] states[4][7] states[4][9] states[4][11](不符合不再加6即不再装入)) 214 | 215 | 216 | // ===> 217 | // 不装 states[i-1][j] == states[i][j] == true 218 | // 装 states[i-1][j] == true && j+weight[i]<=w && states[i][j+weight[i]] 219 | // 最后一行最后一个ture的二维数组元素的二维元素j 即为所求 220 | ``` -------------------------------------------------------------------------------- /demos/01背包问题变体.md: -------------------------------------------------------------------------------- 1 | ## 0-1背包问题 【变体】 2 | #### 解法一:回溯 3 | ```javascript 4 | // 一个背包总承载重量为:W kg 5 | // 有 n 个物品 ,不可分割 6 | // 求选择哪几样物品放入背包且不超过 W 的情况下,背包物品总价值最大 7 | // 分析:对于每个物品来说有 装和不装 两种选择 => 对于 n 个物品来说 总的装法就有 2^n 种, 8 | // 去掉总量超过 W 的,选取最大总价值。 9 | // 回溯:n个物品依次排列 => n个阶段 ,每个阶段对应一个物品选择装与不装 && 装则总重必须不大于 W 10 | // 递归处理剩下的物品 11 | // 选择的物品相同重量可能一样但价值不一定一样所以回溯算法下用不了备忘录模式去掉重复 12 | 13 | //cw 已装物品的重量和 14 | //cv 已装物品的价值和 15 | // i 表示第 i 个物品 16 | // w 背包重量 17 | // n 物品个数 18 | // items 物品重量数组 19 | // value 物品价值数组 20 | 21 | // 存储背包物品中总价值的最大值 22 | var maxV = 0; 23 | function getMaxV(i,cw,cv,items,value,n,w) { 24 | // cw == w 背包装满 ; i == n 遍历完所有物品 25 | if(cw == w || i == n){ 26 | // 背包装满或扫描完所有物品 27 | if(cv > maxV){ 28 | maxV = cv; 29 | } 30 | // 每一轮要退出当前函数否则会造成堆栈溢出 31 | return; 32 | } 33 | // 选择不把当前物品装入背包直接进入下一个 34 | getMaxV(i+1,cw,cv,items,value,n,w); 35 | // 符合背包承受总重时 36 | if(cw+items[i]<=w){ 37 | // 选择把当前物品i放入背包 【加当前物品重量和价值】 38 | getMaxV(i+1,cw+items[i],cv+value[i],items,value,n,w); 39 | } 40 | } 41 | getMaxV(0,0,0,[1,2,4,6,7],[1,2,3,4,5],5,10); 42 | ``` 43 | #### 解法二:动态规划 44 | ```javascript 45 | // states[n][cw=w+1] n:第n个物品 cw=w+1:递增第背包承受重量 46 | // states[i][j] = maxV 表示大于等于1种情况的总价值【合计同等重量的物品总价值不一定一样】 47 | var weight = [2,2,4,6,3];//可选装物品重量数组 48 | var value = [1,2,3,4,5];//可选装物品价值数组 49 | var w = 9;//背包可承载重量 50 | function knapsack() { 51 | var n = weight.length; 52 | // 初始化一个二维数组 53 | var result = Array(n) 54 | for(var i = 0;i=0){ 67 | result[i][j] = result[i-1][j] 68 | } 69 | } 70 | // 选择把第 i 个物品装入背包 【加当前物品重量和价值】 71 | for(var j = 0;j<=w-weight[i];j++){ 72 | // 合并同等重量情况下价值不一的情况,只留一种价值最大的情况 73 | if(result[i-1][j]>=0){//上一行同列 74 | var tmpV = result[i-1][j]+value[i];//增加i种物品价值 75 | if(tmpV>result[i][j+weight[i]]){//当前行下一列 76 | result[i][j+weight[i]] = tmpV; 77 | } 78 | } 79 | } 80 | } 81 | // 输出最大价值结果【结果一定位于最后一行但不一定是最后有有效值的一位】 82 | var maxV = -1; 83 | for(var i = 0;i<=w;i++){ 84 | if(result[n-1][i]>maxV){ 85 | maxV = result[n-1][i] 86 | } 87 | } 88 | return maxV; 89 | } 90 | 91 | // 第22行代码 j如果按照从大到小遍历 则会少重复很多 92 | 93 | // 分析代码可知 时间复杂度为O(n*w),空间复杂度为O(n*w)【二维数组】 94 | // 不装背包的动作似乎可以忽略 => 优化为一维数组 95 | 96 | var weight = [2,2,4,6,3];//可选装物品重量数组 97 | var value = [1,2,3,4,5];//可选装物品价值数组 98 | var w = 9;//背包可承载重量 99 | function knapsack() { 100 | var n = weight.length; 101 | // 初始化一个一维数组 102 | var result = Array(w) 103 | for(var i = 0;i=0;j--){ 113 | // 合并同等重量情况下价值不一的情况,只留一种价值最大的情况 114 | if(result[j]>=0){//同列 115 | var tmpV = result[j]+value[i];//增加i种物品价值 116 | if(tmpV>result[j+weight[i]]){//下一列 117 | result[j+weight[i]] = tmpV; 118 | } 119 | } 120 | } 121 | } 122 | // 输出最大价值结果【结果一定位于最后一行但不一定是最后有有效值的一位】 123 | var maxV = -1; 124 | for(var i = 0;i<=w;i++){ 125 | if(result[i]>maxV){ 126 | maxV = result[i] 127 | } 128 | } 129 | return maxV; 130 | } 131 | 132 | // 分析代码可知 时间复杂度为O(w),空间复杂度为O(w)【一维数组】 133 | ``` -------------------------------------------------------------------------------- /demos/八皇后问题.md: -------------------------------------------------------------------------------- 1 | ## 回溯算法--求解八皇后问题 2 | ```javascript 3 | // 8X8的棋盘上要求放满符合这样条件的棋子: 4 | // 1、每个棋子的横行、竖列和左右对角线上不能有其它任何一颗棋子【皇后】 5 | // 2、求这样的棋子有多少个 6 | 7 | // 设结果数组:result 索引代表行,值代代表列 值可以存在时即为所求 8 | // 初始化8个元素的数组 9 | var result = new Array(8); 10 | 11 | // 主函数 12 | function cal8queens(row) { 13 | // 8 行都放满了 退出 14 | if(row == 8){ 15 | printQueens(result); 16 | return; 17 | } 18 | for(var column = 0;column<8;column++){ 19 | // 符合要求的就落子 20 | if(isOk(row,column)){ 21 | result[row] = column; 22 | cal8queens(row+1); 23 | } 24 | } 25 | } 26 | 27 | // 判断 row 行 column 列放置是否合适 28 | function isOk(row,column) { 29 | // 当前列的左右两列 30 | var leftColumn = column -1; 31 | var rightColumn = column +1; 32 | 33 | // 逐上判断每一行是否能放棋子 34 | for(var i = row - 1;i>=0;i--){ 35 | // 第i行第column列是否有棋子【上一行同一列不能有棋子】 36 | if(result[i] == column){ 37 | return false; 38 | } 39 | // 当左列存在 && 考虑左上对角线【递减斜向上】 第i行第leftColumn列是否有棋子 40 | if(leftColumn>=0){ 41 | if(result[i] == leftColumn){ 42 | return false; 43 | } 44 | } 45 | // 当右列存在 && 考虑右上对角线【递减斜向上】 第i行第rightColumn列是否有棋子 46 | if(rightColumn<8){ 47 | if(result[i] == rightColumn){ 48 | return false; 49 | } 50 | } 51 | leftColumn--; 52 | rightColumn++; 53 | } 54 | return true; 55 | } 56 | 57 | // 打印结果 58 | function printQueens(result) { 59 | console.log(result); 60 | var str = ''; 61 | for(var i=0;i<8;i++){ 62 | for(var r=0;r<8;r++){ 63 | if(result[i] == r){ 64 | str+='Q '; 65 | }else{ 66 | str+='* '; 67 | } 68 | } 69 | str+='\n' 70 | } 71 | console.log(str) 72 | } 73 | ``` -------------------------------------------------------------------------------- /demos/动态规划理论.md: -------------------------------------------------------------------------------- 1 | ## 动态规划理论 2 | ```javascript 3 | // “一模三特” 4 | // 一、模型【多阶段决策最优解模型】 5 | // ----解决问题过程经历n个决策阶段 && 分别对应一组状态 6 | // 二、特征【最优子结构、无后效性、重复子问题】 7 | // ----<1>、最优子结构 8 | // 问题的最优解包含子问题的最优解 => 子问题的最优解推导出问题的最优解 9 | // 后面阶段的状态可以通过前面阶段的状态推导出来 10 | // ----<2>、无后效性 11 | // 1⃣️:不关心当前状态的推导,只关心前面阶段的状态值 12 | // 2⃣️:前一阶段的状态一旦确定就不受之后阶段的决策影响 13 | // ----<3>、重复子问题 14 | // 不同的决策序列,到达某个相同的阶段时,可能会产生重复的状态 15 | 16 | // 实例 17 | // 一个 n*n 的矩阵 w[n][n] 18 | // 求从左上角出发到右下角最短距离【路经的数字和为路径长度】 19 | // 每次只能向右或向下移动一位 20 | 21 | // [i,j] 0 1 2 3 22 | // 0 1 3 5 9 23 | // 1 2 1 3 4 24 | // 2 5 2 6 7 25 | // 3 6 8 4 3 26 | 27 | // (0,0)->(n-1,n-1) 共 2*(n-1)步 => 2*(n-1)个阶段状态集合 <= 向右走/向下走 28 | // min_dist(i,j) <= (0,0)->(i,j)的最短路径长度 29 | // (0,0)->(i-1,j)/(i,j-1)->(i,j) 30 | // ==> 31 | // min_dist(i,j) == Math.min(min_dist(i-1,j),min_dist(i,j-1)) + w[i][j] 32 | 33 | 34 | // 动态规划题目解题思路 35 | // 一、状态转移表法 36 | // 二、状态转移方程法 37 | 38 | // 一: 39 | // 回溯算法->回溯加“备忘录” 40 | // 代码实现 41 | var minDist = 0;//最短路径 42 | // 当前状态最短路径 43 | var dist = 0; 44 | // 定义n*n 45 | var n = 4; 46 | // 初始化随机二维整数【1~10】数组 47 | var w = []; 48 | while (w.length 85 | // 状态转移表 86 | // 一般情况下二维【数组】 (高维时就不适合这种方法了太复杂) 87 | // [i,j] = value 行,列,数组值 88 | // 代码实现 89 | // n*n 90 | var n = 4; 91 | // 初始化二维数组 92 | var result = Array(n); 93 | result[0] = [1,3,5,9]; 94 | result[1] = [2,1,3,4]; 95 | result[2] = [5,2,6,7]; 96 | result[3] = [6,8,4,3]; 97 | function getMinDist(result) { 98 | // 临时最短路径 99 | var minDist = 0; 100 | // 初始化第一行n列数据 101 | for(var i = 0;i=0 && (leftUp = minDist[i][j-1]) 153 | // 求向下走后的上一步最短路径 && 考虑边界情况 154 | i-1>=0 && (topUp = minDist[i-1][j]) 155 | // 求上两种情况的最新上一步路径+当前路径 = 当前结点最小路径 156 | result[i][j] = matrix[i][j] + Math.min(leftUp,topUp); 157 | return result[i][j]; 158 | } 159 | //迭代递推 160 | // ...... 161 | ``` -------------------------------------------------------------------------------- /demos/滑动窗口11道.md: -------------------------------------------------------------------------------- 1 | ## 滑动窗口11道 2 | ### 双指针 + 滑动窗口:***一法破万法*** 之 歼灭 11 道 Sliding Window 问题 3 | #### 参考文章:[labuladong - 滑动窗口算法通用思想](https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong-yong-si-xiang-by-/) 4 | + 阅读前可参考此篇详细讲解 5 | #### [戳看👇leetCode所有题解](https://github.com/Alex660/leetcode) 6 | #### 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/) 7 | #### 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/) 8 | #### 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/) 9 | #### 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/) 10 | #### 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/) 11 | #### 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/) 12 | #### 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/) 13 | #### 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/) 14 | #### 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/) 15 | #### 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/) 16 | #### 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/) 17 | #### 滑动窗口算法 18 | + 题型 19 | + 已知条件 20 | + 模式串 B 21 | + 目标串 A 22 | + 题目问题 23 | + 求 在A中符合 对B一些限定规则的 子串或者对A一些限定规则的结果? 24 | + 题目结果 25 | + 对搜索出来的符合规则的子串做题意的组合或抽取特性即为所求。 26 | + 栗子 27 | + 1、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/) 28 | + 求 在目标串S中 包含模式串T全部字母的 一个子串,且不需按顺序... 29 | + 2、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/) 30 | + 模式串 为目标串中最长的不重复子串 31 | + 子串问题 32 | + 3、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/) 33 | + 跟76题一摸一样,只不过是换了个问法 34 | + 模式串 p 在 目标串 s 中 35 | + 异位词 == 不按顺序 36 | + 4、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/) 37 | + 模式串:s 38 | + 目标串:words 39 | + 虽然换成了数组,其实每种符合的结果条件其中一种模式串都是words 里自行组合的结果模式串 40 | + 依旧是在求 s中 包含words字符串数组中其中一种组合的模式串 41 | + 5、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/) 42 | + 跟3题求当前字符串无重复字符的最长子串问题类似 43 | + 模式串为 两个不同字符的最长子串 44 | + ...... 45 | + 思路 46 | + 1、双指针 47 | + left = 0,right = 0 48 | + 2、窗口 49 | + windows = {} / [] / ... 50 | + 3、在目标A中 符合对B的限定规则 51 | + valid 52 | + 4、借用图栗 53 | + 76 题 54 | + 模式串:T、目标串:S 55 | + 限定规则:needs 56 | + 滑动窗口:window 57 | + 解 58 | + 初始状态 59 | + ![](https://pic.leetcode-cn.com/39b7a9681c5b82760e01aec9b3b59c626abaf9b45239e5b7874e98aab8aa97b7-9c25b5d41cb910ba8318f10acadde3af05235ad4fb46c9567ec1a0181077c655-file_1561042836475.png) 60 | + 根据限定规则 - 窗口增大状态 61 | + ![](https://pic.leetcode-cn.com/3832c548c257af4f5ea2f58248b2fa28c5ffbf15e31aa935eb9ce3b331761809-7f2dd09e457657e9bcd1f7f23dcf7ee3adee660c1a863f553c05c3642ea8ee1a-file_1561042836483.png) 62 | + 根据限定规则 - 窗口缩小状态 63 | + ![](https://pic.leetcode-cn.com/998fc84dda34dd7e6637b3e6f9d8524d338808be4058fdcfd51cd07522f4dc13-e2ed1df5be6bb19eff01d951e46952cb66918f2f35cf31791ec19e4457798a4a-file_1561042836484.png) 64 | + 不符合限定规则 - 窗口状态 65 | + ![](https://pic.leetcode-cn.com/0da74cafbc6ede824ee717038f844c77f38151e73ccd38c281f0d9b3c933674e-57948bb4cd811c190a56e8836a3db8226226c0347c1fe43de96d62f47241f5ac-file_1561042836487.png) 66 | + 因此继续 - 窗口增大状态,即重复以上动作 67 | + 直到到达目标串的末端,算法结束 68 | + 算法抽象框架(伪代码) 69 | ```javascript 70 | // 模式串 B、目标串 A 71 | let B = ...; 72 | let A = ...; 73 | // 双指针 74 | let left = 0,right = 0; 75 | // 滑动窗口 76 | let windows = {}; 77 | // 限定规则 78 | let needs = {}; 79 | // 限定规则操作 80 | let valid; 81 | // 符合规则计数 82 | let match = 0 ; 83 | // 变化窗口 84 | while(right < A.length){ 85 | // 增大窗口 86 | if(valid){ 87 | windows.add(B[right]); 88 | } 89 | // 前进窗口 90 | right++; 91 | // 限定规则计数满足题意 92 | while(match === needs.length){ 93 | if(valid){ 94 | // 缩小窗口 95 | window.remove(B[left]); 96 | // 归位规则计数 97 | match--; 98 | // 继续下一轮、增大前进窗口 99 | left++; 100 | } 101 | } 102 | } 103 | ``` 104 | + 参考大佬简便 抽象框架思想 105 | ```java 106 | int left = 0, right = 0; 107 | while (right < s.size()) { 108 | window.add(s[right]); 109 | right++; 110 | 111 | while (valid) { 112 | window.remove(s[left]); 113 | left++; 114 | } 115 | } 116 | ``` -------------------------------------------------------------------------------- /demos/爬楼梯变体问题.md: -------------------------------------------------------------------------------- 1 | # 爬楼梯问题变种 2 | # 题目描述 3 | > 假设你正在爬楼梯,每次你可以爬***1***个、***2***个或***3***个台阶, 4 | > 那么在***相邻步数不能相同***的条件下, 5 | > 你有多少种不同的方法可以爬到第n阶呢? 6 | #### 解法:动态规划 7 | + 类似题型 8 | + [70. 爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-by-alexer-660/) 9 | + 图解 10 | + ![截屏2019-11-17上午10.47.49.png](https://pic.leetcode-cn.com/e71f6e89c58e36b7dc3f127ebe2cffa1020936fdf2e36577456efdea683788a2-%E6%88%AA%E5%B1%8F2019-11-17%E4%B8%8A%E5%8D%8810.47.49.png) 11 | + 思路 12 | + 去掉限制条件,即此题和**70题**几乎一摸一样 13 | + 到达第i阶走法有三种 14 | + ***前1阶走1步***来到终点**或** 15 | + **如图I 第N-1台阶**,走***1步***来到**end**台阶 16 | + **如图I 第N-2台阶**,走***1步***来到**N-1**台阶 17 | + **如图I 第N-3台阶**,走***2步***来到**N-1**台阶 18 | + **如图I 第N-4台阶**,走***3步***来到**N-1**台阶 19 | + **依次类推......** 20 | + ***前2阶走2步***来到终点**或** 21 | + **如图II 第N-2台阶**,走***2步***来到**end**台阶 22 | + **如图II 第N-3台阶**,走***1步***来到**N-2**台阶 23 | + **如图II 第N-4台阶**,走***2步***来到**N-2**台阶 24 | + **如图II 第N-5台阶**,走***3步***来到**N-2**台阶 25 | + **依次类推......** 26 | + ***前3阶走3步***来到终点 27 | + **如图III 第N-3台阶**,走***3步***来到**end**台阶 28 | + **如图III 第N-4台阶**,走***1步***来到**N-3**台阶 29 | + **如图III 第N-5台阶**,走***2步***来到**N-3**台阶 30 | + **如图III 第N-6台阶**,走***3步***来到**N-3**台阶 31 | + **依次类推......** 32 | + 那么走法总数是以上3种情况之和 33 | + 递推公式:**dp[i] = dp [i-3]+ dp[i-2] + dp[i-1];** 34 | + 加上限制条件即为本题 35 | + 意思是如果当前台阶是走n(n=[1,2,3])步来到的 36 | + 那么它的前一步**不能走相同的n步** 37 | + 如图I、II、III,就是在当前台阶N-1、N-2、N-3三种情况下,把从左往右第一个绿色的箭头去掉 38 | + 也就是把上面不加限制条件的三种情况下其中一种情况去掉即可 39 | + 子问题: 40 | + dp[i]:从0走到第i阶台阶的走法总数 41 | + dp[i] = dp [i-3]+ dp[i-2] + dp[i-1]; 42 | + 状态定义: 43 | + 第i个台阶表示最后一步走n步过来的前i个台阶所用步数总和 44 | + dp[i][1]:增加维度,二维1代表走到最后一个台阶前一步用了1步 45 | + dp[i][2]:增加维度,二维2代表走到最后一个台阶前一步用了2步 46 | + dp[i][3]:增加维度,二维3代表走到最后一个台阶前一步用了3步 47 | + 边界条件: 48 | + 走到前一台阶用的步数 != 走到当前台阶用的步数 49 | + 设B[x]代表走到第x级台阶所用步数 50 | + 则**B[n] != B[n-1]** 51 | + 合并到状态公式当中去就是 52 | + **dp[i][n]走法时,dp[i-n][n]走法不能走** 53 | + 递推公式: 54 | + **dp[i][1] = dp[i-1][2] + dp[i-1][3]** 55 | + **dp[i][2] = dp[i-2][1] + dp[i-2][3]** 56 | + **dp[i][3] = dp[i-3][1] + dp[i-3][2]** 57 | + 建立状态表: 58 | + ![截屏2019-11-17下午12.09.15.png](https://pic.leetcode-cn.com/fc32ccec8d45f211a028fe15dca152da7f71b01962658e0848212a03eb3ab728-%E6%88%AA%E5%B1%8F2019-11-17%E4%B8%8B%E5%8D%8812.09.15.png) 59 | + 结合上述两个图解可知 60 | ```javascript 61 | /** 62 | * @param {number} n 63 | * @return {number} 64 | */ 65 | var climbStairs = function(n) { 66 | if(n < 3){ 67 | return 1; 68 | } 69 | var dp = Array.from(new Array(n+1),() => new Array(4).fill(0)); 70 | // 第i级台阶走n步前i个台阶所用步数总和且第i-n台阶不能走 71 | dp[1][1] = 1; 72 | dp[2][2] = 1; 73 | dp[3][1] = 1; 74 | dp[3][2] = 1; 75 | dp[3][3] = 1; 76 | for(var i = 4;i <= n;i++){ 77 | dp[i][1] = dp[i-1][2]+dp[i-1][3]; 78 | dp[i][2] = dp[i-2][1]+dp[i-2][3]; 79 | dp[i][3] = dp[i-3][1]+dp[i-3][2]; 80 | } 81 | // 合并三种情况 82 | return dp[n][1] + dp[n][2] + dp[n][3]; 83 | }; 84 | ``` 85 | #### [更多leetCode题解敬请戳看👇](https://github.com/Alex660/leetcode) -------------------------------------------------------------------------------- /demos/薅羊毛问题.md: -------------------------------------------------------------------------------- 1 | ## 薅羊毛 2 | ```javascript 3 | // 满200减50元 假设购物车有n个商品 4 | // 在凑够满减条件的前提下 让选出来的商品价格总和接近 满减值 是为 薅羊毛 5 | 6 | // 类似01背包问题动态规划求解方法 7 | // n个商品每个商品买或不买,决策之后对应不同的状态集合;用二维数组 states[n][x]记录决策后的状态 8 | // 0-1背包问题中,找的是 <=w 的最大值 x = 背包的最大承载重量 = w+1 9 | // 对于这个问题 x 为大于等于 200【满减条件】值中最小的 10 | // 求X后 倒推出被选择的商品序列 11 | 12 | // items 商品价格 13 | // n 商品个数 14 | // w 满减条件 15 | 16 | var items = [100,50,60,50,110,106]; 17 | var n = items.length; 18 | var w = 200; 19 | var needBuy = []; 20 | function getNeedShop() { 21 | // 设置满减值的3倍为薅羊毛的最大利益化 && 初始化二维数组 22 | var result = Array(n); 23 | for(var i = 0;i 检查result[i-1][j] 或者 result[i-1][j-itmes[i]]是否为true 61 | // => result[i-1][j]可达 => 没有选择购买第 i 个 商品 62 | // => result[i-1][j-items[i]] 可达 => 购买了第 i 个 商品 63 | // 继续迭代其它商品是否选择购买 64 | for(var i = n-1;i>=1;i--){ 65 | if(j-items[i] >= 0 && result[i-1][j-items[i]] == true){ 66 | needBuy.push(itmes[i]); 67 | // 更新列 68 | j = j -itmes[i]; 69 | }//else 没有购买这个商品,j不变 70 | } 71 | // 动态规划初始值由此推动而来所以要加上 72 | if(j!=0){ 73 | needBuy.push(itmes[0]) 74 | } 75 | return needBuy; 76 | } 77 | ``` -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // 这是一个空文件! -------------------------------------------------------------------------------- /otherShare/前端知识点总结/css篇/img.md: -------------------------------------------------------------------------------- 1 | # div里包img图片下方有空白的问题 2 | ```javascript 3 | 6 | ``` 7 | ### 原理 8 | + 图片的display属性默认是inline, 9 | + 而这个属性的vertical-align的默认值是baseline。 10 | + 所以就会出现上图的情况(图片底部出现一个小留白区域)。 11 | #### 解法 12 | + 把img变成块状元素,所以是否需要留白,可以用过padding来设置。 13 | ```css 14 | img { 15 | display: block; 16 | } 17 | ``` 18 | + 直接修改vertical-align属性值 19 | ```css 20 | img { 21 | vertical-align: middle/bottom; 22 | } 23 | ``` 24 | + 把img元素的底部外边距改成负值 25 | ```css 26 | img { 27 | margin-bottom: -4px; 28 | } 29 | ``` 30 | + 出现留白的原因是因为垂直对齐的方式所导致的,所以可以修改父元素的font-size,把父元素的字体大小改为0,所以就没什么垂直对齐所言 31 | ```css 32 | .banner { 33 | font-size: 0; 34 | } 35 | ``` 36 | + 把img的父元素行高设成0 37 | ```css 38 | .banner { 39 | line-height: 0; 40 | } 41 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/css篇/position.md: -------------------------------------------------------------------------------- 1 | # position属性的理解 2 | + static 3 | + 对象遵循常规流 4 | + 此时4个定位偏移属性不会被应用 5 | + 默认元素都是静态的定位 6 | + relative 7 | + 对象遵循常规流 8 | + 相对定位 9 | + 并且参照自身在常规流中的位置通过top,right,bottom,left属性进行偏移时不影响常规流中的任何元素。 10 | + absolute 11 | + 对象脱离常规流 12 | + 绝对定位 13 | + 使用top,right,bottom,left等属性进行绝对定位,盒子的偏移位置不影响常规流中的任何元素 14 | + 其margin不与其他任何margin折叠。 15 | + 元素定位参考的是离自身最近的定位祖先元素,要满足两个条件, 16 | + 第一个是自己的祖先元素,可以是父元素也可以是父元素的父元素,一直找,如果没有则选择body为对照对象。 17 | + 第二个条件是要求祖先元素必须定位,通俗说就是position的属性值为非static都行。 18 | + fixed 19 | + 与absolute一致,但偏移定位是以窗口为参考。当出现滚动条时,对象不会随着滚动。 20 | + 固定定位 21 | + center 22 | + **CSS3新增** 23 | + 与absolute一致,但偏移定位是以定位祖先元素的中心点为参考。 24 | + 盒子在其包含容器垂直水平居中。 25 | + page 26 | + **CSS3新增** 27 | + 与absolute一致。元素在分页媒体或者区域块内,元素的包含块始终是初始包含块,否则取决于每个absolute模式。 28 | + sticky 29 | + **CSS3新增** 30 | + 对象在常态时遵循常规流。 31 | + 它就像是 relative 和 fixed 的合体, 32 | + 当在屏幕中时按常规流排版, 33 | + 当卷动到屏幕外时则表现如fixed。 34 | + 该属性的表现是现实中你见到的吸附效果。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/css篇/垂直居中.md: -------------------------------------------------------------------------------- 1 | ## 垂直居中的卍种方法 2 | #### 解法一 3 | + 对单行元素进行居中垂直(文本+图片) 4 | + 设置行内元素的父元素:height == line-height 5 | + 设置行内元素:vertical-align:middle,line-height 6 | #### 解法二 7 | + 定位 8 | + position:absolute + margin-top(元素高度一半) 9 | + position:absolute + top:0 + bottom:0 + left:0 + right:0 + marign:auto 10 | + position:absolute + top:calc(50% - 元素height/2) + left:calc(50% - 元素width/2) 11 | + position:absolute + top:50% + left:50% + transform:translate(-50%,-50%) 12 | #### 解法三 13 | + 居于视口单位的解决方案 14 | + margin-top:50vh + transform:translateY(-50%) 15 | #### 解法四 16 | + table 17 | + 父元素 18 | + display:table; 19 | + 子元素 20 | + display:table-cell + vertival-align:middle 21 | #### 解法五 22 | + flex 23 | + 父元素 24 | + display:flex 25 | + 子元素 26 | + margin:auto 27 | + algin-items:center -------------------------------------------------------------------------------- /otherShare/前端知识点总结/css篇/盒模型.md: -------------------------------------------------------------------------------- 1 | ## 盒模型 2 | #### W3C标准模型和IE模型 3 | + 包含:margin、border、padding、content 4 | + 区别 5 | + 标准:width = content 6 | + IE:width = content + padding + border 7 | #### CSS3转换 8 | + box-sizing 9 | + content-box 10 | + 元素的 width = content 11 | + border-box 12 | + 元素的 width = border + padding + content 13 | #### 番外 14 | + margin 15 | + margin-top/margin-bottom 16 | + 行内元素无效 17 | + margin-left/margin-right才有效 18 | + 相邻块级元素 19 | + 都是整数,margin值取两者最大值 20 | + 都是负数,margin值取最小值 21 | + 两者正负相反,margin值取两者之和 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/css篇/避免重排和重绘的小技巧.md: -------------------------------------------------------------------------------- 1 | ## 避免重排和重绘的小技巧 2 | + 浏览器渲染网页过程 3 | + 解析HTML(HTML Parser) 4 | + 构建DOM树(DOM Tree) 5 | + 渲染树构建(Render Tree) 6 | + 绘制渲染树(Painting) 7 | + ![截屏2020-01-30下午4.34.49.png](https://pic.leetcode-cn.com/da9927ecd22a40ad7c6f52af2700410da87e6972896d8da833623bad5e8e04fd-%E6%88%AA%E5%B1%8F2020-01-30%E4%B8%8B%E5%8D%884.34.49.png) 8 | + reflow:重排/回流 9 | + 改变窗口大小 10 | + 改变文字大小 11 | + 添加/删除样式表 12 | + 内容的改变,(用户在输入框中写入内容也会) 13 | + 激活伪类,如:hover 14 | + 操作class属性 15 | + 脚本操作DOM 16 | + 计算offsetWidth和offsetHeight 17 | + 设置style属性 18 | + repaint:重绘 19 | + 在一个元素的外观被改变,但没有改变布局的情况下发生的, 20 | + 如改变了visibility、outline、background等。 21 | + 当repaint发生时,浏览器会验证DOM树上所有其他节点的visibility属性。 22 | + 优化 23 | + translate属性值来替换top/left/right/bottom的切换 24 | + scale属性值替换width/height 25 | + opacity属性替换display/visibility 26 | + 创建文档片段,一次性添加所有动态元素 27 | + document.createDocumentFragment() 28 | + 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。 29 | + 例如有动画效果的元素就最好设置为绝对定位。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/html篇/html5的优缺点.md: -------------------------------------------------------------------------------- 1 | # html5的优缺点 2 | #### 优点 3 | + 网络标准统一、HTML5本身是由W3C推荐出来的。 4 | + 多设备、跨平台 5 | + 即时更新。 6 | + 提高可用性和改进用户的友好体验; 7 | + 有几个新的标签,这将有助于开发人员定义重要的内容; 8 | + 可以给站点带来更多的多媒体元素(视频和音频); 9 | + 可以很好的替代Flash和Silverlight; 10 | + 涉及到网站的抓取和索引的时候,对于SEO很友好; 11 | + 被大量应用于移动应用程序和游戏。 12 | #### 缺点 13 | + 安全:像之前Firefox4的web socket和透明代理的实现存在严重的安全问题,同时web storage、web socket 这样的功能很容易被黑客利用,来盗取用户的信息和资料。 14 | + 完善性:许多特性各浏览器的支持程度也不一样。 15 | + 技术门槛:HTML5简化开发者工作的同时代表了有许多新的属性和API需要开发者学习,像web worker、web socket、web storage 等新特性,后台甚至浏览器原理的知识,机遇的同时也是巨大的挑战 16 | + 性能:某些平台上的引擎问题导致HTML5性能低下。 17 | + 浏览器兼容性:最大缺点,IE9以下浏览器几乎全军覆没。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/html篇/iframe的优缺点.md: -------------------------------------------------------------------------------- 1 | # iframe的优缺点 2 | #### 优点 3 | + iframe能够把嵌入的网页原样展现出来; 4 | + 模块分离,便于更改,如果有多个网页引用iframe,只需要修改iframe的内容,就可以实现调用的每一个页面内容的更改,方便快捷; 5 | + 网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,增加代码的可重用; 6 | + 如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决; 7 | + 重载页面时不需要重载整个页面,只需要重载页面中的一个框架页; 8 | + 方便制作导航栏。 9 | #### 缺点 10 | + iframe会阻塞主页面的 Onload 事件,影响网页加载速度; 11 | + 搜索引擎的检索程序无法解读这种页面,不利于 SEO; 12 | + iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。 13 | + 如果需要使用 iframe ,最好是通过 javascript动态给iframe添加 src 属性值,这样可以绕开以上两个问题。 14 | + 多数小型的移动设备(PDA 手机)无法完全显示框架,设备兼容性差 15 | #### 应用场景 16 | + 分析 17 | + iframe的页面和父页面(parent)是分开的,所以它意味着,这是一个独立的区域,不受 parent的CSS或者全局的JavaScript的影响。 18 | + 应用 19 | + 网页编辑器; 20 | + 跨域通信。JavaScript跨域总结与解决办法 ,类似的还有浏览器多页面通信,比如音乐播放器,用户如果打开了多个tab页,应该只有一个在播放; 21 | + 历史记录管理,解决ajax化网站响应浏览器前进后退按钮的方案,在html5的history api不可用时作为一种替代; 22 | + 纯前端的utf8和gbk编码互转。比如在utf8页面需要生成一个gbk的encodeURIComponent字符串,可以通过页面加载一个gbk的iframe,然后主页面与子页面通信的方式实现转换; 23 | + 用iframe实现无刷新文件上传,在FormData不可用时作为替代方案; 24 | + 创建一个全新的独立的宿主环境。iframe还可以用于创建新的宿主环境,用于隔离或者访问原始接口及对象,比如有些前端安全的防范会覆盖一些原生的方法防止恶意调用,通过创建一个iframe,然后从iframe中取回原始对象和方法来破解这种防范; 25 | + 用来加载广告; 26 | + 一般邮箱使用iframe,如QQ邮箱; 27 | + 一些简单的后台页面。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/html篇/如何实现浏览器内多个标签页之间的通信.md: -------------------------------------------------------------------------------- 1 | # 如何实现浏览器内多个标签页之间的通信? 2 | #### websocket 3 | + 建立在 TCP 协议之上,服务器端的实现比较容易。 4 | + 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 5 | + 数据格式比较轻量,性能开销小,通信高效。 6 | + 可以发送文本,也可以发送二进制数据。 7 | + 没有同源限制,客户端可以与任意服务器通信。 8 | + 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。 9 | #### setInterval+cookie 10 | + 在页面A设置一个使用 setInterval 定时器不断刷新,检查 Cookies 的值是否发生变化,如果变化就进行刷新的操作。 11 | #### localstorage 12 | + code 13 | ```javascript 14 | window.onstorage = (e) => {console.log(e)} 15 | // 或者这样 16 | window.addEventListener('storage', (e) => console.log(e)) 17 | ``` 18 | + 注意 19 | + onstorage以及storage事件,针对都是非当前页面对localStorage进行修改时才会触发,当前页面修改localStorage不会触发监听函数。 20 | + 在对原有的数据的值进行修改时才会触发,比如原本已经有一个key会a值为b的localStorage,你再执行:localStorage.setItem('a', 'b')代码,同样是不会触发监听函数的。 21 | #### html5浏览器的新特性SharedWorker 22 | + 普通的webworker直接使用new Worker()即可创建,这种webworker是当前页面专有的。 23 | + 还有种共享worker(SharedWorker),这种是可以多个标签页、iframe共同使用的。 24 | + SharedWorker可以被多个window共同使用,但必须保证这些标签页都是同源的(相同的协议,主机和端口号) -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/JS编程实现简单模板引擎变量替换.md: -------------------------------------------------------------------------------- 1 | ## JS编程实现简单模板引擎变量替换 2 | ### 解题关键 3 | + replace/正则 4 | ### 参考文献 5 | + [js简单前端模板引擎实现](https://segmentfault.com/a/1190000010366490) 6 | + [JavaScript模板引擎实现原理实例详解](https://www.jb51.net/article/152760.htm) 7 | #### code 8 | ```javascript 9 | let render = (str) => { 10 | return (obj) => { 11 | str = str.replace('YY',obj.YY); 12 | str = str.replace('MM',obj.MM); 13 | str = str.replace('DD',obj.DD); 14 | return str; 15 | } 16 | } 17 | // 栗子 18 | const YY = '2020'; 19 | const MM = '02'; 20 | const DD = '04'; 21 | const str = render('YY-MM-DD')({YY,MM,DD}); 22 | console.log(str);// 2020-02-04 23 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/JavaScript中this.md: -------------------------------------------------------------------------------- 1 | ## JavaScript中this 2 | ### 定义 3 | + **this指的是函数运行时所在的环境** 4 | ### 参考文献 5 | + [阮一峰 - JavaScript 的 this 原理](http://www.ruanyifeng.com/blog/2018/06/javascript-this.html) 6 | ### 使用场景 7 | + 1、作为一般函数执行 8 | + this指向全局对象 9 | + 此处是 window 10 | + 栗子 11 | ```javascript 12 | var obj = { 13 | foo: function () { console.log(this.bar) }, 14 | bar: 1 15 | }; 16 | var foo = obj.foo; 17 | var bar = 2; 18 | foo() // 2 19 | ``` 20 | + 2、作为对象属性执行 21 | + this指代上级对象 22 | + 此处是 obj 23 | + 栗子 24 | ```javascript 25 | var obj = { 26 | foo: function () { console.log(this.bar) }, 27 | bar: 1 28 | }; 29 | var bar = 2; 30 | obj.foo() // 1 31 | ``` 32 | + 3、显示绑定 33 | + new 34 | + call、apply、bind调用 35 | + 栗子 36 | ```javascript 37 | // apply、call 38 | function foo(b){ 39 | console.log(this.a,b); 40 | return this.a+b; 41 | } 42 | var obj = { 43 | a:2 44 | }; 45 | var bar = function() { 46 | return foo.apply(obj,arguments); 47 | }; 48 | var b = bar(3);// 2 3 49 | console.log(b);//5 50 | 51 | // bind 52 | function foo(b){ 53 | console.log(this.a,b); 54 | return this.a+b; 55 | } 56 | var obj = { 57 | a:2 58 | }; 59 | var bar = foo.bind(obj); 60 | var b = bar(3);// 2 3 61 | console.log(b);//5 62 | 63 | // new 64 | function foo(a){ 65 | this.a = a; 66 | } 67 | var bar = new foo(2); 68 | console.log(bar.a);//2 69 | ``` 70 | + 4、箭头函数中的 this 71 | + 不适用于上面任何一种规则 72 | + 根据外层作用域来决定this 73 | + 函数 74 | + 全局 75 | + 一旦绑定,不可再改 76 | + 栗子 77 | ```javascript 78 | // 函数 79 | function User() { 80 | this.name = 'John'; 81 | return () => { 82 | console.log(this); 83 | }; 84 | } 85 | var obj1 = {a:2}; 86 | var obj2 = {b:3}; 87 | const user = new User()(); // {name:"John"} 88 | User()(); // window 、相当于 window.User() 89 | User.bind(obj1)()() // {a: 2, name: "John"} 90 | 91 | // 全局 92 | var o = { 93 | fn:() => { 94 | console.log(this) 95 | } 96 | } 97 | var obj = {a:2}; 98 | o.fn() // window 99 | o.fn.bind(obj)() // window 不可再改 100 | 101 | // 一旦绑定,不可再改 102 | function User() { 103 | this.name = 'John'; 104 | return () => { 105 | console.log(this); 106 | }; 107 | } 108 | var obj1 = {a:2}; 109 | var obj2 = {b:3}; 110 | var user = User.call(obj1); 111 | user(); // {a: 2, name: "John"} 112 | user.call(obj2); // {a: 2, name: "John"} 113 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/Javascript的继承与多态.md: -------------------------------------------------------------------------------- 1 | # Javascript的继承与多态 2 | + ES5 3 | + ES6 4 | + 概述 5 | + JS引入了prototype的概念,可以让我们以另一种方式模仿类,并通过原型链的方式实现了父类子类之间共享属性的继承以及身份确认机制。 6 | ### ES6之前的继承 7 | + 原型赋值方式 8 | + 直接将父类的一个实例赋给子类的原型 9 | + code 10 | ```javascript 11 | function Person(name) { 12 | this.name = name; 13 | this.className = 'person'; 14 | } 15 | Person.prototype.getClassName = function(){ 16 | console.log(this.className); 17 | } 18 | function Man(){ 19 | } 20 | Man.prototype = new Person(); 21 | var man = new Man(); 22 | man.getClassName() // "person" 23 | man instanceof Person // true 24 | ``` 25 | + 分析 26 | + 实现 27 | + 通过new一个父类的实例,赋值给子类的原型,使得 28 | + 父类原型中的方法属性及挂在this上的各种方法属性全赋给了子类的原型 29 | + 问题 30 | + 子类无法通过父类创建私有属性 31 | + 此种方法,每次继承来的人名都是同一个 32 | + 调用构造函数方式 33 | + 子类的在构造函数里用子类实例的this去调用父类的构造函数, 34 | + 从而达到继承父类属性的效果 35 | + call 36 | + apply 37 | + code 38 | ```javascript 39 | function Person(name){ 40 | this.name=name; 41 | this.className="person" 42 | } 43 | Person.prototype.getName=function(){ 44 | console.log(this.name) 45 | } 46 | function Man(name){ 47 | Person.apply(this,arguments) 48 | } 49 | var man1=new Man("Davin"); 50 | var man2=new Man("Jack"); 51 | man1.name // "Davin" 52 | man2.name // "Jack" 53 | man1.getName() // 报错 54 | man1 instanceof Person // false 55 | ``` 56 | + 分析 57 | + 只能继承父类构造函数中声明的实例属性,并没有继承父类原型的属性和方法 58 | + 组合继承 59 | + 原型继承 + 调用构造函数 60 | + code 61 | ```javascript 62 | function Person(name){ 63 | this.name=name||"default name"; 64 | this.className="person" 65 | } 66 | Person.prototype.getName=function(){ 67 | console.log(this.name) 68 | } 69 | function Man(name){ 70 | // 第一次调用Person() 71 | Person.apply(this,arguments) 72 | } 73 | //继承原型 74 | Man.prototype = new Person(); // 第二次调用Person() 75 | var man1=new Man("Tom"); 76 | man1.name // "Tom" 77 | man1.getName() //"Tom" 78 | Man.prototype // Person {name: "default name", className: "person"} 79 | ``` 80 | + 分析 81 | + 由Man.prototype可知实例中的name属性和原型不同, 82 | + 每次创建相当于覆盖原型的name属性,原型的依旧在 83 | + 无论在什么情况下,都会调用两次构造函数: 84 | + 一次是在创建子类型原型时, 85 | + 另一次是在子类型构造函数内部。 86 | + 寄生式继承 87 | + 和工厂模式类似,创建一个仅用于封装继承过程的函数, 88 | + 该函数在内部以某种方式来增强对象,最后返回对象。 89 | + code 90 | ```javascript 91 | function Person(name){ 92 | this.name=name; //1 93 | this.className="person" 94 | } 95 | function createAnother(origin){ 96 | // 浅拷贝原型对象,继承方法 97 | var clone = Object(origin); 98 | // 以某种方式来增强这个对象 99 | clone.sayHi = function(){ 100 | console.log("hi"); 101 | }; 102 | return clone; // 返回这个对象 103 | } 104 | var anotherPeople = createAnother(Person); 105 | anotherPeople.sayHi(); // hi 106 | ``` 107 | + 寄生组合继承(ES5主流继承方式) 108 | + 去除原型直接赋值方式,改用[Object.creat(obj)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create)浅拷贝原型对象 109 | + code 110 | ```javascript 111 | function Person(name){ 112 | this.name=name; //1 113 | this.className="person" 114 | } 115 | Person.prototype.getName=function(){ 116 | console.log(this.name) 117 | } 118 | function Man(name){ 119 | // 继承属性 120 | Person.apply(this,arguments) 121 | } 122 | // 浅拷贝原型对象 - 继承方法 123 | Man.prototype = Object.create(Person.prototype); 124 | // 增强对象 125 | Man.prototype.constructor = Man 126 | var man1=new Man("Tom"); 127 | man1.name // "Tom" 128 | man1.getName() // "Tom" 129 | ``` 130 | + 分析 131 | + 将父类的原型复制给了子类原型 132 | + constructor属性 133 | ```javascript 134 | Person.prototype.constructor 135 | Person(name){ 136 | this.name=name; //1 137 | this.className="person" 138 | } 139 | Man.prototype.constructor 140 | Person(name){ 141 | this.name=name; //1 142 | this.className="person" 143 | } 144 | man1 instanceof Person // true 145 | man1 instanceof Man // true 146 | ``` 147 | + constructor是类的构造函数 148 | + 上述Man的构造函数应该是Man,所以一般需要重置, 149 | + 也叫增强对象 150 | + **Man.prototype.constructor = Man** 151 | + 总结 152 | + ![2.png](https://pic.leetcode-cn.com/523f0c8896abed9e54534784934e610a0ac4a4c5f4a0a1649e48e76f89a6cfc6-2.png) 153 | + 组合寄生:一条实现属性继承,一条实现方法的继承 154 | ### ES6继承 155 | + [Class](http://es6.ruanyifeng.com/#docs/class-extends#%E5%8E%9F%E7%94%9F%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E7%9A%84%E7%BB%A7%E6%89%BF) 156 | + extends 157 | + super 158 | + code 159 | ```javascript 160 | class A {} 161 | // B通过extends关键字继承了A类的所有属性和方法 162 | class B extends A { 163 | constructor(x,y,color){ 164 | // 调用父类的constructor(x,y) 165 | // 相当于 A.prototype.constructor.call(this) 166 | super(x,y); 167 | this.color = color 168 | } 169 | toString(){ 170 | //调用父类的方法 171 | return this.color + ' ' + super.toString(); 172 | } 173 | } 174 | ``` 175 | + 分析 176 | + 一个继承语句同时存在两条继承链:属性继承 + 方法的继承 177 | ```javascript 178 | class B extends A{} 179 | B.__proto__ === A; //继承属性 180 | B.prototype.__proto__ === A.prototype;//继承方法 181 | ``` 182 | + 总结 183 | + ![1.png](https://pic.leetcode-cn.com/baa5c9b2de504917f1170fffe9304b34f12850f38068acdf96524c964264b5bc-1.png) 184 | #### 其它写法 185 | ```javascript 186 | // 基类 187 | function Base() { 188 | 189 | } 190 | // 派生类 191 | function Derived() { 192 | Base.call(this); 193 | } 194 | // 将派生类的原型的原型链挂在基类的原型上 195 | Object.setPrototypeOf(Derived.prototype,Base.prototype); 196 | ``` 197 | #### Object.setPrototypeOf(obj,prototype) 198 | + 参数 199 | + obj 200 | + 要设置其原型的对象 201 | + prototype 202 | + 该对象的新原型(一个对象 或null) 203 | + Polyfill 204 | ```javascript 205 | Object.setPrototypeOf = Object.setPrototypeOf || function (obj,proto) { 206 | obj.__proto__ = proto; 207 | return obj; 208 | } 209 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/for in 和 for of的区别.md: -------------------------------------------------------------------------------- 1 | ## for in 和 for of的区别 2 | ### 参考文献 3 | + [MDN - for...of](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of) 4 | + [MDN - for...in](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in) 5 | + [阮一峰 - Iterator 和 for...of 循环](http://es6.ruanyifeng.com/#docs/iterator#%E8%B0%83%E7%94%A8-Iterator-%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%9C%BA%E5%90%88) 6 | ### for...of 7 | + 定义 8 | + ES6 9 | + 在可迭代对象上创建一个迭代循环 10 | + Array 11 | + Map 12 | + Set 13 | + String 14 | + TypedArray 15 | + arguments 对象 16 | + ... 17 | ### for...in 18 | + 定义 19 | + ES5 20 | + 以任意顺序遍历一个对象的除 Symbol 以外的可枚举属性。 21 | + 特点 22 | + index索引为字符串型数字,不能直接进行几何运算 23 | + 遍历顺序有可能不是按照实际数组的内部顺序 24 | + 会遍历数组所有的可枚举属性,包括原型 25 | ### 区别 26 | + for...of 27 | + 遍历可迭代对象定义要迭代的数据 28 | + 多用于遍历数组,循环出的为 value 29 | + 不能循环普通的对象,因为没有迭代器对象 30 | + 需要Object.keys()搭配,加迭代器对象也无用 31 | ```javascript 32 | var student={ 33 | name:'张三', 34 | age:22, 35 | locate:{ 36 | city:'深圳' 37 | } 38 | } 39 | for(var key of Object.keys(student)){ 40 | console.log(key+": "+student[key]); 41 | } 42 | ``` 43 | + 但是对类数组对象加迭代器有用 44 | + 栗子 45 | ```javascript 46 | let iterable = { 47 | 0: 'a', 48 | 1: 'b', 49 | 2: 'c', 50 | length: 3, 51 | [Symbol.iterator]: Array.prototype[Symbol.iterator] 52 | }; 53 | for (let item of iterable) { 54 | console.log(item); // 'a', 'b', 'c' 55 | } 56 | ``` 57 | + for...in 58 | + 以任意顺序迭代对象的可枚举属性 59 | + 多用于遍历对象属性,循环出的是 key -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/json和xml的区别.md: -------------------------------------------------------------------------------- 1 | # json和xml的区别 2 | #### json 3 | + 定义 4 | + 一种轻量级的数据交换格式,具有更好的可读性和便于快速编写的特性 5 | + 优点 6 | + 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小; 7 | + 易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取; 8 | + 支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析; 9 | + 在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取; 10 | + 因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。 11 | + 缺点 12 | + 没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性; 13 | + JSON格式目前在Web Service中推广还属于初级阶段。 14 | #### xml 15 | + 定义 16 | + 扩展标记语言 (Extensible Markup Language,XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型, 17 | + 是一种允许用户对自己的标记语言进行定义的源语言。 18 | + XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。 19 | + XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。 20 | + 优点 21 | + 格式统一,符合标准; 22 | + 容易与其他系统进行远程交互,数据共享比较方便。 23 | + 缺点 24 | + XML文件庞大,文件格式复杂,传输占带宽; 25 | + 服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护; 26 | + 客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码; 27 | + 服务器端和客户端解析XML花费较多的资源和时间。 28 | #### 区别 29 | + 在可读性方面,JSON和XML的数据可读性基本相同。JSON和XML的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,很难分出胜负。 30 | + 可扩展性方面,XML天生有很好的扩展性,JSON当然也有,没有什么是XML能扩展,JSON不能的。 31 | + 在编码难度方面,XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了。 32 | + 在解码难度方面,XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0。这一点XML输的真是没话说。 33 | + 在流行度方面,XML已经被业界广泛的使用,而JSON才刚刚开始,但是在Ajax这个特定的领域,未来的发展一定是XML让位于JSON。到时Ajax应该变成Ajaj(AsynchronousJavascript and JSON)了。 34 | + JSON和XML同样拥有丰富的解析手段。 35 | + JSON相对于XML来讲,数据的体积小。 36 | + JSON与JavaScript的交互更加方便。 37 | + JSON对数据的描述性比XML较差。 38 | + json的速度要远远快于XML。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/js基本数据类型.md: -------------------------------------------------------------------------------- 1 | # js基本数据类型 2 | #### ES5 3 | + 简单数据类型 4 | + String、Boolean 5 | + Null、Undefined 6 | + Undefined类型表示未定义,它只有一个值为undefined 7 | + undefined是一个变量,并非关键字,在局部作用域下,会被篡改 8 | + 因此判断是否为undefined,可以用 9 | + x !== void 0 10 | + void 0 返回undefined 11 | + Null类型只有一个值,为null 12 | + null是一个关键字 13 | + Number 14 | + 有 18437736874454810627(即 2^64-2^53+3) 个值 15 | + 额外场景 16 | + NaN,占用了 9007199254740990,这原本是符合 IEEE 规则的数字; 17 | + Infinity,无穷大; 18 | + -Infinity,负无穷大。 19 | + 精度问题 20 | + 根据双精度浮点数的定义,Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff,所以 Number 无法精确表示此范围外的整数。 21 | + console.log( 0.1 + 0.2 == 0.3); // false 22 | + 改正 23 | + console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON); // true 24 | + 复杂数据类型 25 | + Object 26 | + 其它规范类型 27 | + List 和 Record: 用于描述函数传参过程。 28 | + Set:主要用于解释字符集等。 29 | + Completion Record:用于描述异常、跳出等语句执行过程。 30 | + Reference:用于描述对象属性访问、delete 等。 31 | + Property Descriptor:用于描述对象的属性。 32 | + Lexical Environment 和 Environment Record:用于描述变量和作用域。 33 | + Data Block:用于描述二进制数据。 34 | #### ES6 35 | + 新增 36 | + [Symbol](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol) -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/promise的简单实现.md: -------------------------------------------------------------------------------- 1 | ## promise的简单实现 2 | + 图解 3 | + ![](https://segmentfault.com/img/bVbmYtD?w=716&h=736) 4 | + code 5 | + 写法一: 6 | ```javascript 7 | let i = 1; 8 | var Promise = (fn) => { 9 | this.id = i++; 10 | this.status = 'PENDING'; 11 | this.value = null; 12 | this.deffered = []; 13 | fn.call(this,this.resolve.bind(this),this.reject.bind(this)); 14 | } 15 | Promise.prototype = { 16 | constructor:Promise, 17 | then(onfulfilled,onrejected){ 18 | let obj = { 19 | onfulfilled:onfulfilled, 20 | onrejected:onrejected 21 | } 22 | obj.promise = new this.constructor(() => {}); 23 | console.log(this.status); 24 | if(this.status === 'PENDING'){ 25 | this.deffered.push(obj); 26 | } 27 | console.log(this.deffered); 28 | return obj.promise; 29 | }, 30 | resolve(data){ 31 | this.status = 'FULFILLED'; 32 | this.value = data; 33 | this.deffered.forEach(item => { 34 | let p = item.onfulfilled(this.value); 35 | if(p && p.constructor === Promise){ 36 | p.deffered = item.promise.deffered; 37 | } 38 | }); 39 | }, 40 | reject(err){ 41 | this.status = 'REJECTED'; 42 | this.value = err; 43 | this.deffered.forEach(item => { 44 | let p = item.onrejected(this.value); 45 | if(p && p.constructor === Promise){ 46 | p.deffered = item.promise.deffered; 47 | } 48 | }) 49 | } 50 | } 51 | ``` 52 | + 写法二: 53 | ```javascript 54 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/不使用NEW运算符如何创建JS对象.md: -------------------------------------------------------------------------------- 1 | ## 不使用NEW运算符如何创建JS对象 2 | #### 解法 3 | + 利用字面量 4 | ```javascript 5 | let a = [],b = {},c = /abc/g; 6 | ``` 7 | + 利用 dom api 8 | ```javascript 9 | let d = document.createElement('p'); 10 | ``` 11 | + 利用JavaScript内置对象api 12 | ```javascript 13 | let e = Object.create(null); 14 | let f = Object.assign({x:2},{y:3}); 15 | let g = JSON.parse('{}'); 16 | ``` 17 | + 利用装箱转换 18 | ```javascript 19 | let h = Object(undefined); 20 | let i = Object(null); 21 | let j = Object(1); 22 | let k = Object('a'); 23 | let l = Object(true); 24 | let m = (function() {return this}).call(1) 25 | ``` 26 | + 利用 ES6构造器 27 | ```javascript 28 | class myObj{ 29 | constructor(a) {this.a = a;} 30 | } 31 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/函数防抖与节流.md: -------------------------------------------------------------------------------- 1 | # 函数防抖与节流 2 | ## 摘要 3 | + 定义 4 | + 思路 5 | + 实现 6 | ### 防抖 7 | + 定义 8 | + 触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间 9 | + 思路 10 | + 每次触发事件时都取消之前的延时调用方法 11 | + 用法 12 | + 处理用户输入 13 | + 实现 14 | ```javascript 15 | let debounce = (fn,wait = 500) => { 16 | let timeout = null; 17 | return (arguments) => { 18 | // 每次触发清除上一个执行的函数 19 | clearTimeout(timeout); 20 | timeout = setTimeout(() => { 21 | fn.apply(this,arguments); 22 | },wait) 23 | } 24 | } 25 | let sayHi = () => { 26 | console.log('防抖成功'); 27 | } 28 | let inp = document.getElementById('inp'); 29 | // 防抖 30 | inp.addEventListener('input',debounce(sayHi)); 31 | ``` 32 | #### 节流 33 | + 定义 34 | + 高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率 35 | + 思路 36 | + 每次触发事件时都判断当前是否有等待执行的延时函数 37 | + 用法 38 | + 防止用户连续执行一个耗时操作 对操作按钮点击函数进行节流处理 39 | + 实现 40 | ```javascript 41 | let throttle = (fn,gapTime = 2000) => { 42 | let lastTime = null; 43 | return (arguments) => { 44 | let nowTime = Date.now(); 45 | if(nowTime - lastTime > gapTime || !lastTime){ 46 | fn.apply(this,arguments); 47 | lastTime = nowTime; 48 | } 49 | } 50 | } 51 | let sayHi = (e) => { 52 | console.log('节流成功:'+document.documentElement.clientHeight) 53 | } 54 | window.addEventListener('resize',throttle(sayHi)); 55 | ``` 56 | + 也可以这样写 57 | ```javascript 58 | function throttle2 (fun,wait) { 59 | let timer = null; 60 | return function (...args) { 61 | if(!timer) { 62 | fun(...args); 63 | timer = setTimeout( () => { 64 | timer = null; 65 | },wait); 66 | } 67 | } 68 | } 69 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/实现ES6的class语法.md: -------------------------------------------------------------------------------- 1 | #### 实现ES6的class语法 2 | ```javascript 3 | function inherit(subType,superType) { 4 | subType.prototype = Object.create(superType.prototype,{ 5 | constructor: { 6 | enumerable:false, 7 | configurable:true, 8 | writable:true, 9 | value:subType 10 | } 11 | }) 12 | Object.setPrototypeOf(subType,superType) 13 | } 14 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/实现call、apply、bind.md: -------------------------------------------------------------------------------- 1 | ## 实现 call、apply、bind 2 | #### 实现 call 3 | ```javascript 4 | Function.prototype.call = function (context) { 5 | // 如果对象传入是空或者null的时候指向window 6 | const cxt = context || window; 7 | // 以对象的方式调用的方式绑定this 8 | cxt.fun = this; 9 | // 获取实参 10 | const args = Array.from(arguments).slice(1); 11 | // 或 [...arguments].splice(1) 或 遍历+eval 12 | const res = arguments.length > 1 ? cxt.fun(...args) : cxt.fun(); 13 | // 删除方法,不然会对传入的对象造成污染(添加该方法) 14 | delete cxt.fun; 15 | // 返回结果 16 | return res; 17 | } 18 | ``` 19 | #### 实现 apply 20 | ```javascript 21 | Function.prototype.apply = function (context) { 22 | const cxt = context || window; 23 | cxt.fun = this; 24 | const res = arguments[1] ? cxt.fun(...arguments[1]) : cxt.fun(); 25 | delete cxt.fun; 26 | return res; 27 | } 28 | ``` 29 | #### 实现 bind 30 | ```javascript 31 | if(!Function.prototype.bind)(function(){ 32 | Function.prototype.bind = () => { 33 | let fun = this; 34 | let cxt = arguments[0]; 35 | let bindArgs = [].slice.call(arguments,1); 36 | if(typeof fun !== 'function'){ 37 | throw new TypeError('Fucntion.prototype.bind -'+'what is trying to be bound is not callable'); 38 | } 39 | // return function (){ 40 | // return fun.apply(cxt,bindArgs.concat([].prototype.slice.call(arguments))) 41 | // } 42 | return function (...callArgs) { 43 | let allArgs = bindArgs.concat(callArgs); 44 | if(this instanceof fun){ 45 | // 如果是 通过 new 调用的,而 new 的优先级高于 bind 46 | return new fun(...allArgs); 47 | } 48 | return fun.call(cxt,...allArgs); 49 | } 50 | } 51 | })(); 52 | ``` 53 | + 或者这样写 54 | ```javascript 55 | Function.prototype.bind = function () { 56 | let self = this; 57 | let cxt = arguments[0]; 58 | let args = [].slice.call(arguments,1); 59 | return function (){ 60 | let allArgs = args.concat([].slice.call(arguments)); 61 | if(this instanceof self){ 62 | // 如果是 通过 new 调用的,而 new 的优先级高于 bind 63 | return new self(...allArgs); 64 | } 65 | return self.apply(cxt,allArgs); 66 | } 67 | } 68 | ``` 69 | + 或者这样写 70 | ```javascript 71 | Function.prototype.bind = function () { 72 | let self = this; 73 | let cxt = arguments[0] || window; 74 | let args = [...arguments].splice(1); 75 | return function () { 76 | let allArgs = [...args,...arguments]; 77 | if(this instanceof self){ 78 | // 如果是 通过 new 调用的,而 new 的优先级高于 bind 79 | return new self(...allArgs); 80 | } 81 | self.apply(cxt,allArgs); 82 | } 83 | } 84 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/实现一个 instanceof.md: -------------------------------------------------------------------------------- 1 | ## 实现一个 instanceof 2 | #### code 3 | ```javascript 4 | function myinstanceof(a,b) { 5 | while (a) { 6 | if( a.__proto__ === b.prototype) return true; 7 | a = a.__proto__; 8 | } 9 | return false; 10 | } 11 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/实现一个 new.md: -------------------------------------------------------------------------------- 1 | ## 实现一个 new 2 | ### 参考文献 3 | + [实现 new](https://zhuanlan.zhihu.com/p/84605717) 4 | ### 定义 5 | 1. 创建一个新的空的对象 6 | 2. 将构造函数的作用域赋给新对象(因而 this 就指向了这个新对象) 7 | 1. 即将新对象的原型链链接到构造函数的原型对象上 8 | 3. 执行构造函数中的代码(为这个新对象添加属性) 9 | 4. 如果构造函数有返回值,则返回;否则,返回新对象。 10 | #### code 11 | ```javascript 12 | function myNew() { 13 | var constr = Array.prototype.shift.call(arguments); 14 | var obj = Object.create(constr.prototype); 15 | var res = constr.apply(obj,arguments); 16 | return res instanceof Object ? res : obj; 17 | } 18 | ``` 19 | + 或者这样写 20 | ```javascript 21 | function myNew(fun,...args) { 22 | if(typeof fun !== 'function'){ 23 | console.log('参数必须是一个函数'); 24 | } 25 | let obj = Object.create(fun.prototype); 26 | let res = fun.call(obj,...args); 27 | if(res !== null && (typeof res === 'object' || typeof res === 'function')){ 28 | return res; 29 | } 30 | return obj; 31 | } 32 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/实现一个 sum(1)(2)(4,1).valueOf() sum(1,2,3,4).valueOf().md: -------------------------------------------------------------------------------- 1 | ## 实现一个 sum(1)(2)(4,1).valueOf() sum(1,2,3,4).valueOf() 2 | #### 解法一:add(1)(2)(3) 6 3 | ```javascript 4 | function add(x) { 5 | var sum = x; 6 | var tmp = function(y) { 7 | sum = sum + y; 8 | return tmp; 9 | } 10 | tmp.toString = function (){ 11 | return sum; 12 | } 13 | return tmp; 14 | } 15 | ``` 16 | #### 解法二:sum(1)(2)(3)、sum(1,2,3) 17 | ```javascript 18 | function sum(...arguments){ 19 | if([...arguments].length == 1){ 20 | var sum = [...arguments][0]; 21 | var tmp = function(y){ 22 | sum += y; 23 | return tmp; 24 | } 25 | // valueOf 26 | tmp.toString = function (){ 27 | return sum; 28 | } 29 | return tmp; 30 | }else{ 31 | var sum2 = 0; 32 | var tmpArr = [...arguments]; 33 | for(var i = 0;i < tmpArr.length;i++){ 34 | sum2 += tmpArr[i]; 35 | } 36 | return sum2; 37 | } 38 | } 39 | ``` 40 | #### 解法三:通用的函数柯里化构造方法 41 | ```javascript 42 | // 通用的函数柯里化构造方法 43 | function curry(func){ 44 | //新建args保存参数,注意,第一个参数应该是要柯里化的函数,所以args里面去掉第一个 45 | var args = [].slice.call(arguments,1); 46 | //新建_func函数作为返回值 47 | var _func = function(){ 48 | //参数长度为0,执行func函数,完成该函数的功能 49 | if(arguments.length === 0){ 50 | return func.apply(this,args); 51 | }else { 52 | //否则,存储参数到闭包中,返回本函数 53 | [].push.apply(args,arguments); 54 | return _func; 55 | } 56 | } 57 | return _func; 58 | } 59 | 60 | function add(){ 61 | return [].reduce.call(arguments,function(a,b){return a+b}); 62 | } 63 | console.log(curry(add,1,2,3)(1)(2)(3,4,5,5)(5,6,6,7,8,8)(1)(1)(1)());//69 64 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/对象深、浅拷贝.md: -------------------------------------------------------------------------------- 1 | # 对象拷贝实现 2 | ### 深拷贝 3 | #### 解法一:JSON全局对象 4 | ```javascript 5 | JSON.parse.JSON.stringfy(obj) 6 | ``` 7 | + 分析 8 | + 只能正确处理Number、String、Boolea、Array、扁平对象及那些能够被json直接表示的数据结构 9 | + 会忽略 undefined 10 | + 会忽略 symbol 11 | + 不能序列化函数 12 | + 不能解决循环引用的对象 13 | + obj.property = obj 14 | + JSON.parse(JSON.stringify(obj)) // TypeError:Converting circular structure to JSON 15 | #### 解法二:框架 16 | + jQuery 17 | + $.extend(true,{},x)($.extend({},x)就是浅拷贝) 18 | + lodash 19 | + .clone(obj,true) == .cloneDeep(obj) 20 | #### 解法三:浅拷贝 + 递归 21 | + code 22 | ```javascript 23 | // 判断不为null的对象 24 | let isObject = (obj) => { 25 | return typeof obj === 'object' && obj !== null; 26 | } 27 | let deepCopy = (obj) => { 28 | if(!isObject) return obj; 29 | let newObj = Array.isArray(obj) ? [] : {}; 30 | for(let key in obj){ 31 | if(obj.hasOwnProperty(key)){ 32 | newObj[key] = isObject(obj[key]) ? deepCopy(obj[key]) : obj[key]; 33 | } 34 | } 35 | return newObj; 36 | } 37 | ``` 38 | + 分析 39 | + 对传入参数校验。传人null返回null 40 | + typeof null === 'object' 41 | + Array.isArray(),兼容数组 42 | + 对象的属性还是对象,则递归 43 | + 循环引用无法深拷贝,即环检测:当检测到访问过该对象时,直接取出该值返回即可 44 | + 使用哈希表 45 | + code 46 | ```javascript 47 | // 判断不为null的对象 48 | let isObject = (obj) => { 49 | return typeof obj === 'object' && obj !== null; 50 | } 51 | let cloneDeep = (obj,hash = new WeakMap()) =>{ 52 | if(!isObject(obj)) return obj; 53 | if(hash.has(obj)) return hash.get(obj); 54 | let newObj = Array.isArray(obj) ? [] : {}; 55 | hash.set(obj,newObj); 56 | for(let key in obj){ 57 | if(obj.hasOwnProperty(key)){ 58 | newObj[key] = isObject(obj[key]) ? cloneDeep(obj[key],hash) : obj[key]; 59 | } 60 | } 61 | return newObj; 62 | } 63 | ``` 64 | + 使用数组 65 | + code 66 | ```javascript 67 | // 判断不为null的对象 68 | let isObject = (obj) => { 69 | return typeof obj === 'object' && obj !== null; 70 | } 71 | // 查找方法 72 | let find = (arr,item) => { 73 | for(let i = 0;i < arr.length;i++){ 74 | if(arr[i].source === item){ 75 | return arr[i]; 76 | } 77 | } 78 | return null; 79 | } 80 | let cloneDeep2 = (obj,hash = []) => { 81 | if(!isObject(obj)) return obj; 82 | let newObj = Array.isArray(obj) ? [] : {}; 83 | let tmp = find(hash,obj); 84 | if(tmp){ 85 | return tmp.newObj; 86 | } 87 | hash.push({ 88 | source:obj, 89 | newObj:newObj 90 | }) 91 | for(let key in obj){ 92 | if(obj.hasOwnProperty(key)){ 93 | newObj[key] = isObject(obj[key]) ? cloneDeep2(obj[key],hash) : obj[key]; 94 | } 95 | } 96 | return newObj; 97 | } 98 | ``` 99 | + 进阶 100 | + 拷贝Symbol 101 | + [Symbol介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol) 102 | + 分析 103 | + Symbol用普通方法遍历不到 104 | + 解法一:Object.getOwnPropertySymbols() 105 | ```javascript 106 | // 判断不为null的对象 107 | let isObject = (obj) => { 108 | return typeof obj === 'object' && obj !== null; 109 | } 110 | let cloneDeep = (obj,hash = new WeakMap()) =>{ 111 | if(typeof obj !== 'object' || obj === null) return obj; 112 | if(hash.has(obj)) return hash.get(obj); 113 | let newObj = Array.isArray(obj) ? [] : {}; 114 | hash.set(obj,newObj); 115 | 116 | // 处理Symbol 117 | let symKeys = Object.getOwnPropertySymbols(obj); 118 | if(symKeys.length){ 119 | symKeys.forEach(symKey => { 120 | newObj[symKey] = isObject(obj[symKey]) ? cloneDeep(obj[symKey],hash) : obj[symKey]; 121 | }) 122 | } 123 | 124 | for(let key in obj){ 125 | if(obj.hasOwnProperty(key)){ 126 | newObj[key] = isObject(obj[key]) ? cloneDeep(obj[key],hash) : obj[key]; 127 | } 128 | } 129 | return newObj; 130 | } 131 | ``` 132 | + 解法二:Reflect.ownKeys() 133 | + 它的返回值等同于 134 | + Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)) 135 | + code 136 | ```javascript 137 | // 判断不为null的对象 138 | let isObject = (obj) => { 139 | return typeof obj === 'object' && obj !== null; 140 | } 141 | let cloneDeep = (obj,hash = new WeakMap()) =>{ 142 | if(!isObject(obj)) return obj; 143 | if(hash.has(obj)) return hash.get(obj); 144 | let newObj = Array.isArray(obj) ? [] : {}; 145 | hash.set(obj,newObj); 146 | Reflect.ownKeys(obj).forEach(key => { 147 | newObj[key] = isObject(obj[key]) ? cloneDeep(obj[key],hash) : obj[key]; 148 | }) 149 | return newObj; 150 | } 151 | ``` 152 | + 或者这样写 153 | ```javascript 154 | // 判断不为null的对象 155 | let isObject = (obj) => { 156 | return typeof obj === 'object' && obj !== null; 157 | } 158 | let cloneDeep = (obj,hash = new WeakMap()) =>{ 159 | if(!isObject(obj)) return obj; 160 | if(hash.has(obj)) return hash.get(obj); 161 | let newObj = Array.isArray(obj) ? [...obj] : {...obj}; 162 | hash.set(obj,newObj); 163 | Reflect.ownKeys(newObj).forEach(key => { 164 | newObj[key] = isObject(obj[key]) ? cloneDeep(obj[key],hash) : obj[key]; 165 | }) 166 | return newObj; 167 | } 168 | ``` 169 | + 进击的巨人 170 | + 破解递归爆栈 171 | ```javascript 172 | // 保持引用关系 173 | function cloneForce(x) { 174 | // ============= 175 | const uniqueList = []; // 用来去重 176 | // ============= 177 | 178 | let root = {}; 179 | 180 | // 循环数组 181 | const loopList = [ 182 | { 183 | parent: root, 184 | key: undefined, 185 | data: x, 186 | } 187 | ]; 188 | 189 | while(loopList.length) { 190 | // 深度优先 191 | const node = loopList.pop(); 192 | const parent = node.parent; 193 | const key = node.key; 194 | const data = node.data; 195 | 196 | // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素 197 | let res = parent; 198 | if (typeof key !== 'undefined') { 199 | res = parent[key] = {}; 200 | } 201 | 202 | // ============= 203 | // 数据已经存在 204 | let uniqueData = find(uniqueList, data); 205 | if (uniqueData) { 206 | parent[key] = uniqueData.target; 207 | break; // 中断本次循环 208 | } 209 | 210 | // 数据不存在 211 | // 保存源数据,在拷贝数据中对应的引用 212 | uniqueList.push({ 213 | source: data, 214 | target: res, 215 | }); 216 | // ============= 217 | 218 | for(let k in data) { 219 | if (data.hasOwnProperty(k)) { 220 | if (typeof data[k] === 'object') { 221 | // 下一次循环 222 | loopList.push({ 223 | parent: res, 224 | key: k, 225 | data: data[k], 226 | }); 227 | } else { 228 | res[k] = data[k]; 229 | } 230 | } 231 | } 232 | } 233 | 234 | return root; 235 | } 236 | 237 | function find(arr, item) { 238 | for(let i = 0; i < arr.length; i++) { 239 | if (arr[i].source === item) { 240 | return arr[i]; 241 | } 242 | } 243 | 244 | return null; 245 | } 246 | ``` 247 | ### 浅拷贝 248 | #### 解法:Object.assign()/ES6解构.../字面量赋值 249 | ```javascript 250 | Object.assign({},obj) 251 | {...obj} 252 | let newObj = obj 253 | ``` 254 | + 局限 255 | + 只能复制一层 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/箭头函数简析.md: -------------------------------------------------------------------------------- 1 | ## ES6箭头函数简析 2 | ### 参考文献 3 | + [箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 4 | ### 定义 5 | + 箭头函数表达式的语法比函数表达式更简洁, 6 | + 并且没有自己的this,arguments,super或new.target。 7 | + 箭头函数表达式更适用于那些本来需要匿名函数的地方, 8 | + 并且它不能用作构造函数。 9 | ### 语法 10 | + (x,y) => { res } 11 | + (x,y) => res 12 | + (x,y) => return res 13 | + (x) => { res } 14 | + x => { res } 15 | + () => { res } 16 | + params => ( { name: alexer} ) 17 | + 返回对象需加括号 18 | + (param1,param2,...rest) => { res } 19 | + 支持 剩余参数 20 | + (param1 = 1,parm2,param = 3) => { res } 21 | + 支持默认参数 22 | + let fn = ( [a,b] = [1,2], {x:c} = {x:a + b}) => a + b + c; 23 | + fn(); // 6 24 | + 支持解构 25 | ### 特点 26 | + 不绑定、没有 this 27 | + 指向的是 定义时的 this,而不是执行时的 this 28 | + 不能用 call()、apply()、bind() 来改变其运行的作用域 29 | + 第一个参数会被忽略 30 | + 不绑定 arguments 31 | + 但可以用[剩余参数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Rest_parameters)实现 32 | + 作为对象的方法时 33 | + this 指向 全局window 34 | + Object.defineProperty() 35 | + get/set的函数用箭头函数时,this.对象属性 == undefined 36 | + 没有 prototype 属性 37 | + var x = () => {}; 38 | + x.prototype // undefined 39 | + 不能用作构造器,和 new 一起用报错 40 | + var x = () => {}; 41 | + new x(); // TypeError: Foo is not a constructor 42 | + 不能用 yield 关键字,不能用作函数生成器(Generator函数) -------------------------------------------------------------------------------- /otherShare/前端知识点总结/js篇/超大数相加.md: -------------------------------------------------------------------------------- 1 | # 超大数相加 2 | #### 解法 3 | ```javascript 4 | let addBigNum = (a,b) => { 5 | let res = '',tmp = 0; 6 | a = a.split(''); 7 | b = b.split(''); 8 | while(a.length || b.length || tmp){ 9 | tmp += ~~a.pop() + ~~b.pop(); 10 | res = (tmp %10)+res; 11 | tmp = tmp > 9; 12 | } 13 | return res.replace(/^0+/,''); 14 | } 15 | ``` -------------------------------------------------------------------------------- /otherShare/前端知识点总结/other/优化网页手段.md: -------------------------------------------------------------------------------- 1 | ## 优化网页手段 2 | ### 参考文献 3 | + [前端性能优化最佳实践](https://csspod.com/frontend-performance-best-practices/#content-ajax-cache) 4 | #### 方法途径 5 | + 页面 6 | + 减少 HTTP 请求数 7 | + 减少 DNS 查询 8 | + 避免重定向 9 | + 缓存 Ajax 请求 10 | + 延迟加载 11 | + 预先加载 12 | + 减少 DOM 元素数量 13 | + 划分内容到不同域名 14 | + 尽量减少 iframe 使用 15 | + 避免 404 错误 16 | + 服务器 17 | + 使用 CDN 18 | + 添加 Expires 或 Cache-Control 响应头 19 | + 启用 Gzip 20 | + 配置 Etag 21 | + 尽早输出缓冲 22 | + Ajax 请求使用 GET 方法 23 | + 避免图片 src 为空 24 | + Cookie 25 | + 减少 Cookie 大小 26 | + 静态资源使用无 Cookie 域名 27 | + CSS 28 | + 把样式表放在 中 29 | + 不要使用 CSS 表达式 30 | + 使用 替代 @import 31 | + 不要使用 filter 32 | + JS 33 | + 把脚本放在页面底部 34 | + 使用外部 JavaScript 和 CSS 35 | + 压缩 JavaScript 和 CSS 36 | + 移除重复脚本 37 | + 减少 DOM 操作 38 | + 使用高效的事件处理 39 | + 图片 40 | + 优化图片 41 | + 优化 CSS Sprite 42 | + 不要在 HTML 中缩放图片 43 | + 使用体积小、可缓存的 favicon.ico 44 | + 移动端 45 | + 保持单个文件小于 25 KB 46 | + 打包内容为分段(multipart)文档 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/other/前端缓存.md: -------------------------------------------------------------------------------- 1 | ## 前端缓存 2 | ### 参考文献 3 | + [HTTP Headers](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers) 4 | + [浏览器缓存机制详解](https://www.cnblogs.com/slly/p/6732749.html) 5 | + [前端缓存最佳实践](https://juejin.im/post/5c136bd16fb9a049d37efc47#heading-7) 6 | ### 浏览器HTTP请求流程 7 | + 第一次请求 8 | + ![](https://images2015.cnblogs.com/blog/1028513/201704/1028513-20170420165022024-1993543549.png) 9 | + 再次请求 10 | + ![](https://images2015.cnblogs.com/blog/1028513/201704/1028513-20170420165136790-834238622.png) 11 | #### 强制缓存 12 | + **Expires** 13 | + HTTP 1.0/绝对时间(当前时间 + 缓存时间) 14 | + 优先级小于 Cache-Control 15 | + **cache-control** 16 | + HTTP1.1 17 | + 属性 18 | + public/private 19 | + no-cache 20 | + no-store 21 | + max-age= 22 | + .... 23 | + 对比 24 | + Pragma 25 | + HTTP1.0的通用首部 26 | + 属性 27 | + no-cache 28 | + 与 Cache-Control:no-cache 一致 29 | #### 协商缓存 30 | + **last-modified** 31 | + 响应头部 32 | + 优先级小于 Etag 33 | + 时间最低单位:秒 34 | + 为第一次请求资源时,服务器返回的字段,表示最后一次更新的时间 35 | + 对应请求头部 36 | + **If-Modified-Since** 37 | + 非第一次请求时,客户端请求携带字段 38 | + 上次 Last-Modified 的值 39 | + 只能用在 GET 或 HEAD 请求中 40 | + 当与 If-None-Match 同时出现时,会被忽略 41 | + 结果 42 | + 修改了,返回200 和 新data 43 | + 未修改,返回304 44 | + **If-Unmodified-Since** 45 | + 请求的资源在指定时间发生了修改,返回 412 46 | + **Etag** 47 | + 资源的实体标识(哈希字符串) 48 | + 响应头部 49 | + 对应请求头部 50 | + **If-Match** 51 | + 用于POST请求时防止空中碰撞 52 | + 即同一时间修改同一文档并同时提交 53 | + 用于检查是否为最新版本 54 | + 否,则说明文档已经被其他人修改,返回412 55 | + **If-None-Match** 56 | + 缓存未更改的资源,请求带上此字段 57 | + 服务器收到后对比资源的Etag 58 | + 匹配,返回304(未修改状态) 59 | + 告诉客户端缓存版本可用 60 | #### 缓存方案 61 | + HTML:使用协商缓存 62 | + CSS&JS&图片:使用强缓存,文件命名带上hash值,时间尽可能长 63 | + 版本更新:文件名加hash 64 | + 代码分包:对不常变的公共独立库打包做持久缓存 65 | #### webpack哈希 66 | + hash 67 | + 只要项目里有文件更改,整个项目构建的hash值都会更改 68 | + 并且全部文件都共用相同的hash值 69 | + chunkhash 70 | + 它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值 71 | + 常用于把一些公共库和程序入口文件区分开,单独打包构建 72 | + 接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响 73 | + contenthash 74 | + chunkhash有个问题: 75 | + 由于index.css被index.js引用了,所以共用相同的chunkhash值 76 | + 如果index.js更改了代码,css文件就算内容没有任何改变,由于是该模块发生了改变,导致css文件会重复构建 77 | + 保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/工程化/webpack 热更新原理.md: -------------------------------------------------------------------------------- 1 | ## webpack 热更新原理 2 | ### 参考文献 3 | + [Webpack HMR 原理解析](https://zhuanlan.zhihu.com/p/30669007) 4 | + [彻底搞懂并实现webpack热更新原理](https://segmentfault.com/a/1190000020310371) 5 | + [Webpack 热更新实现原理分析](https://zhuanlan.zhihu.com/p/30623057) 6 | #### 参考答案 7 | + 图解一 8 | + ![](https://pic1.zhimg.com/80/v2-f7139f8763b996ebfa28486e160f6378_hd.jpg) 9 | + 1、第一步,在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化, 10 | + 根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。 11 | + 2、第二步是 webpack-dev-server 和 webpack 之间的接口交互, 12 | + 而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。 13 | + 3、第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。 14 | + 当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。 15 | + 注意,这儿是浏览器刷新,和 HMR 是两个概念。 16 | + 4、第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。 17 | + 浏览器端根据这些 socket 消息进行不同的操作。 18 | + 当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。 19 | + 5、webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack, 20 | + webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。 21 | + 当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。 22 | + 6、HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求, 23 | + 服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后, 24 | + 该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。 25 | + 7、而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块, 26 | + 在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。 27 | + 8、最后一步,当 HMR 失败后,回退到 live reload 操作, 28 | + 也就是进行浏览器刷新来获取最新打包代码。 29 | + 图解二 30 | + ![](https://user-gold-cdn.xitu.io/2019/9/2/16cf203824359397?w=2088&h=1430&f=jpeg&s=302980) 31 | + webpack-dev-server 解析 32 | + 使用express启动本地服务,当浏览器访问资源时对此做响应。 33 | + express负责搭建请求路由服务。 34 | + 服务端和客户端使用websocket实现长连接 35 | + webpack监听源文件的变化,即当开发者保存文件时触发webpack的重新编译。 36 | + 每次编译都会生成hash值、已改动模块的json文件、已改动模块代码的js文件 37 | + 编译完成后通过socket向客户端推送当前编译的hash戳 38 | + 客户端的websocket监听到有文件改动推送过来的hash戳,会和上一次对比 39 | + 一致则走缓存 40 | + 不一致则通过ajax和jsonp向服务端获取最新资源 41 | + 使用内存文件系统去替换有修改的内容实现局部刷新 42 | + webpack-dev-middleware 43 | + 主要负责构建内存文件系统,把webpack的 OutputFileSystem 替换成 InMemoryFileSystem。 44 | + 同时作为Express的中间件拦截请求,从内存文件系统中把结果拿出来。 45 | + 参考解答一: 46 | + 1、当修改了一个或多个文件; 47 | + 2、文件系统接收更改并通知webpack; 48 | + 3、webpack重新编译构建一个或多个模块,并通知HMR服务器进行更新; 49 | + 4、HMR Server 使用webSocket通知HMR runtime 需要更新,HMR运行时通过HTTP请求更新jsonp; 50 | + 5、HMR运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新。 51 | + 调用 module.hot.accept() 完成热更新 52 | + 参考解答二: 53 | + 1、启动dev-server,webpack开始构建,在编译期间会向 entry 文件注入热更新代码; 54 | + 2、Client 首次打开后,Server 和 Client 基于Socket建立通讯渠道; 55 | + 3、修改文件,Server 端监听文件发送变动,webpack开始编译,直到编译完成会触发"Done"事件; 56 | + 4、Server通过socket 发送消息告知 Client; 57 | + 5、Client根据Server的消息(hash值和state状态),通过ajax请求获取 Server 的manifest描述文件; 58 | + 6、Client对比当前 modules tree ,再次发请求到 Server 端获取新的JS模块; 59 | + 7、Client获取到新的JS模块后,会更新 modules tree并替换掉现有的模块; 60 | + 8、最后调用 module.hot.accept() 完成热更新; -------------------------------------------------------------------------------- /otherShare/前端知识点总结/框架篇/Vue 的响应式原理中 Object.defineProperty 有什么缺陷?.md: -------------------------------------------------------------------------------- 1 | ## Vue 的响应式原理中 Object.defineProperty 有什么缺陷? 2 | ### 为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty? 3 | #### 参考文献 4 | + [Vue为什么不能检测数组变动](https://segmentfault.com/a/1190000015783546#comment-area) 5 | + [抱歉,学会 Proxy 真的可以为所欲为](https://zhuanlan.zhihu.com/p/35080324) 6 | + [阮一峰 - Proxy详解](http://es6.ruanyifeng.com/#docs/proxy) 7 | + [面试官: 实现双向绑定Proxy比defineproperty优劣如何?](https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf) 8 | #### 参考答案 9 | + Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应; 10 | + Vue通过修改数组的八种方法进行了hack处理 11 | + push() 12 | + pop() 13 | + shift() 14 | + unshift() 15 | + splice() 16 | + sort() 17 | + reverse() 18 | + Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。 19 | + Proxy可以劫持整个对象,并返回一个新的对象。 20 | + Proxy有13种劫持操作 21 | + Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。 -------------------------------------------------------------------------------- /otherShare/前端知识点总结/浏览器/cookie 和 token 在什么情况下会被劫持?.md: -------------------------------------------------------------------------------- 1 | ## cookie 和 token 在什么情况下会被劫持? 2 | ### 常见攻击 3 | + CSRF 4 | + Cross-site request forgery(跨站请求伪造) 5 | + 原理是利用用户的身份,执行非用户本身意愿的操作 6 | + 图解 7 | + ![](https://upload-images.jianshu.io/upload_images/6201701-a101b1b5765c4d2c.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) 8 | + 形式 9 | + 图片URL 10 | + 超链接 11 | + Form提交 12 | + 嵌入第三方论坛、文章中 13 | + 防御 14 | + 验证码 15 | + 检验头部Refer字段 16 | + Refer用来记录该HTTP请求的来源地址 17 | + Token 18 | + 浏览器不会自带token 19 | + 过程 20 | + 服务器生成随机数Token 21 | + 并将该随机Token保存在用户Session(或Cookie中) 22 | + 用户在提交(如表单)数据时,该Token会提交到服务器 23 | + 服务器检测提交的Token是否与用户Session(或Cookie)中的是否一致 24 | + XSS攻击 25 | + Cross site script,即跨站脚本 26 | + 形式 27 | + 用户输入没有进行限制或过滤,从而被黑客嵌入恶意代码提交 28 | + 防御 29 | + 设置Cookie的属性为Http only,这样js就无法获取Cookie值 30 | + 对用户输入做过滤和限制 31 | + 提交数据做 Html encode处理 32 | + 去掉