├── docs ├── .nojekyll ├── CNAME ├── note │ ├── basis │ │ ├── regexp.md │ │ ├── object.md │ │ ├── func.md │ │ ├── string.md │ │ ├── array.md │ │ ├── cookie-storage.md │ │ └── async.md │ ├── library │ │ ├── webpack │ │ │ ├── optimization.md │ │ │ ├── plugins.md │ │ │ └── webpack4.md │ │ ├── vue │ │ │ ├── all-posts.md │ │ │ └── skill.md │ │ └── react │ │ │ └── experience-talk.md │ ├── http │ │ ├── get-post.md │ │ ├── url-render.md │ │ ├── cross-domain.md │ │ ├── cache.md │ │ └── ajax.md │ ├── performance │ │ ├── render-page.md │ │ ├── reflow-repaint.md │ │ ├── perf.md │ │ ├── performance.md │ │ ├── h5-perf.md │ │ └── ssr.md │ ├── english │ │ └── 7500-macmillan.md │ ├── specification │ │ ├── aria.md │ │ ├── dtd.md │ │ ├── mime.md │ │ ├── semantic-version.md │ │ └── eslintrc.md │ ├── git │ │ ├── config.md │ │ └── commonly-used.md │ ├── computer │ │ └── concept.md │ ├── algorithm │ │ ├── data-structure.md │ │ ├── queue.md │ │ ├── stack.md │ │ ├── time-space.md │ │ ├── advance-sort.md │ │ ├── basic-sort.md │ │ ├── linked-list.md │ │ ├── bst.md │ │ └── graph.md │ ├── css3 │ │ ├── bfc.md │ │ ├── transform.md │ │ ├── selector.md │ │ └── matrix.md │ ├── security │ │ └── csp.md │ ├── groceries │ │ └── about-interview.md │ ├── h5 │ │ ├── blob.md │ │ └── file-filelist.md │ ├── compatibility │ │ └── compatibility.md │ ├── linux │ │ └── ubuntu-utils.md │ └── dom │ │ ├── dom-event.md │ │ └── dom.md ├── asset │ ├── img │ │ ├── 3.png │ │ ├── 3d.png │ │ ├── he.png │ │ ├── 3d33.png │ │ ├── 3dmr.png │ │ ├── bdjs.jpg │ │ ├── bfs.png │ │ ├── c1.jpeg │ │ ├── csp.png │ │ ├── dfs.png │ │ ├── dxlb.png │ │ ├── hou.png │ │ ├── hsyx.png │ │ ├── ljjg.png │ │ ├── mr1.png │ │ ├── mr2.png │ │ ├── mr3.png │ │ ├── mr4.png │ │ ├── net.png │ │ ├── qian.png │ │ ├── spa.png │ │ ├── ssr.png │ │ ├── sxlb.png │ │ ├── tree.png │ │ ├── zbx.png │ │ ├── zbx2.png │ │ ├── zdxx.png │ │ ├── aaafuck.png │ │ ├── after.png │ │ ├── ajax1.png │ │ ├── ajax2.png │ │ ├── arrow.png │ │ ├── before.png │ │ ├── bianli.png │ │ ├── blob-jr.png │ │ ├── cookie.png │ │ ├── dxxhlb.png │ │ ├── graph.png │ │ ├── ji-xl.png │ │ ├── jisuan.png │ │ ├── juzhen.png │ │ ├── number.png │ │ ├── perf.jpeg │ │ ├── point.png │ │ ├── render.png │ │ ├── sanjiao.png │ │ ├── showpdf.gif │ │ ├── sjccjg.png │ │ ├── sjjg1.png │ │ ├── sxxhlb.png │ │ ├── trans.png │ │ ├── wanqu.png │ │ ├── yiban.png │ │ ├── yuanyin.png │ │ ├── zhichu.png │ │ ├── zuobiao.png │ │ ├── bianhuan2.png │ │ ├── function.png │ │ ├── git-merge.png │ │ ├── hou-plus.png │ │ ├── transform.png │ │ ├── zuobiao2.png │ │ ├── zuobiao3.png │ │ ├── bubble-sort.gif │ │ ├── input-output.png │ │ ├── linjiebiao.png │ │ ├── merge-srot.gif │ │ ├── git-merge-noff.png │ │ ├── insertion-sort.gif │ │ ├── selection-sort.gif │ │ ├── use-the-scene.png │ │ └── git-merge-squash.png │ └── intro.gif ├── README.md └── index.html ├── README.md └── LICENSE /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | kb.zhangzhipeng.net -------------------------------------------------------------------------------- /docs/note/basis/regexp.md: -------------------------------------------------------------------------------- 1 | ## 正则表达式 -------------------------------------------------------------------------------- /docs/note/library/webpack/optimization.md: -------------------------------------------------------------------------------- 1 | ## webpack 优化总结 2 | 3 | #### 提取 manifest 和 runtime -------------------------------------------------------------------------------- /docs/asset/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/3.png -------------------------------------------------------------------------------- /docs/asset/img/3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/3d.png -------------------------------------------------------------------------------- /docs/asset/img/he.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/he.png -------------------------------------------------------------------------------- /docs/asset/intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/intro.gif -------------------------------------------------------------------------------- /docs/asset/img/3d33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/3d33.png -------------------------------------------------------------------------------- /docs/asset/img/3dmr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/3dmr.png -------------------------------------------------------------------------------- /docs/asset/img/bdjs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/bdjs.jpg -------------------------------------------------------------------------------- /docs/asset/img/bfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/bfs.png -------------------------------------------------------------------------------- /docs/asset/img/c1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/c1.jpeg -------------------------------------------------------------------------------- /docs/asset/img/csp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/csp.png -------------------------------------------------------------------------------- /docs/asset/img/dfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/dfs.png -------------------------------------------------------------------------------- /docs/asset/img/dxlb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/dxlb.png -------------------------------------------------------------------------------- /docs/asset/img/hou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/hou.png -------------------------------------------------------------------------------- /docs/asset/img/hsyx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/hsyx.png -------------------------------------------------------------------------------- /docs/asset/img/ljjg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/ljjg.png -------------------------------------------------------------------------------- /docs/asset/img/mr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/mr1.png -------------------------------------------------------------------------------- /docs/asset/img/mr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/mr2.png -------------------------------------------------------------------------------- /docs/asset/img/mr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/mr3.png -------------------------------------------------------------------------------- /docs/asset/img/mr4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/mr4.png -------------------------------------------------------------------------------- /docs/asset/img/net.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/net.png -------------------------------------------------------------------------------- /docs/asset/img/qian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/qian.png -------------------------------------------------------------------------------- /docs/asset/img/spa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/spa.png -------------------------------------------------------------------------------- /docs/asset/img/ssr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/ssr.png -------------------------------------------------------------------------------- /docs/asset/img/sxlb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/sxlb.png -------------------------------------------------------------------------------- /docs/asset/img/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/tree.png -------------------------------------------------------------------------------- /docs/asset/img/zbx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zbx.png -------------------------------------------------------------------------------- /docs/asset/img/zbx2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zbx2.png -------------------------------------------------------------------------------- /docs/asset/img/zdxx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zdxx.png -------------------------------------------------------------------------------- /docs/asset/img/aaafuck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/aaafuck.png -------------------------------------------------------------------------------- /docs/asset/img/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/after.png -------------------------------------------------------------------------------- /docs/asset/img/ajax1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/ajax1.png -------------------------------------------------------------------------------- /docs/asset/img/ajax2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/ajax2.png -------------------------------------------------------------------------------- /docs/asset/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/arrow.png -------------------------------------------------------------------------------- /docs/asset/img/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/before.png -------------------------------------------------------------------------------- /docs/asset/img/bianli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/bianli.png -------------------------------------------------------------------------------- /docs/asset/img/blob-jr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/blob-jr.png -------------------------------------------------------------------------------- /docs/asset/img/cookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/cookie.png -------------------------------------------------------------------------------- /docs/asset/img/dxxhlb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/dxxhlb.png -------------------------------------------------------------------------------- /docs/asset/img/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/graph.png -------------------------------------------------------------------------------- /docs/asset/img/ji-xl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/ji-xl.png -------------------------------------------------------------------------------- /docs/asset/img/jisuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/jisuan.png -------------------------------------------------------------------------------- /docs/asset/img/juzhen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/juzhen.png -------------------------------------------------------------------------------- /docs/asset/img/number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/number.png -------------------------------------------------------------------------------- /docs/asset/img/perf.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/perf.jpeg -------------------------------------------------------------------------------- /docs/asset/img/point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/point.png -------------------------------------------------------------------------------- /docs/asset/img/render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/render.png -------------------------------------------------------------------------------- /docs/asset/img/sanjiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/sanjiao.png -------------------------------------------------------------------------------- /docs/asset/img/showpdf.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/showpdf.gif -------------------------------------------------------------------------------- /docs/asset/img/sjccjg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/sjccjg.png -------------------------------------------------------------------------------- /docs/asset/img/sjjg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/sjjg1.png -------------------------------------------------------------------------------- /docs/asset/img/sxxhlb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/sxxhlb.png -------------------------------------------------------------------------------- /docs/asset/img/trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/trans.png -------------------------------------------------------------------------------- /docs/asset/img/wanqu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/wanqu.png -------------------------------------------------------------------------------- /docs/asset/img/yiban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/yiban.png -------------------------------------------------------------------------------- /docs/asset/img/yuanyin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/yuanyin.png -------------------------------------------------------------------------------- /docs/asset/img/zhichu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zhichu.png -------------------------------------------------------------------------------- /docs/asset/img/zuobiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zuobiao.png -------------------------------------------------------------------------------- /docs/asset/img/bianhuan2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/bianhuan2.png -------------------------------------------------------------------------------- /docs/asset/img/function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/function.png -------------------------------------------------------------------------------- /docs/asset/img/git-merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/git-merge.png -------------------------------------------------------------------------------- /docs/asset/img/hou-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/hou-plus.png -------------------------------------------------------------------------------- /docs/asset/img/transform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/transform.png -------------------------------------------------------------------------------- /docs/asset/img/zuobiao2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zuobiao2.png -------------------------------------------------------------------------------- /docs/asset/img/zuobiao3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/zuobiao3.png -------------------------------------------------------------------------------- /docs/asset/img/bubble-sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/bubble-sort.gif -------------------------------------------------------------------------------- /docs/asset/img/input-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/input-output.png -------------------------------------------------------------------------------- /docs/asset/img/linjiebiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/linjiebiao.png -------------------------------------------------------------------------------- /docs/asset/img/merge-srot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/merge-srot.gif -------------------------------------------------------------------------------- /docs/asset/img/git-merge-noff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/git-merge-noff.png -------------------------------------------------------------------------------- /docs/asset/img/insertion-sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/insertion-sort.gif -------------------------------------------------------------------------------- /docs/asset/img/selection-sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/selection-sort.gif -------------------------------------------------------------------------------- /docs/asset/img/use-the-scene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/use-the-scene.png -------------------------------------------------------------------------------- /docs/asset/img/git-merge-squash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcySunYang/knowledge/HEAD/docs/asset/img/git-merge-squash.png -------------------------------------------------------------------------------- /docs/note/library/vue/all-posts.md: -------------------------------------------------------------------------------- 1 | ## 博客 Vue 文章汇总 2 | 3 | * [Vue2.1.7源码学习](http://hcysun.me/2017/03/03/Vue%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/) 4 | * [探索Vue高阶组件](http://hcysun.me/2018/01/05/%E6%8E%A2%E7%B4%A2Vue%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6/) 5 | -------------------------------------------------------------------------------- /docs/note/http/get-post.md: -------------------------------------------------------------------------------- 1 | ## get 请求和 post 请求的区别 2 | 3 | #### 传递数据的方式 4 | 5 | get 通过 URL 或 cookie 传参 6 | 7 | post 将数据放到请求体(BODY) 中 8 | 9 | #### 传递数据的大小 10 | 11 | get 的URL长度有限制 12 | 13 | post 则可以传递大量的数据 14 | 15 | #### 安全性 16 | 17 | get 传递的参数会暴露在 URL 中 18 | 19 | post 则不会 20 | 21 | 另外注意 GET 请求的参数会保存在浏览器的历史记录中 -------------------------------------------------------------------------------- /docs/note/performance/render-page.md: -------------------------------------------------------------------------------- 1 | ## 浏览器渲染页面的过程 2 | 3 | 浏览器渲染页面的过程如下图: 4 | 5 | 6 | 7 | #### 一、解析HTML创建DOM Tree 8 | 9 | 浏览器解析HTML文档,并构造一颗DOM树(DOM Tree) 10 | 11 | #### 二、解析CSS计算样式数据 12 | 13 | 浏览器构造DOM树的同时,还会解析CSS样式并计算最终的样式数据,生成样式规则。 14 | 15 | #### 三、构造渲染树(Render Tree) 16 | 17 | 根据 `DOM Tree` 和 样式数据构造一颗渲染树(Render Tree) 18 | 19 | 渲染树会忽略不需要渲染的DOM元素(如:head标签、display值为none的元素) 20 | 21 | #### 四、layout布局 22 | 23 | 当渲染树构造完成后,浏览器会对渲染树进行布局,即分配固定的坐标点给DOM元素。 24 | 25 | #### 五、paint绘制 26 | 27 | 布局完成后,浏览器将绘制最终的界面给用户 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## knowledge 2 | 3 | 前端(Not just)工程师终究要掌握的知识 4 | 5 | 访问:[https://kb.zhangzhipeng.net](https://kb.zhangzhipeng.net/#/) 6 | 7 | ## Intro 8 | > 该项目收藏积累了前端(Not just)所应该掌握的知识,包括 JavaScript语言基础、DOM、CSS3、计算机原理、网络、性能、规范以及数学。 9 | 10 | 动图展示部分内容,该文档持续更新。 11 | 12 | ![](./docs/asset/intro.gif) 13 | 14 | ## Contribution 15 | 16 | clone 项目: 17 | 18 | ``` 19 | git clone https://github.com/HcySunYang/knowledge.git 20 | ``` 21 | 22 | 启动web服务: 23 | 24 | ``` 25 | // 你可以使用任何方式,如 26 | http-server -p 9090 27 | ``` 28 | 29 | 访问文档: 30 | 31 | ``` 32 | http://localhost:9090/docs 33 | ``` 34 | 35 | ## Thanks 36 | 37 | 本文档使用开源文档工具 [docute](https://github.com/egoist/docute) -------------------------------------------------------------------------------- /docs/note/english/7500-macmillan.md: -------------------------------------------------------------------------------- 1 | ## 7500核心词汇(整理自《麦克米伦高阶英汉双解词典》) 2 | 3 | ### 7500 核心词汇的意义 4 | 5 | 以下内容摘自知乎书店的免费书籍 [《相见恨晚的英语学习方法》](https://www.zhihu.com/publications/book/119559236)。 6 | 7 | `7500` 核心词汇的意义是什么的,如下图: 8 | 9 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-02-05-150917.jpg) 10 | 11 | * 第一列是单词数 12 | * 第二列是对应单词在英语里出现的频率——也就是你遇到这些单词的可能性 13 | * 第三列是对应单词一些例子 14 | 15 | > 英文中使用频率最高的 `10` 个词竟然占了所有英语 `25%` 的内容,换一种说法——你仅仅需要掌握 `10` 个单词就可以理解英语世界里 `25%` 的内容,更近一步,如果要理解英语世界里一半的内容,你只需要认识 `100` 个单词就够了,如果你再厉害些,掌握了最高频的 `1000` 个单词,你就可以理解 `75%` 的内容了,如果你有 `7000` 的词汇量,你可以理解其中 `90%` 的内容,事实上,由于语境的帮助,剩下 `10%` 的生词你基本可以推测出来。 16 | 17 | 现在感受到这 `7500` 个核心词汇的威力了吗?还等个球?掌握起来,快快快~~~~ 18 | 19 | ### 单词列表 20 | 21 | #### A 22 | 23 | 待录入... -------------------------------------------------------------------------------- /docs/note/library/vue/skill.md: -------------------------------------------------------------------------------- 1 | ## Vue 奇技淫巧 2 | 3 | #### 使用 hook:* 事件监听子组件相应生命周期钩子的触发 4 | 5 | 一图胜千言: 6 | 7 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-06-code%E7%9A%84%E5%89%AF%E6%9C%AC.png) 8 | 9 | #### 如何让所有的后代组件实例都能够访问由根组件注入的选项 10 | 11 | 比如 `Vuex` 注入 `store` 的选项,在所有后代组件中都可以通过 `this.$store` 访问,其实很简单: 12 | 13 | ```js 14 | Vue.mixin({ 15 | beforeCreate() { 16 | this.$store = this.$options.store || (this.$parent && this.$parent.$store) 17 | } 18 | }) 19 | ``` 20 | 21 | 首先对于跟组件来讲,传递给 `Vue` 构造函数的所有选项(包括 `Vue` 提供的选项和自定义选项),都是可以通过 `this.$options.xxx` 访问的,所以如果根实例对象想要访问这个选项,只需要将该选项添加到根实例的某个属性上即可: 22 | 23 | ```js 24 | this.$xxx = this.$options.xxx 25 | ``` 26 | 27 | 但是对于子组件,访问他们自身的 `this.$options.xxx` 是访问不到从根组件注入的选项的,但是可以通过访问之前在父组件实例上定义的属性简介访问: 28 | 29 | ```js 30 | this.$parent && this.$parent.xxx 31 | ``` 32 | 33 | 这样,就使得根组件以及其所有后代组件,都会在他们的实例对象上添加对选项的引用,使得它们全部能够访问由根组件注入进来的选项。 34 | -------------------------------------------------------------------------------- /docs/note/performance/reflow-repaint.md: -------------------------------------------------------------------------------- 1 | ## 重排和重绘 2 | 3 | #### 重排(reflow) 4 | 5 |

重新构造渲染树(Render Tree)的过程,叫做重排

6 | 7 | 当DOM元素的变化影响了几何属性(如:宽高、位置)时,浏览器需要重新计算元素的几何属性,同时其他元素的几何属性也可能受到影响,这个时候浏览器会使渲染树(Render Tree)中受影响的部分失效,并重新构造渲染树,这个过程叫重排。 8 | 9 | > 浏览器在构造渲染树的时候,通常只需要遍历一次 10 | 11 | ###### 重排的触发时机 12 | 13 | * 添加、删除、替换DOM节点 14 | * 改变DOM元素的位置、尺寸、内容 15 | * 浏览器窗口大小发生改变 16 | * 页面初次渲染 17 | 18 | #### 重绘(repaint) 19 | 20 | 浏览器绘制变化的部分到屏幕叫重绘 21 | 22 | 元素的变化不一定触发重排,但一定触发重绘 23 | 24 | #### 渲染树变化队列和刷新 25 | 26 | 浏览器有自己的优化机制,即并不是每次变化都会触发重排,而是将变化缓冲在队列里,当变化达到一定数量后刷新变化队列,触发重排。但是我们在获取一下属性时,浏览器会强制更新变化队列触发重排,因为这些属性需要返回实时的值: 27 | 28 | * offsetTop、offsetLeft、offsetWidth、offsetHeight 29 | 30 | * scrollTop、scrollLeft、scrollWidth、scrollHeight 31 | 32 | * clientTop、clientLeft、clientWidth、clientHeight 33 | 34 | * getComputeStyle()、currentStyle(IE) 35 | 36 | #### 性能优化(最小化重排和重绘) 37 | 38 | 在修改元素多种样式属性时,使用替换class名的方式 代替 使用js脚本逐个修改样式 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/note/performance/perf.md: -------------------------------------------------------------------------------- 1 | ## 性能 2 | 3 | 从两方面思考 `web` 的性能问题:**页面纬度**、**运行纬度** 4 | 5 | * **页面纬度:**页面的加载所经历的过程,思考在这个过程中有哪些是需要重点关注并能做一些优化的事情。 6 | * **运行维度:**通常指动画(js动画、css动画、其他动画等等),以及交互性能问题等。 7 | 8 | #### 需要深入思考的问题 9 | 10 | * 页面到底是什么时间被渲染出来? 11 | 12 | 答:??? 13 | 14 | * 如何测量、上报、提升性能? 15 | 16 | 答:如下图所示: 17 | 18 | 19 | 20 | 开发者通常通过 `DevTools` 来分析,用 `WebPerf APIS` 来获取和上报数据。 21 | 22 | 对于上报,开源的工具可以使用 [grafana](https://github.com/grafana/grafana),或者使用公司自建的上报系统。 23 | 24 | 25 | 26 | #### 与性能相关的资源链接 27 | 28 | * [w3c 性能工作小组](https://github.com/w3c/web-performance) 29 | 30 | `w3c 性能工作小组` 会制定相应的指标规范,以及各个指标的意义。浏览器会根据该规范收集数据并暴露给开发者,开发者需要做的就是获取浏览器所暴露出来的信息。 31 | 32 | * [我理解的前端性能 & 优化](https://zhuanlan.zhihu.com/p/33825610) 33 | 34 | * [Why First Paint as Web Perf API?](https://docs.google.com/document/d/1wdxSXo_jctZjdPaJeTtYYFF-rLtUFxrU72_7h9qbQaM/edit) 35 | 36 | ## 好东西 37 | 38 | * [grafana](https://github.com/grafana/grafana) -------------------------------------------------------------------------------- /docs/note/specification/aria.md: -------------------------------------------------------------------------------- 1 | ## 无障碍访问与ARIA 2 | 3 | #### 无障碍访问 4 | 5 |

概念:通俗的讲就是一句话,没有任何因素能够对你访问该网站产生障碍

6 | 7 | 如果鼠标坏了,那么使用键盘应该能够正常访问,反之亦然,存在视力障碍的用户应该能够通过声音访问该网站,这就是所谓的无障碍访问。 8 | 9 | 例如使用“屏幕阅读器“访问网站时,软件会阅读屏幕的内容以声音的方式传达给视力障碍的用户,这个时候,我们的HTML页面要保证可访问性,例如该使用`` 标签的地方使用 `` 标签,如果你想要使用 `` 标签充当按钮,记得要在 `` 标签上添加 `role="button"` 属性,使得屏幕阅读器准确的访问网站,不至于误导用户。 10 | 11 | #### ARIA 12 | 13 |

ARIA(Accessible Rich Internet Applications[可访问的富互联网应用])

14 | 15 | [ARIA规范](https://www.w3.org/TR/html-aria/) 16 | 17 | ARIA 分为三部分: 18 | 19 | * role属性 20 | 21 | ```css 22 | role="tab", 23 | role="button", 24 | role="radio", 25 | role="checkbox", 26 | role="link", 27 | … 28 | ``` 29 | 30 | * aria属性 31 | 32 | ```css 33 | aria-haspopup, 34 | aria-label, 35 | aria-owns 36 | … 37 | ``` 38 | 39 | * aria状态属性 40 | 41 | ```css 42 | aria-checked, 43 | aria-checked,, 44 | aria-selected, 45 | aria-expanded, 46 | aria-hidden, 47 | aria-invalid, 48 | … 49 | ``` -------------------------------------------------------------------------------- /docs/note/specification/dtd.md: -------------------------------------------------------------------------------- 1 | ## 文档模式 2 | 3 | #### 严格模式与混杂模式 4 | 5 | ###### 不同文档模式的由来 6 | 7 | 相信了解过浏览器或浏览器厂商发展历史的同学都知道,在浏览器的发展初期,是没有什么标准可言的,各大浏览器厂商各自实现一套解析文档的方式,这对于开发者来说就如同灾难一样,开发者需要针对不同厂商的浏览器做兼容处理。慢慢的人们开始注意到标准的重要性,并成立了规范组织制定相应的标准,各个浏览器厂商也开始向标准靠拢,但随之而来的问题就是,如果一味的向标准靠拢,那必然会导致一个问题:老旧的网站将无法正常显示。为了做到向后兼容,浏览器厂商就保留了原有的文档解析方式,也就是现在所说的 `混杂模式`,同时将向标准靠拢的解析方式称为 `标准模式`,又称 `严格模式`。 8 | 9 | 也就是说,两种模式所代表的是浏览器解析文档的方式。而至于如果开启这两种模式,可以使用 `` 标签。 10 | 11 | #### 文档类型 12 | 13 | ###### 14 | 15 | `` 用来声明文档类型,目的告诉浏览器使用哪种模式去解析文档。说白了就是告诉浏览器在解析文档的时候是采用 `混杂模式` 还是 `标准模式`。 16 | 17 | ###### 开启 混杂模式(quirks mode) 18 | 19 | 如果浏览器发现在文档开始处没有 `文档类型声明`,即没有 `` 标签,那么浏览器默认会使用混杂模式解析文档,当然不写 `` 标签是极其不推荐的方式。 20 | 21 | ###### 开启 标准模式(standards mode) 22 | 23 | ```html 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | 以上三种的任意一种都可以触发标准模式 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 HcySunYang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /docs/note/git/config.md: -------------------------------------------------------------------------------- 1 | ## git config 配置项 2 | 3 | #### core.autocrlf 4 | 5 | 是否自动将 LF 转换为 CRLF,当 git 提示 `warning: LF will be replaced by CRLF` 时,可以将该选项设为 `false` 关闭提示。 6 | 7 | ```sh 8 | git config core.autocrlf false 9 | ``` 10 | 11 | #### core.filemode 12 | 13 | 是否忽略文件权限 14 | 15 | ```sh 16 | # 忽略文件全向 17 | git config core.filemode false 18 | ``` 19 | 20 | #### credential.helper 21 | 22 | 认证助手,当我们使用HTTPS方式clone项目时,每次 pull、push 都会提示输入密码,为了不每次都输入密码,就需要配置 `credential.helper` 选项。 23 | 24 | ```sh 25 | # cache 将凭据存储在内存中 26 | git config credential.helper cache 27 | # store 将凭据保存在磁盘上 28 | git config credential.helper store 29 | ``` 30 | 31 | 操作步骤如下: 32 | 33 | ###### 在家目录创建并编辑 .git-credentials 文件 34 | 35 | ```sh 36 | cd ~ 37 | touch .git-credentials 38 | vim .git-credentials 39 | ``` 40 | 41 | ###### 把如下内容写入.git-credentials文件中,保存并退出 42 | 43 | ``` 44 | # {username} 你的git账户名 45 | # {password} 你的git密码 46 | # example.com 你的git仓库域名 47 | # 例子:https://hcysunyang:12345678@github.com 48 | 49 | https://{username}:{password}@example.com 50 | ``` 51 | 52 | ###### 设置凭据存储方式 53 | 54 | ```sh 55 | git config credential.helper store 56 | ``` 57 | 58 | #### user.name 59 | 60 | 配置git用户名 61 | 62 | #### user.email 63 | 64 | 配置用户邮箱 -------------------------------------------------------------------------------- /docs/note/http/url-render.md: -------------------------------------------------------------------------------- 1 | ## 从输入URL到页面加载完成的过程中都发生了什么事情? 2 | 3 | #### 解析URL 4 | 5 | 当你在浏览器中输入URL并敲回车之后,浏览器会把URL分成几部分: 6 | 7 | * 1、协议:从计算机获取资源的方式,常见的HTTP、FTP等 8 | * 2、网络地址:域名或者IP,指示网络中的哪一台计算机 9 | * 3、资源路径:指示在该计算机上获取哪一个资源 10 | 11 | #### DNS域名解析 12 | 13 | 当浏览器发现网络地址并不是IP,而是域名的时候,浏览器会向DNS服务器发送请求,查找域名对应的IP,如果该DNS服务器没有找到该域名对应的IP,那吗会向上级请求,直到根节点,结果只有两个:要吗找到了,要吗找不到。 14 | (你电脑里的网络设置里面有DNS的服务器IP) 15 | 16 | 扩展:例如百度、淘宝这些访问量及其大的网站,在DNS域名解析时,在不同的区域或不同网络下解析出来的IP可能是不同的,这就涉及负载均衡的第一步:在DNS解析域名时,将你的访问分配到不同的入口,同时尽可能保证你访问的入口是在所有入口中可能较快的一个。 17 | 18 | #### 确定端口 19 | 20 | 如果网络地址中不包含端口,那么会使用协议默认的端口。HTTP协议默认端口是80,HTTPS协议默认端口是443 21 | 22 | #### 发送HTTP请求 23 | 24 | 当浏览器对域名完成一系列的解析之后,就会构建一个HTTP请求,HTTP属于应用层协议,真正的数据传输是传输层协议TCP完成的,这就涉及到TCP建立连接的“三次握手”: 25 | 26 | * 1、发送端发送带有 SYN 标志的数据包给接收端,并在一定的延迟时间内等待回复 27 | * 2、接收端收到数据包后传回一个带有 SYN/ACK 标志的数据包以示确认传达信息 28 | * 3、发送端收到信息后还会发送一个带有 ACK 标志的数据包给接收端以示握手成功,连接建立完成 29 | 30 | #### 服务器处理并响应请求 31 | 32 | 服务器收到客户端发送的HTTP请求后,分析请求报文,并查找相应的请求资源,并返回响应报文。 33 | 34 | 响应报文中包含一个重要的信息,状态吗: 35 | 36 | 常见的 4 开头的状态吗一般表示请求出了问题,如 404 表示请求的资源不存在 37 | 3 开头的状态吗一般表示重定向,如 301表示永久重定向 38 | 5 开头的状态吗一般表示服务器出了问题,如 500 表示服务器出错 39 | 2 开头的一般代码成功,如 200 40 | 41 | #### 页面渲染 42 | 43 | 请查看 [浏览器渲染页面的过程](/note/performance/render-page) 44 | 45 | -------------------------------------------------------------------------------- /docs/note/performance/performance.md: -------------------------------------------------------------------------------- 1 | ## 尽量少用 innerHTML 2 | * 原因:因为 innerHTML 会调用一个沉重且高消耗的HTML解析器。 3 | 4 | ## 缓存DOM操作 5 | * `NodeList` 或 `HTMLCollection` 由于这两个集合的实时性原因,每次访问此集合都会重新查询文档。这将导致性能问题,应该做缓存处理 6 | 7 | ## JavaScript默认是同步解析的 8 | * 当DOM在解析时遇到 ` 80 | 81 | ``` 82 | 83 | 效果如下: 84 | 85 | 86 | 87 | 注意: 88 | 89 | 以上代码在火狐(53.0.3)中可以在本地环境运行,在Chrome(59.0.3071.115)和Safari中必须要在服务器环境下,才能正常运行。 90 | 91 | 值得关注的是,除此应用之外,由于 `File` API继承且基于 `Blob`,所以对于 `File` 的应用,也算是对 `Blob` 的应用。 92 | 93 | -------------------------------------------------------------------------------- /docs/note/compatibility/compatibility.md: -------------------------------------------------------------------------------- 1 | ###### 一、IE6/7/8 不支持HTML5标签 2 | ``` 3 | 解决方案一: 4 | 可以使用 document.createElement() 动态创建HTML5标签,如 header footer 等,但是在 IE6/7/8 看来,这种方法创建的标签属于自定义标签,自定义标签默认为内联元素,所以为了将一些元素转为块元素,还需加上如下js代码 5 | 6 | header{ 7 | display: block; 8 | } 9 | 解决方案二: 10 | 使用一套成熟的js库,github地址:https://github.com/aFarkas/html5shiv 11 | ``` 12 | 13 | ###### 二、IE6/7 不支持 BFC 14 | 15 | 这导致 在标准浏览器中原本能够触发BFC的属性,不能在IE6/7中正常触发。会有很多奇怪的问题,比如两个float的元素的内容会互相影响 等等。 16 | 17 | 不过IE有自己的一个东西,叫做 haslayout , 18 | 19 | 但以上描述的问题在IE6/7中基本可以靠 触发IE的haslayout解决,如:zoom:1 20 | 21 | ###### 三、IE6下,如果子元素的宽高超过父级元素的宽高,父级元素被子元素撑开。 22 | 23 | 不过,一般不会有人这么写,更不建议这么写 24 | 25 | ###### 四、IE6不支持 display: inline-block; 26 | 27 | ``` 28 | 解决方法: 29 | { 30 | *display: inline; 31 | zoom: 1; 32 | } 33 | ``` 34 | 35 | ###### 五、IE6下div最小高度 19px 36 | ``` 37 | 解决办法: 38 | { 39 | overflow: hidden; 40 | } 41 | ``` 42 | 43 | ###### 六、IE6下块儿元素浮动会产生双倍 margin 值 44 | ``` 45 | 解决办法: 46 | 将块儿元素转为行内元素,即添加 display: inline; 47 | ``` 48 | 49 | ###### 七、li下元素都浮动,那么在IE6/7下,每个li元素间会有4px的间隔 50 | ``` 51 | 解决方案: 52 | 给 li 元素添加 vertical-align: top; 53 | ``` 54 | 55 | ###### 八、IE6文字溢出bug,两个浮动元素中间有内敛元素或者注释节点,且其中一个浮动元素的宽度和父级宽度相差小于3px。那么这个浮动元素内的文本会被复制出一个“小尾巴” 56 | 57 | ###### 九、IE6/7 下,父级的 overflow: hidden; 包不住拥有相对定位(position: relative;) 的子元素 58 | ``` 59 | 解决方案: 60 | 给父级也加相对定位 position: relative; 61 | ``` 62 | 63 | ###### 十、在IE6下绝对定位元素父级的宽高是奇数时,绝对定位元素的right和bottom值会有1px的偏差。 64 | ``` 65 | 解决办法:避免绝对定位元素的父级宽高是奇数 66 | ``` 67 | 68 | ###### 十一、在IE6下,绝对定位元素和浮动元素并列,绝对定位元素消失 69 | ``` 70 | 解决办法:不让其同级就可以了 71 | ``` 72 | 73 | ###### 十二、IE6下input表单元素上下各有1px的间隙。 74 | ``` 75 | 解决办法,给input元素加浮动 76 | ``` 77 | 78 | ###### 十三、IE6输入类型的表单背景图随着用户输入而移动的bug 79 | ``` 80 | 解决办法:在设置background属性时加 fixed:(background: url(img/a.jpg) no-repeat fixed;) 81 | ``` 82 | 83 | ###### 十四、IE8不会重绘伪类元素,除非修改伪类元素的 content 值 84 | ``` 85 | 解决办法:定义新样式,修改伪类元素content的值。 86 | ``` 87 | 88 | ###### 十五、IE9 不支持 dataset 89 | 90 | ``` 91 | 解决办法:使用 getAttribute/setAttribute/removeAttribute/hasAttribute 代替 92 | ``` 93 | 94 | ###### 十六、IE8及以下版本不支持 pageX/Y 95 | 96 | ``` 97 | 解决办法:可以通过 clientX/Y + scrollLeft/Top 计算 98 | ``` 99 | 100 | ###### 十七、IE8及以下不支持 metaKey 101 | 102 | ``` 103 | event.shiftKey // 按住 shift 键为true 104 | event.ctrlKey // 按住 Ctrl 键为true 105 | event.altKey // 按住 alt 键为true 106 | event.metaKey // Mac下按住 command 键为true,windows 按住 Windows 键为true 107 | ``` 108 | 109 | ###### 十八、IE8及以下不支持 DOMcontentLoaded 110 | 111 | ``` 112 | 解决办法:使用 window.onload 代替 113 | ``` 114 | 115 | ###### 十九、部分 Android 手机中,键盘事件获取 event.keyCode 异常 116 | 117 | ``` 118 | 使用 textInput 事件,根据按键内容区分按键 119 | ``` -------------------------------------------------------------------------------- /docs/note/css3/transform.md: -------------------------------------------------------------------------------- 1 | ## transform 2D 2 | 3 | #### 旋转 4 | * 单位:度数单位,常用角度(deg) 5 | * 示例: 6 | ```css 7 | transform: rotate(20deg); 8 | ``` 9 | 10 | #### 斜切 11 | * 单位:度数单位,常用角度(deg) 12 | * 示例: 13 | ```css 14 | transform: skew(20deg, 40deg); 15 | transform: skewX(20deg); 16 | transform: skewY(20deg); 17 | ``` 18 | 19 | #### 缩放 20 | * 单位:倍数,无需指定单位 21 | * 示例: 22 | ```css 23 | transform: scale(2, 1); 24 | transform: scaleX(2); 25 | transform: scaleY(2); 26 | ``` 27 | 28 | #### 位移 29 | * 单位:长度单位,常用(px) 30 | * 示例: 31 | ```css 32 | transform: translate(100px, 100px); 33 | transform: translateX(100px); 34 | transform: translateY(100px); 35 | ``` 36 | 37 | #### transform-origin 38 | * 描述:设置变换基点 39 | * 示例: 40 | ```css 41 | transform-origin: 关键字/百分比/距离单位; 42 | ``` 43 | 44 |

坑点:通过js的方式来设置变换基点(dom.style.WebkitTransformOrigin = '0 0';),不能快速同步给变换,在css中没问题

45 | 46 | #### 执行顺序 47 | 48 | 先写后执行,应该说 先写的变换 会影响 后边的变换 49 | 50 | #### 矩阵 matrix(a, b, c, d, e, f) 51 | * 默认值:matrix(1, 0, 0, 1, 0, 0) 52 | 53 | rotate / skew / scale / translate 等变换都是通过矩阵实现的,只不过是浏览器给我们封装好的函数 54 | 55 | * 计算方法 56 | ``` 57 | x 轴位移: 58 | e = e + x 59 | y 轴位移: 60 | f = f + y 61 | 62 | x 轴斜切: 63 | c = Math.tan(Math.PI / 180 * x) 64 | y 轴斜切: 65 | b = Math.tan(Math.PI / 180 * y) 66 | 67 | x 轴缩放: 68 | a = a * x 69 | c = c * x 70 | e = e * x 71 | y 轴缩放: 72 | b = b * y 73 | c = c * y 74 | f = f * y 75 | 旋转 76 | a = Math.cos(Math.PI / 180 * deg) 77 | b = Math.sin(Math.PI / 180 * deg) 78 | c = -Math.sin(Math.PI / 180 * deg) 79 | d = Math.cos(Math.PI / 180 * deg) 80 | ``` 81 | 82 | ## transform 3D 83 | 84 | #### perspective 85 | * 描述:设置景深 86 | * 单位:无 87 | * 示例: 88 | ```css 89 | perspective: 200; 90 | ``` 91 | * 注意:该属性要加给需要做3D变换的父级元素 92 | 93 | #### perspective-origin 94 | * 描述:景深基点,可以理解为视线方向 95 | * 示例: 96 | ```css 97 | perspective-origin: 关键字/距离单位; 98 | ``` 99 | 100 | #### transform-style 101 | * 描述:当元素做3D变换时是否保留子元素的3D变换 102 | * 示例: 103 | ```css 104 | transform-style: flat; /* 不保留 */ 105 | transform-style: preserve-3d; /* 保留 */ 106 | ``` 107 | 108 | #### backface-visibility 109 | * 描述:隐藏背面 110 | * 示例: 111 | ```css 112 | backface-visibility: visible; /* 可见 */ 113 | backface-visibility: hidden; /* 不可见 */ 114 | ``` 115 | 116 | #### 3D 旋转 117 | * 单位:度数单位,常用角度(deg) 118 | * 示例: 119 | ```css 120 | /* 围绕Z轴旋转 */ 121 | transform: rotateZ(); 122 | /* XYZ结合写法 */ 123 | transform: rotate3D(); 124 | ``` 125 | 126 | #### 3D 位移 127 | * 单位:长度单位,常用单位(px) 128 | * 示例: 129 | ```css 130 | /* Z轴位移 */ 131 | transform: translateZ(); 132 | /* XYZ结合写法 */ 133 | transform: translate3D(); 134 | ``` 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/note/h5/file-filelist.md: -------------------------------------------------------------------------------- 1 | ## File 和 FileList 2 | 3 | #### 简介 4 | 5 | `File` API 顾名思义,是与文件相关的API,它提供了文件信息,并允许JavaScript访问文件的内容。 6 | 7 | `File` API 继承了 `Blob`,如下证明: 8 | 9 | ```js 10 | // Chrome 中 11 | File.__proto__ // Blob 12 | ``` 13 | 14 | ##### File 对象出现在哪里 15 | 16 | ###### FileList 17 | 18 | 通过 `` 元素选择文件时的 `FileList` 对象访问,`FileList` 对象中的每个元素都是一个 `File` 对象实例。 19 | 20 | ```html 21 | 22 | 23 | 29 | 30 | ``` 31 | 32 | ###### DataTransfer 33 | 34 | 拖放事件对象的 `dataTransfer.files` 属性中的每一个元素都是 `FIle` 对象实例。 35 | 36 | #### File 构造函数 37 | 38 | `File` 是一个构造函数,使用方式如下: 39 | 40 | ```js 41 | new Blob(bits, name[, options]) 42 | ``` 43 | 44 | 参数: 45 | * `{array} bits` bits是一个数组,可以包含 `ArrayBuffer`、`ArrayBufferView`、`Blob` 或者 `DOMString`。 46 | * `{string} name` 文件的名字或者路径 47 | * `{object} options` 用来设置文件属性的选项,有两个值可以设置:`type` 和 `lastModified` 48 | * `{string} type` 内容的 `MIME type`。 49 | * `{number} lastModified` 最后的修改时间,毫秒数。 50 | 51 | 示例: 52 | ```js 53 | // 验证 File 继承 Blob 54 | var file = new File(["foo"], "foo.txt", { 55 | type: "text/plain", 56 | }); 57 | 58 | file instanceof Blob // true 59 | ``` 60 | 61 | ##### File 对象的属性 62 | 63 | `File` 继承了 `Blob`,所以 `File` 示例对象的属性中包含继承自 `Blob` 的属性,比如:`size` 和 `type`。 64 | 65 | ###### lastModified 66 | 67 | * `{Number} lastModified` 文件的最后修改时间,毫秒数 68 | 69 | ###### lastModifiedDate 70 | 71 | * `{Date} lastModifiedDate` 文件的最后修改日期 72 | 73 | ###### name 74 | 75 | * `{string} name` 文件的名称 76 | 77 | ###### size 78 | 79 | * `{number} size` 文件的尺寸 80 | 81 | ###### type 82 | 83 | * `{string} type` 文件的 `MIME type` 84 | 85 | ###### webkitRelativePath 86 | 87 | * `{string} webkitRelativePath` 文件的相对路径 88 | 89 | ##### File 对象的方法 90 | 91 | `File` 实例有三个方法,不过都不是标准的(Non-standard),且已经不推荐使用了。推荐使用 `FileReader` 实例对象的对用方法替代: 92 | 93 | ###### getAsBinary() 94 | 95 | 使用 `FileReader` 对象的 `readAsBinaryString()` 代替 96 | 97 | ###### getAsDataURL() 98 | 99 | 使用 `FileReader` 对象的 `readAsDataURL()` 代替 100 | 101 | ###### getAsText() 102 | 103 | 使用 `FileReader` 对象的 `readAsText()` 代替 104 | 105 | ## FileList 106 | 107 | `FileList` 对象是一个类数组对象,拥有 `length` 属性,对象的每个元素都是一个 `File` 对象实例,比如: 108 | 109 | ```html 110 | 111 | 112 | 119 | ``` 120 | 121 | 其中 `this.files` 就是一个 FileList 对象实例,可以想数组一样访问其元素 `this.files[0]`,代表一个 `File` 对象实例。 122 | 123 | 也可以通过 `this.files.item(0)` 来访问其元素。 124 | 125 | -------------------------------------------------------------------------------- /docs/note/algorithm/stack.md: -------------------------------------------------------------------------------- 1 | ## 栈 2 | 3 | #### 概念 4 | 5 | 栈是一种特殊的列表,栈内的元素只能通过列表的一端访问,这一端称为栈顶,栈是一种后入先出(last-in-first-out)的数据结构。 6 | 对栈的操作有以下几种: 7 | 8 | * 1、压入栈(将一个元素压入栈顶) 9 | * 2、弹出栈(弹出栈顶的元素) 10 | * 3、预览栈顶(获取但不弹出栈顶的元素) 11 | * 4、清空栈(清空栈内所有元素) 12 | * 5、获取栈内元素的个数 13 | 14 | #### JavaScript实现 15 | 16 | 我们知道,JavaScript数组原生提供了栈方法,如:`push`、`pop`、`unshift`、`shift` 等等,所以我们既可以用数组来模拟栈,又可以用数组来模拟队列,但正因为这样,数组既不能严格的作为栈使用,也不能严格的作为队列使用,所以我们有必要手动封装严格的栈的定义。 17 | 18 | ###### 栈的方法和属性 19 | 20 | 根据对栈的操作定义,我们可以总结栈所拥有的属性和方法 21 | 22 | * 属性 23 | * 无 24 | * 或者你自定义需要的属性 25 | * 方法 26 | * push(element) 27 | * pop() 28 | * peek() 29 | * clear() 30 | * length() 31 | 32 | ###### 代码实现 33 | 34 | ```js 35 | function Stack () { 36 | this.store = [] 37 | } 38 | Stack.prototype = { 39 | constructor: Stack, 40 | push: function (element) { 41 | return this.store.push(element) 42 | }, 43 | pop: function () { 44 | return this.store.pop() 45 | }, 46 | peek: function () { 47 | return this.store[this.length() - 1] 48 | }, 49 | clear: function () { 50 | this.store.length = 0 51 | }, 52 | length: function () { 53 | return this.store.length 54 | } 55 | } 56 | ``` 57 | 58 | 上面的代码是一段极简的实现,栈的底层数据结构是使用的数组:`this.store`,我们可以对上面的代码进行如下测试: 59 | 60 | ```js 61 | var stack = new Stack() 62 | 63 | console.log(stack.peek()) // undefined 64 | 65 | stack.push(1) 66 | stack.push(2) 67 | stack.push(3) 68 | 69 | console.log(stack.peek()) // 3 70 | 71 | stack.pop() 72 | 73 | console.log(stack.peek()) // 2 74 | console.log(stack.length()) // 2 75 | 76 | stack.clear() 77 | 78 | console.log(stack.length()) // 0 79 | ``` 80 | 81 | #### 栈的应用 82 | 83 | ###### 回文问题 84 | 85 | “回文” 简单的说就是正着读和反着读是一样的,比如 `abcba`,`12321` 等等。那么问题来了,如何确定一个字符串是不是回文呢? 86 | 87 | 有的同学可能已经想到了,js数组有一个方法 `reverse` ,用来反转一个数组的顺序,于是我们可以借助数组以及数组的 `reverse` 方法来判断: 88 | 89 | ```js 90 | var str = '12321' 91 | var reStr = str.split('').reverse().join('') 92 | if (str === reStr) { 93 | // 是回文 94 | } 95 | ``` 96 | 97 | 那么问题来了,如果js在语言层面没有给我们提供 `reverse` 方法,需要我们手动封装怎么办呢?这,就用到了栈的知识,接下来我们手动封装 `reverse` 方法,用来对数组进行反转: 98 | 99 | ```js 100 | function reverse (arr) { 101 | var stack = new Stack() 102 | for (var i = 0; i < arr.length; i++) { 103 | stack.push(arr[i]) 104 | } 105 | var reArr = [] 106 | while (stack.length() > 0) { 107 | reArr.push(stack.pop()) 108 | } 109 | 110 | return reArr 111 | } 112 | ``` 113 | 114 | 然后,我们可以这样使用我们的 `reverse` 方法: 115 | 116 | ```js 117 | var str = '12321' 118 | var reStr = reverse(str.split('')).join('') 119 | if (str === reStr) { 120 | // 是回文 121 | } 122 | ``` 123 | 124 | 原理其实很简单,我们把每个元素按照原来的顺序依次入栈,然后在依次将元素从栈顶弹出,那么弹出后元素的顺序应该是原来元素顺序的反转。 125 | 126 | 除了判断回文,数制转换也可以应用栈,判断表达式中的括号是否匹配等等,都可以应用到栈,其原理无非是利用了 `入栈` 与 `出栈` 后的顺序变化,以及栈内元素的数量来作出相应的判断。另外有些问题需要多个栈配合来解决,有兴趣的可以多在网上搜一搜用栈解决的一些问题。 127 | -------------------------------------------------------------------------------- /docs/note/git/commonly-used.md: -------------------------------------------------------------------------------- 1 | ## git常用命令及技巧 2 | 3 | #### 删除远程分支 4 | 5 | ```sh 6 | git push origin --delete [远程分支名称] 7 | ``` 8 | 9 | #### git merge --no-ff 10 | 11 | 默认情况下,如果没有冲突那么 `git merge` 采用 `fast-forward`(快进) 的模式进行合并,所谓 `fast-forward` 指的是:不产生新的提交历史,直接移动 `HEAD` 至要合并的分支,显而易见的缺点是合并历史信息不清晰,如下图(一条线): 12 | 13 | 14 | 15 | 所以为了保留分支的 `commit` 历史记录,我们可以采用 `--no-ff` 选项,这样合并后的历史记录图类似于这样: 16 | 17 | 18 | 19 | #### git merge --squash 20 | 21 | `--squash` 选项用于压缩多个“无用”的 `commit` 为一个 `commit`,效果类似下图: 22 | 23 | 24 | 25 | #### fork 的仓库与远程仓库同步 26 | 27 | 以 `Github` 为例 28 | 29 | * 1、设置 `upstream` 为原仓库地址 30 | 31 | ```sh 32 | git remote add upstream https://github.com/some_project/some_project.git 33 | ``` 34 | 35 | * 2、获取原仓库的更新到本地 36 | 37 | ```sh 38 | git fetch upstream 39 | ``` 40 | 41 | * 3、合并更新,以 `master` 分支为例 42 | 43 | ``` 44 | git checkout master 45 | git merge upstream/master 46 | ``` 47 | 48 | #### 修改提交历史消息 49 | 50 | ##### 修改最近一次提交的历史 51 | 52 | ```sh 53 | git commit --amend -m "New commit message" 54 | ``` 55 | 56 | 注意,在执行上面的命令修改提交历史之前,要保证本地没有暂存的修改,否则改修改也会被提交(当你忘记提交一些内容的时候,利用这个特性补充) 57 | 58 | ##### 修改更早(最近一次提交之前的提交)的提交历史 59 | 60 | `git` 没有提供直接修改任意一次提交历史的功能,但是我们可以通过 `rebase` 间接来完成这个任务,我们用一个例子说明。 61 | 62 | 通过 `git log` 命令查看提交历史,假设如下: 63 | 64 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-123440.jpg) 65 | 66 | 上图中展示了过去三次提交,假设这三次提交的历史消息不正确,需要修改,那么首先你要执行: 67 | 68 | ```sh 69 | git rebase -i HEAD~2 70 | ``` 71 | 72 | `-i` 选项用来告诉 `git` 我们是在交互式的 `rebase`(变基)。`HEAD~2` 中的 `2` 代表修改最近两次提交(注意:`2` 其实指的是第三次提交,也就是你要修改的最近几次提交的父提交),你当然也可以写 `3`、`4` 随你便。 73 | 74 | 结果如下图: 75 | 76 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-124043.jpg) 77 | 78 | 可以看到 `git rebase -i HEAD~2` 这条命令把我们带到了文本编辑器。接下来你只要将需要修改的提交前面的 `pick` 改为 `edit` 即可,然后保存退出: 79 | 80 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-124910.jpg) 81 | 82 | 保存退出之后,可以看到如下图内容: 83 | 84 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-125114.jpg) 85 | 86 | 通过上图可以看到,`git` 已经给了我们非常好的提示了,它告诉我们可以使用 `git commit --amend` 修改本次提交历史,并且修改完成之后可以使用 `git rebase --continue` 语句应用该提交并继续执行交互式行为,那么我们就尝试一下执行 `git commit --amend`,结果如下图: 87 | 88 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-125454.jpg) 89 | 90 | 可以看到,把我们带到了编辑器,这时我们就可以修改提交信息了,修改完成之后保存退出,接着执行 `git rebase --continue`,如下图: 91 | 92 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-125717.jpg) 93 | 94 | 可以看到,交互式行为执行到了下一步,因为我们要修改两个提交的提交历史(因为我们把上两次提交的 `pick` 都改成了 `edit`),这时重复以上步骤,即执行 `git commit --amend` 修改历史,完成后执行 `git rebase --continue` 如下图: 95 | 96 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-130024.jpg) 97 | 98 | 由于已经没有可交互内容,那么此时将自动退出交互模式,回到当前分支。现在我们通过 `git log` 命令查看提交历史,可以发现我们的历史提交信息成功修改了: 99 | 100 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-03-01-130258.jpg) 101 | 102 | #### 常用团队合作操作规范 103 | 104 | ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-02-20-113110.jpg) -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Knowledge 2 | 3 | 知识梳理 4 | 5 | ## 笔记列表 6 | 7 | #### 基础知识 8 | 9 | ##### 语言基础 10 | ###### [函数](/note/basis/func) 11 | ###### [数组](/note/basis/array) 12 | ###### [字符串](/note/basis/string) 13 | ###### [正则表达式](/note/basis/regexp) 14 | ###### [异步处理](/note/basis/async) 15 | ###### [Object.*](/note/basis/object) 16 | 17 | ##### DOM 18 | ###### [DOM操作](/note/dom/dom) 19 | ###### [DOM事件](/note/dom/dom-event) 20 | 21 | ##### 规范与概念 22 | ###### [js编码规范 .eslitrc.js](/note/specification/eslintrc) 23 | ###### [无障碍访问与ARIA](/note/specification/aria) 24 | ###### [文档模式](/note/specification/dtd) 25 | ###### [MIME type](/note/specification/mime) 26 | ###### [语义版本控制](/note/specification/semantic-version) 27 | 28 | ##### 计算机基础 29 | ###### [概念汇总](/note/computer/concept) 30 | 31 | #### H5/CSS 32 | ##### CSS 33 | ###### [CSS选择器整理](/note/css3/selector) 34 | ###### [transform](/note/css3/transform) 35 | ###### [从矩阵与空间操作的关系理解CSS3的transform](/note/css3/matrix) 36 | ###### [BFC](/note/css3/bfc) 37 | ##### H5 38 | ###### [Blob(binary large object)](/note/h5/blob) 39 | ###### [File 和 FileList](/note/h5/file-filelist) 40 | 41 | #### Git 42 | ##### Git 43 | ###### [config配置项](/note/git/config) 44 | ###### [git常用命令及技巧](/note/git/commonly-used) 45 | 46 | ##### Linux 47 | ###### [Ubuntu使用知识积累](/note/linux/ubuntu-utils) 48 | 49 | #### 网络/安全 50 | ##### 网络 51 | ###### [get和post请求的区别](/note/http/get-post) 52 | ###### [从输入URL到页面加载完成的过程中都发生了什么事情?](/note/http/url-render) 53 | ###### [ajax及其优缺点](/note/http/ajax) 54 | ###### [跨域及跨域的方案](/note/http/cross-domain) 55 | ###### [强缓存与协商缓存](/note/http/cache) 56 | ###### [cookie和storage](/note/basis/cookie-storage) 57 | 58 | ##### 安全 59 | ###### [CSP(内容安全策略)](/note/security/csp) 60 | 61 | #### 性能/兼容性 62 | ###### [性能](/note/performance/perf) 63 | ###### [H5 性能优化整理](/note/performance/h5-perf) 64 | ###### [基础性能知识](/note/performance/performance) 65 | ###### [浏览器渲染页面的过程](/note/performance/render-page) 66 | ###### [重排和重绘的概念及触发条件查看这里](/note/performance/reflow-repaint) 67 | ###### [数据直出与服务端渲染的首屏优化](/note/performance/ssr) 68 | ###### [兼容性](/note/compatibility/compatibility) 69 | 70 | #### 数据结构/算法 71 | ##### 数据结构 72 | ###### [数据结构以及相关术语的概念](/note/algorithm/data-structure) 73 | ###### [栈](/note/algorithm/stack) 74 | ###### [队列](/note/algorithm/queue) 75 | ###### [链表](/note/algorithm/linked-list) 76 | ###### [二叉树和二叉查找树](/note/algorithm/bst) 77 | ###### [图](/note/algorithm/graph) 78 | 79 | ##### 算法 80 | ###### [算法的时间复杂度和空间复杂度](/note/algorithm/time-space) 81 | ##### 排序算法 82 | ###### [基本排序算法](/note/algorithm/basic-sort) 83 | ###### [高级排序算法](/note/algorithm/advance-sort) 84 | ##### 检索算法 85 | 86 | #### 框架 87 | ##### Vue 88 | ###### [博客 Vue 文章汇总](/note/library/vue/all-posts) 89 | ###### [Vue 奇技淫巧](/note/library/vue/skill) 90 | ##### React 91 | ###### [React技术栈相关经验总结](/note/library/react/experience-talk) 92 | ##### Webpack 93 | ###### [webpack插件收集](/note/library/webpack/plugins) 94 | ###### [webpack4.0+ 特性总结](/note/library/webpack/webpack4) 95 | ###### [webpack优化总结](/note/library/webpack/optimization) 96 | 97 | #### 英语 98 | ##### 单词 99 | ###### [7500核心词汇(整理自《麦克米伦高阶英汉双解词典》)](/note/english/7500-macmillan) -------------------------------------------------------------------------------- /docs/note/basis/object.md: -------------------------------------------------------------------------------- 1 | ## Object.* 2 | 3 | #### 对象的扩展/密封/冻结相关方法 4 | 5 | ##### Object.preventExtensions() 6 | 7 | * 描述: 8 | 9 | 该方法用来将一个对象变成不可扩展的对象,不可扩展指的是: 10 | 11 | > * 不允许为该对象添加新的属性 12 | 13 | > * 该对象的原型是不可变的(`immutable`),即原型不可被修改 14 | 15 | 但是现有的属性可以: 16 | 17 | > * 被删除(使用 `delete` 操作符) 18 | 19 | > * 现有属性的值可以被修改 20 | 21 | 如果给一个不可扩展的对象添加新的属性,会静默失败,如果在严格模式(`'use strict'`)下会抛出一个错误。 22 | 23 | * 参数: 24 | 25 | 期望接收一个对象作为参数,如果不是对象,那么按照 `ES5` 的规范会报错,而 `ES6` 规定把非对象参数将被视为不可扩展的普通对象,不会报错 26 | 27 | * 返回值:传入的参数是什么,就返回什么。 28 | 29 | ##### Object.seal() 30 | 31 | * 描述: 32 | 33 | 该方法用来密封(`seal`)一个对象,被密封的对象将: 34 | 35 | > * 不允许为该对象添加新的属性,不允许删除(`delete`)现有属性 36 | 37 | > * 该对象的所有属性将变成不可配置的(`non-configurable`) 38 | 39 | > * 不允许将该对象的数据属性(`data properties`)转为访问器属性(`accessor properties`),反之亦然 40 | 41 | > * 该对象的原型是不可变的(`immutable`),即原型不可被修改 42 | 43 | 但是可以: 44 | 45 | > * 修改现有属性的值 46 | 47 | * 参数: 48 | 49 | 期望接收一个对象作为参数,如果不是对象,那么按照 `ES5` 的规范会报错,而 `ES6` 规定把非对象参数将被视为不可扩展的普通对象,不会报错 50 | 51 | * 返回值:传入的参数是什么,就返回什么。 52 | 53 | ##### Object.freeze() 54 | 55 | * 描述:冻结一个对象,被冻结的对象将 56 | 57 | > * 不允许为该对象添加新的属性,不允许删除(`delete`)现有属性,不允许修改属性的值 58 | 59 | > * 该对象的所有属性将变成不可配置的(`non-configurable`) 60 | 61 | > * 不允许将该对象的数据属性(`data properties`)转为访问器属性(`accessor properties`),反之亦然 62 | 63 | > * 该对象的原型是不可变的(`immutable`),即原型不可被修改 64 | 65 | * 注意:冻结指的是浅冻结(`freeze is shallow`),需要递归的冻结一个深对象。 66 | 67 | * 参数: 68 | 69 | 期望接收一个对象作为参数,如果不是对象,那么按照 `ES5` 的规范会报错,而 `ES6` 规定把非对象参数将被视为不可扩展的普通对象,不会报错 70 | 71 | * 返回值:传入的参数是什么,就返回什么。 72 | 73 | ##### Object.isExtensible() 74 | 75 | * 描述:检查一个对象是否是可扩展的 76 | 77 | * 参数:期望接收一个对象作为参数,如果不是对象,那么按照 `ES5` 的规范会报错,而 `ES6` 规定把非对象参数将被视为不可扩展的普通对象,返回 `false` 不会报错 78 | 79 | * 返回值:一个布尔值,如果为 `true` 代表对象可扩展,为 `false` 代表对象不可扩展 80 | 81 | * 注意:使用 `Object.preventExtensions`、`Object.seal` 和 `Object.freeze` 处理的对象都是不可扩展的。 82 | 83 | ##### Object.isSealed() 84 | 85 | * 描述:检查一个对象是否是密封的,满足以下条件的对象都属于密封的对象: 86 | 87 | > * 使用 `Object.seal` 处理的对象是密封的 88 | 89 | > * 使用 `Object.preventExtensions` 处理的 `空对象` 是密封的 90 | 91 | > * 如果一个对象所有的属性都是不可配置的(`non-configurable`),则该对象是密封的 92 | 93 | * 参数:期望接收一个对象作为参数,如果不是对象,那么按照 `ES5` 的规范会报错,而 `ES6` 规定把非对象参数将被视为不可扩展的普通对象,返回 `true` 不会报错 94 | 95 | * 返回值:一个布尔值,如果为 `true` 代表对象是密封的,为 `false` 代表对象不是密封的 96 | 97 | * 注意:使用 `Object.freeze` 处理的对象也是密封的。 98 | 99 | ##### Object.isFrozen() 100 | 101 | * 描述:检查一个对象是否被冻结,满足以下条件的对象都属于被冻结的对象: 102 | 103 | > * 使用 `Object.freeze` 处理的对象是被冻结的 104 | 105 | > * 使用 `Object.preventExtensions` 或 `Object.seal` 处理的 `空对象` 是被冻结的 106 | 107 | > * 如果一个对象所有的属性都是不可写的(`non-writable`)且不可配置的(`non-configurable`),则该对象是被冻结的 108 | 109 | ##### `preventExtensions`、`seal` 和 `freeze` 的比较 110 | 111 | 简单总结如下: 112 | 113 | * 使用 `Object.preventExtension` 处理过的对象为不可扩展的,意思是不能够为该对象添加新属性,但已有属性时可写且可配置的 114 | 115 | * 使用 `Object.seal` 处理的对象是密封的,在不可扩展的基础上,已有属性都是不可配置的,但可写 116 | 117 | * 使用 `Object.freeze` 处理的对象是冻结的,在密封的基础上,不可写 118 | 119 | 三个方法都是用来限制对象的扩展性的,他们的关系如下: 120 | 121 | * 如果一个对象是被冻结(`frozen`)的,那么该对象一定是密封(`seal`)的和不可扩展(`preventExtensions`)的 122 | * 如果一个对象是密封(`seal`)的,那么该对象一定是不可扩展(`preventExtensions`)的 123 | 124 | 三个方法限制一个对象的强烈程度为: 125 | 126 | `Object.freeze` > `Object.seal` > `Object.preventExtension` -------------------------------------------------------------------------------- /docs/note/http/cache.md: -------------------------------------------------------------------------------- 1 | ## 强缓存与协商缓存 2 | 3 | #### 浏览器缓存策略 4 | 5 | ##### 请求资源时缓存的应用过程 6 | 7 | * 浏览器在加载资源时根据 `http header` 判断是否命中强缓存,如果命中,则直接使用缓存里的资源,不会发送请求到服务器。 8 | 9 | * 如果没有命中强缓存,浏览器会发送请求到服务器,服务器根据 `http header` 判断是否命中协商缓存,如果命中则浏览器会响应这个请求,但不会返回资源,会告诉浏览器使用缓存里的资源。 10 | 11 | * 如果协商缓存也没有命中,那么正常响应请求,返回资源。 12 | 13 | ##### 强缓存和协商缓存的区别与联系 14 | 15 | 区别:命中强缓存不会发送请求到服务器,但是协商缓存需要向服务器发送请求(因为是否命中协商缓存由服务器判断) 16 | 17 | 共同点:都是从浏览器的缓存中获取资源 18 | 19 | #### 强缓存 20 | 21 | 开启强缓存需要 `http` 响应头中包含指定的字段:`Expires` 或 `Cache-Control`。 22 | 23 | ##### Expires 24 | 25 | ###### Expires 的特点 26 | 27 | `Expires` 是 `http1.0` 提出一个表示资源过期的 `respone header` 字段,他有以下特点: 28 | 29 | * 返回的是绝对时间。 30 | * GMT格式的字符串表示,如:`Thu, 31 Dec 2037 23:55:55 GMT` 31 | 32 | ###### Expires 的原理 33 | 34 | `Expires` 字段的原理是:浏览器第一次请求资源的时候,服务器在 `response header` 中添加 `Expires` 字段,浏览器将请求回来的资源以及 `response header` 一并存到缓存中,当浏览器再次请求这个资源的时候,先从缓存中找到该资源,然后拿到上次缓存的 `Expires` 字段的值,与当前请求的时间作比较,如果发现当前请求的时间晚于上一次的 `Expires` 字段的值,说明缓存失效了,重新请求资源,并更新 `Expires` 字段的值。 35 | 36 | ###### Expires 存在的问题 37 | 38 | 在介绍 `Expires` 的特点时我们知道,`Expires` 字段的值代表未来的一个绝对时间,比如 `2018年1月1日`,这会有什么问题呢?其实很明显,如果我本机时间与服务器时间相差较大的时候,`Expires` 过期很容易失效了。 39 | 40 | ##### Cache-Control 41 | 42 | ###### Cache-Control 的特点 43 | 44 | 正因为 `Expires` 的绝对时间存在上述的问题,`http1.1` 提出了一个新的 `respone header` 即:`Cache-Control`,它具有以下特点: 45 | 46 | * 返回的是相对时间 47 | * 可以通过 `Cache-Control` 字段的属性值 `max-age` 配置缓存的时间长度,单位是秒,如下: 48 | 49 | ``` 50 | Cache-Control:max-age=315360000 51 | ``` 52 | 53 | ###### Cache-Control 的原理 54 | 55 | `Cache-Control` 缓存的原理与 `Expires` 类似,区别在于过期判断: 56 | 57 | * `Expires` 是使用本机时间与服务器返回的一个绝对时间对比,这会出问题,上面也有讲过 58 | 59 | * `Cache-Control` 则不同,它是使用上一次请求的时间加上设置的相对时间,计算出一个过期时间,与本次请求的时间作对比,使用的时间都是本机时间。 60 | 61 | ##### 注意事项 62 | 63 | `Expires` 与 `Cache-Control` 可以同时存在于 `respone header`,`Cache-Control` 的优先级高于 `Expires`。 64 | 65 | #### 协商缓存 66 | 67 | 浏览器自身就可以判断是否命中强缓存,但是协商缓存是由服务器来判断的,所以当强缓存没有命中后,浏览器会发送请求到服务器,服务器根据相应的 `http header` 判断是否命中协商缓存。如果命中协商缓存,则响应头的状态码为 `304` 即未改变(Not Modified),告诉浏览器使用缓存中的资源。 68 | 69 | 开启协商缓存有两种方案: 70 | 71 | * 请求头:`If-Modified-Since` 配合 响应头:`Last-Modified` 72 | * 请求头:`If-None-Match` 配合 响应头:`ETag` 73 | 74 | ##### Last-Modified / If-Modified-Since 75 | 76 | ###### 原理 77 | 78 | * 当浏览器第一次向服务器请求资源时,服务器在响应头中会添加 `Last-Modified` 字段,该字段的值代表资源的最后一次修改时间。 79 | 80 | * 浏览器再向服务器发送请求时,会在请求头部添加字段:`If-Modified-Since`,该字段的值为之前缓存下来的 `Last-Modified` 的值。 81 | 82 | * 服务器拿到 `If-Modified-Since` 的值进行对比,如果一致则命中协商缓存,返回 `304` 状态码,告诉浏览器从缓存中获取资源。 83 | 84 | * 如果没有命中协商缓存,则正常返回资源,以及新的 `Last-Modified` 值,浏览器进行更新。 85 | 86 | 注意:在命中协商缓存的情况下,服务器不会在响应头中添加:`Last-Modified` 字段,因为没有必要。 87 | 88 | ###### 缺点 89 | 90 | 有的时候会出现这种情况:资源实际已经被修改了,但是他的最后修改时间没有变,这就会导致新的资源无法得到更新 91 | 92 | ##### ETag / If-None-Match 93 | 94 | 正如 `Last-Modified / If-Modified-Since` 的缺点所描述的那样,为了避免这个问题,就有了 `ETag / If-None-Match`。 95 | 96 | ###### 原理 97 | 98 | 原理类似于 `Last-Modified / If-Modified-Since`,不同的时候,`ETag` 不是资源的最后修改时间,而是服务器根据当前请求的资源生成的唯一标示,是一个字符串。 99 | 100 | ##### Last-Modified 与 ETag 的对比 101 | 102 | ###### 字段值 103 | 104 | * `Last-Modified` 是资源的最后修改时间 105 | * `ETag` 是当前请求的资源的唯一标示字符串 106 | 107 | ###### 命中缓存后是否最为响应头返回 108 | 109 | * 如果命中协商缓存,`Last-Modified` 并不会最为响应头返回,因为没有必要 110 | * 如果命中协商缓存,`ETag` 仍然会作为响应头返回,因为不管资源有没有改变,资源的唯一标示又重新生成了。 111 | 112 | #### 如何利用缓存(待续...) 113 | 114 | -------------------------------------------------------------------------------- /docs/note/algorithm/time-space.md: -------------------------------------------------------------------------------- 1 | ## 算法的时间复杂度和空间复杂度 2 | 3 | #### 算法的评估 4 | 5 | 对于一个问题,经常有多种不同的求解算法,这时候我们就需要一个对算法进行评估的标准,找出最优的方案,评估一个算法有以下几个维度: 6 | 7 | * 正确性:能正确的实现功能,满足问题的需求。 8 | * 易读性:通常,写出一个利与人类阅读的代码和利于机器阅读的代码一样重要 9 | * 健壮性:对于预料之外的输入,也能做出合适的处理。 10 | * 时空性:算法的时间性能(算法的计算量)和空间性能(算法需要的存储量)、 11 | 12 | #### 时间复杂度 13 | 14 | ###### 时间复杂度的计算方法 15 | 16 | 时间复杂度:在给定输入(问题规模)下,算法的计算量。 17 | 18 | 所以说,求一个算法的时间复杂度,就是求这个算法在给定问题规模下的计算量,那么问题来了:如何求算法的计算量? 19 | 20 | 算法计算量的求法规则如下: 21 | 22 | * 1、在算法中选择几种“基本操作”,例如:赋值语句、数学运算语句等等。 23 | * 2、给定输入下,计算算法执行了多少次“基本操作”。 24 | * 3、“基本操作”的次数即可作为计算量。 25 | 26 | ###### 实例与大O表示法 27 | 28 | 我们以一个例子来说明,求如下表达式的值: 29 | 30 | ``` 31 | // 阶乘的和 32 | 1! + 2! + 3! + ... + n! 33 | ``` 34 | 35 | 我们可以写出如下程序(js代码): 36 | 37 | ```js 38 | function factorial (n) { 39 | var s = 0, 40 | temp = 1 41 | for (var i = 1; i <= n; i++) { 42 | temp = 1 43 | for (var j = 1; j <= i; j++) { 44 | temp *= j 45 | } 46 | s += temp 47 | } 48 | return s 49 | } 50 | ``` 51 | 52 | 我们根据之前总结的算法计算量的求法规则可知,求解一个算法的计算量分为三个步骤,第一步:确定基本操作,对于上面的代码我们所挑选的基本操作如下: 53 | 54 | 55 | * 第一部分赋值语句: 56 | ```js 57 | var s = 0 58 | temp = 1 59 | ``` 60 | 61 | 当我们的输入规模即 `n` 变化时,这两条语句的执行次数没有变,始终是 `2` 次。 62 | 63 | * 第二部分赋值语句: 64 | ```js 65 | for (var i = 1; i <= n; i++) { 66 | temp = 1 67 | ... 68 | } 69 | ``` 70 | 第一层循环里的 `temp = 1`,该语句的执行次数等于输入规模 `n`。 71 | 72 | * 乘法计算语句: 73 | 74 | ```js 75 | for (var j = 1; j <= i; j++) { 76 | temp *= j 77 | } 78 | ``` 79 | 80 | 第二层循环里的 `temp *= j`,该语句的执行次数,当 `n = 1` 时执行 1 次,当 `n = 2` 时执行 `1 + 2` 次,当 `n = 3` 时执行 `1 + 2 + 3` 次,所以该语句的执行次数与输入规模 `n` 的关系是 `1 + 2 + 3 + ... + n = n(n + 1) / 2`。 81 | 82 | * 加法计算语句: 83 | ```js 84 | for (var i = 1; i <= n; i++) { 85 | ... 86 | s += temp 87 | } 88 | ``` 89 | 第一层循环里的加法赋值语句,该语句的执行次数等于输入规模 `n`。 90 | 91 | 综上所述,根据我们选择的“基本操作”,可以计算出该算法的基本操作次数与输入规模 `n` 的关系如下: 92 | 93 | ```js 94 | T(n) = 2 + n + n(n + 1) / 2 + n = 1/2n^2 + 3/2n + 2 95 | ``` 96 | 97 | 当 `n` 足够大时,`n^2` 起支配作用,使用 `O(n^2)` 表示 `T(n)` 的近似值,这种表示法成为 `大 O 表示法`。 98 | 99 | ###### 常见的时间复杂度阶数 100 | 101 | * 常熟阶 O(1):即算法的计算量不随着输入规模的变化而变化。 102 | * 线性阶 O(n) 103 | * 多项式阶 O(n^c):常见的多项式阶如 O(n^2)、O(n^3) 104 | * 指数阶 O(C^n):常见的指数阶如 O(2^n) 105 | 106 | 一般我们认为一个算法的时间复杂度为指数阶的时候,该算法是实际不可运算的,大家可以想象一下,如果一个算法的时间复杂度为 `O(2^n)` 当 `n = 1000` 时,这个数值是何等恐怖。更何况我们的输入规模 `n` 很可能远大于 1000。 107 | 108 | 另外我们认为时间复杂度为 `O(n)`、`O(log2N)`、`O(n^2)` 是高效的算法。对于合理大的 `n`,`O(n^3)` 也是可以接受的。 109 | 110 | #### 空间复杂度 111 | 112 | ###### 空间复杂度的计算方法 113 | 114 | 空间复杂度:给定输入(问题规模)下,算法运行时所占用的临时存储空间。 115 | 116 | 一个算法执行期间所占用的存储量分为三部分: 117 | 118 | * 算法本身的代码所占用的空间 119 | * 输入数据所占用的空间 120 | * 辅助变量所占用的空间 121 | 122 | 由于实现不同算法所需的代码不会有数量级的差别,所以算法本身代码所占用的空间我们可以不考虑 123 | 124 | 输入的数据所占用的空间是由问题决定的,与算法无关,所以我们也不需要考虑 125 | 126 | 我们需要考虑的只有一个:程序执行期间,辅助变量所占用的空间。 127 | 128 | 计算方法类似于计算算法的时间复杂度,空间复杂度我们用 `S(n)` 来表示,它同样是输入数据规模 `n` 的函数,用大 O 表示法记为: 129 | 130 | ``` 131 | S(n) = O(g(n)) 132 | ``` 133 | 134 | 其中 `g(n)` 是一个关于 `n` 的函数,如:`g(n) = n`、`g(n) = n^2`、`g(n) = log2N` 等等。 135 | 136 | ###### 实例 137 | 138 | 假设我们有一个数组,该数组有100个元素,写一个转置该数组的算法: 139 | 140 | ```js 141 | function reverse (arr) { 142 | for (var i = 0; i <= arr.length / 2 - 1; i++) { 143 | var temp = arr[i] 144 | arr[i] = arr[arr.length - i - 1] 145 | arr[arr.length - i - 1] = temp 146 | } 147 | } 148 | ``` 149 | 150 | 上面的算法中,我们采用中间变量 `temp` 对数组的值进行逐个对应的首尾交换,最终达到转置的目的,我们可以看到,辅助变量只有一个即 `temp`,该变量存储一个数字类型的值,`temp` 所占用的内存不会随输入数组规模的增大而增大,所以上面算法的控件复杂度为 `O(1)`,是常数阶,即上面算法的空间复杂度 `S(n) = O(1)`。 -------------------------------------------------------------------------------- /docs/note/basis/func.md: -------------------------------------------------------------------------------- 1 | ## 一、创建函数 2 | 3 | #### 函数声明 4 | 5 | ```js 6 | function fnName(){ 7 | 8 | } 9 | ``` 10 | 11 | #### 函数表达式 12 | 13 | ```js 14 | var fnName = function(){ 15 | 16 | } 17 | ``` 18 | 19 | #### 使用Function构造函数 20 | 21 | ```js 22 | // 参数:Function 接收任意多的参数,但最后一个参数总被认为是函数体,前面的参数是传入新函数的参数 23 | var fnName = new Function(a, b, c, 'return a + b + c') 24 | ``` 25 | 26 | #### 【ES6】箭头函数 27 | 28 | ```js 29 | var fnName = () => { 30 | 31 | } 32 | ``` 33 | 34 | ## 二、函数的内部属性 35 | 36 | #### arguments 37 | 38 | * 类型:类数组对象,包含着传入函数的所有参数,和length属性 39 | * 属性: 40 | * arguments.length // 实际传入函数参数的个数 41 | * arguments.callee【严格模式报错】 // 指向拥有这个anguments对象的函数,即函数本身 42 | 43 | #### this 44 | 45 | 函数据以执行的执行环境 46 | 47 |

箭头函数无 this

48 | 49 | ## 三、函数的属性和方法 50 | 51 | #### 属性 52 | 53 | ###### fnName.caller 54 | * 描述:保存着调用当前函数的函数的引用,如果在全局作用域调用当前函数,则返回 `null` 55 | 56 | ###### fnName.length 57 | * 描述:表示函数希望接收的命名参数的个数,只读 58 |

59 | 注意:anguments.length 是实际传入函数参数的个数,而 fnName.length 是函数希望接收命名参数的个数,【ES6函数默认值对length的影响】:指定默认值以及在指定默认值的参数之后的所有参数(包括默认值),都不会计算到length中 60 |

61 | 62 | ###### fnName.prototype 63 | * 描述:保存函数的原型对象 64 | 65 | ###### 【ES6】fnName.name 66 | * 描述:获取函数的函数名 67 | * 返回值: 68 | * 对于函数声明:返回函数名 69 | * 对于匿名函数表达式:ES5返回空字符串,ES6返回变量的名字 70 | * 对于具名函数表达式:返回函数的原名字 71 | * 对于使用 new Function 创建的函数:返回 'anonymous' 72 | * 对于使用bind方法返回的函数:返回 'bound 函数名' 73 | 74 | #### 方法 75 | 76 | ###### fnName.apply() 77 | ###### fnName.call() 78 | * 描述:上面两个方法都用来在特殊的作用域调用函数,实际上等于设置函数体内的 `this` 对象的值 79 | * 参数: 80 | * 第一个参数都是 `this` 的值 81 | * 第二个参数:`apply` 接收 `anguments` 对象或数组,`call` 必须逐个列举出来 82 | 83 | ###### fnName.bind(thisArg, [, arg1[, arg2[, ...]]]) 84 | * 描述:根据已有函数,创建一个被绑定新 `this` 值的函数 85 | * 参数: 86 | * 第一个参数是 `this` 的值 87 | * `arg1`、`arg2`、`arg3`...:这些参数将被传入原始函数,且前置与调用原始函数时所传递的参数 88 | * 返回值: 89 | * `{Function}` 被指定 `this` 值的新函数 90 | 91 | ## 四、ES6对函数的扩展 92 | 93 | #### 参数默认值 94 | 95 | ```js 96 | function (a = 2, b = 3){ 97 | 98 | } 99 | ``` 100 |

【注意:函数的length属性,不会计算指定默认值的参数以及其后的所有参数】

101 | 102 | #### rest参数 [...变量名] 103 | * 描述:用于获取函数多余的参数,将其放入一个数组 104 | * 注意: 105 | * 1、rest参数后面,不能有其他参数,否则会报错 106 | * 2、rest参数不会被计算到函数的length属性中 107 | #### 箭头函数 108 | 109 | 箭头函数有几点需要注意: 110 | 111 | * 函数体内的 `this` 对象是函数定义是所在的对象,而不是使用时的对象 112 | * 不能用箭头函数当做构造函数,也就是说不能使用new命令,否则会报错 113 | * 不可以使用 `arguments` 对象,该对象在函数体内不存在。如果要用,可以用 `Rest参数` 代替。 114 | * 不可以使用 `yield` 命令,因此箭头函数不能用作 `Generator` 函数。 115 | * 由于箭头函数没有自己的 `this`,所以当然也就不能用 `call()`、`apply()`、`bind()` 这些方法去改变 `this` 的指向。 116 | 117 | #### 尾递归 118 | 119 |

【注意:ES6的尾调用优化只在严格模式下开启,正常模式是无效的。】

120 | 121 | ``` 122 | ES6明确规定,所有ECMAScript的实现,都必须部署“尾调用优化”。这就是说,在ES6中,只要使用尾递归,就不会发生栈溢出,相对节省内存。 123 | 相关概念: 124 | 1、尾调用:函数的最后一步调用另一个函数,叫做尾调用 125 | 尾调用的好处是:只保留内层函数的调用帧,节省内存 126 | 2、尾递归:函数尾调用自身,叫做尾递归 127 | 3、柯里化:将多参数函数转成单参数函数 128 | 因为尾调用优化的本质是,只保留内层函数的调用帧,ES6的尾调用只在严格模式下生效,那么在非严格模式下是否可以进行尾调用优化呢?但是可以的,有两种方案,一种是使用蹦床函数,一种是真正的尾调用,阮一峰的教程里有讲 129 | ``` 130 | 131 | #### new.target【ES6】 132 | 133 | `new` 操作符用来调用函数或ES6的类,从而创建一个实例,ES6为new操作符添加一个属性即:`new.target`,它保存着 `new` 操作符所作用的那个函数或类,一般用在构造函数里,如果使用函数或类时没有使用 `new` 操作符,那么 `new.target` 的值为 `undefined`。 134 | 135 | 利用 `new.target` 就可以写出不能单独被实例化,必须要继承后才能使用的类: 136 | 137 | ```js 138 | class Super { 139 | constructor () { 140 | if (new.target === Super) { 141 | throw new Error('不能单独实例化') 142 | } 143 | } 144 | } 145 | class Sub extends Super { 146 | 147 | } 148 | 149 | new Super() // 报错 150 | new Sub() // 正常使用 151 | ``` 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /docs/note/algorithm/advance-sort.md: -------------------------------------------------------------------------------- 1 | ## 高级排序算法 2 | 3 | 高级排序算法常用来处理大型的数据集合,这个数据集可达上百万个元素,而不是几百几千个。通过你需要排序的数据集合规模较小,建议使用基本排序算法处理,因为在小规模数据下使用高级排序算法,并不能起到优化的作用,有的时候甚至会由于算法本身的实现方式而拖慢速度,比如小规模数据使用快速排序算法性能反而不好。 4 | 5 | #### 希尔排序 6 | 7 | ##### 算法简介 8 | 9 | 希尔排序名字的来自于它的发明者:Donald Shell。希尔排序是对插入排序的改编版本,它和插入排序的不同在于,希尔排序会先比较离得较近的元素,而非相邻的元素,这在处理大规模数据的时候,能够使得元素更快的回到它应该出现的位置。 10 | 11 |

算法的平均时间复杂度为 O(nlog n)

12 | 13 | ##### 算法描述 14 | 15 | 希尔排序的关键在于 `间隔序列`,这个间隔序列可以是事先指定好的,也可以是动态生成的。间隔序列的特点为,每个间隔序列的值依次递减且最后一个值必须为1,假设间隔序列为:`[g1, g2, g3, g4, ...., gk]`,那么有 16 | 17 | ```js 18 | g1 > g2 > g3 > g4 > ... > gk 19 | ``` 20 | 21 | 且 22 | 23 | ```js 24 | gk === 1 25 | ``` 26 | 27 | 也可以动态生成间隔序列,动态生成间隔序列的算法是在《算法(第4版)》的合著者 `Robert Sedgewick` 提出的: 28 | 29 | ```js 30 | // 计算后得到的 h 为间隔序列的最大值 31 | let len = arr.length 32 | let h = 1 33 | while (h < len / 3) { 34 | h = h * 3 + 1 35 | } 36 | 37 | // 然后每趟间隔序列排序完成后,将 h 的值递减,直到 h 等于 1 为止,根据上面的生成算法,递减算法为: 38 | h = (h - 1) / 3 39 | ``` 40 | 41 | 有了间隔序列之后,对数组依次使用间隔序列的值进行排序,而非像插入排序那样每次都从有序序列的后面逐个向前扫描,这样做的好处是可以使元素更快的出现在它应该在的“范围之内”,这个范围会随着间隔序列的值逐渐减小,直到当间隔序列值为 1 的时候,便会逐个比较,不过此时由于元素已经出现在离正确位置很近的位置,所以排序进行的会很快。 42 | 43 | ##### 代码实现 44 | 45 | ```js 46 | function shellSort (arr) { 47 | var len = arr.length 48 | var g = 1 49 | // 动态计算间隔序列 50 | while (g < len / 3) { 51 | g = g * 3 + 1 52 | } 53 | // 最后一趟比较的间隔应该为 1 54 | while (g >= 1) { 55 | // 开始一趟以 g 为间隔的比较,初始化 i = g,且 i 逐渐累加到数组的最后一个元素 56 | for (var i = g; i < len; i++) { 57 | // j 的初始化值为 i,j每次循环都会减少g,意味着:从第 i 个元素开始向前与相隔g个单位的元素进行比较。 58 | for (var j = i; j >= g && arr[j - g] > arr[j]; j -= g) { 59 | var temp = arr[j - g] 60 | arr[j - g] = arr[j] 61 | arr[j] = temp 62 | } 63 | } 64 | // 更新间隔值 65 | g = (g - 1) / 3 66 | } 67 | } 68 | ``` 69 | 70 | #### 归并排序 71 | 72 | ##### 算法简介 73 | 74 | 归并排序的实现方式有两种,一种是自顶向下,另一种是子底向上。 75 | 76 | 归并排序是分治法的典型应用,他的名字来自与其排序的方式。 77 | 78 |

算法的平均时间复杂度为 O(nlog n),空间复杂度为 O(n)

79 | 80 | ##### 算法描述 81 | 82 | ###### 自顶向下 83 | 84 | 首先将长度为 `n` 的数组分成两个长度为 `n/2` 的子数组,然后将两个子数组递归的执行一分为二的操作,直到每个子数组中至多包含一个元素为止。由于这是一个递归的过程,当程序回溯的时候,将两个子数组进行排序合并,直到回溯完毕,此时排序完成,如下图: 85 | 86 | 87 | 88 | 上图描述了一个数组被递归拆分的过程,除此之外,在程序回溯的时候,一次保证数组有序,如下图: 89 | 90 | 91 | 92 | ###### 自顶向下的代码实现 93 | 94 | 代码来自:[https://github.com/damonare/Sorts](https://github.com/damonare/Sorts),并做了适当的修改和注释: 95 | 96 | ```js 97 | // 调用的主要方法 98 | function mergeSort (arr) { 99 | let len = arr.length 100 | // 递归结束的条件 101 | if(len < 2) { 102 | return arr 103 | } 104 | // 对数组进行拆分,拆为 left 和 right 105 | let middle = Math.floor(len / 2), 106 | left = arr.slice(0, middle), 107 | right = arr.slice(middle) 108 | // 递归调用,采用尾递归优化 109 | return merge(mergeSort(left), mergeSort(right)) 110 | } 111 | 112 | // merge方法用来对两个数组进行排序,并返回排好序的数组 113 | function merge (left, right) { 114 | var result = [] 115 | // 对两个子数组进行排序 116 | while (left.length && right.length) { 117 | if (left[0] <= right[0]) { 118 | result.push(left.shift()) 119 | } else { 120 | result.push(right.shift()) 121 | } 122 | } 123 | 124 | // 由于两个子数组的元素个数不一定相等,所以在上一个while循环排序完成后, 125 | // 要检查left和right中是否还有元素,如果有则推入结果数组中 126 | while (left.length) { 127 | result.push(left.shift()) 128 | } 129 | while (right.length) { 130 | result.push(right.shift()) 131 | } 132 | 133 | return result 134 | } 135 | ``` 136 | 137 | #### 快速排序 138 | 139 | 140 | -------------------------------------------------------------------------------- /docs/note/algorithm/basic-sort.md: -------------------------------------------------------------------------------- 1 | ## 基本排序算法 2 | #### 冒泡排序 3 | 4 | ##### 算法简介 5 | 6 | 冒泡排序属于基本排序算法之一,基本排序算法的一个特点是:需要两层嵌套的循环。外层循环用于遍历数组的每一项,内层循环用于对元素进行比较。 7 | 8 |

算法的时间复杂度为 O(n²)

9 | 10 | ##### 算法描述 11 | 12 | 冒泡这个名字来自于该算法的排序过程,即数据会像气泡一样从数组的一端浮动到另外一端。 13 | 14 | 以升序为例,算法的过程为: 15 | * 1、使用数组的第一个元素和数组的第二个元素作比较,如果第一个元素大于第二个元素,就叫唤它们的位置。 16 | * 2、再使用数组的第二个元素和数组的第三个元素作比较,比较方式同步骤一 17 | * 3、如步骤一和步骤二的方式,当第一轮比较结束之后,能够保证数组最右端的项为最大值 18 | * 4、进入下一轮比较,此时由于已知数组的最后一项为最大值,所以比较的次数可以减少一次 19 | * 5、重复步骤4直到排序完成 20 | 21 | 一图胜千言(图片来自:[https://github.com/damonare/Sorts](https://github.com/damonare/Sorts)): 22 | 23 | 24 | 25 | ##### 代码实现 26 | 27 | ```js 28 | function bubbleSort (arr) { 29 | var num = arr.length 30 | for (var i = 0; i < num - 1; i++) { 31 | // 这里的 num - 1 - i 中的 i 很关键,保证了每轮的比较次数减一 32 | for (var j = 0; j < num - 1 - i; j++) { 33 | if (arr[j] > arr[j + 1]) { 34 | // 交换元素 35 | var temp = arr[j] 36 | arr[j] = arr[j + 1] 37 | arr[j + 1] = temp 38 | } 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | ##### 优化冒泡排序 45 | 46 | 假设我们有如下数组: 47 | 48 | ```js 49 | var arr = [2, 1, 3, 4, 5, 6, 7, 8, 9] 50 | ``` 51 | 52 | 对于该数组的排序,其实我们只需要将数组的第一个元素 `2` 与数组的第二个元素 `1` 交换一下位置即可,因为后边的数据已经是有序的了。 53 | 54 | 但是我们上面的冒泡排序算法,在对该数组进行排序时第一轮排序依然会对比到数组的最后一个元素,第二个排序会对比到数组的倒数第二个元素。这个时候,我们是有一些优化手段的,其中一个解决办法就是设置一个标志,该标志存储了最后一次数据位置交换的索引。大家可以想象一下 `最后一次位置交换` 以为着什么?它意味着该位置后边的元素已经是有序的了。所以下一轮比较只需要比较到该位置即可。 55 | 56 | 优化后的代码如下: 57 | 58 | ```js 59 | function bubbleKeySort (arr) { 60 | // pos为最后一次交换数据位置的标志,初始化为数组最后一个元素 61 | var pos = arr.length - 1 62 | // 只要 pos 大于0,就开启新一轮的比较 63 | while (pos > 0) { 64 | // 变量 p 用来存储本轮比较最后一次交换数据的位置,初始化为 0,这个很关键,因为当本轮比较结束之后如果 p 仍然为 0,那么就不会再执行 while 循环,此时数据已经排好序 65 | var p = 0 66 | for (var j = 0; j < pos; j++) { 67 | if (arr[j] > arr[j + 1]) { 68 | // 更新 p 的值 69 | p = j 70 | var temp = arr[j] 71 | arr[j] = arr[j + 1] 72 | arr[j + 1] = temp 73 | } 74 | } 75 | // 本轮比较结束之后,更新 pos 的值 76 | pos = p 77 | } 78 | } 79 | ``` 80 | 81 | #### 选择排序 82 | 83 | ##### 算法简介 84 | 85 | 选择排序和冒泡排序一样,同属于基本排序算法。 86 | 87 |

算法的时间复杂度为 O(n²)

88 | 89 | ##### 算法描述 90 | 91 | 选择排序的关键在于 `选择` 二字,拿数组的第一个元素和数组其他元素作比较,然后找到数组中最小(大)的元素,将最小的元素 `选择` 出来,放到数组的第一个位置,然后再拿数组的第二个元素和其他元素其他元素作比较,重复之前的比较步骤。这样由于每次都选择最小(大)的元素,将这些元素一次摆放就可以将数组排好序。 92 | 93 | 一图胜千言(图片来自:[https://github.com/damonare/Sorts](https://github.com/damonare/Sorts)): 94 | 95 | 96 | 97 | ##### 代码实现 98 | 99 | 以升序为例: 100 | 101 | ```js 102 | function selectionSort (arr) { 103 | // 从数组的 第一个 元素遍历到数组的 倒数第二个 元素 104 | for (var i = 0; i < arr.length - 1; i++) { 105 | // min为最小值的索引,被初始化为i,即假设每轮比较的首个元素就是最小的 106 | var min = i 107 | // 从数组的 i + 1 个元素遍历到数组的 最后一个 元素 108 | for (var j = i + 1; j < arr.length; j++) { 109 | // 与预期的最小值进行比较,如果比预期的最小值还小,就更新最小值 110 | if (arr[j] < arr[min]) { 111 | min = j 112 | } 113 | } 114 | // 第二层循环结束时,min索引指向的元素即为最小的元素,交换位置开始下一轮比较 115 | var temp = arr[i] 116 | arr[i] = arr[min] 117 | arr[min] = temp 118 | } 119 | } 120 | ``` 121 | 122 | #### 插入排序 123 | 124 | ##### 算法简介 125 | 126 | 插入排序也属于基本排序算法。 127 | 128 |

算法的时间复杂度为 O(n²)

129 | 130 | ##### 算法描述 131 | 132 | 插入排序的思路是构建有序序列,取出数组的第一个元素,认为其实有序的,然后再取出数组的第二个元素与有序序列中的元素进行比较,然后插入合适的位置,知道数组中的所有元素都被 `取出-比较-插入-完成`。 133 | 134 | 一图胜千言(图片来自:[https://github.com/damonare/Sorts](https://github.com/damonare/Sorts)): 135 | 136 | 137 | 138 | ##### 代码实现 139 | 140 | 以升序为例: 141 | 142 | ```js 143 | function insertionSort (arr) { 144 | // 从数组第二项开始遍历,即默认第一项为有序的 145 | for (var i = 1; i < arr.length; i++) { 146 | // j代表数有序序列的最后一项 147 | var j = i - 1 148 | var temp = arr[i] 149 | // 从有序序列最后一项向前扫描,与 temp 对比,如果比temp大,则后移一位 150 | while (j >= 0 && arr[j] > temp) { 151 | arr[j + 1] = arr[j] 152 | j-- 153 | } 154 | // 退出循环后,arr[j]的元素是小于等于temp的,所以将temp插入到 j+1 的位置 155 | arr[j + 1] = temp 156 | } 157 | } 158 | ``` -------------------------------------------------------------------------------- /docs/note/specification/semantic-version.md: -------------------------------------------------------------------------------- 1 | ## 语义版本控制(Semantic Versioning 2.0) 2 | 3 | #### 基本介绍 4 | 5 | 语义化版本由三部分组成:`major.minor.patch`。 6 | 7 | * `major`:主版本号,当你的包拥有不兼容的API变更时需要增加该版本号的值。 8 | * `minor`:次版本号,当你以向后兼容的方式为你的包增加功能时需要增加该版本号。 9 | * `patch`:补丁版本号,当你的包拥有向后兼容的bug修复时需要增加该版本号。 10 | 11 | 实际上你可以在 `major.minor.patch` 格式上进行扩展,添加预发布版本(`pre-release version`)和版本的构建元数据(`build metadata`)。 12 | 13 | 语义版本控制的好处是,如果一个包的版本号变更完全依照语义版本控制的规定,那么使用者将很容易的从版本号的变更中得知此包底层代码的变更,这对于包的使用者来说意义重大。同时一个依照语义版本控制的包也将会被包的发现者认为是更加“靠谱的”。 14 | 15 | #### 语义版本空的规范 16 | 17 | 请注意下文中出现的关键词,如:**必须**、**不能/不得**、**需要**、**可以** 等。实际上这些关键词也是有规范来定义的,具体查看:[https://tools.ietf.org/html/rfc2119](https://tools.ietf.org/html/rfc2119) 18 | 19 | * 1、使用语义版本的项目**必须**要有对该项目公共API清晰且全面的介绍。 20 | * 2、普通的版本号格式**必须**符合 `major.minor.patch` 的格式,且 `major`、`minor` 和 `patch` 都是非负整数(即正整数或0),并且**不能**包含前导0。每个版本号元素都**必须**以数字递增的方式增加,例如:`1.9.0` -> `1.10.0` -> `1.11.0` 21 | * 3、一个包发布了某个版本之后,该版本的内容将**不得**修改。任何修改**必须**作为新版本再次发布。 22 | * 4、如果你要发布一个初始开发的包(即正在开发中或未经测试的包),那么该包的主版本号**需要**为 `0`,例如 `0.1.2`。这意味着该包的API还不稳定,随时可能发生变化。 23 | * 5、`1.0` 版本包意味着该包定义了稳定的公共的API,该包版本号后续的变更取决于该包的更改方式和API的变更。 24 | * 6、如果你只是引入了向后兼容的bug修复,则**必须**增加补丁版本号(`patch`)。 25 | * 7、如果新的且向后兼容的功能被添加到公共API,则**必须**增加次版本号(`minor`),同样的如果把现有API标记为被反对的,也**必须**增加次版本号(`minor`)。如果你的包添加一些实质性的新功能或者改善,你也**可以**增加次版本号(`minor`)。最后要注意的是,如果次版本号(`minor`)增加了,那么**必须**要将补丁版本号(`patch`)重置为0。 26 | * 8、如果有新的且不向后兼容的功能被添加到了API,那么主版本号(`major`)**必须**增加。注意,如果主版本号(`major`)增加了,那么**必须**要将次版本号(`minor`)以及补丁版本号(`patch`)全部重置为0。 27 | * 9、**可以**在补丁版本号(`patch`)之后使用连字符(`-`)和一些列的点分隔符(`.`)来标识预发布版本号(`pre-release version`)。标识符**必须**只能由字母、数字或者连字符(`-`)组成,且不能为空,数字类型的标识符**不能**有前导0。预发布版本号(`pre-release version`)的优先级低于其关联的普通版本。一个预发布版本号(`pre-release version`)代表着该版本是不稳定的,并且该版本可能不满足其关联的普通版本的兼容性要求。一些预发布版本号的例子:`1.0.0-alpha`、`1.0.0-alpha.1`、`1.0.0-0.3.7`、`1.0.0-x.7.z.92`。 28 | * 10、**可以**在补丁版本号(`patch`)或预发布版本号(`pre-release version`)之后使用加号(`+`)以及一系列的点分隔符(`.`)为该版本添加构建元数据(`build metadata`),构建元数据的标识符**必须**只能由字母、数字、连字符组成且不能为空。构建元数据不参与版本号优先级的比较,这意味着只有构建元数据不同的两个版本具有相同的优先级。例如这几个版本具有相同的优先级:`1.0.0-alpha+001`、`1.0.0+20130313144700`、`1.0.0-beta+exp.sha.5114f85`。 29 | * 11、优先级定义了在版本号排序的过程中版本号是如何互相比较的,**必须**通过将版本号按顺序分为 `major`、`minor`、`patch` 和 `pre-release` 并一次对比他们的标识符来计算优先级。当从左到右比较这些标识符中的每一个时,优先级由第一个差异确定,规则如下: 30 | * `major`、`minor` 和 `patch` 版本的大小总是通过比较数字大小来决定的,例如:`1.0.0` < `2.0.0` < `2.1.0` < `2.1.1`。 31 | * 当 `major`、`minor` 和 `patch` 版本相同时,带有 `pre-release version` 的版本的优先级始终小于普通版本。例如:`1.0.0-alpha` < `1.0.0`。 32 | * 比较两个预发布版本的规则与对比 `major`、`minor` 和 `patch` 版本类似,将预发布版本按点分隔符分开,从左到右依次对比这些标识符,且优先级由第一个差异确定,规则如下: 33 | * 只包含数字的标识符通过比较数字的大小确定优先级,包含字符和中横线的标识符则通过比较字符在 ASCII 码中的顺序来比较优先级。 34 | * 数字标识符的优先级低于非数字标识符。 35 | * 通过点分隔符将预发布版本号分割之后,拥有更多的标识符的版本也将拥有更高的优先级。 36 | * 如果全部都相等,则两个版本优先级相同。 37 | 38 | 下面是一个完整的例子: 39 | 40 | `1.0.0-alpha` < `1.0.0-alpha.1` < `1.0.0-alpha.beta` < `1.0.0-beta` < `1.0.0-beta.2` < `1.0.0-beta.11` < `1.0.0-rc.1` < `1.0.0` 41 | 42 | #### 一些建议 43 | 44 | 以上内容来自:[https://semver.org/#semantic-versioning-200](https://semver.org/#semantic-versioning-200)。如果你正在创建一个包并准备发布它,那么请你遵守这套规则。你可以在你将此链接添加到你的 README 中,这样当别人看到你的包时即可明确的知道你的包时遵守语义版本控制的,这样使用者会对你的包好感倍增,因为使用者会认为遵守语义版本控制的包将更加靠谱,同时也显得更加专业。 45 | 46 | #### npm的语义化版本控制 47 | 48 | ##### 版本范围 49 | 50 | `npm` 的语义化版本控制遵守以上介绍的通用规则,如果你是一个包的发布者希望你一定要遵守。如果你是一个包的使用者,那么你还需要注意一些其他内容。比如我们经常会在 `package.json` 中的依赖项中看到诸如下面这样的依赖版本: 51 | 52 | `1.0.x`、`~1.0.3`、`^1.0.4`、`2.x`、`*` 等等。 53 | 54 | 他们代表什么意思呢? 55 | 56 | `npm` 中除了明确指定某个包的依赖版本之外,还可以指定版本范围。以 `lodash` 为例: 57 | 58 | * `"lodash": "2.4.1"`:只会匹配明确的 `2.4.1` 版本。 59 | 60 | 但是我们知道 `lodash` 的版本控制是完全依照语义化规范的,所以我们希望如果 `loadsh` 发布了补丁版本则自动安装,此时我们可以使用 `~`: 61 | 62 | * `"lodash": "~2.4.1"`:这会匹配补丁版本号大于 `1` 的版本,如 `2.4.2`,但不会匹配诸如 `2.5` 或 `3.0` 等版本 63 | 64 | 另外既然 `lodash` 的版本控制是完全依照语义化规范的,我们知道次版本号的增加是向后兼容的,所以我们可以考虑如果 `lodash` 发布了次版本则自动安装之,此时我们可以使用 `^`: 65 | 66 | * `"lodash": "^2.0.1"`:这会匹配所有大于 `2.0.1` 版本的 `2.x.x` 版本,如 `2.1.0`、`2.4.2` 等等,但不会匹配 `2.0.0`,因为 `2.0.0` 小于 `2.0.1`。 67 | 68 | 如果你非常激进,只要有新版本就安装之,那么你可以指定 `*` 或 `x`,例如: 69 | 70 | * `"lodash": "*"`:匹配所有**非预发布版本**。 71 | 72 | 感受详细的计算规则大家可以使用:[https://semver.npmjs.com/](https://semver.npmjs.com/) 73 | 74 | ##### 分发标签(Distribution tags) 75 | 76 | 分发标签是对 [semantic versioning](http://semver.org/) 的补充,用于管理一个包的不同版本,分发标签具有比语义化版本更高的可读性。 77 | 78 | 可以使用 `npm dist-tag add @ []` 命令为包的某个版本打上标签。 79 | 80 | 我们使用 `npm publish` 命令发布一个包的时候,其默认会给当前版本的包打上 `latest` 标签,实际上,当我们使用 `npm install ` 命令安装一个包时,默认安装的就是 `@latest` 版本,除非手动指定版本。 81 | 82 | 可以使用 `--tag` 指定发布标签,如:`npm publish --tag beta`。 83 | 84 | 安装的时候也可以指定安装的标签:`npm install somepkg@beta`。 85 | 86 | **需要注意的是,由于 `tag` 与 `semver` 具有同样的名称空间,所以在指定标签名字的时候,不要以数字或字母 `v` 开头**。 -------------------------------------------------------------------------------- /docs/note/library/react/experience-talk.md: -------------------------------------------------------------------------------- 1 | ## React技术栈相关经验总结 2 | 3 | #### 1、react-redux 与 react-router 配合的问题 4 | 5 | 我项目中使用的 `react` 版本是 `15.5.10`,`react-redux()` 的版本是 `3.7.2`,`react-router` 的版本是 `4.1.2`。 6 | 7 | 有用过 `redux` 的同学应该清楚,`redux` 和 `react` 没有一毛钱关系,所以就有了 `react-redux`,它是 `redux` 针对 `react` 的绑定库,更多的时候我们使用的都是 `react-redux`,其实我们是完全可以不用这个绑定库而直接使用 `redux` 的,我们使用 `store.subscribe()` 监听数据状态的变化,然后通过 `store.getState()` 获取最新的数据状态,然后使用最新的数据状态渲染组件,说上去简单,但要注意这涉及几个问题: 8 | 9 | 1、你要手动把 `state` 树以 `props` 的方式传递给根组件,根组件再层层传递给子组件。 10 | 11 | 2、你要还要把 `dispatch` 和 `Action Creators` 以 `props` 的形式传递给根组件,根组件再层层传递给子组件。 12 | 13 | 对于第一个问题其实还好,我们只需要这样: 14 | 15 | ```js 16 | const state = store.getState() 17 | 18 | ``` 19 | 20 | 上面的代码我们把数据状态以 `props` 的形式从根组件注入了进去,对于第二个问题我们可以类似于处理第一个问题一样: 21 | 22 | ```js 23 | import action from './actions' 24 | const state = store.getState() 25 | 26 | ``` 27 | 28 | 上面的代码中,我们把 `Action Creators` 以及 `dispatch` 方法手动的注入给根组件,然后在 `App` 组件或者其子组件中可以手动触发: 29 | 30 | ```js 31 | // 其中 changeList() 为一个 Action Creators 32 | this.props.dispatch(this.props.changeList()) 33 | ``` 34 | 35 | 虽然能够实现需求,但这样做是有问题的,首先子组件感知到了 `redux` 的存在,因为你使用了 `dispatch`,这违背了 [容器组件和展示组件相分离](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) 的思想,使得 `react` 与 `redux` 强耦合,当这些组件拿到其他地方的时候未必可用,这就是我们为什么要用 `react-redux`,它能够让子组件感知不到 `redux` 的存在,并且提供了一些好用的方法来辅助你做 `state=>props`,`Action Creators=>props`。 36 | 37 | 使用 `react-redux` 之后,对根组件进行处理: 38 | 39 | ```js 40 | const mapStateToProps = state => ({...state}); 41 | 42 | function mapDispatchToProps(dispatch) { 43 | return bindActionCreators({ ...action }, dispatch); 44 | } 45 | 46 | // 使用 connect 装饰 App 组件 47 | const Root = connect(mapStateToProps, mapDispatchToProps)(App); 48 | ``` 49 | 50 | 这样在 `App` 组件中,我们就可以直接调用 `Action Creators` 从而触发 `Action`: 51 | 52 | ```js 53 | this.props.changeList() 54 | ``` 55 | 56 | 上面这句代码,在 `react-redux` 环境中,它是在触发 `Action`,如果不在 `react-redux` 环境中,那就是在调用一个方法,所以上面的代码是可移植的,对于 `App` 本身来讲,对 `redux` 是无感知的。 57 | 58 | 的确,看上去很好,但是当我们的相中同时使用 `react-redux` 和 `react-router` 的时候,你要注意,你可能遇到这个问题:*路由变化了,但是路由匹配的组件并没有被渲染*。 59 | 60 | 这是因为 `react-redux` 实现了 `shouldComponentUpdate` 方法为你做了一些优化,所以当你URL改变的时候,对于子组件来讲,他们并没有接收到新的 `props`,所以并不会渲染,解决的方案就是采用 `withRouter()` 包装一下由 `content()` 装饰的容器组件即可: 61 | 62 | ```js 63 | const Root = withRouter(connect(mapStateToProps, mapDispatchToProps)(App)); 64 | ``` 65 | 66 | 其原理简单来讲,就是当你的路由变化的时候,组件将被强制 `re-render`,可以在这里看到解释:[https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/redux.md](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/redux.md) 67 | 68 | #### 2、react-router 4 实现按需加载 69 | 70 | 在 `react-router` V4 之前,`react-router` 的思想是静态路由,类似于 Angular 和 Express中配置的路由,且自动支持异步组件的写法(使用 `genComponent`),但在 `react-router 4`,其思想由静态路由转变为动态路由,同时不再提供异步组件的写法,需要通过 `webpack` 的 `bundle-loader` 实现。 71 | 72 | 具体的实现方法其官方文档已有介绍,地址:[https://reacttraining.com/react-router/web/guides/code-splitting](https://reacttraining.com/react-router/web/guides/code-splitting)。 73 | 74 | 这里我要说的是,在使用 `bundle-loader` 异步加载组件的时候,你的 `webpack` 配置里一定要配置 `output.publicPath` 选项,否则异步加载的组件可能会 `404(Not Found)`。 75 | 76 | #### 3、react中阻止事件冒泡 77 | 78 | 遇到过以下问题: 79 | 80 | 首先在 `componentDidMount()` 方法中使用原生js在 `document` 对象上监听了一个 `click` 事件: 81 | 82 | ```js 83 | componentDidMount () { 84 | document.addEventListener('click', () => { 85 | // ...... 86 | }) 87 | } 88 | ``` 89 | 90 | 然后在 `react` 的事件中阻止冒泡: 91 | 92 | ```js 93 | 94 | handleClick (e) { 95 | e.stopPropagation() 96 | } 97 | 98 | render () { 99 | return ( 100 |
click
101 | ) 102 | } 103 | ``` 104 | 105 | 发现,这样并不能成功阻止事件冒泡,事件依然冒泡到了 `document`。 106 | 107 | 但是如果你采用 `react` 的事件绑定方式给 `document` 对象添加事件,却发现能够成功阻止,原因是: 108 | 109 | `react` 的合成事件其实现方式也是采用了事件委托,所以当你再任何事件函数中阻止事件冒泡时,除非 `react` 手动截获你阻止冒泡的行为后替你去阻止之外,别无它法,这就是为什么能够成功阻止 `react` 事件,而不能阻止原生绑定的事件的原因,解决办法是: 110 | 111 | ```js 112 | 113 | handleClick (e) { 114 | // 将这句 115 | // e.stopPropagation() 116 | // 替换成 117 | e.nativeEvent.stopImmediatePropagation() 118 | } 119 | 120 | render () { 121 | return ( 122 |
click
123 | ) 124 | } 125 | ``` 126 | 127 | 关于 `stopImmediatePropagation` 的信息可以看这里:[stopImmediatePropagation](/note/dom/dom-event?id=event-stopimmediatepropagation)。 128 | 129 | 附上关于这个问题的参考链接:[ReactJS SyntheticEvent stopPropagation() only works with React events?](https://stackoverflow.com/questions/24415631/reactjs-syntheticevent-stoppropagation-only-works-with-react-events) -------------------------------------------------------------------------------- /docs/note/linux/ubuntu-utils.md: -------------------------------------------------------------------------------- 1 | ## Ubuntu使用知识积累 2 | 3 | 4 | 5 | #### Ubuntu下安装git和node 6 | 7 | 当时在做项目迁移的时候并没有服务器,所以只能用旁边的一台windows安装个双系统,来充当服务器了,安装方法网上很多,不过我推荐一个朋友的博客中的一篇文章,我所安装的Ubuntu的版本是14.04的,并且是server版的,与该博客不符,但是原理相同,贴出[博客地址](https://xuri.me/2013/04/09/easybcd-install-ubuntu.html)。 8 | 9 | #### Ubuntu 下安装 git 10 | 11 | Ubuntu的包管理是apt-get,可以使用如下命令安装git: 12 | 13 | ```sh 14 | sudo apt-get install git 15 | ``` 16 | 17 | 但是这样安装的git版本是比较老的,并不推荐这样安装,如果想安装最新可用版本的可以执行下面的命令: 18 | 19 | ```sh 20 | sudo add-apt-repository ppa:git-core/ppa 21 | sudo apt-get update 22 | sudo apt-get install git 23 | ``` 24 | 25 | 如果提示 26 | 27 | ```sh 28 | sudo: add-apt-repository: command not found 29 | ``` 30 | 31 | 则需要先安装依赖包: 32 | 33 | ```sh 34 | sudo apt-get install python-software-properties 35 | ``` 36 | 37 | 然后再次执行那三条命令即可,安装完成后可以查看git版本,确定安装是否成功 38 | 39 | ``` 40 | git --version 41 | ``` 42 | 43 | #### Ubuntu 下安装 node 44 | 45 | 首先非常推荐大家使用 [nvm](https://github.com/creationix/nvm/blob/master/README.md#installation) 来管理 `node`,nvm 允许我们安装多个版本的node并且轻松管理、切换、使用。 46 | 47 | 如果要采用的源码编译安装的方式,首先去[node得资源列表](https://nodejs.org/dist/)找到你要现在的资源包,如我所选择的资源包是: 48 | 49 | https://nodejs.org/dist/v5.0.0/node-v5.0.0.tar.gz 50 | 51 | 选择好资源包后执行下面的命令即可: 52 | 53 | ``` 54 | # 下载资源 55 | wget https://nodejs.org/dist/v5.0.0/node-v5.0.0.tar.gz 56 | # 解压 57 | tar zxvf node-v5.0.0.tar.gz 58 | # 编译安装 59 | cd node-v5.0.0 60 | ./configure 61 | make 62 | make install 63 | cp /usr/local/bin/node /usr/sbin/ 64 | ``` 65 | 66 | 查看是否安装成功 67 | 68 | ``` 69 | node -v 70 | ``` 71 | 72 | 如果正确输出版本号,那么说明我们安装成功了,也有其他安装方式,大家去网上看看吧 73 | 74 | #### Ubuntu默认是用dash解析shell脚本 75 | 76 | 由于项目中我使用到了shell脚本,所以当项目迁移到Ubuntu的时候报错: 77 | 78 | ``` 79 | source xxx.sh时提示 source: not found 80 | ``` 81 | 82 | 原因是当时项目在Mac下跑得,Mac解析shell脚本使用的是bash,而Ubuntu默认是用的dash,我们可以使用下面的命令查看: 83 | 84 | ``` 85 | ls -l `which sh` 86 | ``` 87 | 88 | 如下图: 89 | 90 | 91 | 92 | 我们可以修改Ubuntu解析shell的方式: 93 | 94 | ``` 95 | sudo dpkg-reconfigure dash 96 | ``` 97 | 98 | 执行该命令后会出现如下图所示的选项,我们只需要选择否即可: 99 | 100 | 101 | 102 | 这时在查看一下Ubuntu解析shell的方式: 103 | 104 | ``` 105 | ls -l `which sh` 106 | ``` 107 | 108 | 如下图,已经变为了 bash 109 | 110 | 111 | 112 | #### Ubuntu下mongodb启动失败 113 | 114 | 如下图: 115 | 116 | 117 | 118 | 造成启动失败的原因有很多,其中最常见的就是非正常退出,下一次再启动的时候就会遇到如上情况,在Mac下或者windows下,一般我们需要把mongod.lock文件删除就可以成功重启了,但是今天在Ubuntu下却不行,在Ubuntu下记得要把mongodb-27017.sock文件也删除掉: 119 | 120 | ``` 121 | sudo rm -rf /var/lib/mongodb/mongod.lock 122 | sudo rm -rf /tmp/mongodb-27017.sock 123 | ``` 124 | 125 | 之后重启: 126 | 127 | ``` 128 | sudo service mongod start 129 | ``` 130 | 131 | 就可以正常启动了 132 | 133 | #### nohup 命令让进程在后台运行 134 | 135 | 当时我是使用ssh远程登录的,又不想多开窗口,所以希望node服务在后台运行,这时候可以使用nohup命令,该命令能够让进程在后台运行,nohup命令的格式如下: 136 | 137 | ``` 138 | nohup Command [ Arg ... ] [ & ] 139 | ``` 140 | 141 | 例如我想要node的web服务在后台运行,在我的项目中可以执行下面的代码带到目的: 142 | 143 | ``` 144 | nohup node admin.js & 145 | ``` 146 | 147 | #### Ubuntu下添加用户并赋予root权限 148 | 149 | 在Ubuntu下可以使用 adduser 命令添加一个用户: 150 | 151 | ``` 152 | adduser 153 | ``` 154 | 155 | 执行该命令之后会让你为该用户设置密码,但是这样添加的用户是没有使用root权限的用户,我们需要一些配置来赋予该用户使用root权限的能力,修改配置需要编辑 /etc/sudoers 文件,这个文件默认是只读的,所以第一步修改文件为可写: 156 | 157 | ``` 158 | chmod u+w /etc/sudoers 159 | ``` 160 | 161 | 然后编辑该文件: 162 | 163 | ``` 164 | sudo vim /etc/sudoers 165 | ``` 166 | 167 | 找到下面这句话: 168 | 169 | ``` 170 | # Allow members of group sudo to execute any command 171 | %sudo ALL=(ALL:ALL) ALL 172 | ``` 173 | 174 | 在其 %sudo ALL=(ALL:ALL) ALL 下面再添加一行: 175 | 176 | ``` 177 | %username ALL=(ALL:ALL) ALL 178 | ``` 179 | 180 | username为你创建用户的用户名,之后保存退出,并还原 sudoers 文件只读的权限: 181 | 182 | ``` 183 | chmod u-w /etc/sudoers 184 | ``` 185 | 186 | 接下来新用户就可以登录并使用root权限了。 187 | 188 | #### 安装完Ubuntu后乱码 189 | 190 | 修改/etc/default/locale 191 | 192 | ``` 193 | sudo vim /etc/default/locale 194 | ``` 195 | 196 | 将: 197 | 198 | ``` 199 | LANG=zh_CN.UTF-8 200 | LANGUAGE=zh_CN:zh 201 | ``` 202 | 203 | 修改为: 204 | 205 | ``` 206 | LANG="en_US.UTF-8" 207 | LANGUAGE="en_US:en" 208 | ``` 209 | 210 | 最后reboot一下就可以了: 211 | 212 | ``` 213 | sudo reboot 214 | ``` 215 | 216 | #### 查看端口占用情况,并杀掉进程 217 | 218 | ```sh 219 | # 其中 xxxx 代表你要查的端口号 220 | lsof -i:xxxx 221 | ``` 222 | 223 | 上面的命令会列出占用该端口的进程,找到对应的PID,杀掉即可: 224 | 225 | ```sh 226 | # 其中 xxxxx 表示PID 227 | kill -9 xxxxx 228 | ``` -------------------------------------------------------------------------------- /docs/note/specification/eslintrc.md: -------------------------------------------------------------------------------- 1 | 根据 JavaScript 编码规范编写的 eslist 配置 2 | 3 | ```js 4 | module.exports = { 5 | "env": { 6 | "browser": true, 7 | "es6": true 8 | }, 9 | "parserOptions": { 10 | "sourceType": "module" 11 | }, 12 | /* 需要安装 eslint-plugin-vue */ 13 | "plugins": ["vue"], 14 | "rules": { 15 | /* 缩进 tab */ 16 | "indent": [ 17 | "error", 18 | 4, 19 | { 20 | "SwitchCase": 1 21 | } 22 | ], 23 | /* 使用单引号 */ 24 | "quotes": [ 25 | "error", 26 | "single" 27 | ], 28 | /* 分号必须 */ 29 | "semi": [ 30 | "error", 31 | "always" 32 | ], 33 | /* 函数不允许有重复的参数 */ 34 | "no-dupe-args": "error", 35 | /* 不允许有多余的分号 */ 36 | "no-extra-semi": "error", 37 | /* 不允许有多余的空格 */ 38 | "no-multi-spaces": "error", 39 | /* 禁止变量重复声明 */ 40 | "no-redeclare": "error", 41 | /* 禁止未使用的变量 */ 42 | "no-unused-vars": "error", 43 | /** 44 | * 逗号前不可以有空格,逗号后必须有空格。 45 | * 变量声明: 46 | * ✅var a = 1, b = 2 47 | * 数组: 48 | * ✅[1, 2] 49 | * 对象: 50 | * ✅{a: 1, b: 2} 51 | * 函数参数: 52 | * ✅function (a, b) {} 53 | * ✅fn(1, 2) 54 | */ 55 | "comma-spacing": [ 56 | "error", 57 | { 58 | "before": false, 59 | "after": true 60 | } 61 | ], 62 | /** 63 | * 调用函数时,禁止函数名称与括号间的间隔 64 | * ❎:fn () 65 | * ✅:fn() 66 | */ 67 | "func-call-spacing": "error", 68 | /** 69 | * 对象字面量冒号前不允许有空格,冒号后必须且只有一个空格 70 | * ✅:{a: 1} 71 | * ✅:{ 72 | * a: 1, 73 | * b: 2 74 | * } 75 | */ 76 | "key-spacing": [ 77 | "error", 78 | { 79 | "beforeColon": false, 80 | "afterColon": true, 81 | "mode": "strict" 82 | } 83 | ], 84 | /** 85 | * 关键字前后各至少一个空格,包括的关键字查看: 86 | * http://eslint.org/docs/rules/keyword-spacing#rule-details 87 | */ 88 | "keyword-spacing": [ 89 | "error", 90 | ], 91 | /** 92 | * 变量声明后强制一个空行,生命变量包括使用 var let const 等 93 | */ 94 | "newline-after-var": "error", 95 | // 语句块前必须要有空格,语句块只得就是花括号 {} 96 | "space-before-blocks": "error", 97 | /** 98 | * function 关键之与后边第一个圆括号之间必须有空格。包括:匿名函数、命名函数、async修饰的箭头函数 99 | * ✅:function () {} 100 | * ✅:function set () {} 101 | * ✅:class Foo { 102 | * constructor () { 103 | * // ... 104 | * } 105 | * } 106 | * ✅:let foo = { 107 | * bar () { 108 | * // ... 109 | * } 110 | * }; 111 | * ✅:let foo = async (a) => await a 112 | */ 113 | "space-before-function-paren": [ 114 | "error", 115 | { 116 | "anonymous": "always", 117 | "named": "always", 118 | "asyncArrow": "always" 119 | } 120 | ], 121 | // 多元运算符前后要有空格 122 | "space-infix-ops": "error", 123 | /** 124 | * 一元关键字运算符(操作符)后必须有空格:new, delete, typeof, void, yield 等 125 | * 一元运算符前后不能有空格如:-, +, --, ++, !, !! 等 126 | * ✅:new Foo(); 127 | * ✅:++foo; 128 | * ✅:foo--; 129 | * ✅:-foo; 130 | * ✅:+"3"; 131 | */ 132 | "space-unary-ops": [ 133 | "error", 134 | { 135 | "words": true, 136 | "nonwords": false 137 | } 138 | ], 139 | 140 | // ================================ 以下是 ES6 规范 ================================ 141 | 142 | /** 143 | * 箭头函数中的箭头前后必须各有一个空格 144 | * ✅:() => 1 145 | */ 146 | "arrow-spacing": "error", 147 | /** 148 | * 继承时子类的 constructor 方法中必须调用 super 方法 149 | * ✅:class A extends B { 150 | * constructor() { 151 | * super(); 152 | * } 153 | * } 154 | */ 155 | "constructor-super": "error", 156 | // 不允许给常量(const)赋值 157 | "no-const-assign": "error", 158 | // 在 constructor 中不允许在 super 方法被调用之前调用 this 或 super 关键字 159 | "no-this-before-super": "error", 160 | // 不允许使用 var 声明变量,使用 let 或 const 代替 161 | "no-var": "error" 162 | } 163 | } 164 | ``` -------------------------------------------------------------------------------- /docs/note/css3/selector.md: -------------------------------------------------------------------------------- 1 | ## CSS选择器整理 2 | 3 | #### 基本选择器 4 | 5 | | 选择器 | 名称 | 描述 | 6 | | ------------- |:-------------:| :----- | 7 | | * | 通配选择器 | 选择文档中所有的HTML元素 | 8 | | E | 元素选择器 | 选择指定类型的HTML元素,例如:li、p等等 | 9 | | #id | ID 选择器 | 选择ID属性值为 “id” 的元素 | 10 | | .class | 类选择器 | 选择class属性值为 “class” 的一组元素 | 11 | | selector1, selector2 | 群组选择器 | 将每一个选择器匹配的集合合并 | 12 | 13 | #### 层次选择器 14 | 15 | | 选择器 | 名称 | 描述 | 16 | | ------------- |:-------------:| :-----| 17 | | selector1 selector2 | 后代选择器(包含选择器) | 选择selector2所匹配的一组元素,且selector2是selector1的后代元素 | 18 | | selector1 > selector2 | 子选择器 | 选择selector2所匹配的一组元素,且selector2是selector1的直接子元素 | 19 | | selector1 + selector2 | 相邻兄弟选择器 | 选择selector2所匹配的元素,且selector2位于selector1的后面 | 20 | | selector1 ~ selector2 | 通用选择器 | 选择selector2所匹配的一组元素,且该组元素位于selector1后面 | 21 | 22 | #### 伪类选择器 23 | 24 | ##### 动态伪类选择器 25 | 26 | | 选择器 | 名称 | 描述 | 27 | | ------------------|:-------------------:| :-----| 28 | | selector:link | 连接伪类选择器 | 选择selector所匹配的元素,且该元素被定义了超链接并未被访问过,常用于a标签 | 29 | | selector:visited | 连接伪类选择器 | 选择selector所匹配的元素,且该元素被定义了超链接并已被访问过,常用于a标签 | 30 | | selector:active | 用户行为伪类选择器 | 选择selector所匹配的元素,且该元素被激活,常用于连接或按钮上 | 31 | | selector:hover | 用户行为伪类选择器 | 选择selector所匹配的元素,且用户鼠标停留在该元素上 | 32 | | selector:focus | 用户行为伪类选择器 | 选择selector所匹配的元素,且该元素获得焦点 | 33 | 34 | ##### 目标伪类选择器 35 | 36 | | 选择器 | 名称 | 描述 | 37 | | ------------------|:-------------------:| :-----| 38 | | selector:target | 目标伪类选择器 | 选择selector所匹配的元素,且该元素的ID值等于页面URL片段标识符(即#号后面的值)的值 | 39 | 40 | ##### 语言伪类选择器 41 | 42 | | 选择器 | 名称 | 描述 | 43 | | ------------------|:-------------------:| :-----| 44 | | selector:lang(language) | 语言伪类选择器 | 选择selector所匹配的元素,且该元素指定了值为 language 的 lang 属性,多用于多语言网站不同样式的处理上 | 45 | 46 | ##### UI元素状态伪类选择器 47 | 48 | | 选择器 | 名称 | 描述 | 49 | | ------------------|:-------------------:| :-----| 50 | | selector:checked | 选中状态伪类选择器 | 匹配选中的单选按钮/复选按钮 | 51 | | selector:enabled | 启用状态伪类选择器 | 选择selector所匹配的表单元素,其该元素为启用状态 | 52 | | selector:disabled | 禁用状态伪类选择器 | 选择selector所匹配的表单元素,其该元素为禁用状态 | 53 | 54 | ##### 结构伪类选择器 55 | 56 | | 选择器 | 描述 | 57 | | -----------------------|:-------------------:| 58 | | selector:first-child | 选择selector所匹配的元素,且该元素是其父元素的第一个子元素(不算文本节点,也不区分元素类型),等价于 selector:nth-child(1) | 59 | | selector:last-child | 选择selector所匹配的元素,且该元素是其父元素的最后一个子元素(不算文本节点,也不区分元素类型),等价于 selector:nth-last-child(1) | 60 | | selector:nth-child(n) | 选择selector所匹配的元素,且该元素是其父元素的第n个子元素(不算文本节点,也不区分元素类型),其中 n 的值可以使正数(1、2、3...),也可以是关键字(even、odd),也可以是公式(2n+1、2n-1...),且 n 的起始值是1而不是0 | 61 | | selector:nth-last-child(n) | 选择selector所匹配的元素,且该元素是其父元素的倒数第n个子元素(不算文本节点,也不区分元素类型) | 62 | | selector:first-of-type | 选择selector所匹配的元素,且该元素是其父元素的第一个特定类型的子元素(不算文本节点,区分元素类型) | 63 | | selector:last-of-type | 选择selector所匹配的元素,且该元素是其父元素的最后一个特定类型的子元素(不算文本节点,区分元素类型) | 64 | | selector:nth-of-type(n) | 选择selector所匹配的元素,且该元素是其父元素的第n个特定类型的子元素(不算文本节点,区分元素类型) | 65 | | selector:nth-last-of-type(n) | 选择selector所匹配的元素,且该元素是其父元素的倒数第n个特定类型的子元素(不算文本节点,区分元素类型) | 66 | | selector:only-child | 选择selector所匹配的元素,且其父元素只有它一个子元素(不算文本节点,也不区分元素类型) | 67 | | selector:only-of-type | 选择selector所匹配的元素,且其父元素只有它一个特定类型的子元素(不算文本节点,区分元素类型) | 68 | | selector:root | 选择selector所匹配的元素所在文档的根元素,即html元素 | 69 | | selector:empty | 选择selector所匹配的元素,且该元素没有任何子元素(包括文本节点) | 70 | 71 | 72 | ##### 否定伪类选择器 73 | 74 | | 选择器 | 描述 | 75 | | -----------------------|:-------------------:| 76 | | selector1:not(selector2) | 选择所有不包含selector2的selector1元素 | 77 | 78 | #### 伪元素 79 | 80 | | 选择器 | 描述 | 81 | | -----------------------|:-------------------:| 82 | | selector::first-letter | 选择文本块的第一个字母 | 83 | | selector::first-line | 选择文本块的第一行文本 | 84 | | selector::before | 用来为selector所匹配的元素的所有子元素前面插入内容,插入的内容不会成为DOM的一部分,但是依然可以设置样式 | 85 | | selector::after | 用来为selector所匹配的元素的所有子元素后面插入内容,插入的内容不会成为DOM的一部分,但是依然可以设置样式 | 86 | | selector::selection | 匹配被鼠标选中的文本 | 87 | 88 |

双冒号代表伪元素,单冒号代表伪类

89 | 90 | #### 属性选择器 91 | 92 | | 选择器 | 描述 | 93 | | -----------------|:-------------------:| 94 | | selector[attr] | 选择selector所匹配的元素,且该元素拥有attr属性。可以省略selector,表示匹配所有拥有attr属性的元素 | 95 | | selector[attr=val] | 选择selector所匹配的元素,且该元素拥有值为val的attr属性。可以省略selector,表示匹配所有拥有值为val的attr属性的元素 | 96 | | selector[attr~=val] | 选择selector所匹配的元素,且该元素attr属性值具有多个空格分隔的值,其中一个值等于val。可以省略selector| 97 | | selector[attr*=val] | 选择selector所匹配的元素,且该元素attr属性值的任意位置包含val。可以省略selector| 98 | | selector[attr^=val] | 选择selector所匹配的元素,且该元素attr属性值以val开头。可以省略selector| 99 | | selector[attr$=val] | 选择selector所匹配的元素,且该元素attr属性值以val结尾。可以省略selector| 100 | 101 | 除了上表中介绍的属性选择器之外,还有一个属性选择器没有写在其中,如下: 102 | 103 | ```css 104 | /* 选择selector所匹配的元素,且该元素拥有值以val或val-开头的attr属性,可以省略selector */ 105 | selector[attr|=val] { 106 | 107 | } 108 | ``` 109 | 110 | 你可能会问我为什么没有这个选择器写在上面的表格中,是这样的,这个选择器中有字符 `|`,这个字符在markdown表格中是表格的分界线。日了狗了..... -------------------------------------------------------------------------------- /docs/note/basis/string.md: -------------------------------------------------------------------------------- 1 | ## 一、创建字符串 2 | 3 | #### 字面量创建 4 | 5 | ```js 6 | var str = 'abc' 7 | ``` 8 | 9 | #### 包装类型 10 | 11 | ```js 12 | var str = new String('abc') 13 | ``` 14 | 15 | #### ES6模板字符串 16 | 17 | ```js 18 | var str = `abc` 19 | ``` 20 | 21 | #### 使用 String.fromCharCode() / 【ES6】String.fromCodePoint() 将字符编码转成字符串 22 | 23 | ## 二、字符串基本包装类型的方法 24 | 25 | #### 字符方法: 26 | 27 | ###### str.charAt() 28 | * 描述:访问字符串中特定位置的字符 29 | * 参数:一个数字,表示字符串的位置 30 | * 返回值:返回该位置的字符 31 | 32 | ###### str.charCodeAt() 33 | * 描述:功能用法与 charAt 相同,唯一不同的是,charCodeAt 返回的是字符编码而不是字符 34 | * 返回值:字符编码 35 | 36 | ###### 【ES6】str.codePointAt() 37 | * 描述:弥补 `charCodeAt` 不能正确处理需要4个字节表示的字符的缺陷,`codePointAt` 能够正确处理4个字节储存的字符,返回一个字符的码点。可以使用`codePointAt` 来检测一个字符是由两个字节组成,还是由4个字节组成 38 | * 参数:一个数字,字符在字符串中的位置 39 | * 返回值:字符编码 40 | 41 | #### 字符串操作方法 42 | 43 | ###### str.concat() 44 | * 描述:连接多个字符串为一个字符串 45 | * 参数:任意多个参数,要依次连接的字符串 46 | * 返回值:新字符串 47 | * 是否改变原字符串:否 48 | 49 | ###### str.slice() 50 | ###### str.substr() 51 | ###### str.substring() 52 | ``` 53 | 描述:上面三个方法都能基于已有字符串创建新字符串,但不会影响原字符串。 54 | 参数:第一个参数:指定起始位置 55 | 如果传正数:三个函数行为相同 56 | 如果传负数:substring 会把负数转为0,相当于 substring(0) 57 | 第二个参数:可选 58 | 如果是正数:substr 指定的是返回字符串的数量,substring 和 slice 指定的是字符串的结束位置(返回字符串不包含该位置) 59 | 如果是负数:substr 和 substring 都会把负数变为0 60 | substring 会自动调整 0 的位置比如 【substring(3, 0) ==>转化为==> substring(0, 3)】 61 | 返回值:新字符串 62 | 是否改变原字符串:否 63 | ``` 64 | 65 | ###### str.trim() 66 | * 描述:去掉字符串的前后空格 67 | * 参数:无 68 | * 返回值:新字符串 69 | * 是否改变原字符串:否 70 | 71 | ###### str.toLowerCase() 72 | ###### str.toUpperCase() 73 | ###### str.toLocaleLowerCase() 74 | ###### str.toLocaleUpperCase() 75 | * 描述:字符串转大小写 76 | * 参数:无 77 | * 返回值:转变后的字符串 78 | * 是否改变原字符串:否 79 | 80 | ###### 【ES6】str.repeat() 81 | * 描述:方法返回一个新字符串,表示将原字符串重复n次 82 | * 参数: 83 | 84 | 一个大于 `-1` 的数字,表示重复的次数(传递小于 `-1` 的数字会报错,传递小数会取整,之所以可以传递 -1 ~ 0 之间的数字是因为,-1 ~ 0之间的数字取整后都为0,并不会报错) 85 | 86 | * 返回值:新字符串 87 | * 是否改变原字符串:否 88 | 89 | ###### 【ES7】padStart() 90 | ###### 【ES7】padEnd() 91 | ``` 92 | 描述:对字符串进行补全,padStart 前补全,padEnd 后补全 93 | 参数:第一个参数:字符串的最小长度 94 | 第二个参数:可选,用来填充的字符串,默认用空格填充 95 | 是否改变原字符串:否 96 | ``` 97 | 98 | #### 字符串位置方法 99 | 100 | ###### str.indexOf() 101 | ###### str.lastIndexOf() 102 | ``` 103 | 描述:从一个字符串中搜索子字符串,并返回子字符串第一次出现的位置(不同的是:indexOf 从前往后找, lastIndexOf 从后往前找) 104 | 参数:第一个参数:要查找的子字符串 105 | 第二个参数:查找的起始位置 106 | 返回值:子字符串第一次出现的位置,未找到返回-1 107 | 是否改变原字符串:否 108 | ``` 109 | 110 | ###### 【ES6】str.includes() 111 | ###### 【ES6】str.startsWith() 112 | ###### 【ES6】str.endsWith() 113 | ``` 114 | 描述:上面三个函数都是查找字符串中是否包含子字符串 115 | str.includes() :字符串是否包含子字符串 116 | str.startsWith() :字符串是否以子字符串开头 117 | str.endsWith() :字符串是否以子字符串结尾 118 | 参数:第一个参数:子字符串 119 | 第二个参数:查找的起始位置【注意:给endsWith指定第二个参数n是,它指的是前n个字符】 120 | 返回值:满足条件返回 true,否则返回 false 121 | ``` 122 | 123 | 124 | #### 模式匹配方法 125 | 126 | ###### str.match() 127 | * 描述:通过模式匹配字符串 128 | * 参数:字符串 / 正则表达式 / RegExp 对象 129 | * 返回值:如果匹配成功则返回数组,数组的第一项是与整个模式匹配的项,后面的每一项保存着与正则表达式中的捕获组匹配的字符串。如果匹配失败返回 `null` 130 | 131 | ###### str.search() 132 | * 描述:和 indexOf 功能相似,唯一不同的是:该方法支持正则 133 | * 参数:字符串 / 正则表达式 / RegExp 对象 134 | * 返回值:返回第一个匹配项的索引,没有找到返回 -1 135 | 136 | ###### str.replace() 137 | * 描述:通过模式替换匹配的字符串 138 | * 参数: 139 | ``` 140 | 第一个参数:字符串 / 正则表达式 141 | 第二个参数:字符串 / 函数 142 | 如果第二个参数是字符串:可以使用特殊的字符序列: 143 | $$ =====> $ 144 | $& =====> 匹配整个模式的字符串,与RegExp.lastMatch的值相同 145 | $' =====> 匹配的子字符串之后的子字符串,与RegExp.rightContext的值相同 146 | $` =====> 匹配的子字符串之前的子字符串,与RegExp.leftContext的值相同 147 | $n =====> 匹配第n(0 ~ 9)个捕获组的子字符串,如果正则表达式中没有捕获组,则使用空字符串 148 | $nn =====> 匹配第nn(01 ~ 99)个捕获组的子字符串,如果正则表达式中没有捕获组,则使用空字符串 149 | 如果第二个参数是函数:(该函数接收的参数与正则表达式有关) 150 | 1、正则表达式只有一个匹配项,即无捕获组 151 | 该函数接收三个参数:第一个:模式的匹配项 152 | 第二个:模式匹配项在字符串中的位置 153 | 第三个:原始字符串 154 | 2、正则表达式中定义了多个捕获组 155 | 该函数接收参数如下:第一个:模式的匹配项 156 | 第二个 ~ 第n个:第二个捕获组的匹配项 ~ 第n个捕获组的匹配项 157 | 最后两个参数分别是:模式匹配项在字符串中的位置 和 原始字符串 158 | ``` 159 | * 返回值:新字符串 160 | * 是否改变原字符串:否 161 | 162 | ###### str.split() 163 | * 描述:基于指定的字符或模式,将字符串分割成数组 164 | * 参数: 165 | * 第一个参数:字符串 / 正则表达式 166 | * 第二个参数:指定数组的长度 167 | 168 | #### 字符串比较方法 169 | 170 | ###### str.localeCompare(str2) 171 | * 描述:对两个字符串进行比较 172 | * 参数:字符串 173 | * 返回值: 174 | ``` 175 | 如果 str > str2 ,返回 1 176 | 如果 str = str2 ,返回 0 177 | 如果 str < str2 ,返回 -1 178 | ``` 179 | 180 | ###### 【ES6】str1.normalize() 181 | * 描述:ES6提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为Unicode正规化。 182 | * 参数: 183 | ``` 184 | 'NFC' : 185 | 默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。 186 | 187 | 'NFD' : 188 | 表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。 189 | 'NFKC' : 190 | 表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来举例,normalize方法不能识别中文。) 191 | 'NFKD' : 192 | 表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。 193 | ``` 194 | #### String构造函数的静态方法 195 | 196 | ###### String.fromCharCode() 197 | * 描述:接受一至多个字符编码,然后将他们转换成一个字符串 198 | * 参数:1 ~ n 个字符编码 199 | * 返回值:字符串 200 | 201 | ###### 【ES6】String.fromCodePoint() 202 | * 描述:接受一至多个字符编码,然后将他们转换成字符串,该方法弥补 `String.fromCharCode()` 方法不能识别字符编码大于0xFFFF的缺陷 203 | 204 | #### 字符串的其他扩展 205 | 206 | ES6为字符串添加了遍历器接口(Iterator),这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的 `for` 循环无法识别这样的码点。 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /docs/note/basis/array.md: -------------------------------------------------------------------------------- 1 | ## 一、创建数组 2 | 3 | #### 使用 new 操作符调用构造函数 4 | 5 | ```js 6 | var arr = new Array(20) // 创建了一个包含20项的数组 7 | var arr = new Array('a', 'b', 'c') // 创建了包含字符串 a b c 的三项数组 8 | ``` 9 | 10 | #### 省略 new 操作符 11 | 12 | ```js 13 | var arr = Array(20) 14 | var arr = Array('a', 'b', 'c') 15 | ``` 16 | 17 | #### 数组字面量 18 | 19 | ```js 20 | var arr = [] 21 | var arr = ['a', 'b', 'c'] 22 | ``` 23 | 24 | #### 【ES6】、Array.of() 25 | 26 | * 描述: 27 | 28 | 用于创建数组,用法和 new Array() 一样。弥补 Array() 构造函数的不足(即参数不同,行为不同),Array.of() 的行为始终一致,将传入的值作为数组的项,产生数组 29 | 30 | * 参数:任意数量任意值 31 | * 返回值:创建的数组 32 | 33 | #### 【ES6】、Array.from(obj, func, context) 34 | 35 | * 描述:用于将 类数组对象(拥有length属性的对象) 和 可遍历对象(部署iterable接口的对象,包括 Set/Map) 转为真正的数组 36 | * 参数: 37 | * `{Object} obj` 要转为数组的对象 38 | * `{Function} func` 一个函数,功能类似于数组的map方法,对每一个对象属性执行该函数,并返回由该函数的返回值组成的数组 39 | * `{Object} context` 第二个函数参数的执行环境(this指向) 40 | * 返回值:生成的数组 41 | 42 | ## 二、数组检测 43 | 44 | #### 使用 instanceof 操作符 45 | 46 | ```js 47 | if(value instanceof Array){ 48 | // 对数组执行某些操作 49 | } 50 | ``` 51 | 52 | #### 使用 Array.isArray() 方法 53 | 54 | ```js 55 | if(Array.isArray(value)){ 56 | // 对数组执行某些操作 57 | } 58 | ``` 59 | 60 | #### 使用 Object.prototype.toString.call() 61 | 62 | ```js 63 | if(Object.prototype.toString.call(obj) === '[object Array]'){ 64 | // 对数组执行某些操作 65 | } 66 | ``` 67 | 68 | ## 三、数组方法 69 | 70 | #### 转换方法 71 | 72 | ###### toLocalString() 73 | ###### toString() 74 | ###### valueOf() 75 | ###### join() 76 | 77 | 78 | #### 栈/队列 方法 79 | 80 | ###### push() 81 | * 描述:向数组的尾部追加项,并返回数组长度 82 | * 参数:n多个值,会依次推入数组尾部 83 | * 返回值: 84 | * `{Number}` 数组长度 85 | * 是否改变原数组:是 86 | 87 | ###### pop() 88 | * 描述:移除数组最后一项,并返回该项 89 | * 参数:无 90 | * 返回值:返回移除项 91 | * 是否改变原数组:是 92 | 93 | ###### shift() 94 | * 描述:移除数组第一项,并返回该项 95 | * 参数:无 96 | * 返回值:返回移除项 97 | * 是否改变原数组:是 98 | 99 | ###### unshift() 100 | * 描述:在数组最前端添加项,并返回数组长度 101 | * 参数:n多个值,会依次添加到数组前端 102 | * 返回值: 103 | * `{Number}` 数组长度 104 | * 是否改变原数组:是 105 | 106 | #### 排序方法 107 | 108 | ###### reverse() 109 | * 描述:反转数组项的顺序 110 | * 参数:无 111 | * 返回值:修改后的数组 112 | * 是否改变原数组:是 113 | 114 | ###### sort() 115 | * 描述:对数组进行排序,默认情况下,按照升序排序,sort方法调用每个数组项的 toString() 方法,进行字符串比较 116 | * 参数:【可选】函数 117 | ``` 118 | 1、如果第一个参数 应该位于 第二个参数 之前 返回一个负数 119 | 2、如果第一个参数 应该位于 第二个参数 之后 返回一个正数 120 | 3、并列返回0 121 | ``` 122 | * 返回值:排序后的数组 123 | * 是否改变原数组:是 124 | 125 | #### 操作方法 126 | 127 | ###### concat() 128 | * 描述:基于当前数组的所有项创建一个新数组 129 | * 参数:【可选】任意数量的任意值 130 | * 返回值:返回新数组 131 | * 是否改变原数组:否 132 | 133 | ###### slice() 134 | * 描述:基于当前数组一或多个项创建新数组(截取数组片段) 135 | * 参数:接收一或两个参数,分别是返回项的起始和结束位置 136 | * 返回值: 137 | ``` 138 | 1、只传起始位置(即一个参数) 139 | 返回:从起始位置到数组末尾的项组成的数组 140 | 2、传递两个参数 141 | 返回:从起始位置到结束位置(不包含结束位置)的项组成的数组 142 | 3、传递负数 143 | 返回:会用数组长度加上该负数来确定相应的位置,并按照1、2的规则返回新数组 144 | 4、起始位置大于结束位置 145 | 返回:空数组 146 | ``` 147 | * 是否改变原数组:否 148 | 149 | ###### splice() 150 | * 描述:对数组的项进行 删除、插入、替换 等操作,功能十分强大 151 | * 参数: 152 | ``` 153 | 第一个参数:要删除的第一项的位置 154 | 第二个参数:要删除的项数 155 | 第三个参数(第四个、第五个......):插入的项 156 | ``` 157 | * 返回值:由删除的项组成的数组 158 | * 是否改变原数组:是 159 | 160 | ###### 【ES6】copyWithin() 161 | * 描述:在数组内部,将指定位置的成员拷贝到其他位置(会覆盖原有成员) 162 | * 参数: 163 | ``` 164 | 第一个参数:要拷贝的目标位置(target) 165 | 第二个参数:从该位置读取数据,默认是0,负值表示倒数 166 | 第三个参数:读取到该位置结束,默认是 数组的长度,不包含该位置,负值表示倒数 167 | ``` 168 | * 返回值:修改后的数组 169 | * 是否改变原数组:是 170 | 171 | ###### 【ES6】fill() 172 | * 描述:使用给定值,填充数组 173 | * 参数: 174 | ``` 175 | 第一个参数:填充的值 176 | 第二个参数:填充的起始位置 177 | 第三个参数:填充的结束位置(不包含该位置) 178 | ``` 179 | * 返回值:修改后的数组 180 | * 是否改变原数组:是 181 | 182 | #### 查找/位置方法 183 | 184 | ###### indexOf() 185 | ###### lastIndexOf() 186 | * 描述:在数组中查找某一项的位置 indexOf() 从前往后查找, lastIndexOf() 从后往前查找 187 | * 参数: 188 | ``` 189 | 第一个参数:要查找的项 190 | 第二个参数:查找起点位置索引 191 | ``` 192 | * 返回值:返回查找项在数组中的位置,未找到返回-1 193 |

注意:在查找过程中使用全等操作符(===)

194 | 195 | ###### 【ES6】find() 196 | * 描述:用于找到第一个符合条件的数组成员 197 | * 参数:一个函数,函数的参数:1、项。2、项的索引。3、数组对象本身 198 | * 返回值:如果有符合添加的项,返回该项的值,如果没有找到符合条件的项,返回 `undefined` 199 | 200 | ###### 【ES6】findIndex() 201 | * 描述:与find()方法功能一样,唯一不同的是,返回的是项的位置,未找到返回 -1 202 | 203 | ###### 【ES7】includes() 204 | * 描述:查找数组中是否包含给定值 205 | * 参数: 206 | ``` 207 | 第一个参数:要查找的值 208 | 第二个参数:查找的起始位置,默认是0,负数表示倒数,超出范围会重置为0 209 | ``` 210 | * 返回值:`true` 包含, `false` 不包含 211 |

includes 相比于 indexOf 的优势有两点:1、更加语义化,不需要判断返回值是否为 -1。2、由于 indexOf 底层在判断是否相等时使用的是全等操作符 ===,这会导致使用 indexOf 查找 NaN 时查不到,而 includes 则不存在这样的问题

212 | 213 | #### 迭代方法 214 | 215 | ###### forEach() 216 | ###### every() 217 | ###### some() 218 | ###### filter() 219 | ###### map() 220 | ``` 221 | 描述:迭代数组,对数组的每一项执行给定函数 222 | 参数:第一个参数:函数 223 | 函数接收三个参数 224 | 1、数组的项 225 | 2、该项在数组中的位置 226 | 3、数组对象本身 227 | 第二个参数:第一个参数的执行环境(this指向) 228 | 返回值: 229 | forEach() 无返回值 230 | every() 对数组运行给定函数,如果该函数对每一项都返回true,则返回true 231 | some() 对数组运行给定函数,如果该函数对任意一项返回true,则返回true 232 | filter() 对数组执行给定函数,返回该函数返回true的项组成的数组 233 | map() 对数组执行给定函数,返回每次函数调用结果组成的数组 234 | ``` 235 | ###### 【ES6】entries(),keys()和values() 236 | * 描述:`entries()`,`keys()` 和 `values()` 都用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用 `for...of` 循环进行遍历,唯一的区别是 `keys()` 是对键名的遍历、`values()` 是对键值的遍历,`entries()` 是对键值对的遍历。 237 | 238 | #### 归并方法 239 | ###### reduce() 240 | ###### reduceRight() 241 | * 描述:迭代数组的所有项,构建一个最终的返回值 242 | * 参数: 243 | ``` 244 | 第一个参数:函数,函数接收的参数: 245 | 1、前一项(pre) 246 | 2、当前项(cur) 247 | 3、当前项的索引(index) 248 | 4、数组对象本身(array) 249 | 第二个参数:归并的初始值 250 | ``` 251 | * 返回值:迭代的最终值 252 | 253 |

254 | 注意:第一次迭代的时候,pre是数组的第一项,cur是数组的第二项,reduce() 和 reduceRight() 除了迭代方向不一致外,其他完全相同 255 |

256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /docs/note/basis/cookie-storage.md: -------------------------------------------------------------------------------- 1 | ## cookie 详谈 2 | 3 | #### 概述 4 | 5 | `cookie` 最初被设计用来存储回话信息。 6 | 7 | 我们知道 `HTTP` 是一种无状态的请求响应,即每次请求响应之后连接会立即或延时(保持一定的有效期)断开。下一次请求再重新建立连接,那么服务器如何知道你是上一次的那个连接?这个时候就需要一种方式,告诉服务器你的身份信息,`cookie` 就是来做这件事的。 8 | 9 | `cookie` 用来跟踪会话信息,第一次响应设置 `cookie`,以后的每次请求都会发送该 `cookie`。 10 | 11 | #### Set-Cookie 响应头 12 | 13 | 具体流程是: 14 | 15 | 对于一个 `HTTP` 请求,服务器可以通过 `Set-Cookie` 响应头设置 `cookie`: 16 | 17 | ``` 18 | HTTP/1.1 200 OK 19 | Set-Cookie: key=val 20 | ... 21 | ``` 22 | 23 | 上面的响应头中,设置了名字为 `key`,值为 `val` 的cookie,这里需要注意的是,`key` 和 `val` 必须是 URL 编码的(`encodeURIComponent`)。在随后的请求中,浏览器通过在请求头中添加 `Cookie` 字段,携带信息给服务器: 24 | 25 | ``` 26 | GET /index.html HTTP/1.1 27 | Cookie: key=val 28 | ... 29 | ``` 30 | 31 | #### Cookie 的限制 32 | 33 | ##### 跨域的限制 34 | 35 | 受同源策略的保护和限制,cookie 是绑定在特定的域名下的,这个限制一定程度上保护了 cookie 中的信息不回被其他域使用。 36 | 37 | ##### 数量和大小的限制 38 | 39 | cookie 是存在浏览器中的,如果一个 cookie 不是会话cookie,那么就要保存在磁盘中。这个时候,我们需要对 cookie 所能够存储的数据量进行限制,否则恶意的存储会撑爆用户的磁盘,这个限制是从数量和大小两个维度上定义的: 40 | 41 | ###### 数量的限制 42 | 43 | 数量限制是对于每个域而言的: 44 | 45 | * IE6限制每个域 `20` 个cookie 46 | * IE7限制每个域最多 `50` 个cookie 47 | * Firefox限制每个域最多 `50` 个cookie 48 | * Opera限制每个域最多 `30` 个cookie 49 | * Safari 和 Chrome 没有明确硬性规定 50 | 51 | 如果设置的 cookie 超过数量限制,各个浏览器的行为不同,有的会清空最早设置的cookie腾出空间,有的则会删除最近最少使用(LRU)的cookie腾出空间 52 | 53 | ###### 大小的限制 54 | 55 | 大多数浏览器限制整个 cookie 的长度大约为 4096B,即4KB。大小限制是针对所有cookie,而非单个cookie。 56 | 57 | #### Cookie 的结构 58 | 59 | 如下图: 60 | 61 | 62 | 63 | 上图中展示了响应头的 `Set-Cookie` 字段中设置cookie的所有可选的标志: 64 | 65 | ##### 名称 66 | 67 | cookie 的名称,cookie的名称是不区分大小写的,但是由于服务器在处理 cookie 的时候,可能会区分大小写,所以实践中,我们最好区分大小写。 68 | 69 | 注意:cookie名称必须是URL编码的 70 | 71 | ##### 值 72 | 73 | 存储在相应名称下的字符串值,该值也是URL编码的。 74 | 75 | ##### 域 - domain 76 | 77 | 指定cookie是对于哪个域有效的。 78 | 79 | 可以是子域(www.example.com),也可不是(.example.com),如果是主域,那么该cookie对所有子域都有效。 80 | 81 | ##### 路径 - path 82 | 83 | 指定一个域下的一个路径,cookie 只对该路径有效。 84 | 85 | 例如:`path=/books/`,假设域为:`www.example.com`,那么cookie只有 `www.example.com/books/` 才能访问,而 `www.example.com` 则不能。 86 | 87 | ##### 过期时间 - expires 88 | 89 | 表示cookie如何被删除。默认情况下,浏览器回话结束时会删除所有cookie,不过可以通过 `expires` 字段手动设置失效时间,`expires` 字段的值为 `GMT` 格式的日期字符串。 90 | 91 | 在 js 中,可以使用如下代码获取 `GMT` 格式的日期字符串: 92 | 93 | ```js 94 | new Date().toGMTString() 95 | ``` 96 | 97 | ##### 安全标志 - secure 98 | 99 | 指定后,cookie 只有使用 SSL 连接的时候才会发送给服务器。 100 | 101 | ##### HttpOnly 标志 102 | 103 | 指定后,cookie只能用于http层面,不能被客户端脚本读取。 104 | 105 |

以上字段中,只有名称和值是必须的

106 | 107 | #### Cookie 操作的封装 108 | 109 | 使用 JavaScript 操作cookie,即操作 `document.cookie` 属性,这个属性设计的并不友好,这已经是众所周知的事实..... 110 | 111 | 当通过 `document.cookie` 获取 cookie 的时候,它返回一个字符串,包含所有可用的cookie信息,这个字符串又等号和分号分隔: 112 | 113 | ``` 114 | key1=val1;key2=val2;key3=val3 115 | ``` 116 | 117 | 其中,名称和值都是URL编码的,所以要通过 `decodeURIComponent` 解码。 118 | 119 | 当通过 `document.cookie` 设置值时,你要设置一个字符串,这个字符串与 `Set-Cookie` 响应头的格式一样,由分号和空格分隔。 120 | 121 | 设置 `document.cookie` 并不会覆盖cookie,除非设置的cookie已经存在。而会解析字符串,将你设置的cookie添加到已存在的cookie集合中。 122 | 123 | 需要注意的是,在设置cookie的时候,名称和值也是需要URL编码的。 124 | 125 | 为了更直观和方便的操作cookie,我们通常封装对 `document.cookie` 的操作,下面的代码来自《JavaScript高程》,并做了相应的修改: 126 | 127 | ```js 128 | var cookieUtil = { 129 | get: function (name) { 130 | var cookieName = encodeURIComponent(name) + '=', 131 | cookieStart = document.cookie.indexOf(cookieName), 132 | cookieValue = null 133 | 134 | if (cookieStart > -1) { 135 | var cookieEnd = document.cookie.indexOf(';', cookieStart) 136 | if (cookieEnd === -1) { 137 | cookieEnd = document.cookie.length 138 | } 139 | cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)) 140 | } 141 | return cookieValue 142 | }, 143 | set: function (name, value, expires, domain, path, secure, HttpOnly) { 144 | var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value) 145 | 146 | if (expires instanceof Date) { 147 | cookieText += '; expires=' + expires.toGMTString() 148 | } 149 | if (path) { 150 | cookieText += '; path=' + path 151 | } 152 | if (domain) { 153 | cookieText += '; domain=' + domain 154 | } 155 | if (secure) { 156 | cookieText += '; secure' 157 | } 158 | if (HttpOnly) { 159 | cookieText += '; HttpOnly' 160 | } 161 | 162 | document.cookie = cookieText 163 | }, 164 | unset: function (name, domain, path, secure, HttpOnly) { 165 | this.set(name, '', new Date(0), domain, path, secure, HttpOnly) 166 | } 167 | } 168 | ``` 169 | 170 | #### cookie 与 sessionStorage 和 localStorage 的区别 171 | 172 | ##### 区别 173 | 174 | ###### 数据的生命周期 175 | 176 | cookie 可以设置失效时间,默认是关闭浏览器后失效 177 | 178 | sessionStorage 仅在当前回话下有效,关闭标签或浏览器后失效 179 | 180 | localStorage 除非手动清除,否则永久保存 181 | 182 | ###### 存储数据的大小 183 | 184 | cookie 在 4kb 左右 185 | 186 | sessionStorage 和 localStorage 在 5MB 左右,因浏览器而异 187 | 188 | ###### 与服务器端通讯 189 | 190 | cookie 会被携带在HTTP的请求头中,所以用cookie存储太多数据会带有性能问题 191 | 192 | sessionStorage 和 localStorage 仅存在客户端,不参与服务器通讯 193 | 194 | 正因为 cookie 会参与服务器通讯,所以 storage 是不能取代 cookie 的,因为cookie是HTTP协议的一部分 195 | 196 | ###### 易用性 197 | 198 | cookie 的原生接口不友好,需要手动封装读写操作 199 | 200 | storage 的原生接口还是可以接受的 201 | 202 | ##### 注意 203 | storage在存储数据的时候,都是以字符串的形式进行保存的,所以无论你存储的是数字类型还是对象类型,当你读取的时候,你得到的将是该数据的字符串表示 204 | 205 | ##### 封装 206 | 207 | ###### cookie 的增删改查 208 | 209 | cookie 的操作是对 document.cookie 的封装,前面我们已经讲过了。 210 | 211 | ###### storage 的增删改查 212 | 213 | 曾: 214 | 215 | ```js 216 | localStorage.age = 24 217 | // 等价于 218 | localStorage.setItem('age', 24) 219 | ``` 220 | 221 | 查: 222 | 223 | ```js 224 | localStorage.age 225 | // 等价于 226 | localStorage.getItem('age') 227 | ``` 228 | 229 | 改: 230 | 231 | 修改类似于增加,在原有的 `key` 上重新设置值就可以了 232 | 233 | ```js 234 | localStorage.age = 24 235 | // 修改 236 | localStorage.age = 30 237 | ``` 238 | 239 | 删: 240 | 241 | 删除全部存储的内容 242 | 243 | ```js 244 | localStorage.clear() 245 | ``` 246 | 247 | 删除某个键值 248 | 249 | ```js 250 | localStorage.removeItem('age') 251 | ``` 252 | 253 | 获取对应索引的键值: 254 | 255 | ```js 256 | localStorage.key(index) 257 | ``` 258 | 259 | 260 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | docs 8 | 9 | 19 | 20 | 21 |
22 | 23 | 24 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/note/algorithm/linked-list.md: -------------------------------------------------------------------------------- 1 | ## 链表 2 | 3 | #### 链表的优势 4 | 5 | 链表是一种特殊的列表,不同于数组,数组不总是组织数据的最佳数据结构,原因如下: 6 | 7 | * 在有些变成语言中: 8 | * 数组的长度是需要预先设定好的,也就是说数组的长度是固定的,想要扩展会很困难。 9 | * 向数组中添加或删除元素也很麻烦,因为需要将数组的元素向前或向后循环位移。 10 | * 在JavaScript中 11 | * JavaScript显然不存在上述两个数组缺点的说法,因为数组的长度是可变的,另外可以使用 `splice` 方法轻松的对数组进行添加删除的操作。 12 | * 但也正因如此,我们不要忘了,在JavaScript中,数组也是对象,js中的数组是作为对象被实现的,所以与其他语言(如:C++、java)相比,性能会差很多。 13 | 14 | 另外,数组中元素的存储地址是连续的,但是当我们的数据量很大时,可能会很难寻找到空间足够大又连续的内存空间,而链表的存储方式不要求地址连续。 15 | 16 | 但并不是说链表一定比数组好,数组的优势在于随机访问,比如我们访问数组的第20个元素,可以很快的访问到,然而链表就会比较困难,另外在链表中,我们也不会说第几元素,我们会用链表中节点之间的引用来描述元素间的关系。 17 | 18 | #### 链表的概念 19 | 20 | ###### 节点(Node) 21 | 22 | 链表中的元素称为节点 23 | 24 | ###### 链 25 | 26 | 节点间的引用称为链 27 | 28 | ###### 节点的种类 29 | 30 | * 单向链表 31 | * 单向循环链表 32 | * 双向链表 33 | * 双向循环链表 34 | 35 | #### JavaScript实现 36 | 37 | ###### 单向链表 38 | 39 | 通常情况下,我们会选择一个头结点(Head)作为链表的接入点,如果用一张图来表示单向链表,那么应该是这样的: 40 | 41 | 42 | 43 | 我们需要两个类,一个节点类(Node),另外一个是链表类(Llist),先创建 `Node` 类,如下: 44 | 45 | ```js 46 | function Node (data) { 47 | this.data = data 48 | this.next = null 49 | } 50 | ``` 51 | 52 | 然后是链表类 `Llist`: 53 | 54 | 在创建 `Llist` 之前,我们要明确单向链表的属性和方法: 55 | 56 | * 属性: 57 | * head (头节点的引用) 58 | * 方法: 59 | * insertAfter 60 | 61 | ```js 62 | function Llist () { 63 | // 头节点 64 | this.head = new Node('head') 65 | } 66 | Llist.prototype = { 67 | constructor: Llist, 68 | // 插入节点:将 newNode节点 插入到 node节点 之后 69 | insertAfter: function (newNode, data) { 70 | 71 | }, 72 | // 删除节点 73 | remove: function (data) { 74 | 75 | }, 76 | // 查找节点 77 | find: function (data) { 78 | 79 | }, 80 | // 显示全部节点 81 | display: function () { 82 | 83 | } 84 | } 85 | ``` 86 | 87 | 首先,我们来看一下 `insertAfter` 方法,`insertAfter` 方法用来将新节点 `newNode` 插入到 `data` 属性为给定值的节点的后面。所以我们需要 `find` 方法,该方法用来寻找 `data` 属性值为给定值的节点,`find` 方法实现如下 88 | 89 | ```js 90 | find: function (data) { 91 | var current = this.head 92 | while (current && current.data != data) { 93 | current = current.next 94 | } 95 | return current 96 | } 97 | ``` 98 | 99 | 有了 `find` 方法,我们就可以轻松实现 `insertAfter` 方法了: 100 | 101 | ```js 102 | insertAfter: function (newNode, data) { 103 | var target = this.find(data) 104 | newNode.next = target.next 105 | target.next = newNode 106 | } 107 | ``` 108 | 109 | `remove` 方法用来删除 `data` 属性值为给定值的节点,删除节点的思路是:找到要删除的节点的前一个节点,然后让该节点的 `next` 属性指向要删除的节点的 `next` 属性所指向的节点,为此,我们需要一个辅助方法 `findPrev` 用来寻找要删除节点的前一个节点: 110 | 111 | ```js 112 | findPrev: function (data) { 113 | var current = this.head 114 | while (current && current.next && current.next.data != data) { 115 | current = current.next 116 | } 117 | return current 118 | } 119 | ``` 120 | 121 | 有了 `findPrev` 我们就可以实现 `remove` 方法了: 122 | 123 | ```js 124 | remove: function (data) { 125 | var prev = this.findPrev(data) 126 | if (prev.next) { 127 | prev.next = prev.next.next 128 | } 129 | } 130 | ``` 131 | 132 | 最后,我们来写一个遍历所有节点的方法,用来显示节点数据: 133 | 134 | ```js 135 | display: function () { 136 | var current = this.head 137 | while (current.next) { 138 | console.log(current.next.data) 139 | current = current.next 140 | } 141 | } 142 | ``` 143 | 144 | 接下来,我们就可以写一个测试脚本了: 145 | 146 | ```js 147 | var list = new Llist() 148 | var new1 = new Node('new1') 149 | var new2 = new Node('new2') 150 | var new3 = new Node('new3') 151 | list.insertAfter(new1, 'head') 152 | list.insertAfter(new2, 'new1') 153 | list.insertAfter(new3, 'new1') 154 | list.display() // 输出:new1 new3 new2 155 | list.remove('new3') 156 | list.display() // 输出:new1 new2 157 | ``` 158 | 159 | ###### 双向链表 160 | 161 | 双向链表与单向链表相比,每一个节点多出一个属性 `prev`,该属性指向当前节点的前一个节点,头节点的 `prev` 属性指向 `Null`: 162 | 163 | 164 | 165 | 双向链表相比于单向链表,在删除节点的时候,效率会更高,还记我们在单向链表中删除节点时需要使用 `findPrev` 方法去查找目标节点的前一个节点吗?在双向链表中,我们仅仅通过目标节点的 `prev` 属性即可访问其前一个节点,而不需要遍历查找,效率自然会高,但在插入节点的时候我们需要做更多的事情,除了保证 `next` 属性指向正确之外,还要保证 `prev` 属性指向正确,下面是完整的双向链表实现: 166 | 167 | ```js 168 | function Llist () { 169 | // 头节点 170 | this.head = new Node('head') 171 | } 172 | Llist.prototype = { 173 | constructor: Llist, 174 | // 插入节点:将 newNode节点 插入到 node节点 之后 175 | insertAfter: function (newNode, data) { 176 | var target = this.find(data) 177 | newNode.next = target.next 178 | if (target.next) { 179 | target.next.prev = newNode 180 | } 181 | target.next = newNode 182 | newNode.prev = target 183 | }, 184 | // 删除节点 185 | remove: function (data) { 186 | var target = this.find(data) 187 | target.prev.next = target.next 188 | target.next.prev = target.prev 189 | target.next = target.prev = null 190 | }, 191 | // 查找节点 192 | find: function (data) { 193 | var current = this.head 194 | while (current && current.data != data) { 195 | current = current.next 196 | } 197 | return current 198 | }, 199 | // 显示全部节点 200 | display: function () { 201 | var current = this.head 202 | while (current.next) { 203 | console.log(current.next.data) 204 | current = current.next 205 | } 206 | console.log('======') 207 | } 208 | } 209 | ``` 210 | 211 | ###### 单向循环链表 212 | 213 | 我们仅仅需要添加一行代码,就可以把单向链表修改为单向循环链表。 214 | 215 | 还记的单向链表中的头节点的 `next` 属性最初指向的是什么吗?是 `Null`,当我们在头节点后面添加一个节点之后,头节点的 `next` 便指向了新添加的节点,而新添加的节点会指向 `Null`,也就是说,随着节点的添加,头节点最初的指向,总会传递给最后一个节点。既然如此,如果我们在初始化链表的时候,使头节点的 `next` 属性指向自身会怎么样?答案是:随着节点的添加,最后一个节点总会指向头节点。这样我们就得到了一个单向循环链表: 216 | 217 | 只需要在 `Llist` 类中添加一句: 218 | 219 | ```js 220 | this.head.next = this.head 221 | ``` 222 | 223 | 完整代码如下: 224 | 225 | ```js 226 | function Llist () { 227 | this.head = new Node('head') 228 | this.head.next = this.head 229 | } 230 | ``` 231 | 232 | 最终效果如下图: 233 | 234 | 235 | 236 | ###### 双向循环链表 237 | 238 | 对于双向循环链表,我们同样可以通过对双向链表的修改得到,我们来看看双向链表的示意图: 239 | 240 | 241 | 242 | 对于双向循环链表,我们期望应该是这样的: 243 | 244 | 245 | 246 | 我们增加了两条线(图中的 线1 和 线2),对于第一条线,我们同样可以利用创造单向循环链表的方式来构建,即: 247 | 248 | ```js 249 | function Llist () { 250 | this.head = new Node('head') 251 | this.head.next = this.head // 这句话是重点 252 | } 253 | ``` 254 | 255 | 对于线2,我们可以在插入节点的方法 `insertAfter` 中做文章,如下: 256 | 257 | ```js 258 | insertAfter: function (newNode, data) { 259 | var target = this.find(data) 260 | newNode.next = target.next 261 | if (target.next) { 262 | target.next.prev = newNode 263 | } 264 | target.next = newNode 265 | newNode.prev = target 266 | 267 | // 这里是新添加的内容 268 | if (newNode.next === this.head) { 269 | this.head.prev = newNode 270 | } 271 | } 272 | ``` 273 | 274 | 我们添加了一个 `if` 语句,这段代码的意思是,当我们在添加完节点之后,对新节点进行判断,如果新节点的 `next` 属性指向头节点,那么说明新添加的节点是最后一个节点,那么我们只需要让头节点的 `prev` 属性指向该节点即可了,这样我们就得到了一个双向循环链表 -------------------------------------------------------------------------------- /docs/note/algorithm/bst.md: -------------------------------------------------------------------------------- 1 | ## 二叉树和二叉查找树 2 | 3 | #### 树 4 | 5 | ##### 简介 6 | 7 | 树是计算机科学中经常用到的一种数据结构,以分层的方式存储数据,所以经常用来存储具有层级关系的数据,比如文件系统。 8 | 9 | 此外,树也被用来存储有序的列表,例如二叉查找树,在二叉查找树上进行查找非常快。 10 | 11 | ##### 树的相关概念 12 | 13 | 如下图,展示了有关树的术语: 14 | 15 | 16 | 17 | 上图描述了一个树,图中的每一个圆圈都是一个 `节点`,连接节点的线叫做 `边`。一棵树最上面的节点称为 `根节点`,如果一个节点下面连接多个节点,那么称该节点为 `父节点`,下面的节点为 `子节点`,没有子节点的节点称为 `叶子节点`。 18 | 19 | 从一个节点到与它不直接相连的节点的这一组边称为 `路径`,如图中(23-17-19)。 20 | 21 | 树可以分为几个层次,根节点是第0层,根节点的子节点是第1层,子节点的子节点是第2层,以此类推。层的数量代表了树的 `深度`。 22 | 23 | 任何一层的节点,都可以看做是子树的根。 24 | 25 | #### 二叉树和二叉查找树 26 | 27 | ##### 概念 28 | 29 | 子节点的数量不超过2个的树称为 `二叉树`。 30 | 31 | ##### 简介 32 | 33 | 二叉树是一种特殊的树,二叉树中的任何一个节点的子节点的数量最多为2个,我们分别称这两个节点为 `左节点` 和 `右节点`。 34 | 35 | 通过把节点的数量限定为2,可以写出高效的程序在树种插入,查找,删除。 36 | 37 | ##### 二叉查找树 38 | 39 | 二叉查找树是一种特殊的二叉树。相对较小的值存储在左节点中,较大的值存储在右节点中,这一特性使得查找效率非常高。 40 | 41 | #### 实现二叉查找树 42 | 43 | ##### 插入节点 44 | 45 | 树是由节点组成的,所以我们需要一个节点类(Node): 46 | 47 | ```js 48 | function Node (data) { 49 | this.data = data 50 | this.left = null 51 | this.right = null 52 | } 53 | ``` 54 | 55 | 每个节点除了保存自身的数据之外,还保存着对子节点的引用(left/right)。 56 | 57 | 之后我们就可以构建二叉查找树了,假设我们的二叉查找树类是 `Bst`: 58 | 59 | ```js 60 | function Bst () { 61 | this.root = null 62 | } 63 | // 插入节点 64 | BST.prototype.insert = function (data) { 65 | 66 | } 67 | ``` 68 | 69 | `Bst` 类在初始状态保存着整个二叉查找树的根节点(root),`Bst` 拥有一个用来插入节点的方法:`insert`,接收节点的数据,并插入相应的位置,根据二叉查找树的特性:较小的值存放在左节点中,较大的值存放在右节点中,所以 `insert` 算法如下: 70 | 71 | * 1、根据输入的数据 `data` 创建一个新的节点。 72 | * 2、检查是否有根节点,如果没有根节点证明这是一颗新树,将该节点作为跟节点。 73 | * 3、否则,开始遍历树,将根节点设为当前节点,使用新节点与当前节点作比较,如果新节点的值小于当前节点。 74 | * 3.1、如果当前节点的左子节点为null,则将新节点设为当前节点的左子节点,退出循环。 75 | * 3.2、如果当前节点的左子节点不为null,则更新当前节点为当前节点的左子节点,执行下一次循环。 76 | * 4、如果新节点的值大于当前节点。 77 | * 4.1、如果当前节点的右子节点为null,则将新节点设为当前节点的右子节点,退出循环。 78 | * 4.2、如果当前节点的右子节点不为null,则更新当前节点为当前节点的右子节点,执行下一次循环。 79 | 80 | 根据上面的算法,我们可以写出如下代码: 81 | 82 | ```js 83 | BST.prototype.insert = function (data) { 84 | var n = new Node(data) 85 | if (!this.root) { 86 | this.root = n 87 | return 88 | } 89 | var current = this.root 90 | while (true) { 91 | if (data < current.data) { 92 | if (!current.left) { 93 | current.left = n 94 | break 95 | } 96 | current = current.left 97 | } else { 98 | if (!current.right) { 99 | current.right = n 100 | break 101 | } 102 | current = current.right 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | ##### 遍历树 109 | 110 | 由于二叉查找树的性质,较小的值存放在左边,较大的值存放在右边,所以通过对树的遍历,我们很容易实现按照特定的顺序显示数据,比如数字从小到大,字母的先后顺序等。 111 | 112 | 遍历二叉查找树的方式有三种:中序、先序、后序,代码实现起来很简单,如下: 113 | 114 | ```js 115 | // 中序 116 | BST.prototype.inOrder = function (node) { 117 | if (node) { 118 | this.inOrder(node.left) 119 | console.log(node.data) 120 | this.inOrder(node.right) 121 | } 122 | } 123 | // 先序 124 | BST.prototype.preOrder = function (node) { 125 | if (node) { 126 | console.log(node.data) 127 | this.preOrder(node.left) 128 | this.preOrder(node.right) 129 | } 130 | } 131 | // 后序 132 | BST.prototype.postOrder = function (node) { 133 | if (node) { 134 | this.postOrder(node.left) 135 | this.postOrder(node.right) 136 | console.log(node.data) 137 | } 138 | } 139 | ``` 140 | 141 | 我们使用如下代码来测试以上遍历的实现: 142 | 143 | ```js 144 | var tree = new Bst() 145 | tree.insert(23) 146 | tree.insert(45) 147 | tree.insert(16) 148 | tree.insert(37) 149 | tree.insert(3) 150 | tree.insert(99) 151 | tree.insert(22) 152 | 153 | console.log('=====中序=====') 154 | tree.inOrder(tree.root) 155 | console.log('=====先序=====') 156 | tree.preOrder(tree.root) 157 | console.log('=====后序=====') 158 | tree.postOrder(tree.root) 159 | ``` 160 | 161 | 可以在控制台看到中序、先序、后序的遍历顺序分别为: 162 | 163 | 164 | 165 | ##### 查找 166 | 167 | 二叉查找树的查找分为: 168 | * 查找最小值 169 | * 查找最大值 170 | * 查找给定值 171 | 172 | ###### 查找最小值 173 | 174 | 我们知道,二叉查找树的特性就是较小的值存储在左边,所以要找到最小的值,只需要遍历左子树到最后一个节点即可,该节点即保存着最小值: 175 | 176 | ```js 177 | BST.prototype.getMin = function () { 178 | var current = this.root 179 | while (current.left) { 180 | current = current.left 181 | } 182 | return current.data 183 | } 184 | ``` 185 | 186 | ###### 查找最大值 187 | 188 | 类似于查找最小值,只不过遍历的是右子树: 189 | 190 | ```js 191 | BST.prototype.getMax = function () { 192 | var current = this.root 193 | while (current.right) { 194 | current = current.right 195 | } 196 | return current.data 197 | } 198 | ``` 199 | 200 | ###### 查找给定值 201 | 202 | 查找给定值稍微复杂一点,算法如下: 203 | 204 | * 1、设当前节点为根节点,对树进行遍历 205 | * 2、比较要查找的值是否等于当前节点的值,如果是则返回当前节点 206 | * 3、如果要查找的值小于当前节点的值,则更新当前节点为当前节点的左节点,执行第 2 步 207 | * 4、如果要查找的值大于当前节点的值,则更新当前节点为当前节点的右节点,执行第 2 步 208 | * 5、未找到返回 null 209 | 210 | 代码如下: 211 | 212 | ```js 213 | BST.prototype.find = function (data) { 214 | var current = this.root 215 | while (current) { 216 | if (current.data === data) { 217 | return current 218 | } 219 | if (data < current.data) { 220 | current = current.left 221 | } 222 | if (data > current.data) { 223 | current = current.right 224 | } 225 | } 226 | return null 227 | } 228 | ``` 229 | 230 | ##### 二叉树左视图 231 | 232 | 左视图即为树在左方向上的投影,也可以理解从左向右观察,从最上面的图看,树的左视图为`23,17,3` 233 | 234 | 先将二叉树转换成二维数组的数据结构,整个为一个大的数组,子数组内为每一层的节点,顺序为树的从左到右对应数组的从前到后 235 | 236 | 代码如下: 237 | 238 | ```js 239 | BST.prototype.floors = function () { 240 | let floors = [] 241 | 242 | function currentFloor(prevFloor) { 243 | const currentFloor = [] 244 | prevFloor.forEach(item => { 245 | if (item.left) { 246 | currentFloor.push(item.left) 247 | } 248 | if (item.right) { 249 | currentFloor.push(item.right) 250 | } 251 | }) 252 | 253 | return currentFloor 254 | } 255 | 256 | let prevF = [this.root] 257 | while (prevF.length) { 258 | floors.push(prevF) 259 | prevF = currentFloor(prevF) 260 | } 261 | 262 | return floors 263 | } 264 | ``` 265 | 266 | 这样左视图就很好获取了,只需要遍历整个二维数组的最外层,拿到每一项数组的第一项 267 | 268 | 代码如下: 269 | 270 | ```js 271 | BST.prototype.leftSideView = function () { 272 | const floors = this.floors(), 273 | leftView = [] 274 | 275 | floors.forEach(item => { 276 | leftView.push(item[0].data) 277 | }) 278 | return leftView 279 | } 280 | ``` 281 | 282 | ##### 二叉树右视图 283 | 284 | 右视图即为从左视图的反方向观察,获取即为遍历整个二维数组的最外层,拿到每一项数组的最后项 285 | 286 | 代码如下: 287 | 288 | ```js 289 | BST.prototype.rightSideView = function () { 290 | const floors = this.floors(), 291 | rightView = [] 292 | 293 | floors.forEach(item => { 294 | rightView.push(item[item.length - 1].data) 295 | }) 296 | return rightView 297 | } 298 | ``` 299 | 300 | ##### 二叉树的层数 301 | 302 | 即为整个二维数组最外层的长度 303 | 304 | 代码如下: 305 | 306 | ```js 307 | BST.prototype.floorNumber = function () { 308 | return this.floors().length 309 | } 310 | ``` 311 | -------------------------------------------------------------------------------- /docs/note/algorithm/graph.md: -------------------------------------------------------------------------------- 1 | ## 图 2 | 3 | #### 图的现实意义 4 | 5 | 可以用图对现实中很多系统进行建模,比如:交通流量模型,航空公司的飞行系统,局域网或广域网等计算机网络,可以说图的应用场景非常多。 6 | 7 | #### 一些概念 8 | 9 | ##### 图、边、顶点 10 | 11 | 图由 `边的集合` 和 `顶点的集合` 组成。我们可以拿来一张中国地图,每一个省份可以看做是一个 `顶点`,临接的省份之间可以画一条线,这条线就是 `边`。 12 | 13 | ##### 顶点对 14 | 15 | `边` 是由 `顶点对` 定义的,比如边 `l` 是由顶点对 `(p1, p2)` 定义的。 16 | 17 | ##### 有向图、顶点的流向 18 | 19 | `顶点` 是有 `权重` 的,或者叫做 `成本`,可以根据 `权重` 对 `顶点对` 进行排序,如果一个图的 `顶点对` 是有序的,我们称之为 `有向图`。 20 | 21 | `有向图` 表明了 `顶点的流向` 22 | 23 | ##### 无向图 24 | 25 | 如果图是无序的,我们称之为 `无序图` 26 | 27 | ##### 路径、路径的长度 28 | 29 | 从一个顶点到达另外一个顶点所经过的边组成 `路径`。 30 | 31 | `路径的长度` 是由边的数量定义的。 32 | 33 | ##### 环 34 | 35 | 指向自身的顶点构成的路径成为 `环`,`环` 的长度为 `0`。 36 | 37 | ##### 圈、简单圈、平凡圈 38 | 39 | `圈` 是 `长度 >= 1` 的路径,且路径的第一个顶点和最后一个顶点相同。 40 | 41 | 除了第一个和最后一个顶点之外,没有重复的顶点或重复边的圈成为 `简单圈`,否则成为 `平凡圈`。 42 | 43 | ##### 顶点强连通、有向图强连通 44 | 45 | 如果两个顶点之间有 `路径`,那么 `这两个点就是强连通` 的。 46 | 47 | 如果 `有向图` 的所有顶点都是强连通的,那么这个 `有向图也是强连通` 的。 48 | 49 | #### 分析 50 | 51 | ##### 如何存储(表示)图? 52 | 53 | 我们不妨先来看一下 `图` 和 `二叉树` 的图片示意: 54 | 55 | ###### 树的示意图 56 | 57 | 58 | 59 | ###### 图的示意图 60 | 61 | 62 | 63 | 我们之前讲过树的存储结构是,需要节点类表示树中的节点,每个节点除了保存自身数据外,还有用对子节点的引用,从而构造一颗树。 64 | 65 | 根据上面树和图的示意图,可能很多同学觉得他们很像,你可能尝试用构建树的方法去构建图,即在节点中保存相连接节点的引用,但实际上这种方法是有问题的,原因是:树是有清晰的层次结构的,而图的层次结构是很复杂的。 66 | 67 | 所以我们需要思考:如何存储图? 68 | 69 | ###### 邻接表 70 | 71 | 一种用来存储图的方式是使用邻接表数组,首先我们需要用一个数组来存储图中所有的顶点,假设我们如下5个顶点: 72 | 73 | ``` 74 | A B C D E 75 | ``` 76 | 77 | 我们使用一个数组来存储这5个顶点,称之为顶点数组: 78 | 79 | ```js 80 | let vertex = ['A', 'B', 'C', 'D', 'E'] 81 | ``` 82 | 83 | 顶点 `A` 在数组中的位置是 `0`,`B` 在数组中的位置是 `1`,以此类推。 84 | 85 | 接下来,我们同样还需要一个数组,即我们的邻接表数组,该数组存储为由一个顶点相邻的顶点组成的数组,并以顶点在顶点数组中的位置作为索引,这样我们根据顶点在顶点数组中的索引,访问邻接表数组,就可以迅速的知道有哪些顶点与其相连: 86 | 87 | ```js 88 | let vertex = ['A', 'B', 'C', 'D', 'E'] 89 | let adj = [ 90 | ['B', 'D'], 91 | ['A'], 92 | ['D', 'E'], 93 | ['A', 'C'], 94 | ['C'] 95 | ] 96 | ``` 97 | 98 | 如上面的代码顶点 `A` 在 `vertex` 中的索引为 `0`,我们通过访问 `adj[0]` 可以得到数组 `['B', 'D']`,说明顶点 `A` 与顶点 `B` 和 `D` 相连。 99 | 100 | ###### 代码实现 101 | 102 | 实现图的过程,实际上就是构造顶点数组 `vertex` 以及 邻接表数组 `adj` 的过程。 103 | 104 | 首先顶点可能由很多复杂的数据构成,比如地图中的城市拥有名称、经纬度等等,所以我们需要一个顶点类,类似于二叉树中的节点类,如下: 105 | 106 | ```js 107 | // 顶点类 108 | function Vert (data, visited) { 109 | // 顶点的数据 110 | this.data = data 111 | // 标示着顶点是否被访问过 112 | this.visited = visited 113 | } 114 | ``` 115 | 116 | 有了顶点类,我们就可以构造顶点数组了,为了方便,我们顶点中只保存整型数据: 117 | 118 | ```js 119 | let vertex = [ 120 | new Vert(0), 121 | new Vert(1), 122 | new Vert(2), 123 | new Vert(3), 124 | new Vert(4) 125 | ] 126 | ``` 127 | 128 | 下一步就是构造邻接表数组,这个邻接表数组实际上就是图的描述,我们需要一个 `Graph` 类: 129 | 130 | ```js 131 | // 图类,构造邻接表数组 adj,传递顶点数组 vertex 进行初始化 132 | function Graph (vertex) { 133 | this.vertex = vertex 134 | // 顶点数量 135 | this.quantity = vertex.length 136 | // 边的数量 137 | this.edges = 0 138 | // 邻接表数组 139 | this.adj = [] 140 | for (let i = 0; i < this.quantity; i++) { 141 | this.adj[i] = [] 142 | } 143 | } 144 | ``` 145 | 146 | 如上代码,`Graph` 类通过传递给它的顶点数组构造一个图,在初始状态,图中的顶点之间没有任何关系,所以边的数量为 0,邻接表数组被初始化为与顶点相对应的空数组。 147 | 148 | 接下来,我们的 `Graph` 类需要一个方法 `addEdge`: 149 | 150 | ```js 151 | Graph.prototype.addEdge = function (v1, v2) { 152 | 153 | } 154 | ``` 155 | 156 | `addEdge` 方法接收两个顶点数据作为参数,并根据数据确定对应顶点,然后将数据值为 `data2` 的顶点 `v2` 添加到数据值为 `data1` 的顶点 `v1` 对应的邻接表数组中,即 `v2` 与 `v1` 相连,实现如下: 157 | 158 | ```js 159 | Graph.prototype.addEdge = function (data1, data2) { 160 | // 查找顶点 v1 在顶点数组的位置 161 | var v1Index = this.searchPos(data1) 162 | // 查找顶点 v2 在顶点数组的位置 163 | var v2Index = this.searchPos(data2) 164 | 165 | this.adj[v1Index].push(v2Index) 166 | this.adj[v2Index].push(v1Index) 167 | this.edges++ 168 | } 169 | ``` 170 | 171 | 上面的代码中我们使用了 `searchPos` 方法,该方法查找拥有指定数据的顶点在顶点数组中的位置,实现如下: 172 | 173 | ```js 174 | Graph.prototype.searchPos = function (data) { 175 | var index = -1 176 | for (var i = 0; i < this.quantity; i++) { 177 | if (this.vertex[i].data == data) { 178 | index = i 179 | break 180 | } 181 | } 182 | return index 183 | } 184 | ``` 185 | 186 | 为了方便查看,我们需要一个 `showGraph` 方法,用来查看图: 187 | 188 | ```js 189 | Graph.prototype.showGraph = function () { 190 | var putStr = '' 191 | for (var i = 0; i < this.quantity; i++) { 192 | putStr = '' 193 | putStr += i + ' -> ' 194 | for (var j = 0; j < this.quantity; j++) { 195 | if (this.adj[i][j] !== undefined) { 196 | putStr += ' ' + this.adj[i][j] + ' ' 197 | } 198 | } 199 | console.log(putStr) 200 | } 201 | } 202 | ``` 203 | 204 | `showGraph` 方法只是简单的对邻接表数组进行遍历输出。 205 | 206 | 现在,我们可以写一些测试代码: 207 | 208 | ```js 209 | // 顶点数组 210 | var vertex = [ 211 | new Vert(0), 212 | new Vert(1), 213 | new Vert(2), 214 | new Vert(3), 215 | new Vert(4) 216 | ] 217 | 218 | var g = new Graph(vertex) 219 | g.addEdge(0, 1) 220 | g.addEdge(0, 2) 221 | g.addEdge(1, 3) 222 | g.addEdge(2, 4) 223 | 224 | g.showGraph() 225 | ``` 226 | 227 | 打开浏览器控制台,应该看到如下输出: 228 | 229 | 230 | 231 | ##### 图的搜索 232 | 233 | 经常用图来解决的问题比如:从一个城市到另外一个城市的最短距离,这个问题实际上可以抽象出从图中的一个顶点到达另外一个顶点的最短路径的问题。图的这一操作叫做搜索,对图有两种基本搜索方式:`深度优先搜索`、`广度优先搜索`。 234 | 235 | ###### 深度优先搜索 236 | 237 | 深度优先搜索的思路是,从一条路径的顶点开始,直到到达最后一个顶点,然后回溯,继续追溯下一条路径。 238 | 239 | 具体到我们的存储结构:`顶点数组` 和 `邻接表数组`,我们找到起始顶点在 `顶点数组` 中的位置,然后找到相应位置 `邻接表数组` 中存储的相邻顶点,做递归搜索即可,代码如下: 240 | 241 | ```js 242 | Graph.prototype.dfs = function (data) { 243 | // 找到起始顶点在邻接表数组中的位置 244 | var index = this.searchPos(data) 245 | // 将其设置为已访问 246 | this.vertex[index].visited = true 247 | console.log(this.vertex[index].data) 248 | 249 | // 遍历邻接表数组中存储的相邻顶点,递归搜索 250 | for (var i = 0; i < this.adj[index].length; i++) { 251 | var key = this.adj[index][i] 252 | if (!this.vertex[key].visited) { 253 | this.dfs(this.vertex[key].data) 254 | } 255 | } 256 | } 257 | ``` 258 | 259 | 针对上面的例子写如下测试代码: 260 | 261 | ```js 262 | g.dfs(0) 263 | ``` 264 | 265 | 输出如下: 266 | 267 | 268 | 269 | ###### 广度优先搜索 270 | 271 | 深度优先搜索是纵向延伸的搜索,而广度优先搜索是横向延伸的搜索。递归可以解决深度优先所搜,而广度优先搜索需要使用一个队列,来操作,具体代码并不复杂,如下: 272 | 273 | ```js 274 | Graph.prototype.bfs = function (data) { 275 | // 一个队列 276 | var queue = [] 277 | // 找到起始顶点在邻接表数组中的位置 278 | var index = this.searchPos(data) 279 | // 将起始顶点入队 280 | queue.push(index) 281 | 282 | // 遍历队列的过程就是在横向搜索 283 | while (queue.length > 0) { 284 | var i = queue.shift() 285 | this.vertex[i].visited = true 286 | console.log(this.vertex[i].data) 287 | 288 | for (var j = 0; j < this.adj[i].length; j++) { 289 | if (!this.vertex[this.adj[i][j]].visited) { 290 | queue.push(this.adj[i][j]) 291 | } 292 | } 293 | } 294 | } 295 | ``` 296 | 297 | 还是针对前面的例子,写如下测试代码: 298 | 299 | ```js 300 | g.bfs(0) 301 | ``` 302 | 303 | 输出如下: 304 | 305 | -------------------------------------------------------------------------------- /docs/note/http/ajax.md: -------------------------------------------------------------------------------- 1 | ## ajax 2 | 3 |

本文将不会讨论IE7之前版本浏览器的兼容实现,以标准的 XMLHttpRequest 实现

4 | 5 | #### 小例子演示 XHR 的使用 6 | 7 | 为了更好的演示,我们将会结合 Nodejs 实现一个简单的服务器端程序,假设我们有如下文件: 8 | 9 | * index.html ---- 首页 10 | * app.js ---- Nodejs 编写的处理请求的程序 11 | * ajax.js ---- 被 index.html 引用的 js 文件,用来测试 XHR 代码 12 | * test.txt ---- 存储数据,供XHR请求 13 | 14 | 来看一下 app.js 的代码: 15 | 16 | ```js 17 | const http = require('http') 18 | const fs = require('fs') 19 | const path = require('path') 20 | 21 | // 创建一个 sever 对象,监听 9090 端口 22 | http.createServer((request, response) => { 23 | // 分析请求的资源路径 24 | let reqUrl = request.url === '/' ? '/index.html' : request.url 25 | let extName = path.extname(reqUrl) 26 | let fileName = path.basename(reqUrl, extName) 27 | extName = extName ? extName.substring(1) : 'html' 28 | 29 | // 根据分析请求的资源,返回相应的mime类型 30 | response.writeHead(200, { 31 | 'Content-Type': `text/${extName}` 32 | }) 33 | 34 | // 如果请求的不是 favicon.ico 就将资源返回 35 | if (reqUrl !== '/favicon.ico') { 36 | response.end(fs.readFileSync('.' + reqUrl)) 37 | } 38 | 39 | }).listen(9090) 40 | 41 | console.log('Server running at http://127.0.0.1:9090/') 42 | ``` 43 | 44 | 上面的代码是一段很简单的node程序,根据请求的资源返回相应的内容。此时在终端执行: 45 | 46 | ```sh 47 | node app.js 48 | ``` 49 | 50 | 访问:http://127.0.0.1:9090/ ,我们将看到 `index.html` 的内容,因为 app.js 中我们做了如下处理: 51 | 52 | ```js 53 | let reqUrl = request.url === '/' ? '/index.html' : request.url 54 | ``` 55 | 56 | 如果请求的根路径 `/`,那么返回 `/index.html` 的内容。 57 | 58 | 再来看看 `index.html` 的内容: 59 | 60 | ```html 61 | 62 | 63 | 64 | test ajax 65 | 66 | 67 | test ajax 68 | 69 | 70 | 71 | 72 | ``` 73 | 74 | 简单的不得了,仅仅引用了 `ajax.js` 文件。 75 | 76 | 这个时候,我们的服务端程序会接收到对 `ajax.js` 文件的请求,我们做了正确的处理并返回。 77 | 78 | 最后,我们还需要一个 `test.txt` 文件,我们随意写一些内容然后保存,比如我们写一句话: 79 | 80 | ``` 81 | // test.txt 82 | This is a test file 83 | ``` 84 | 85 | 下面,我们就可以使用 XHR 来请求 `test.txt` 的内容了。 86 | 87 | 编辑 `ajax.js` 文件,最简单的 XHR 使用仅仅需要三行代码,如下: 88 | 89 | ```js 90 | var xhr = new XMLHttpRequest() 91 | xhr.open('get', 'test.txt') 92 | xhr.send() 93 | ``` 94 | 95 | 首先实例化 `XMLHttpRequest` 对象,然后调用实例的 `open` 方法打开一个连接,最后调用 `send` 方法发送请求。注意:调用 `open` 方法并不会发送请求。 96 | 97 | `open` 方法接受三个参数:1、要发送请求的方法类型。2、请求的URL。3、一个boolean值,代表是否异步。如果不传第三个参数,默认是异步的。 98 | 99 | 重启服务,发现控制台会有如下输出: 100 | 101 | 102 | 103 | 证明我们的请求成功了,但是我们还没有能够获取到数据,那么如何获取到数据呢?实际上,如果请求成功且数据成功返回,那么数据会自动填充到 xhr 对象的相应属性下,我们需要注意的 xhr 属性如下: 104 | 105 | * `xhr.responseText` ---- 作为响应主体被返回的文本 106 | * `xhr.responseXML` ---- 如果响应的内容类型(Content-Type)为 `text/xml` 或者 `application/xml`,那么这个属性将保存着响应数据的 XML DOM 文档。 107 | * `status` ---- 响应的 HTTP 状态码 108 | * `statusText` ---- 相应状态码的文字说明 109 | 110 | 如果请求是异步的,那么还需要注意如下属性: 111 | 112 | * `readyState` ---- 该属性是一个 Number 类型值,分别为:0,1,2,3,4。代表请求/响应的不同阶段: 113 | * 0:未初始化,即还没有调用 `open` 方法 114 | * 1:启动,已经调用 `open`,但还没有调用 `send` 115 | * 2:发送,已经调用 `send`,但还没有接收到响应 116 | * 3:接收,已经接收到数据,但还没有接收完成 117 | * 4:完成,已经接收完成,数据可用了 118 | 119 | 异步的情况下,除了要注意 `readyState` 属性外,还需要一个事件,因为我们需要在这个事件处理程序中操作数据,这个事件的名字叫做:`readystatechange`,顾名思义,这个事件代表着当 `readyState` 变化时触发。一般请求下,我们只考虑 `readyState` 值变为 4 的阶段,因为这个阶段的数据已经接收完成,且可以使用了。 120 | 121 | 那么针对之前的例子,我们只需要添加如下代码,就可以获取到请求的数据: 122 | 123 | ```js 124 | var xhr = new XMLHttpRequest() 125 | xhr.addEventListener('readystatechange', function (event) { 126 | if (xhr.readyState === 4) { 127 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { 128 | console.log(xhr.responseText) 129 | } else { 130 | console.log(xhr.status) 131 | } 132 | } 133 | }) 134 | 135 | xhr.open('get', 'test.txt') 136 | xhr.send() 137 | ``` 138 | 139 | 我们通过 `addEventListener` 给 `xhr` 对象添加了 `readystatechange` 事件,并在里面作如下判断: 140 | 141 | 当 `xhr.readyState` 状态等于 4 时才要处理数据,接着判断状态码,只有状态码 在区间 `[200, 300)` 时或者等于 `304` 时才代表获取数据成功,正常显示数据,否则将状态码打印出来。 142 | 143 | 另外要注意,该事件必须要在调用 `open` 方法之前设置。 144 | 145 | 刷新页面,我们能看到数据已经得到了: 146 | 147 | 148 | 149 | #### XHR 属性、方法、事件 汇总 150 | 151 | 一下列出的属性、方法以及事件,包含XHR1级以及2级的全部规范内容,由于浏览器对2级规范的实现并不完善,所以对于XHR2级规范会标注出来。 152 | 153 | ##### 属性 154 | 155 | ###### responseText 156 | 157 | * 描述:保存中响应主体返回的文本 158 | * 类型:`String` 159 | 160 | ###### responseXML 161 | 162 | * 描述:如果响应的内容类型(Content-Type)为 `text/xml` 或者 `application/xml` 那么这个属性保存着包含响应内容的XML DOM文档。 163 | 164 | ###### status 165 | 166 | * 描述:响应的HTTP状态码,如:`200`、`304` 等 167 | * 类型:`Number` 168 | 169 | ###### statusText 170 | 171 | * 描述:对 `status` 状态码的文本描述 172 | * 类型:`String` 173 | 174 | ###### readyState 175 | 176 | * 描述:一个数字,标示着当前请求/响应的某一个阶段 177 | * 0:未初始化,即还没有调用 `open` 方法 178 | * 1:启动,已经调用 `open`,但还没有调用 `send` 179 | * 2:发送,已经调用 `send`,但还没有接收到响应 180 | * 3:接收,已经接收到数据,但还没有接收完成 181 | * 4:完成,已经接收完成,数据可用了 182 | * 类型:`Number` 183 | 184 | ###### 【XHR2】timeout 185 | 186 | * 描述:可以给 `xhr.timeout` 属性设置一个数字值,代表请求多少毫秒之后超时,超时后将触发同样是XHR2级规范定义的 `timeout` 事件。 187 | * 示例: 188 | ```js 189 | xhr.timeout = 1000 // 1秒后超时 190 | ``` 191 | 192 | ###### upload 193 | 194 | * 描述:`xhr.upload` 属性返回一个 `XMLHttpRequestUpload` 对象,用来表示上传的进度,该对象是不透明的,可以通过为其绑定事件来跟踪进度。 195 | * 示例: 196 | 197 | ```js 198 | const xhr = new XMLHttpRequest() 199 | xhr.open('POST', url) 200 | xhr.onreadystatechange = () ={ 201 | // ... 202 | } 203 | xhr.upload.addEventListener('progress', event => { 204 | // event.lengthComputable 文件是否可计算 205 | if (event.lengthComputable) { 206 | // 计算上传进度百分比 207 | let percentage = Math.round(event.loaded / event.total * 100) 208 | } else { 209 | console.log('无法计算') 210 | } 211 | }) 212 | xhr.upload.addEventListener('load', () => {}) 213 | xhr.upload.addEventListener('error', () => {}) 214 | ``` 215 | 216 | 能够在 `xhr.upload` 上监听的事件有: 217 | 218 | | 事件 | 描述 | 219 | | ------------- |:-------------:| 220 | | loadstart | 开始上传 | 221 | | progress | 传输中 | 222 | | abort | 终止操作 | 223 | | error | 失败 | 224 | | load | 成功 | 225 | | timeout | 超时 | 226 | | loadend | 完成(不论成功与否) | 227 | 228 | ##### 方法 229 | 230 | ###### open(method, url[, async]) 231 | 232 | * 描述:启动一个请求,但不会发送。 233 | 234 | * 参数: 235 | * `{String} method` 请求的方法,如:`get`、`post` 等 236 | * `{String} url` 请求的URL 237 | * `{Boolean} async` 一个布尔值,代表着是否异步发送请求,默认 `true` 异步 238 | 239 | ###### send(data) 240 | 241 | * 描述:发送通过 `open` 方法启动的请求 242 | 243 | * 参数: 244 | * `{String/FormData} data` 作为请求主体发送的数据 245 | 246 | * 【XHR2】扩展:XHR2允许给 `send` 方法传递一个 `FormData` 实例。`FormData` 接收一个可选的参数,参数为 form 表单元素,如下: 247 | 248 | ```js 249 | var form = document.forms[0] 250 | var data = new FormData(form) 251 | ``` 252 | 253 | 在XHR1级的时候,我们要手动序列化表单的数据然后构造一个合适的字符串。而 `FormData` 会自动序列化表单,用于创建与表单格式相同的数据用于XHR传输,这样服务端接收数据的时候就能够对传统的表单提交一视同仁,为我们节省了不少事情。 254 | 255 | `FormData` 除了上述好处之外,也不需要我们手动设置请求头部,在XHR1级的时候,除了手动序列化表单,为了模拟真正的表单提交,我们需要设置相应的请求头部信息才行,比如: 256 | 257 | ```js 258 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') 259 | ``` 260 | 261 | ###### setRequestHeader(key, val) 262 | 263 | * 描述:设置要发送的请求头部信息 264 | 265 | * 参数: 266 | * `{String} key` 头部字段的名称 267 | * `{String} val` 头部字段的值 268 | 269 | * 注意:该方法必须要在调用 `open` 方法之后且调用 `send` 方法之前发送才能生效 270 | 271 | ###### getResponseHeader(key) 272 | 273 | * 描述:根据指定的响应头部字段名称,获取响应头部字段的值 274 | 275 | * 参数: 276 | * `{String} key` 响应头部字段的名称 277 | 278 | ###### getAllResponseHeaders() 279 | 280 | * 描述:获取所有头部信息作为一个长字符串 281 | 282 | * 示例:如第一小节的例子中,调用该方法将得到如下内容: 283 | ``` 284 | Date: Fri, 09 Jun 2017 08:37:44 GMT 285 | Connection: keep-alive 286 | Transfer-Encoding: chunked 287 | Content-Type: text/txt 288 | ``` 289 | 290 | ###### 【XHR2】overrideMimeType() 291 | 292 | * 描述:重写响应数据的mime类型 293 | * 意义:我们知道 `xhr` 对象拥有 `responseXML` 属性,当服务端返回的数据的内容类型是 `text/xml` 或 `application/xml` 时,数据将最为XML DOM保存在 `responseXML` 属性中,但是,如果服务端响应的内容类型是:`text/plain`,而事实上数据确实是可以作为 XML 解析的,此时 `responseXML` 属性为空,为了重新让该属性保存着能够用于 XML 解析的数据,我们就可以使用 `overrideMimeType` 方法重写mime类型: 294 | 295 | ```js 296 | var xhr = new XMLHttpRequest() 297 | xhr.open('get', 'xml.php') 298 | xhr.overrideMimeType('text/xml') 299 | xhr.send() 300 | ``` 301 | 302 | ##### 事件 303 | 304 | ###### readystatechange 305 | 306 | * 描述:当 `xhr.readyState` 属性值变化时触发。 307 | * 注意:该事件必须要在调用 `open` 方法之前设置 308 | 309 | ###### 【XHR2】timeout 310 | 311 | * 描述:当请求在 `xhr.timeout` 属性所设置的规定事件内没有完成,将触发该事件,代表请求超时 312 | 313 | ##### 进度事件 314 | 315 | ###### loadstart 316 | 317 | * 描述:接收到响应数据的第一个字节时触发 318 | 319 | ###### progress 320 | 321 | * 描述:接收响应数据期间持续触发 322 | * 事件对象的重要属性: 323 | * `event.lengthComputable` ---- 一个boolean值,表示进度信息是否可用 324 | * `event.position` ---- 表示已经接收的字节数 325 | * `event.totalSize` ---- 表示根据 `Content-Length` 响应头部确定的预期字节数 326 | 327 | ###### error 328 | 329 | * 描述:请求发生错误时触发 330 | 331 | ###### abort 332 | 333 | * 描述:调用 `xhr.abort()` 方法终止连接时触发 334 | 335 | ###### load 336 | 337 | * 描述:响应数据接受完毕时触发 338 | * 注意:实际上 `load` 事件是为了取代 `readystatechange` 事件而定义的,`load` 事件的好处是,我们不需要手动判断 `readyState` 属性的值。 339 | 340 | ###### loadend 341 | 342 | * 描述:触发 `error`、`abort`、`load` 事件后触发 343 | 344 | #### ajax的优缺点 345 | 346 | ###### ajax的优点 347 | 348 | * 无刷新更新数据,不影响用户交互 349 | * 传统方式每次与服务器交互都返回整个HTML页面内容,ajax仅获取必要的数据,减少带宽 350 | * ajax是前后端分离能够实现的重要桥梁 351 | 352 | ###### ajax的缺点 353 | 354 | * 越过浏览器的历史记录,页面无法返回前一个状态 355 | * 不利于搜索引擎优化(SEO) -------------------------------------------------------------------------------- /docs/note/dom/dom-event.md: -------------------------------------------------------------------------------- 1 | ## 一、绑定事件的方法 2 | 3 | #### HTML内联属性绑定 4 | 5 | ```html 6 |
7 | ``` 8 | 9 | #### js获取DOM元素添加事件属性 10 | 11 | ```js 12 | document.getElementById('box').onclick = function (){...} 13 | ``` 14 | 15 | #### 使用 addEventListener() 16 | 17 |

18 | 第一种方法,HTML内联属性绑定事件的方式不推荐,这违反了最佳实践。第二种方法的缺点是,只能同时给事件绑定一个callback,所以推荐一直使用 addEventListener() 方法给元素绑定事件 19 |

20 | 21 |

22 | 如果要移除一个通过 addEventListener 添加的事件处理函数,那么给 removeEventListener 传递的两个参数必须与 addEventListener 的前两个参数完全相同。这意味着,给一个元素绑定匿名事件处理函数将无法被移除 23 |

24 | 25 | ## 二、事件流 26 | 27 | #### 事件流 28 | 29 | 页面中接收事件的顺序。分为三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段 30 | 31 | 32 | #### 事件冒泡 33 | 34 | 事件首先由嵌套层次最深的节点接收,然后沿DOM树依次逐级向父代节点传播。 35 | 36 | #### 事件捕获 37 | 38 | 不太具体的节点(或嵌套层次最浅的节点,通常是document)应该最先接收事件,然后沿DOM树依次向子代节点传递直到一个具体的子节点 39 | 40 | ## 三、绑定事件的方法 41 | 42 | #### 标准方法 43 | 44 | ###### el.addEventListener(eventName, handle, useCapture | options) 45 | 46 | * 描述:给DOM元素添加指定的事件处理函数 47 | 48 | * 参数: 49 | * `{String} eventName` 事件名称 50 | * `{Function} handle` 事件函数 51 | * 第三个参数可以是一个选项对象或者是一个 `Boolean` 值 52 | * `{Boolean} useCapture` 是否在事件捕获阶段触发事件,`true` 代表捕获阶段触发,`false` 代表在冒泡阶段触发 53 | * `{Object} options` 选项对象解释如下: 54 | 55 | ```js 56 | { 57 | capture: 一个 boolean 值,同 useCapture 58 | once: 一个 boolean 值,如果为 true 代表事件监听器只被执行一次 59 | passive: 一个 boolean,如果为 true 代表该事件不会调用 preventDefault() 60 | } 61 | ``` 62 | 63 | ###### el.removeEventListener(eventName, handle) 64 | 65 | * 描述:移除通过 addEventListener 添加的事件处理函数 66 | 67 | * 参数: 68 | * `{String} eventName` 事件名称 69 | * `{Function} handle` 事件函数 70 | 71 |

72 | 如果要移除一个通过 addEventListener 添加的事件处理函数,那么给 removeEventListener 传递的两个参数必须与 addEventListener 的前两个参数完全相同。这意味着,给一个元素绑定匿名事件处理函数将无法被移除 73 |

74 | 75 | #### IE8及以下 76 | 77 | addEventListener 和 removeEventListener 在IE8及以下不被支持 78 | 79 | ###### el.attachEvent(eventName, handle) 80 | 81 | * 描述:给DOM元素添加指定的事件处理函数 82 | 83 | * 参数: 84 | * `{String} eventName` 事件名称 85 | * `{Function} handle` 事件函数 86 | 87 | ###### el.detachEvent(eventName, handle) 88 | 89 | * 描述:移除通过 addEventListener 添加的事件处理函数 90 | 91 | * 参数: 92 | * `{String} eventName` 事件名称 93 | * `{Function} handle` 事件函数 94 | 95 | #### 对比 96 | 97 | attachEvent/detachEvent 与 addEventListener/removeEventListener 的区别: 98 | 99 | * 由于IE8不支持事件捕获,所以通过 attachEvent/detachEvent 绑定的时间也只能在冒泡阶段触发 100 | * 通过 attachEvent/detachEvent 绑定的事件函数会在全局作用域中运行,即: this === window 101 | * 通过 attachEvent/detachEvent 绑定的事件函数以绑定时的先后顺序倒序被执行 102 | * attachEvent/detachEvent 的第一个参数要在事件名称前面加 'on' 103 | 104 | 105 | ## 四、事件对象 106 | 107 | #### 标准 108 | 109 | ##### 属性 110 | 111 | ###### event.bubbles 112 | * 读写特性:只读 113 | * 描述:表示事件是否冒泡 114 | 115 | ###### event.cancelable 116 | * 读写特性:只读 117 | * 描述:表示事件是否可以取消事件默认行为 118 | 119 | ###### event.currentTarget 120 | * 读写特性:只读 121 | * 描述:currentTarget的值始终等于 this,即指向事件所绑定到的元素 122 | 123 | ###### event.target 124 | * 读写特性:只读 125 | * 描述:真正触发事件的元素 126 | 127 | ###### event.defaultPrevented 128 | * 读写特性:只读 129 | * 描述:为 true 表示已经调用了 preventDefault() 130 | 131 | ###### event.detail 132 | * 读写特性:只读 133 | * 描述:与事件相关的细节信息 134 | 135 | ###### event.eventPhase 136 | * 读写特性:只读 137 | * 描述:调用该事件处理函数的阶段 `0` 表示没有事件正在被处理 `1` 表示捕获阶段 `2` 表示处于目标阶段 `3` 表示冒泡阶段 138 | 139 | ###### event.trusted 140 | * 读写特性:只读 141 | * 描述:为true表示事件是由浏览器生成的,false表示事件是由人工使用JavaScript创建的 142 | 143 | ###### event.type 144 | * 读写特性:只读 145 | * 描述:事件类型 146 | 147 | ###### event.type 148 | * 读写特性:只读 149 | * 描述:事件类型 150 | 151 | ##### 方法 152 | 153 | ###### event.preventDefault() 154 | 155 | * 描述:阻止事件的默认行为 156 | 157 |

只有 event.cancelable 属性为 true 的事件,才能够通过 preventDefault() 方法取消默认行为

158 | 159 | ###### event.stopImmediatePropagation() 160 | 161 | * 描述:与 event.stopPropagation() 一样,可以阻止事件冒泡,除此之外,还能阻止执行该语句之后的所有事件监听 162 | 163 | #### IE特有 164 | 165 | ###### IE中获取事件对象的方法 166 | 167 |

IE中获取事件对象的方法与绑定事件的方式有关

168 | 169 | 1、DOM0级绑定事件的方式,即 `el.onclick = function () {}`,其事件对象通过window获取: 170 | 171 | ```js 172 | el.onclick = function () { 173 | window.event 174 | } 175 | ``` 176 | 177 | 2、DOM2级绑定事件对象的方式,即 `el.attachEvent('click', function (event) {})`,既可以通过window获取,也可以通过event参数获取: 178 | 179 | ```js 180 | el.attachEvent('click', function (event) { 181 | window.event 182 | // 或 183 | event 184 | }) 185 | ``` 186 | 187 | ##### 属性 188 | 189 | ###### event.srcElement 190 | * 读写特性:只读 191 | * 描述:与规范中的 event.target 属性相同。 192 | 193 | ###### event.returnValue 194 | * 读写 195 | * 描述:默认为 `true`,如果将其设为 `false` 即可取消事件默认行为,相当于规范中的:`event.preventDefault()` 196 | 197 | ###### event.cancelBubble 198 | * 读写 199 | * 描述:默认为 `false` ,如果设为 `true` 即可取消事件冒泡,相当于规范中的:`event.stopPropagation()` 200 | 201 | #### 事件总结 202 | 203 |

204 | 在规范中,事件处理函数的this对象始终等于 event.currentTarget 属性,但在IE中就不一定。比如:使用 attachEvent 绑定的事件处理函数是在全局作用域中运行的,所以this对象指向window,而不是 event.srcElement 205 |

206 | 207 | ###### 对照表 208 | 209 | ``` 210 | | standard | IE | 211 | | ------------- | ------------- | 212 | | target | srcElement | 213 | | preventDefault() | returnValue = false | 214 | | stopPropagation() | cancelBubble = true | 215 | ``` 216 | 217 | ## 五、事件类型及讲解 218 | 219 | #### UI事件 220 | 221 | ###### load 222 | 223 | ``` 224 | window上触发: 225 | 当页面完全加载完,包括所有图像、js文件、css文件、内嵌对象等等 226 | 图片: 227 | 当图片加载完成后触发 228 |