├── .prettierrc ├── CSS(3) ├── BEM 命名规范笔记.md ├── CSS 实现等比缩放.md ├── CSS 重排与重绘笔记.md ├── CSS面试题总结.md ├── images │ ├── bem-block.jpg │ ├── browser-render.png │ ├── deep-understand-font-size.png │ ├── font-size-support.png │ ├── inline-elem-margin-padding.png │ ├── rander-reflow.png │ ├── render-tree.png │ ├── stacking-and-float-1.png │ └── stacking-and-float-2.png ├── 七种水平垂直居中方法总结.md ├── 各种像素概念.md └── 深入理解 font-size 笔记.md ├── HTML(5) └── HTML5 │ ├── 1px 的解决方案.md │ ├── HTML5 知识总结.md │ ├── WebSocket 学习总结.md │ ├── images │ ├── border-image-1px-line1.png │ ├── border-image-1px-line2.png │ ├── border-image-1px-line4.png │ ├── cut_img_ps_action.png │ ├── fake_array_to_array.png │ ├── h5_ability_model.png │ ├── h5_datalist.gif │ ├── h5_location.png │ ├── h5_location_error.png │ ├── h5_location_succ.png │ ├── h5_media_attr.png │ ├── h5_oninvalid.png │ ├── h5_upload_drag.gif │ ├── h5_upload_multiply.gif │ ├── h5_yuyihua.png │ └── html_yuyihua.png │ ├── 像素、分辨率相关概念.md │ ├── 大厂 H5 开发实战 │ └── 基础页面开发.md │ └── 探究移动端适配.md ├── JavaScript ├── ES6 笔记 │ ├── ES6 基础.md │ ├── ES6-异步操作.md │ └── ES6-面向对象.md ├── JS 基础知识点1.md ├── JS 基础知识点2.md ├── __test__ │ └── bind.test.js ├── images │ ├── circle_refer.png │ ├── js-prototype-chain-all.png │ ├── js-prototype-chain1.png │ └── regexp_knowledge_map.png ├── 从 JS 高阶函数到柯里化.md ├── 手写系列 │ ├── 手写 Ajax.md │ ├── 手写 JSONP.md │ ├── 手写 XXX.md │ ├── 手写柯里化.md │ └── 手写防抖节流.md ├── 正则表达式总结.md └── 零碎知识笔记.md ├── LICENSE ├── Node ├── .gitignore ├── Node.js 包教不包会 │ ├── README.md │ ├── lesson1 │ │ ├── README.md │ │ ├── app.js │ │ └── package.json │ ├── lesson2 │ │ ├── README.md │ │ ├── app.js │ │ ├── imgs │ │ │ ├── have_query.png │ │ │ └── no_query.png │ │ └── package.json │ ├── lesson3 │ │ ├── README.md │ │ ├── app.js │ │ ├── package.json │ │ └── show.png │ ├── lesson4 │ │ ├── README.md │ │ ├── app.js │ │ ├── cnode_crawler.png │ │ └── package.json │ ├── lesson5 │ │ ├── README.md │ │ ├── app.js │ │ ├── async_crewler.png │ │ └── package.json │ ├── lesson6 │ │ ├── READMD.md │ │ ├── app.js │ │ ├── coverage │ │ │ ├── coverage.json │ │ │ ├── lcov-report │ │ │ │ ├── base.css │ │ │ │ ├── index.html │ │ │ │ ├── lesson6 │ │ │ │ │ ├── index.html │ │ │ │ │ └── main.js.html │ │ │ │ ├── prettify.css │ │ │ │ ├── prettify.js │ │ │ │ ├── sort-arrow-sprite.png │ │ │ │ └── sorter.js │ │ │ └── lcov.info │ │ ├── main.js │ │ ├── package.json │ │ └── test │ │ │ └── main.test.js │ └── lesson7 │ │ ├── package.json │ │ └── vendor │ │ ├── index.html │ │ ├── mocha.css │ │ ├── mocha.js │ │ └── tests.js └── 七天学会 Node.js │ ├── README.md │ ├── cat.test │ └── tests.js │ ├── cat │ ├── body.js │ ├── head.js │ └── index.js │ ├── myCopy │ ├── big-copy.js │ └── small-copy.js │ ├── node-echo.test │ └── tests.js │ └── node-echo │ ├── README.md │ ├── bin │ └── node-echo.js │ ├── lib │ └── echo.js │ └── package.json ├── README.md ├── React └── egghead │ └── start-learning-react │ ├── JSX编译器.md │ ├── README.md │ ├── React.children和React.cloneElement.md │ ├── imgs │ ├── React.children_React.cloneElement.gif │ ├── react-HOC.gif │ ├── react-event-system.gif │ ├── react_ref.gif │ └── reusable_component.gif │ ├── 事件系统.md │ ├── 编写可重用的组件.md │ ├── 获取组件的引用.md │ └── 高价组件.md ├── UI 设计 ├── MBE风格图标设计 │ ├── README.md │ └── imgs │ │ ├── 515_cloud_disk_logo.psd │ │ ├── 515_cloud_disk_logo_background.jpg │ │ ├── 515_cloud_disk_logo_transparent.png │ │ ├── 515_cloud_disk_origin.png │ │ ├── chandle.psd │ │ ├── chandle_background.jpg │ │ ├── chandle_origin.jpg │ │ ├── chandle_transparent.png │ │ ├── egg_love.psd │ │ ├── egg_love_background.jpg │ │ ├── egg_love_origin.png │ │ ├── egg_love_transparent.png │ │ ├── ice_cream.psd │ │ ├── ice_cream2.psd │ │ ├── ice_cream2_background.jpg │ │ ├── ice_cream2_origin.jpg │ │ ├── ice_cream2_transparent.png │ │ ├── ice_cream_background.jpg │ │ ├── ice_cream_origin.jpg │ │ └── ice_cream_transparent.png └── Photoshop (PS) │ └── README.md ├── 代码规范 └── Stranard 规范总结.md ├── 性能优化 ├── Web 性能优化总结.md ├── images │ ├── css_backflow.png │ ├── file_compress.png │ ├── html_render_process.png │ ├── png_type.png │ ├── service_cache.png │ ├── web-cache.png │ ├── web_speed_cookie.png │ └── what_happen_when_url_send.png └── 浏览器缓存机制总结.md ├── 打包工具 ├── gulp │ ├── README.md │ ├── gulpfile.js │ └── package.json ├── rollup │ └── README.md └── webpack │ └── README.md ├── 模仿面试 ├── 八股速查.md └── 封装 request 专题 │ ├── 取消前一个 pending 的,然后发送下一个.js │ ├── 只发送第一个,后面的直接复用第一个的结果.js │ └── 控制最大并发量、错误重试.js ├── 浏览器相关 ├── images │ ├── event-loop-callstack.png │ ├── event-loop-excute-demo1.gif │ ├── event-loop-task-queue.png │ ├── localstorage-weixin.png │ └── url-struct.png ├── 前端路由原理和实现.md ├── 深入了解浏览器存储.md ├── 深入理解 JS 事件循环机制(浏览器篇).md └── 跨域及其实现原理.md ├── 版本控制 ├── Git 相关总结.md └── images │ ├── a-picture-to-understand-git.png │ ├── git-flow-1.png │ ├── git-flow-2.png │ ├── git-flow-3.png │ ├── git-flow-4.png │ ├── git-merge.png │ └── modify-pr.png ├── 网络相关 ├── HTTP(S) 相关 │ ├── images │ │ └── tls-four-handshake.png │ └── 深入探究 TLS 协议.md ├── TCP 相关 │ ├── images │ │ ├── tcp-four-wave.png │ │ ├── tcp-message-struct.png │ │ ├── tcp-three-handshake-fail-in-three.png │ │ └── tcp-three-handshake.png │ └── 深入探究 TCP 协议.md └── Web 安全 │ ├── CSRF 攻击 │ ├── README.md │ └── imgs │ │ ├── csrf-attack-yuanli.png │ │ ├── csrf-double-cookie-test.png │ │ ├── github-same-site.png │ │ ├── referer-policy-value.png │ │ ├── referer-policy.png │ │ ├── same-site-browser-support-mobile.png │ │ └── same-site-browser-support-pc.png │ ├── Cookie 安全 │ ├── README.md │ └── imgs │ │ └── github-cookies-secure.png │ ├── HTML5 安全 │ ├── README.md │ └── imgs │ │ ├── h5-postMessage-iframe-secure.gif │ │ ├── h5-postMessage-iframe.gif │ │ ├── h5-postMessage-win-open.gif │ │ └── simple-req.png │ ├── SQL 注入攻击 │ └── README.md │ ├── Web 安全总结.md │ ├── XSS 攻击 │ ├── README.md │ └── imgs │ │ ├── browser-csp-value.png │ │ ├── browser-xss-protectino.png │ │ └── github-csp-example.png │ ├── 上传文件攻击 │ └── README.md │ ├── 密码安全 │ └── README.md │ └── 点击劫持攻击 │ ├── README.md │ └── imgs │ ├── click-hijack.png │ ├── frame-ancestors.png │ └── x-frame-options.png ├── 设计模式 └── 前端常用的设计模式.md └── 读书笔记 ├── JS 高级程序设计 ├── DOM.md ├── README.md └── images │ └── section10 │ └── node_map.png └── 你不知道的 JS ├── README.md ├── 上卷.md ├── 下卷.md └── 中卷.md /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /CSS(3)/BEM 命名规范笔记.md: -------------------------------------------------------------------------------- 1 | - [BEM 规范](#bem-规范) 2 | - [B -- Block](#b----block) 3 | - [E -- Element](#e----element) 4 | - [M -- Modifier](#m----modifier) 5 | - [混合](#混合) 6 | - [命名规则](#命名规则) 7 | - [命名风格](#命名风格) 8 | - [文件结构](#文件结构) 9 | - [如何定位一个块](#如何定位一个块) 10 | - [选择器](#选择器) 11 | - [如何切换到 BEM 样式的 CSS](#如何切换到-bem-样式的-css) 12 | - [如何在已有的项目中使用 BEM](#如何在已有的项目中使用-bem) 13 | 14 | # BEM 规范 15 | 16 | ## B -- Block 17 | 18 | 块。表示有意义的实体,块名称用于描述它的目的。例如:`header`, `container`, `main`, `menu`, `input`。 19 | 20 | 特点: 21 | 22 | - 不能影响其他元素(即不能设置 `margin` 或 `position`)。 23 | - 可以相互嵌套(任意嵌套级别)。 24 | 25 | 示例: 26 | 27 | ![bem block](./images/bem-block.jpg) 28 | 29 | ```html 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 |
...
38 |
39 | ``` 40 | 41 | > 块的位置是任意的,可以在页面或项目之间随意移动,且无需修改 CSS 或 JavaScript 代码。因此,要将块实现为独立的实体。 42 | 43 | ## E -- Element 44 | 45 | 元素。块的一部分,语义上与块相关联,没有独立意义,**不能单独使用**。例如:`menu item`, `list item`, `header title`。 46 | 47 | 结构:`block__element`。 48 | 49 | 特点: 50 | 51 | - 可以相互嵌套(任意嵌套级别)。 52 | - 不能单独使用,没有块就不能使用元素。 53 | - 元素是可选的,并不是所有块都要有元素。 54 | 55 | 示例: 56 | 57 | ```html 58 | 59 | 64 | 65 | 66 | 67 | 68 |
69 | 70 |
71 | 72 | 73 | 78 | ``` 79 | 80 | ## M -- Modifier 81 | 82 | 修饰符。用于定义块或元素的外观、状态、行为。例如: 83 | 84 | > `disabled, highlighted, checked, fixed, big, yellow` 85 | 86 | 结构: 87 | 88 | - `block_modifier` -- 块 + 修饰符。 89 | - `block__element_modifier` -- 块 + 元素 + 修饰符。 90 | - `block_modifier_value` -- 块 + 修饰符 + 修饰符的值。 91 | - `block__element_modifier_value` -- 块 + 元素 + 修饰符 + 修饰符的值。 92 | 93 | 特点: 94 | 95 | - 修饰符名称与 B 或 E 之间用单线划线 `_` 分隔。 96 | - 不能同时使用多个具有不同值的修饰符。 97 | - 不能单独使用,没有块或元素就不能使用修饰符。 98 | 99 | 示例: 100 | 101 | ```html 102 | 103 | 104 | 110 | 111 | 112 | 121 | 122 | 123 |
124 |
125 | 126 |
127 |
128 | ``` 129 | 130 | 上面的示例中可以看到,元素和修饰符名称的前面带有块的名称,这样做的好处是:[为什么在修饰符和元素名中包含块名](https://en.bem.info/methodology/faq/#why-include-the-block-name-in-modifier-and-element-names)。 131 | 132 | ## 混合 133 | 134 | 特点: 135 | 136 | - 在不复制代码的情况下组合多个实体的行为和样式。 137 | - 基于现有的 UI 组件创建语义上新的 UI 组件。 138 | 139 | 示例: 140 | 141 | ```html 142 |
143 | 144 | 145 |
146 | ``` 147 | 148 | ## 命名规则 149 | 150 | `block__elem_mod_value` 151 | 152 | - 名称全小写。 153 | - 如果名称中有多个单词,使用 `-` 分隔。 154 | 155 | 例如:`search-form__input-username` 156 | 157 | ## 命名风格 158 | 159 | 除了上述示例的命名风格外,还有其他几种可供选择: 160 | 161 | - `block__elem--mod--value` -- 双破折号风格 162 | - `blockName-elemName_modName_modVal` -- 小驼峰风格 163 | - `BlockNmae-ElemName_modName_modVal` -- 大驼峰风格 164 | - 自定义风格(要符合 BEM 的规范) 165 | 166 | ## 文件结构 167 | 168 | 文件结构中目录或文件的命名也适用于 BEM 命名。详见:[File structure](https://en.bem.info/methodology/quick-start/#file-structure) 169 | 170 | ## 如何定位一个块 171 | 172 | 块具有独立性,不能影响其他块的定位。下面是定位块的几种方法。 173 | 174 | - 通过混合来定位一个块 175 | 176 | ```html 177 | 178 | 179 | 180 | 181 | ``` 182 | 183 | ```css 184 | /* 影响定位的属性写在混合元素 `page__header` 上 */ 185 | .page__header { 186 | margin: 20px; 187 | } 188 | ``` 189 | 190 | - 添加额外的元素来定位一个快 191 | 192 | ```html 193 | 194 | 195 |
196 | 197 | ``` 198 | 199 | ```css 200 | .page__inner { 201 | margin: 0 auto; 202 | width: 960px; 203 | } 204 | ``` 205 | 206 | ## 选择器 207 | 208 | BEM 命名中,不能使用**标签选择器**和 **ID 选择器**。所有的元素都可以使用类选择器定位到。 209 | 210 | 对于一些特殊情况,可以使用下面几种选择器(都不推荐使用): 211 | 212 | - 联合标签选择器和类选择器。例如:`button.button_active`。 213 | - 联合类选择器。例如:`.button.button_active`。 214 | - 嵌套选择器。例如:`.button_hovered .button__text`。 215 | 216 | 这几种选择器改变了选择器的权重,增加了耦合度,不利于维护,所以不推荐使用。 217 | 218 | ## 如何切换到 BEM 样式的 CSS 219 | 220 | 在项目中应用以下 BEM 准则: 221 | 222 | - 抛开 DOM 模型,尝试创建块。 223 | - 不要使用 ID 或 标签选择器。 224 | - 减少嵌套选择器的数目。 225 | - 将一个块的 CSS 属性添加到修饰符上,如果它们看起来可能会更改。 226 | - 使用混合。 227 | - 重用块。 228 | 229 | ## 如何在已有的项目中使用 BEM 230 | 231 | - 根据 BEM 的准则创建新的组件,并根据需要修改旧的组件。 232 | - 在 CSS 类名前加私有前缀(例如:`bem-`),以便于和旧代码区分。 233 | 234 | --- 235 | 236 | 本文来源于:[BEM Methodology](https://en.bem.info/methodology/quick-start/),进行总结和概括后,作为笔记记录下来。 237 | -------------------------------------------------------------------------------- /CSS(3)/CSS 实现等比缩放.md: -------------------------------------------------------------------------------- 1 | - [CSS 实现等比缩放](#css-实现等比缩放) 2 | - [vw 方案](#vw-方案) 3 | - [padding 方案](#padding-方案) 4 | - [padding 方案改进](#padding-方案改进) 5 | 6 | # CSS 实现等比缩放 7 | 8 | 元素的等比缩放(`宽:高 = 固定比例`)是一个常见的需求,也是面试中可能问到的问题。 9 | 10 | 我们最终要实现的效果是:**「实现一个宽度自适应父元素,高度始终为宽度 N % 的元素」**。 11 | 12 | 这里,我们以高是宽的 30% 作为举例。 13 | 14 | ## vw 方案 15 | 16 | CSS 中 `vw` 单位相对于**浏览器视口**宽度,100vw 等于视口宽度的 100%。其中“浏览器视口”指的是浏览器可视区域的大小,也就是 `window.innerWidth / window.innerHeight` 获取到的大小。 17 | 18 | 因此,如果**有一个元素是横向占满空间**,那么想要保持该元素的宽高比,就可以使用 `vw` 单位。如下所示: 19 | 20 | ```html 21 | 27 | 28 |
我的宽占满屏幕,我的高是宽的 30%
29 | ``` 30 | 31 | 如果宽度不是占满屏幕,想要两边“留点缝”,可以借助 `calc()` 函数。如下所示: 32 | 33 | ```html 34 | 43 | 44 |
我的左右两边距离屏幕两侧各 20px,我的高是宽的 30%
45 | ``` 46 | 47 | 从上面的介绍可以看出,`vw` 方案存在如下局限性:**需要确定元素宽度和浏览器视口宽度的比例**。也就是说,元素宽度和浏览器视口宽度之比,需要是 100%、50%、30% 等等,必须是一个确定的比例才行。 48 | 49 | 由于 `vw` 方案的局限性,无法实现我们最终想要的效果“宽度自适应父元素,高度始终为宽度 N %”,因此这里仅做介绍,下面才是今天的主角 `padding-top / padding-bottom`。 50 | 51 | ## padding 方案 52 | 53 | 要知道,`padding-top / padding-bottom` 设置百分比的值时,是相对于父元素的宽度来计算的。利用这一特点,可以很容易实现我们最终的效果。如下所示: 54 | 55 | ```html 56 | 64 | 65 |
我的高是宽的 30%,使用 padding-top 实现
66 | ``` 67 | 68 | 其中,将元素的 `height` 设为 0,然后使用 `padding-top` 来撑开元素的高度,由于 `padding-top` 是根据元素宽度来计算的,所以就实现了等比缩放。 69 | 70 | ## padding 方案改进 71 | 72 | 由于元素 `height` 为 0,高度由 `padding-top` 来撑起,所以该元素有子元素时,就会被“挤到外面”。因此需要用 `position: absolute;` 来解决, 如下所示: 73 | 74 | ```html 75 | 100 | 101 |
102 |
103 |
hello world
104 |
105 |
106 | ``` 107 | 108 | 改进后的方案中,对于等比缩放元素来说,如果想要**改变其宽度**或**在其两边留点缝**,需要再包一层父元素来控制。实现也很简单,这里不再赘述。 109 | -------------------------------------------------------------------------------- /CSS(3)/CSS 重排与重绘笔记.md: -------------------------------------------------------------------------------- 1 | - [重排与重绘](#重排与重绘) 2 | - [浏览器渲染过程](#浏览器渲染过程) 3 | - [生成渲染树](#生成渲染树) 4 | - [重排](#重排) 5 | - [重绘](#重绘) 6 | - [触发条件](#触发条件) 7 | - [浏览器的优化机制](#浏览器的优化机制) 8 | - [优化点](#优化点) 9 | 10 | # 重排与重绘 11 | 12 | 想要深入了解浏览器的重排或重绘,需要先知道浏览器的工作原理。 13 | 14 | ## 浏览器渲染过程 15 | 16 | 浏览器的渲染流程如下: 17 | 18 | ![](./images/browser-render.png) 19 | 20 | 总结来说,如下: 21 | 22 | - HTML 解析为 DOM 树,CSS 解析为 CSSOM 树,然后 DOM 和 CSSOM 合并为渲染树(Render Tree) 23 | - 布局(Layout):根据渲染树进行布局,得到节点的几何信息(位置、大小) 24 | - 绘制(Painting):根据渲染树和布局得到的几何信息,得到节点的绝对像素 25 | - 显示(Display):将像素发送给 GPU,显示在页面上 26 | 27 | ## 生成渲染树 28 | 29 | ![](./images/render-tree.png) 30 | 31 | 构建渲染树的流程如下: 32 | 33 | - 从 DOM 树的根节点开始遍历所有**可见**节点 34 | - 对于每个可见节点,找到其在 CSSOM 中的对应样式并应用 35 | - 最后,将所有节点和样式组合成渲染树 36 | 37 | 上面提到了可见节点,因此我们需要知道,什么样的算是可见节点。**不可见节点如下**: 38 | 39 | - 不会渲染到页面上的节点。例如 `script`, `meta`, `link` 节点 40 | - 通过 CSS 隐藏的节点。例如:`display: none` 41 | 42 | > 注意:通过 `visibility` 或 `opacity` 隐藏的节点,仍会被添加到渲染树上,所以会在页面上渲染,只不过是人眼不可见而已。 43 | 44 | ## 重排 45 | 46 | 生成了渲染树之后,还需要知道元素在页面上的具体位置,这个计算元素具体位置的阶段就是重排。 47 | 48 | 例如,有以下节点: 49 | 50 | ```html 51 |
52 |
Hello world
53 |
54 | ``` 55 | 56 | 在重排阶段,会根据视口(viewport)的具体宽度,计算这些节点的位置。如图所示: 57 | 58 | ![](./images/rander-reflow.png) 59 | 60 | ## 重绘 61 | 62 | 经过了构造渲染树和重排阶段,接下来需要将渲染树上的节点都转换为屏幕上的实际像素,这个阶段就叫重绘。 63 | 64 | ## 触发条件 65 | 66 | 触发重排的条件如下: 67 | 68 | - 页面一开始渲染的时候(无法避免) 69 | - 删除 / 添加可见 DOM 元素 70 | - 浏览器窗口大小变化 71 | - 元素位置变化 72 | - 元素尺寸变化(包括 `margin`, `padding`, `border` 等) 73 | - 元素内容变化。比如:文字变化、图片大小改变 74 | - 激活 CSS 伪类(例如 `:hover`) 75 | 76 | ## 浏览器的优化机制 77 | 78 | 现代的浏览器都很聪明,自身是有一些优化策略的。其中**浏览器会维护一个队列,把所有引起重排、重绘的操作放入这个队列中,等到队列中的操作达到一定的阈值或达到一定的时间间隔,浏览器就会刷新这个队列,批量执行其中的操作,这样就使得多次重排重绘变成了一次重排重绘**。 79 | 80 | 当你使用某些属性时,浏览器会强制刷新维护的队列,并立即进行重排、重绘,因此要尽量避免使用这些属性。这些属性部分如下: 81 | 82 | ``` 83 | offsetTop、offsetLeft、offsetWidth、offsetHeight 84 | scrollTop、scrollLeft、scrollWidth、scrollHeight 85 | clientTop、clientLeft、clientWidth、clientHeight 86 | getComputedStyle()、currentStyle() 87 | getBoundingClientRect()、getClientRects() 88 | ``` 89 | 90 | 查看所有会引起重排或重绘的属性,点击[这里](https://gist.github.com/paulirish/5d52fb081b3570c81e3a)。 91 | 92 | ## 优化点 93 | 94 | - 将多次修改 CSS 样式,合并为一次修改 95 | 96 | 有以下两种方法: 97 | 98 | - 将样式通过 CSS 的 `class` 一次性添加 99 | - 使用 `cssText`(兼容性较差) 100 | 101 | ```js 102 | var elem = document.getElementById('div'); 103 | elem.style.cssText = 'margin: 10px; padding: 5px;'; 104 | ``` 105 | 106 | - 使用 `transform` 代替 `top`、`bottom`、`left`、`right` 107 | - 使用 `opacity` 代替 `visibility` 108 | - **离线修改** DOM 样式 109 | 110 | 具体步骤是:先将 DOM 隐藏(`display: none;`),然后修改样式,最后在将其显示(`display: block;`)。 111 | 112 | 这种方法只会在隐藏、显示 DOM 的时候触发重排,因此适用于频繁修改样式的操作。 113 | 114 | - 使用**文档片段**(`DocumentFragment`),将频繁的 DOM 操作合并 115 | - 减少使用那些会强制刷新 flush 队列的属性,如果使用不要放在循环中 116 | - 尽量避免 `table` 布局 117 | 118 | 1. HTML 中的 `table` 布局,只要其中的一个元素发生变化,就会引起整个 `table` 的重排、重绘。 119 | 2. 浏览器在渲染 `table` 布局的时候,会花费比同等其他 DOM 元素多好几倍的时间。 120 | 121 | - 动画的的频率不要设置太高 122 | - 将动画涉及的 DOM 元素放入单独的合成层 123 | - 使用 GPU 加速 124 | -------------------------------------------------------------------------------- /CSS(3)/images/bem-block.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/bem-block.jpg -------------------------------------------------------------------------------- /CSS(3)/images/browser-render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/browser-render.png -------------------------------------------------------------------------------- /CSS(3)/images/deep-understand-font-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/deep-understand-font-size.png -------------------------------------------------------------------------------- /CSS(3)/images/font-size-support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/font-size-support.png -------------------------------------------------------------------------------- /CSS(3)/images/inline-elem-margin-padding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/inline-elem-margin-padding.png -------------------------------------------------------------------------------- /CSS(3)/images/rander-reflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/rander-reflow.png -------------------------------------------------------------------------------- /CSS(3)/images/render-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/render-tree.png -------------------------------------------------------------------------------- /CSS(3)/images/stacking-and-float-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/stacking-and-float-1.png -------------------------------------------------------------------------------- /CSS(3)/images/stacking-and-float-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/CSS(3)/images/stacking-and-float-2.png -------------------------------------------------------------------------------- /CSS(3)/各种像素概念.md: -------------------------------------------------------------------------------- 1 | # 物理像素、逻辑像素、CSS 像素、PPI、DPR 是什么? 2 | 3 | - 物理像素(Physical Pixel):是指显示设备(如屏幕)的实际像素,它是设备硬件上的一个点。 4 | 5 | - 逻辑像素(Logical Pixel):也称为**设备独立像素**(Device Independent Pixel,DIP),是指应用程序中使用的虚拟像素,是对物理像素的一个抽象。在不同的设备上,同一个逻辑像素的尺寸可能会有所不同。 6 | 7 | - CSS 像素(CSS Pixel):是指网页上的一个虚拟像素,是 Web 开发中使用的一个单位。**CSS 像素通常等于逻辑像素**。 8 | 9 | 当页面缩放比不为 1 时(`CSS 像素:逻辑像素 != 1:1`)。比如当页面放大 200%,则 1 个 CSS 像素等于 4 个逻辑(形如:`口 -> 田`)。 10 | 11 | - PPI(Pixels Per Inch):是指**每英寸的物理像素数量**,用来度量显示设备的像素密度。像素密度越高,屏幕显示的图像越清晰。 12 | 13 | 计算:`PPI = 对角线物理像素数 / 屏幕尺寸(对角线英寸数)`。 14 | 15 | 例如:iPhone12 mini,物理像素数:1080x2040,屏幕尺寸 5.4 英寸,则: 16 | 17 | `PPI = Math.sqrt(1080², 2040²) / 5.4` 18 | 19 | - DPR,设备像素比(Device Pixel Ratio):是指设备上**物理像素的数量与逻辑像素的数量之比**。 20 | 21 | 例如,如果一个设备的物理像素是 320×240,而逻辑像素是 160×120,则该设备的 DPR 为 2。 22 | 23 | 在 JS 中可以通过 `window.devicePixelRatio` 读取/设置(不推荐手动设置) DPR 值。 24 | -------------------------------------------------------------------------------- /CSS(3)/深入理解 font-size 笔记.md: -------------------------------------------------------------------------------- 1 | - [深入理解 font-size](#深入理解-font-size) 2 | - [字体相关概念](#字体相关概念) 3 | - [基线](#基线) 4 | - [em-box](#em-box) 5 | - [content-area](#content-area) 6 | - [半行距](#半行距) 7 | - [x-height](#x-height) 8 | 9 | # 深入理解 font-size 10 | 11 | ## 字体相关概念 12 | 13 | ![](./images/deep-understand-font-size.png) 14 | 15 | ### 基线 16 | 17 | 基线就是图中 x 字母下面的线,所有的字符都是基于这条线定位的。`vertical-align: baseline;` 就是指的这条线。 18 | 19 | ### em-box 20 | 21 | `1em` 和 `font-size` 的大小一致,而 `em-box` 就是 `1em` 的大小。例如,`font-size: 20px` 那么 `em-box` 就是 `20px`。 22 | 23 | ### content-area 24 | 25 | 这个区域没有在图中标出,`content-area` 翻译成中文就是内容区域,默认情况下,和 `line-height` 一样高。 26 | 27 | 给**内联元素**设置一个背景,这个背景就可以看作是内容区域。 28 | 29 | `content-area` 与 `font-size` 和 `font-family` 有关。因此,有时我们设置了相同的 `font-size`,但是实际显示大小却不同,这就是因为 `font-family` 不同导致的。 30 | 31 | ### 半行距 32 | 33 | 行距的计算是:`content-area` - `em-box`,也就是 `line-height` - `font-size`。半行距就是行距的一半。 34 | 35 | ### x-height 36 | 37 | `x-height` 顾名思义就是 `x` 字符高度的意思。这个概念通常没什么用,但是有助于我们理解一些概念,例如:`vertical-align: middle;` 这个属性指的就是,与 `x-height` 高度的一半对齐。 38 | -------------------------------------------------------------------------------- /HTML(5)/HTML5/WebSocket 学习总结.md: -------------------------------------------------------------------------------- 1 | - [WebSocket](#websocket) 2 | - [WebSocket 基本用法](#websocket-基本用法) 3 | 4 | # WebSocket 5 | 6 | 在 H5 WebSocket 出现之前,为了实现消息推送,最常用的方式有三种:**轮询**、**长轮询**、**iframe 流**。它们的优缺点以及适用场景如下: 7 | 8 | - 轮询:客户端定时向服务器发送 Ajax 请求,服务器接到请求后马上返回响应信息并关闭连接。 9 | 10 | 优点:后端程序编写比较容易。 11 | 缺点:请求大半是无用的、浪费带宽和服务器资源。 12 | 实例:适于小型应用。 13 | 14 | - 长轮询:客户端向服务器发送 Ajax 请求,服务器接到请求后 hold 住连接,直到有新消息或超时才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。 15 | 16 | 优点:在无消息的情况下不会频繁的请求。 17 | 缺点:服务器 hold 连接会消耗资源。 18 | 实例:WebQQ、Hi 网页版、Facebook IM。 19 | 20 | - iframe 流:在页面里嵌入一个隐蔵 iframe,将这个隐蔵 iframe 的 src 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。 21 | 22 | 优点:消息即时到达、不发无用请求。 23 | 缺点:服务器维护一个长连接会增加开销、无法准确知道连接状态。 24 | 实例:Gmail 聊天。 25 | 26 | - WebSocket:客户端通过 `new WebSocket()` 发起通信连接,之后不论客户端还是服务端,均可向对方发送报文。 27 | 28 | 优点:双向通信实时性强、可发送二进制文件、较少的控制开销。 29 | 缺点:浏览器兼容较差、不支持断开重连。 30 | 实例:网络游戏、支付。 31 | 32 | ## WebSocket 基本用法 33 | 34 | 首先在前台创建一个 WebSocket 实例。 35 | 36 | `index.html` 37 | 38 | ```html 39 | 55 | ``` 56 | 57 | 然后,后台接收和向前台发送消息。后台创建 WebSocket 服务之前,需要安装 ws 包: 58 | 59 | ```bash 60 | npm install ws -S 61 | ``` 62 | 63 | 找个目录,新建 server.js 编写后台服务代码: 64 | 65 | ```js 66 | var express = require('express'); 67 | var app = express(); 68 | 69 | app.use(express.static(__dirname)); 70 | // 监听 3000 端口 71 | app.listen(3000); 72 | 73 | // 创建 WebScoket 服务 74 | var Server = require('ws').Server; 75 | // 设置服务器端口号 76 | var ws = new Server({ port: 9999 }); 77 | 78 | // 监听客户端和服务端的连接 79 | ws.on('connection', function (socket) { 80 | // 客户端发来消息 81 | socket.on('message', function (msg) { 82 | console.log('客户端发来的消息:', msg); 83 | socket.send('服务端返回的消息:', '你好,客户端'); 84 | }); 85 | }); 86 | ``` 87 | 88 | 在 `server.js` 文件的目录下执行指令:`node ./server.js`,然后将客户端 `index.html` 在浏览器里运行,打开控制台即可看见效果。 89 | 90 | --- 91 | 92 | 参考文章: 93 | 94 | - [Web 实时推送技术的总结](https://juejin.im/post/5c20e5766fb9a049b13e387b) 95 | - [WebSocket 是时候展现你优秀的一面了](https://juejin.im/post/5bc7f6b96fb9a05d3447eef8) 96 | -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/border-image-1px-line1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/border-image-1px-line1.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/border-image-1px-line2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/border-image-1px-line2.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/border-image-1px-line4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/border-image-1px-line4.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/cut_img_ps_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/cut_img_ps_action.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/fake_array_to_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/fake_array_to_array.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_ability_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_ability_model.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_datalist.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_datalist.gif -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_location.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_location_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_location_error.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_location_succ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_location_succ.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_media_attr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_media_attr.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_oninvalid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_oninvalid.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_upload_drag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_upload_drag.gif -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_upload_multiply.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_upload_multiply.gif -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/h5_yuyihua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/h5_yuyihua.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/images/html_yuyihua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/HTML(5)/HTML5/images/html_yuyihua.png -------------------------------------------------------------------------------- /HTML(5)/HTML5/像素、分辨率相关概念.md: -------------------------------------------------------------------------------- 1 | - [设备像素比 DPR](#设备像素比-dpr) 2 | - [PPI 和 DPI](#ppi-和-dpi) 3 | 4 | ## 设备像素比 DPR 5 | 6 | DRP 在 JS 中可以通过 `window.devicePixelRatio` 获取,指的是:物理像素和设备独立像素的比例。 7 | 8 | > 其中,物理像素(physical pixel)是:**设备能控制显示的最小单位**。 9 | > 10 | > 我们常说的**分辨率**指的就是**物理像素**,比如 iPhone 6S Plus 的分辨率是 `1920x1080`,表示横向有 1920 个物理像素,竖向有 1080 个物理像素。 11 | > 12 | > 设备独立像素(又称设备无关像素 Device Independent Pixels,DIP 或 DP)是:**与设备无关,用于逻辑上衡量长度的单位,由底层系统的程序使用,最后会转成物理像素**。 13 | > 14 | > 所以它只是一个虚拟像素单位,那么为什么会有这种虚拟像素的产生呢? 15 | > 16 | > 举个例子,iPhone 3GS 和 iPhone 4/4s 的尺寸都是 3.5 寸,但 iPhone 3GS 的分辨率是 320x480,iPhone 4/4s 的分辨率是 640x960,这也就是意味着同样长度的屏幕,iPhone 3GS 有 320 个物理像素,iPhone 4/4s 有 640 个物理像素。 17 | > 18 | > 如果我们按照真实的物理像素进行布局,比如说我们按照 320 物理像素进行布局,到了 640 物理像素的手机上就会有一半的空白,为了避免这种问题,就产生了虚拟像素单位。 19 | > 20 | > 我们统一 iPhone 3GS 和 iPhone 4/4s 都是 320 个虚拟像素,只是在 iPhone 3GS 上,最终 1 个虚拟像素换算成 1 个物理像素,在 iPhone 4s 中,1 个虚拟像素最终换算成 2 个物理像素。 21 | > 22 | > 至于 1 个虚拟像素被换算成几个物理像素,这个数值我们称之为设备像素比(DPR)。 23 | > 24 | > 作者:冴羽 25 | > 链接:https://www.zhihu.com/question/26653449/answer/541637971 26 | 27 | 我们平时写程序时,使用的 px 值就是设备独立像素,最终一个虚拟像素被转换成几个物理像素,由底层的图形系统决定。 28 | 29 | ## PPI 和 DPI 30 | 31 | PPI(Pixels Per Inch):每英寸的像素数。是像素密度(pixel density)度量单位。 32 | DPI(Dots Per Inch):每英寸的点数。是打印机、鼠标等设备分辨率的度量单位。 33 | 34 | 计算公式: 35 | 36 | ``` 37 | √ 水平像素数² + 垂直像素数² 38 | PPI = ---------------------------- 39 | 尺寸 40 | ``` 41 | 42 | 例如,iPhone 6 的 PPI 计算如下: 43 | 44 | ``` 45 | √ 1334² + 750² 46 | PPI = ---------------- = 325.6 ≈ 326 47 | 4.7 48 | ``` 49 | -------------------------------------------------------------------------------- /HTML(5)/HTML5/大厂 H5 开发实战/基础页面开发.md: -------------------------------------------------------------------------------- 1 | - [基础页面开发](#基础页面开发) 2 | - [H5 开发能力参考模型](#h5-开发能力参考模型) 3 | - [分 5 步完成设计稿的页面开发](#分-5-步完成设计稿的页面开发) 4 | - [设计稿审查](#设计稿审查) 5 | - [编写页面的骨骼框架](#编写页面的骨骼框架) 6 | - [填充网页的内容](#填充网页的内容) 7 | - [润色](#润色) 8 | - [兼容性测试](#兼容性测试) 9 | 10 | # 基础页面开发 11 | 12 | ## H5 开发能力参考模型 13 | 14 | ![h5_ability_model](../images/h5_ability_model.png) 15 | 16 | > 基础页面开发 -> 响应式页面开发 -> 滑屏应用开发 -> 动画效果开发 -> 游戏开发 17 | 18 | ## 分 5 步完成设计稿的页面开发 19 | 20 | ### 设计稿审查 21 | 22 | - 确定设计的开发友好性(是否有还原成本高 或 无法还原的地方) 23 | - 确定特殊元素是否有合理的边界处理(文案超出容器的边界怎么办) 24 | - 确定页面的整体结构框架(Layout) 25 | - 确定跨页面可复用的组件(Site Component) 26 | - 确定当前页面可复用的组件(Page Component) 27 | - ... 28 | 29 | ### 编写页面的骨骼框架 30 | 31 | - 重置盒模型 32 | 33 | ```css 34 | *, 35 | *::before, 36 | *::after { 37 | -webkit-box-sizing: border-box; 38 | -moz-box-sizing: border-box; 39 | box-sizing: border-box; 40 | } 41 | ``` 42 | 43 | - 布局 44 | 45 | - 普通文档流布局 46 | - 浮动布局(Float) 47 | - 绝对布局(Absolute) 48 | - 弹性布局(Flex) 49 | - 网格布局(Grid) 50 | 51 | > 根据场景选择合适的布局方案 52 | 53 | - 语义化 54 | 55 | 进行语义化的目的:1、便于阅读和维护 2、SEO 友好 56 | 57 | ![html_yuyihua](../images/html_yuyihua.png) 58 | 59 | ### 填充网页的内容 60 | 61 | > 切图。下面是几种 PS 切图技巧 62 | 63 | 1、Extract Assets 资源生成器 64 | 65 | 通过 `Extract Assets`,你可以: 66 | 67 | - 将 PSD 中的图层或图层组导出为一个或多个的图像资源 68 | - 导出 JPG、PNG、GIF 或 SVG 类型的图像资源 69 | - 为所有图像资源设置 1x、2x 等多分辨率的版本 70 | - 预览每个图像资源 71 | - 轻松将图像资源导出到你首选的文件夹中 72 | - 确保每当 PSD 发生变化时,被导出的资源都能得到自动更新 73 | 74 | **使用方法:** 75 | 76 | - 首选项 -> 增效工具 -> 启用生成器 77 | - 打开 PSD 文件后,选择:文件 -> 生成 -> 图像资源 78 | - 更改图层或图层组的名字为适当的文件格式扩展名 79 | 80 | > 这样资源生成器默认会在 PSD 的同一层目录下创建 assets 文件夹,并根据你修改的扩展名生成图片。 81 | 82 | 2、Extract Assets 进阶 83 | 84 | - 从一个图层或图层组中生成多个资源,请用半角逗号分隔该图层或图层组的名称 85 | 86 | ```text 87 | music.png, music.jpg, music_on.png 88 | ``` 89 | 90 | - 图像资源保存到子文件夹中 91 | 92 | ```text 93 | 子文件夹/music.png 94 | ``` 95 | 96 | - 指定图像品质和大小参数 97 | 98 | > 默认情况下, JPEG 资源会以 90% 品质生成, PNG 资源会以 32 位图像生成, GIF 资源则会以基本 Alpha 透明度生成 99 | 100 | 我们可以通过以下办法设置 JPEG 资源的参数: 101 | 102 | - 添加所需的输出品质作为资源名称的后缀:`jpg(1-10)` 或者 `jpg(1-100%)`,例如: 103 | 104 | ```text 105 | music.jpg5 106 | music.jpg50% 107 | ``` 108 | 109 | - 添加所需的输出图片大小(相对大小或者支持的单位:px, in, cm 和 mm)作为该资源名称前缀。Photoshop 会相应的缩放图像,例如: 110 | 111 | ```text 112 | 200% music.png 113 | 240x300 music.png 114 | ``` 115 | 116 | > 注意:前缀和资源名称之间要添加一个空格字符 117 | 118 | - 为资源指定默认位置 119 | 120 | 可以为生成的资源指定文件的默认位置,例如想将图层导出到 `hi-res/` (存放二倍图,并加上 `@2x` 的后缀),`lo-res/` 存放缩小 50% 的图标,可进行如下配置: 121 | 122 | A. 创建空图层 123 | B. 更改空图层的名称为 `default hi-res/@2x + 50% lo-res/` 124 | 125 | 3、PS 动作切图 126 | 127 | 用 `Extract Assets` 切图存在一个问题,它只能切画布范围内的资源,超出画布的部分会直接被裁减掉。如果想切完整的图片该怎么办?建议用原始的「导出图层」的方式来切图,步骤如下: 128 | 129 | - 右键点击图层或图层组 130 | - 选择转换为智能对象 131 | - 编辑内容 132 | - 导出图片 133 | 134 | > 为了避免重复劳动,可以用 PS 录制一个切图动作。 135 | 136 | ![cut_img_ps_action](../images/cut_img_ps_action.png) 137 | 138 | ### 润色 139 | 140 | > CSS 编写起来比较容易,但是维护起来却不那么简单。这里重点说一下 CSS 的命名问题! 141 | 142 | **BEM 命名法:** 143 | 144 | BEM 的意思就模块(Block)、元素(Element)、修饰符(Modifier),使用这种命名方式可以让 CSS 的类名变得有实际意义且能自我解释,具有更高的开发友好性。 145 | 146 | ```text 147 | Block - 模块,名字的单词之间用 `-` 符号连接。 148 | Element - 元素,模块中的子元素,用 `__` 符号连接。 149 | Modifier - 修饰符,表示父元素或子元素的其他形态,用 `--` 符号连接。 150 | ``` 151 | 152 | 例如: 153 | 154 | 普通命名: 155 | 156 | ```html 157 | 158 | 164 | 165 | ``` 166 | 167 | 使用 BEM 命名: 168 | 169 | ```html 170 | 171 | 177 | 178 | ``` 179 | 180 | 通常会配合 CSS 预处理语言,如 Scss、Less 等。来一定程度上解决手写冗长的命名。 181 | 182 | ```css 183 | .search-bar { 184 | &__input { 185 | } 186 | &__button { 187 | } 188 | } 189 | ``` 190 | 191 | 如果 HTML 嵌套层级越来越深,类名也会越来越长。那么如何解决 BEM 命名冗长的问题? 192 | 193 | **姓氏命名法:** 194 | 195 | 姓氏命名法中 Block 就是「姓」,Element 就是「名字」,而 Modifier 就是这个人的状态。例如:「张三--很有钱」 196 | 197 | 例如: 198 | 199 | 使用 BEM 命名: 200 | 201 | ```html 202 |
203 |
204 | 205 |
206 |
207 |
208 |
209 | 去围观 210 |
211 |
212 |
213 | ``` 214 | 215 | 使用姓氏命名进行优化: 216 | 217 | ```html 218 |
219 |
220 | 221 |
222 |
223 |
224 |
225 | 去围观 226 |
227 |
228 |
229 | ``` 230 | 231 | 进一步优化: 232 | 233 | ```html 234 | 235 |
236 |
237 | 238 |
239 |
240 |
241 |
242 | 去围观 243 |
244 |
245 |
246 | ``` 247 | 248 | **小结:** 249 | 250 | - className 的命名应该尽量精短、明确,以英文单词命名,且全部字母为小写,避免意义不明的缩写。 251 | - 单词之间用下划线 `_` 或 `-` 连接均可,但必须统一。 252 | - 学习 BEM 的思想,参考使用姓氏命名法规范。 253 | - 定义样式模块,提高代码的可复用性。 254 | 255 | ### 兼容性测试 256 | 257 | 对于兼容性测试,主要关注两点: 258 | 259 | - 在不同浏览器、不同分辨率下网页的显示情况(HTML / CSS 兼容)。 260 | - 在不同浏览器中网页能否正常使用(JS 兼容)。 261 | 262 | **兼容性的基本原则是:** 263 | 264 | > 渐进增强与平稳退化 265 | 266 | **如果遇到兼容性问题,可以按如下步骤处理:** 267 | 268 | - 确认触发的场景:什么浏览器,什么版本,什么情况下触发的问题,做到稳定复现。 269 | - 找出问题原因:是什么问题导致的,具体表现如何? 270 | - 确定解决办法:参考现成的解决方案,如哪些属性不能使用以及相应的 Hack 处理。 271 | - **收集兼容性处理方法,积累成文档**。 272 | 273 | --- 274 | 275 | 本文来源于:[掘金小册 -- 大厂 H5 开发实战手册](https://juejin.im/book/5a7bfe595188257a7349b52a),进行总结和概括后,作为笔记记录下来。 276 | -------------------------------------------------------------------------------- /JavaScript/ES6 笔记/ES6-异步操作.md: -------------------------------------------------------------------------------- 1 | - [ES6 中的异步操作](#es6-中的异步操作) 2 | - [Promise](#promise) 3 | - [async / await](#async--await) 4 | 5 | # ES6 中的异步操作 6 | 7 | ## Promise 8 | 9 | `Promise` 是异步编程的一种解决方案,简单来说就是对异步操作的封装。其中保存着未来才会结束的事件(通常是一个异步操作)的状态。 10 | 11 | > ```javascript 12 | > new Promise((resolve, reject) => { // ... }); 13 | > ``` 14 | > 15 | > 异步操作执行成功会调用 resolve,并将 promise 的状态改为 fulfilled(完成) 16 | > 异步操作执行失败会调用 reject,并将 promise 的状态改为 rejected(失败) 17 | 18 | 例子: 19 | 20 | ```javascript 21 | // 延迟一秒后输出结果 22 | const promise = new Promise((resolve, reject) => { 23 | setTimeout(() => { 24 | resolve('foo'); 25 | }, 1000); 26 | }); 27 | 28 | // then 中接收的两个函数分别就是 resolve 和 reject 29 | promise.then( 30 | (res) => { 31 | console.log(res); // => foo 32 | }, 33 | (err) => { 34 | console.log(err); 35 | } 36 | ); 37 | ``` 38 | 39 | 其实 `Jquery` 库中的 `Ajax` 本身就是一个 `Promise`: 40 | 41 | ```javascript 42 | $.ajax(// ...).then(res => { 43 | console.log(res); 44 | }, err => { 45 | console.log(err); 46 | }); 47 | ``` 48 | 49 | 和上面 `例子` 中的 `promise` 写法一模一样。 50 | 51 | > 注意:`Promise` 内部的报错不会被抛出,需要手动捕获(`try-catch` 包裹异步操作代码) 52 | 53 | - `promise.all` 54 | 55 | 将多个异步操作包成一个 Promise 实例。 56 | 57 | > ```javascript 58 | > const p = Promise.all([p1, p2, p3]); 59 | > ``` 60 | 61 | 例子: 62 | 63 | ```javascript 64 | // p1,p2,p3 是异步操作 65 | Promise.all([p1, p2, p3]).then( 66 | (res) => { 67 | console.log(res); 68 | }, 69 | (err) => { 70 | console.log(err); 71 | } 72 | ); 73 | 74 | // 当所有的异步操作都成功时,才会执行 `resolve` 函数;只要有一个异步操作失败,就会执行 `reject` 函数。 75 | ``` 76 | 77 | ## async / await 78 | 79 | 使得异步操作变得更接近同步操作。实际上它们分别就是代替 `Generator` 和 `yield` 的语法糖。`async` 的返回值是 `Promise` 对象,进一步的说,`async` 完全可以看作多个异步操作,包装成的一个 `Promise` 对象。 80 | 81 | > 使用 async 关键字,表名函数内部有异步操作。调用该函数会立即返回一个 Promise 对象。 82 | > 使用 await 关键字,代码会等待 “被 await 修饰的异步操作” 执行完毕后,再继续向下执行。 83 | 84 | 例子: 85 | 86 | ```javascript 87 | // 间隔指定时间,且异步操作成功后,输出 value 88 | let timeout = (value, time) => { 89 | return new Promise((resolve) => { 90 | setTimeout(() => { 91 | resolve(value); 92 | }, time); 93 | }).then((res) => { 94 | console.log(res); 95 | }); 96 | }; 97 | 98 | async function asyncPrint(value, time) { 99 | console.log('start'); 100 | await timeout(value, time); 101 | console.log('end'); 102 | } 103 | 104 | asyncPrint('hello world', 1000); 105 | // 加上 await 关键字: 106 | // 立即输出:start,间隔 1 秒后,输出 hello world 和 end 107 | 108 | // 不加 await 关键字: 109 | // 立即输出 start 和 end,间隔 1 秒后,输出 hello world 110 | ``` 111 | 112 | > 注意:使用 `await` 时,必须有 `async` 关键字 113 | -------------------------------------------------------------------------------- /JavaScript/ES6 笔记/ES6-面向对象.md: -------------------------------------------------------------------------------- 1 | - [ES6 中的面向对象和模块化](#es6-中的面向对象和模块化) 2 | - [1、class、extends、constructor、super](#1classextendsconstructorsuper) 3 | - [2、ES5 中的伪面向对象](#2es5-中的伪面向对象) 4 | - [3、模块化](#3模块化) 5 | - [3.1、导入](#31导入) 6 | - [3.2、导出](#32导出) 7 | - [3.3、简单的 webpack 配置](#33简单的-webpack-配置) 8 | 9 | # ES6 中的面向对象和模块化 10 | 11 | ## 1、class、extends、constructor、super 12 | 13 | ```javascript 14 | // 父类 Person 15 | class Person { 16 | constructor(name, age) { 17 | this.name = name; 18 | this.age = age; 19 | } 20 | 21 | showName() { 22 | console.log(this.name); 23 | } 24 | 25 | showAge() { 26 | console.log(this.age); 27 | } 28 | } 29 | 30 | // 子类 Worker 31 | class Worker extends Person { 32 | constructor(name, age, job) { 33 | super(name, age); 34 | 35 | this.job = job; 36 | } 37 | 38 | showJob() { 39 | console.log(this.job); 40 | } 41 | } 42 | 43 | let coder = new Worker('liuyibo', 20, 'coding'); 44 | 45 | coder.showName(); // => liuyibo 46 | coder.showAge(); // => 20 47 | coder.showJob(); // => coding 48 | ``` 49 | 50 | ## 2、ES5 中的伪面向对象 51 | 52 | ES5 中没有专门声明类的方法,所以使用构造函数来充当(伪)类 53 | 54 | ```javascript 55 | // 父类 Person 56 | function Person(name, age) { 57 | // 既是构造函数又充当类 58 | this.name = name; 59 | this.age = age; 60 | } 61 | 62 | Person.prototype.showName = function () { 63 | console.log(this.name); 64 | }; 65 | Person.prototype.showAge = function () { 66 | console.log(this.age); 67 | }; 68 | 69 | // 子类 Worker 70 | function Worker(name, age, job) { 71 | Person.call(this, name, age); // 通过修改 this 指向来继承属性 72 | 73 | this.job = job; // 添加子类自己的属性 74 | } 75 | 76 | Worker.prototype = new Person(); // 将子类的原型指向父类的实例,从而继承父类的方法(子类的 constructor 也指向了父类) 77 | Worker.constructor = Worker; // 修正子类的 constructor 78 | 79 | // 添加子类自己的方法 80 | Worker.prototype.showJob = function () { 81 | console.log(this.job); 82 | }; 83 | 84 | let coder = new Worker('liuyibo', 20, 'coding'); 85 | 86 | coder.showName(); // => liuyibo 87 | coder.showAge(); // => 20 88 | coder.showJob(); // => coding 89 | ``` 90 | 91 | ## 3、模块化 92 | 93 | ### 3.1、导入 94 | 95 | ```javascript 96 | import 'xxx'; // 只导入不引用 97 | import mod from 'xxx'; // 只导入 default 成员,并起一个别名 mod 98 | import * as mod from 'xxx'; // 导入所有成员,并起一个总的别名 mod 99 | import { mod1, mod2 as name2 } from 'xxx'; // 导入名称为 mod1,mod2 的成员,并给 mod2 起一个别名 name2 100 | let promise = import('xxx'); // 异步导入,返回一个 Promise 对象(导入之后不会立即使用,而是按需使用) 101 | ``` 102 | 103 | ### 3.2、导出 104 | 105 | ```javascript 106 | export default xxx; 107 | 108 | export let a = 10; 109 | export {name: 'zhangsan', age: 20} 110 | export function () { // ... } 111 | export class Person { // ... } 112 | ``` 113 | 114 | ### 3.3、简单的 webpack 配置 115 | 116 | 目前所有的浏览器还不支持模块化(简单来说就是:`import` 这样的语法浏览器还不认识),所有需要进行打包编译。 117 | 118 | Demo: 119 | 120 | ```javascript 121 | // mod.js 122 | export let a = 1; 123 | export let b = 2; 124 | ``` 125 | 126 | ```javascript 127 | // index.js 128 | import * as mod from './mod'; // 文件名后缀可以省略(默认是 JS 文件) 129 | 130 | console.log(mod.a, mod.b); // 1 2 131 | ``` 132 | 133 | ```node 134 | // webpack.config.js 135 | const path = require('path'); 136 | 137 | module.exports = { 138 | mode: "production", // 生产模式(development:开发模式) 139 | entry: "./src/index.js", 140 | output: [ 141 | path: path.resolve(__dirname, "build"), // 使用 node 的一个包,将路径转换为绝对路径,避免一些问题 142 | filename: "bundle.js" 143 | ] 144 | }; 145 | ``` 146 | 147 | 在项目目录下的命令行中执行:`webpack` 148 | 149 | 程序会自动寻找 `webpack.config.js` 文件,然后按照文件里的配置,进行打包编译。 150 | -------------------------------------------------------------------------------- /JavaScript/JS 基础知识点2.md: -------------------------------------------------------------------------------- 1 | - [JavaScript 基础 2](#javascript-基础-2) 2 | - [new](#new) 3 | - [call、apply 和 bind 的区别](#callapply-和-bind-的区别) 4 | - [上下文 和 执行上下文](#上下文-和-执行上下文) 5 | - [继承](#继承) 6 | - [防抖和节流](#防抖和节流) 7 | - [防抖的应用场景](#防抖的应用场景) 8 | - [节流的应用场景](#节流的应用场景) 9 | - [模块化](#模块化) 10 | - [Promise](#promise) 11 | - [Generator](#generator) 12 | - [async/await](#asyncawait) 13 | - [实现并发请求](#实现并发请求) 14 | - [并发数量控制](#并发数量控制) 15 | - [Map、FlapMap 和 Reduce](#mapflapmap-和-reduce) 16 | - [Proxy](#proxy) 17 | - [为什么 `0.1 + 0.2 != 0.3`](#为什么-01--02--03) 18 | - [正则表达式](#正则表达式) 19 | 20 | # JavaScript 基础 2 21 | 22 | > - 这部分的知识总结,大部分来源于对掘金小册 [《前端面试之道》](https://juejin.im/book/5bdc715fe51d454e755f75ef) 的学习。 23 | > - 文中提到的:实现一个 xxx,表示实现其原理,并不代表将其功能完全实现。 24 | 25 | 涉及的知识点参考了下图中的 JS 部分: 26 | 27 | ![interview_map](https://raw.githubusercontent.com/liuyib/picBed/master/collection/20190727161531.png) 28 | 29 | ## new 30 | 31 | // TODO 32 | 33 | ## call、apply 和 bind 的区别 34 | 35 | // TODO 36 | 37 | ## 上下文 和 执行上下文 38 | 39 | > 上下文始终是 this 的值 40 | > 执行上下文是不同于上下文的另一个概念 41 | 42 | [https://github.com/mqyqingfeng/Blog/issues/4](https://github.com/mqyqingfeng/Blog/issues/4) 43 | 44 | ## 继承 45 | 46 | // TODO 47 | 48 | ## 防抖和节流 49 | 50 | [手写防抖节流.md](./手写系列/手写防抖节流.md) 51 | 52 | ### 防抖的应用场景 53 | 54 | - 每次 `resize` 触发的事件 55 | - 文本输入验证(连续输入文字后发送 Ajax 验证请求,验证一次就好了) 56 | - 监听页面滚动事件,只让事件执行一次(`scroll`) 57 | 58 | ### 节流的应用场景 59 | 60 | - DOM 元素的拖拽功能实现(`mousemove`) 61 | - 射击游戏的 `mousedown / keydown` 事件(单位时间内只能发射一颗子弹) 62 | - 计算鼠标移动距离(`mousemove`) 63 | - Canvas 模拟画板功能(`mousemove`) 64 | - 搜索联想功能(`keyup`) 65 | - 监听页面滚动事件,让事件以固定时间间隔执行(`scroll`) 66 | 67 | ## 模块化 68 | 69 | // TODO 70 | 71 | ## Promise 72 | 73 | // TODO 74 | 75 | ## Generator 76 | 77 | // TODO 78 | 79 | ## async/await 80 | 81 | 在 ES8 引入,是 Generator 和 Promise 的语法糖。 82 | 83 | 底层实现如下,举例: 84 | 85 | ```js 86 | async function log() {} 87 | 88 | async function print() { 89 | return await log(); 90 | } 91 | ``` 92 | 93 | 会被 Babel 编译成([在线尝试](https://babeljs.io/repl#?browsers=chrome%3E%3D54&build=&builtIns=usage&corejs=3.21&spec=false&loose=false&code_lz=IYZwngdgxgBAZgV2gFwJYHsIwDboOYAUAlDAN4C-AUJaJLIihlgA4BOqEyxZlAkKwFNkCVlmAB3YKmQ58xANyVyQA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=module&lineWrap=false&presets=env&prettier=true&targets=&version=7.21.3&externalPlugins=&assumptions=%7B%7D)): 94 | 95 | ```js 96 | function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { 97 | try { 98 | var info = gen[key](arg); 99 | var value = info.value; 100 | } catch (error) { 101 | reject(error); 102 | return; 103 | } 104 | 105 | if (info.done) { 106 | resolve(value); 107 | } else { 108 | Promise.resolve(value).then(_next, _throw); 109 | } 110 | } 111 | 112 | function _asyncToGenerator(fn) { 113 | return function () { 114 | var self = this, 115 | args = arguments; 116 | 117 | return new Promise(function (resolve, reject) { 118 | var gen = fn.apply(self, args); 119 | 120 | function _next(value) { 121 | asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value); 122 | } 123 | function _throw(err) { 124 | asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err); 125 | } 126 | 127 | _next(undefined); 128 | }); 129 | }; 130 | } 131 | 132 | function log() { 133 | return _log.apply(this, arguments); 134 | } 135 | 136 | function _log() { 137 | _log = _asyncToGenerator(function* () {}); 138 | return _log.apply(this, arguments); 139 | } 140 | 141 | function print() { 142 | return _print.apply(this, arguments); 143 | } 144 | 145 | function _print() { 146 | _print = _asyncToGenerator(function* () { 147 | return yield log(); 148 | }); 149 | return _print.apply(this, arguments); 150 | } 151 | ``` 152 | 153 | 由此可见,编译后:一个 `await` 对应一个 `yield`,没什么特别。主要是 `async`,只要函数被 `async` 修饰,整个函数体就会被包裹到一个特殊的函数中,这个特殊的函数形如(不准确,只是方便理解)下面的格式: 154 | 155 | ```js 156 | new Promise(function (resolve, reject) { 157 | function* () { 158 | // 函数体的所有代码在这 159 | } 160 | }) 161 | ``` 162 | 163 | ### 实现并发请求 164 | 165 | 请求组装到数组中,然后根据业务具体情况使用 `Promise.all` / `Promise.allSettled` / `Promise.race` 来并发执行数组中的请求。 166 | 167 | ### 并发数量控制 168 | 169 | 实现“错误重试、并发控制”,代码如下:[控制最大并发量、错误重试.js](../模仿面试/封装%20request%20专题/控制最大并发量、错误重试.js)。 170 | 171 | ## Map、FlapMap 和 Reduce 172 | 173 | // TODO 174 | 175 | ## Proxy 176 | 177 | // TODO 178 | 179 | ## 为什么 `0.1 + 0.2 != 0.3` 180 | 181 | // TODO 182 | 183 | ## 正则表达式 184 | 185 | [正则表达式](https://github.com/liuyib/study-note/tree/master/JavaScript/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F) 186 | -------------------------------------------------------------------------------- /JavaScript/__test__/bind.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 这些测试用例总结自 MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind 3 | */ 4 | 5 | // ========================= 6 | // case: this 只会被改变一次 7 | // ========================= 8 | 9 | function foo(...args) { 10 | return [this, ...args]; 11 | } 12 | 13 | const boundLog = foo.bind('a', 1, 2); 14 | // this 一旦被 bind 绑定,就不会再被改变了,这里不会指向 b 15 | const boundLog2 = boundLog.bind('b', 3, 4); 16 | 17 | console.assert(boundLog2(5, 6).join('') === 'a123456'); 18 | 19 | // ================================ 20 | // case: 使用 new 后,正确的原型指向 21 | // ================================ 22 | 23 | class Foo { 24 | constructor(...args) { 25 | console.assert(new.target === Foo); 26 | console.assert(args.join('') === '1234'); 27 | } 28 | } 29 | 30 | const BoundFoo = Foo.bind(null, 1, 2); 31 | const boundFoo = new BoundFoo(3, 4); 32 | 33 | console.assert(boundFoo instanceof Foo); 34 | console.assert(boundFoo instanceof BoundFoo); 35 | 36 | // =============================================== 37 | // case: bind 后的函数,prototype 丢了,不能再被继承 38 | // =============================================== 39 | 40 | try { 41 | class Derived extends class {}.bind(null) {} 42 | // 不应该执行到这一步 43 | console.assert(1 === 2); 44 | } catch (error) { 45 | console.assert( 46 | String(error) === 47 | 'TypeError: Class extends value does not have valid prototype property undefined' 48 | ); 49 | } 50 | 51 | // ==================== 52 | // case: 正确的原型指向 53 | // ==================== 54 | 55 | class Foo2 {} 56 | const BoundFoo2 = Foo2.bind(null, 1, 2); 57 | 58 | console.assert(JSON.stringify(Foo2.prototype) === '{}'); 59 | console.assert(JSON.stringify(BoundFoo2.prototype) === undefined); 60 | console.assert(new Foo2() instanceof Foo2); 61 | console.assert(new Foo2() instanceof BoundFoo2); 62 | console.assert(new BoundFoo2() instanceof Foo2); 63 | console.assert(new BoundFoo2() instanceof BoundFoo2); 64 | 65 | // =================== 66 | // case: 正确的 name 67 | // =================== 68 | 69 | function Foo3() {} 70 | const BoundFoo3 = Foo3.bind(null); 71 | 72 | console.assert(BoundFoo3.name === 'bound Foo3'); 73 | 74 | // ==================== 75 | // case: 正确的接收参数 76 | // ==================== 77 | 78 | function list(...args) { 79 | return args; 80 | } 81 | 82 | function addArguments(arg1, arg2) { 83 | return arg1 + arg2; 84 | } 85 | 86 | console.assert(list(1, 2, 3).join('') === '123'); 87 | console.assert(addArguments(1, 2) === 3); 88 | 89 | const leadingThirtySevenList = list.bind(null, 37); 90 | const addThirtySeven = addArguments.bind(null, 37); 91 | 92 | console.assert(leadingThirtySevenList().join('') === '37'); 93 | console.assert(leadingThirtySevenList(1, 2, 3).join('') === '37123'); 94 | console.assert(addThirtySeven(5) === 42); 95 | // 10 将会被忽略,因为 bind 前的函数 addArguments 只接收两个参数 96 | console.assert(addThirtySeven(5, 10) === 42); 97 | 98 | // ========================================================= 99 | // case: 绑定类时,当前类上的所有静态方法将丢失,但继承父类的不会丢 100 | // ========================================================= 101 | 102 | class Base { 103 | static baseProp = 'base'; 104 | } 105 | 106 | class Derived extends Base { 107 | static derivedProp = 'derived'; 108 | } 109 | 110 | const BoundDerived = Derived.bind(null); 111 | 112 | console.assert(BoundDerived.baseProp === 'base'); 113 | console.assert(BoundDerived.derivedProp === undefined); 114 | console.assert(new BoundDerived() instanceof Derived); 115 | -------------------------------------------------------------------------------- /JavaScript/images/circle_refer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/JavaScript/images/circle_refer.png -------------------------------------------------------------------------------- /JavaScript/images/js-prototype-chain-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/JavaScript/images/js-prototype-chain-all.png -------------------------------------------------------------------------------- /JavaScript/images/js-prototype-chain1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/JavaScript/images/js-prototype-chain1.png -------------------------------------------------------------------------------- /JavaScript/images/regexp_knowledge_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/JavaScript/images/regexp_knowledge_map.png -------------------------------------------------------------------------------- /JavaScript/从 JS 高阶函数到柯里化.md: -------------------------------------------------------------------------------- 1 | - [从 JS 高阶函数到函数柯里化](#从-js-高阶函数到函数柯里化) 2 | - [函数柯里化](#函数柯里化) 3 | - [高阶函数实战](#高阶函数实战) 4 | 5 | # 从 JS 高阶函数到函数柯里化 6 | 7 | 所谓高阶函数,只要满足一下条件之一即可: 8 | 9 | - 函数作为参数 10 | - 返回值为函数 11 | 12 | 我们平时一直在用高阶函数,只不过可能没有了解过这个概念。JS 中内置的高阶函数有很多,例如:`forEach`、`filter`、`reduce` 等等。 13 | 14 | 为了更好的理解,这里举两个例子: 15 | 16 | 1)函数作为参数 17 | 18 | ```js 19 | function calc(a, b, fn) { 20 | return fn(a) + fn(b); 21 | } 22 | 23 | function multi(a) { 24 | return a * a; 25 | } 26 | 27 | calc(3, 4, multi); // => 25 28 | ``` 29 | 30 | 2)返回值为函数 31 | 32 | ```js 33 | function isType(type) { 34 | return function (obj) { 35 | return Object.prototype.toString.call(obj) === '[object ' + type + ']'; 36 | }; 37 | } 38 | 39 | isType('String')('liuyib'); // => true 40 | ``` 41 | 42 | ## 函数柯里化 43 | 44 | 在这里介绍函数柯里化,是因为柯里化函数中运用了很多高阶函数。 45 | 46 | **柯里化(Currying)是一种技术,用于把多个参数的函数转换为一系列使用单个参数的函数**。 47 | 48 | 柯里化函数在执行时,并不会立即求值,而是通过闭包把参数保存起来,当参数的数量可以满足函数执行时(可能是**参数为空时**,也可能是**参数全部接收后**),就开始执行函数了。 49 | 50 | 举例如下: 51 | 52 | 1)参数为空时,执行柯里化函数 53 | 54 | 场景:统计每天的销售额,到月底计算一次销售总额。 55 | 56 | ```js 57 | // 柯里化函数 58 | function currying(fn) { 59 | // 这个变量会保持在内存中(通过闭包) 60 | var args = []; 61 | 62 | return function () { 63 | // 不传变量的时候,执行 fn 64 | if (arguments.length === 0) { 65 | return fn(args); 66 | } else { 67 | // 传入变量时,将变量收集起来 68 | Array.prototype.push.apply(args, arguments); 69 | } 70 | }; 71 | } 72 | 73 | // 实际用于计算的函数 74 | function total(arr) { 75 | var sum = 0; 76 | arr.forEach((a) => { 77 | sum += a; 78 | }); 79 | return sum; 80 | } 81 | 82 | var sellAmount = currying(total); 83 | sellAmount(100); 84 | sellAmount(200); 85 | sellAmount(300); 86 | sellAmount(); // => 600 87 | ``` 88 | 89 | 2)参数全部接收后,执行柯里化函数 90 | 91 | ```js 92 | function currying(fn, args) { 93 | return function () { 94 | // 收集上次递归传来的参数,形如 currying(fn, [1, 2, ...]) 95 | var _args = args || []; 96 | // 收集 fn 的参数,形如 fn(1, 2, ...) 97 | Array.prototype.push.apply(_args, arguments); 98 | 99 | // 递归出口(收集的参数达到了指定数量) 100 | // fn.length 就是函数的参数个数 101 | if (_args.length === fn.length) { 102 | return fn.apply(this, _args); 103 | } else { 104 | // 把收集的参数递归向下传递 105 | return currying(fn, _args); 106 | } 107 | }; 108 | } 109 | 110 | // 实际用于计算的函数 111 | function add(a, b, c, d) { 112 | return a + b + c + d; 113 | } 114 | 115 | // 将 add 函数柯里化 116 | var sum = currying(add); 117 | sum(1)(2)(3)(4); // => 10 118 | sum(1, 2)(3)(4); // => 10 119 | sum(1, 2, 3)(4); // => 10 120 | sum(1, 2, 3, 4); // => 10 121 | 122 | // 或者 123 | 124 | var sum2 = currying(add, [1]); 125 | sum2(2)(3)(4); // => 10 126 | 127 | var sum3 = currying(add, [1, 2]); 128 | sum3(3)(4); // => 10 129 | 130 | var sum4 = currying(add, [1, 2, 3]); 131 | sum4(4); // => 10 132 | 133 | var sum5 = currying(add, [1, 2, 3, 4]); 134 | sum5(); // => 10 135 | ``` 136 | 137 | 更近一步,就是处理含有“占位符”的情况,例如 [lodash 的柯里化函数](https://www.lodashjs.com/docs/lodash.curry): 138 | 139 | ```js 140 | var abc = function (a, b, c) { 141 | return [a, b, c]; 142 | }; 143 | 144 | var curried = _.curry(abc); 145 | 146 | // 其中 _ 表示占位符 147 | curried(1)(_, 3)(2); // => [1, 2, 3] 148 | ``` 149 | 150 | ## 高阶函数实战 151 | 152 | - 函数节流 153 | 154 | ```js 155 | function throttle(fn, wait) { 156 | var isRunning = false; 157 | 158 | return function () { 159 | if (isRunning) return; 160 | 161 | isRunning = true; 162 | fn.apply(this, arguments); 163 | 164 | setTimeout(function () { 165 | isRunning = false; 166 | }, wait); 167 | }; 168 | } 169 | 170 | window.onscroll = throttle(function () { 171 | console.log('execute'); 172 | }, 300); 173 | ``` 174 | 175 | - `...` 176 | -------------------------------------------------------------------------------- /JavaScript/手写系列/手写 Ajax.md: -------------------------------------------------------------------------------- 1 | - [手写 Ajax](#手写-ajax) 2 | - [初识 XMLHttpRequest](#初识-xmlhttprequest) 3 | - [封装代码](#封装代码) 4 | - [改进参数](#改进参数) 5 | - [添加请求头](#添加请求头) 6 | - [使用 ES6 简化代码](#使用-es6-简化代码) 7 | - [使用 Promise](#使用-promise) 8 | - [本文总结](#本文总结) 9 | 10 | # 手写 Ajax 11 | 12 | ## 初识 XMLHttpRequest 13 | 14 | 要发送一个 Ajax 请求,需要经过四个步骤:**初始化、连接、发送、接收**。 15 | 16 | 使用举例如下,使用 `XMLHttpRequest` 对象: 17 | 18 | ```js 19 | // 1. 初始化 20 | var xhr = new XMLHttpRequest(); 21 | 22 | // 2. 连接 23 | xhr.open('GET', url, true); 24 | 25 | // 3. 发送 26 | xhr.send('hello world'); 27 | 28 | // 4. 接收 29 | xhr.onreadystatechange = function (e) { 30 | var status = xhr.status; 31 | 32 | if (xhr.readyState == 4) { 33 | if ((status >= 200 && status < 300) || status == 304) { 34 | fnSucc(xhr.responseText); 35 | } else { 36 | fnFail(xhr); 37 | } 38 | } 39 | }; 40 | ``` 41 | 42 | > 其中,`xhr.send()` 是异步的,所以放在第三步或第四步都行。 43 | 44 | 如果需要兼容 IE 和低版本的 Edge,需要改造如下代码: 45 | 46 | ```js 47 | // 初始化 48 | var xhr = null; 49 | 50 | if (window.XMLHttpRequest) { 51 | xhr = new XMLHttpRequest(); 52 | } else { 53 | xhr = new ActiveXObject('Microsoft.XMLHTTP'); 54 | } 55 | ``` 56 | 57 | 下文的代码示例中,不考虑兼容 IE。 58 | 59 | ## 封装代码 60 | 61 | 上面的代码段是可以复用的,因此我们用函数将其封装起来,并提供一些可配置的参数: 62 | 63 | ```js 64 | var ajax = function (method, url, body, fnSucc, fnFail) { 65 | var xhr = new XMLHttpRequest(); 66 | 67 | xhr.open(method, url, true); 68 | xhr.send(body); 69 | xhr.onreadystatechange = function (e) { 70 | var status = xhr.status; 71 | 72 | if (xhr.readyState == 4) { 73 | if ((status >= 200 && status < 300) || status == 304) { 74 | fnSucc(xhr.response); 75 | } else { 76 | fnFail(xhr); 77 | } 78 | } 79 | }; 80 | }; 81 | ``` 82 | 83 | 使用如下: 84 | 85 | ```js 86 | ajax( 87 | 'GET', 88 | '/api.json', 89 | '', 90 | // 成功回调 91 | function (res) { 92 | console.log('res', res); 93 | }, 94 | // 失败回调 95 | function (e) { 96 | console.log('e', e); 97 | } 98 | ); 99 | ``` 100 | 101 | ## 改进参数 102 | 103 | 上面封装的函数中,我们很难知道每个参数的含义(需要借助**编辑器+函数注释** 或 **查看源码**),因此下面是对参数的改进: 104 | 105 | ```js 106 | var ajax = function (options) { 107 | var method = options.method; 108 | var url = options.url; 109 | var body = options.body; 110 | var fnSucc = options.fnSucc; 111 | var fnFail = options.fnFail; 112 | 113 | var xhr = new XMLHttpRequest(); 114 | 115 | xhr.open(method, url, true); 116 | xhr.send(body); 117 | xhr.onreadystatechange = function (e) { 118 | var status = xhr.status; 119 | 120 | if (xhr.readyState == 4) { 121 | if ((status >= 200 && status < 300) || status == 304) { 122 | fnSucc(xhr.response); 123 | } else { 124 | fnFail(xhr); 125 | } 126 | } 127 | }; 128 | }; 129 | ``` 130 | 131 | 使用如下: 132 | 133 | ```js 134 | ajax({ 135 | method: 'GET', 136 | url: '/api.json', 137 | body: '', 138 | fnSucc: function (res) { 139 | console.log('res', res); 140 | }, 141 | fnFail: function (e) { 142 | console.log('e', e); 143 | } 144 | }); 145 | ``` 146 | 147 | ## 添加请求头 148 | 149 | ```diff 150 | var ajax = function (options) { 151 | var method = options.method; 152 | var url = options.url; 153 | var body = options.body; 154 | + var headers = options.headers; 155 | var fnSucc = options.fnSucc; 156 | var fnFail = options.fnFail; 157 | 158 | var xhr = new XMLHttpRequest(); 159 | 160 | xhr.open(method, url, true); 161 | 162 | + for (var key in headers) { 163 | + var value = headers[key]; 164 | + xhr.setRequestHeader(key, value); 165 | + } 166 | 167 | xhr.send(body); 168 | xhr.onreadystatechange = function (e) { 169 | var status = xhr.status; 170 | 171 | if (xhr.readyState == 4) { 172 | if ((status >= 200 && status < 300) || status == 304) { 173 | fnSucc(xhr.response); 174 | } else { 175 | fnFail(xhr); 176 | } 177 | } 178 | }; 179 | }; 180 | ``` 181 | 182 | 使用如下: 183 | 184 | ```js 185 | ajax({ 186 | method: 'GET', 187 | url: '/api.json', 188 | body: '', 189 | headers: { 190 | 'Content-Type': 'multipart/form-data;' 191 | }, 192 | fnSucc: function (res) { 193 | console.log('res', res); 194 | }, 195 | fnFail: function (e) { 196 | console.log('e', e); 197 | } 198 | }); 199 | ``` 200 | 201 | ## 使用 ES6 简化代码 202 | 203 | ```js 204 | var ajax = function ({ method, url, body, headers, fnSucc, fnFail }) { 205 | var xhr = new XMLHttpRequest(); 206 | 207 | xhr.open(method, url, true); 208 | 209 | for (var key in headers) { 210 | var value = headers[key]; 211 | xhr.setRequestHeader(key, value); 212 | } 213 | 214 | xhr.send(body); 215 | xhr.onreadystatechange = function (e) { 216 | var status = xhr.status; 217 | 218 | if (xhr.readyState == 4) { 219 | if ((status >= 200 && status < 300) || status == 304) { 220 | fnSucc(xhr.response); 221 | } else { 222 | fnFail(xhr); 223 | } 224 | } 225 | }; 226 | }; 227 | ``` 228 | 229 | ## 使用 Promise 230 | 231 | ```js 232 | const ajax = function ({ url, method, data, headers }) { 233 | return new Promise((resolve, reject) => { 234 | const xhr = new XMLHttpRequest(); 235 | 236 | xhr.open(method, url, true); 237 | 238 | for (const key in headers) { 239 | const value = headers[key]; 240 | xhr.setRequestHeader(key, value); 241 | } 242 | 243 | xhr.send(data); 244 | xhr.onreadystatechange = function () { 245 | const state = xhr.readyState; 246 | const status = xhr.status; 247 | 248 | if (state == 4) { 249 | if ((status >= 200 && status < 300) || status == 304) { 250 | resolve(xhr.response); 251 | } else { 252 | reject(xhr); 253 | } 254 | } 255 | }; 256 | }); 257 | }; 258 | ``` 259 | 260 | 使用如下: 261 | 262 | ```js 263 | ajax({ 264 | url: '/api.json', 265 | method: 'GET', 266 | data: { name: 'liuyib', age: 22 }, 267 | headers: { 268 | 'Content-Type': 'text/plain' 269 | } 270 | }) 271 | .then((res) => { 272 | console.log('res', res); 273 | }) 274 | .catch((err) => { 275 | console.log('err', err); 276 | }); 277 | ``` 278 | 279 | 使用了 `Promise` 后,就不用将回调函数放入参数,直接使用 `Promise` 的链式语法 `then`、`catch` 即可。 280 | 281 | ## 本文总结 282 | 283 | 以上代码在健壮性、兼容性、易用性等方面,还存在着很大的问题,不能用于生产环境,仅供学习参考使用。在正式的生产环境中,还是得用现有的 Ajax 库,例如:axios。 284 | -------------------------------------------------------------------------------- /JavaScript/手写系列/手写 JSONP.md: -------------------------------------------------------------------------------- 1 | - [手写 JSONP](#手写-jsonp) 2 | - [初识 JSONP](#初识-jsonp) 3 | - [封装 JSONP](#封装-jsonp) 4 | 5 | # 手写 JSONP 6 | 7 | JSONP 是解决浏览器跨域问题的一种方案,虽然现在已很少使用,但是由于其兼容性好,在一些比较老的项目中仍可能会使用。因此,有必要学习其原理。 8 | 9 | ## 初识 JSONP 10 | 11 | 为了演示效果,我们利用 Express 框架搭建一个简易的 Web 服务器: 12 | 13 | ```js 14 | const express = require('express'); 15 | const app = express(); 16 | 17 | app.get('/jsonp', function (req, res) { 18 | const { data, callback } = req.query; 19 | 20 | // data 是前端传给后端的数据 21 | console.log('data', data); 22 | 23 | // 后端通过字符串,将数据传给前端 24 | res.send(`${callback}('hello world')`); 25 | res.end(); 26 | }); 27 | ``` 28 | 29 | 前端使用 JSONP 跨域: 30 | 31 | ```html 32 | 37 | 38 | ``` 39 | 40 | > 为了便于讲述原理,这里只给出了最简单的 JSONP 用法。 41 | 42 | 其原理就是:将数据放入 URL 的查询参数中(`data=123`),然后将回调函数名称(`callback=show`)传给后端,后端将**回调函数名称**和**响应数据**拼接成字符串,返回给前端。最后,前端解析接收到的数据,回调函数得以执行。 43 | 44 | ## 封装 JSONP 45 | 46 | 为了便于使用,我们利用 `Promise` 封装一个 JSONP 函数,来实现下面的使用方式: 47 | 48 | ```js 49 | jsonp({ 50 | url: 'http://localhost:8080/jsonp', 51 | params: { name: '文一', age: '22' } 52 | }) 53 | .then((res) => { 54 | console.log(res); 55 | }) 56 | .catch((err) => { 57 | console.log(err); 58 | }); 59 | ``` 60 | 61 | 封装代码如下: 62 | 63 | ```js 64 | function jsonp({ url, params }) { 65 | return new Promise((resolve, reject) => { 66 | const arr = []; 67 | 68 | // 将数据拼接成 URL 查询参数 69 | for (const key in params) { 70 | const value = params[key]; 71 | arr.push(`${key}=${value}`); 72 | } 73 | 74 | const callback = `jsonp_${Date.now()}`; 75 | const script = document.createElement('script'); 76 | script.src = `${url}?${arr.join('&')}&callback=${callback}`; 77 | 78 | // 回调函数的名称不重要,主要用于触发 resolve 或 reject 逻辑 79 | window[callback] = (res) => { 80 | delete window[callback]; 81 | document.body.removeChild(script); 82 | 83 | if (res) { 84 | resolve(res); 85 | } else { 86 | reject('没有接收到参数'); 87 | } 88 | }; 89 | 90 | // 发送请求 91 | document.body.appendChild(script); 92 | }); 93 | } 94 | ``` 95 | 96 | 进一步的,需要处理跨域请求失败时的情况: 97 | 98 | ```diff 99 | function jsonp({ url, params }) { 100 | return new Promise((resolve, reject) => { 101 | const callback = `jsonp_${Date.now()}`; 102 | const arr = []; 103 | 104 | // 将数据拼接成 URL 查询参数 105 | for (const key in params) { 106 | const value = params[key]; 107 | arr.push(`${key}=${value}`); 108 | } 109 | 110 | const _url = `${url}?${arr.join("&")}&callback=${callback}`; 111 | const script = document.createElement("script"); 112 | script.src = _url; 113 | 114 | // 回调函数的名称不重要,主要用于触发 resolve 或 reject 逻辑 115 | window[callback] = (res) => { 116 | delete window[callback]; 117 | document.body.removeChild(script); 118 | 119 | if (res) { 120 | resolve(res); 121 | } else { 122 | reject("没有接收到参数"); 123 | } 124 | }; 125 | 126 | + script.addEventListener('error', () => { 127 | + delete window[callback]; 128 | + document.body.removeChild(script); 129 | + 130 | + reject('JSONP 跨域请求失败'); 131 | + }, false); 132 | 133 | // 发送请求 134 | document.body.appendChild(script); 135 | }); 136 | } 137 | ``` 138 | -------------------------------------------------------------------------------- /JavaScript/手写系列/手写 XXX.md: -------------------------------------------------------------------------------- 1 | - [手写 instanceof](#手写-instanceof) 2 | - [手写 new](#手写-new) 3 | - [手写 call/apply/bind](#手写-callapplybind) 4 | 5 | ## 手写 instanceof 6 | 7 | `instanceof` 的原理很简单:**沿左边变量的原型链向上查找,如果能找到右边的变量,就返回 `true`,否则返回 `false`**。 8 | 9 | 因此按照该原理,实现代码如下: 10 | 11 | ```js 12 | function instanceof(a, b) { 13 | var left = a.__proto__; 14 | var right = b.prototype; 15 | 16 | while (true) { 17 | if (left === null) return false; 18 | if (left === right) return true; 19 | 20 | left = left.__proto__; 21 | } 22 | } 23 | ``` 24 | 25 | ## 手写 new 26 | 27 | ```js 28 | function _new(constructor, ...args) { 29 | const instance = Object.create(constructor.prototype); 30 | const result = constructor.apply(instance, args); 31 | 32 | return typeof result === 'object' || result === null ? result : instance; 33 | } 34 | ``` 35 | 36 | ## 手写 call/apply/bind 37 | 38 | `call`: 39 | 40 | ```js 41 | Function.prototype.call = function (context) { 42 | context = context ? Object(context) : window; 43 | context.fn = this; 44 | 45 | var args = []; 46 | // 注意循环从 1 开始,否则会把 context 包含进去 47 | for (var i = 1; i < arguments.length; i++) { 48 | args.push('args[' + i + ']'); 49 | } 50 | 51 | var fn = new Function('ctx,args', `return ctx.fn(${args.join(',')})`); 52 | var result = fn(context, arguments); 53 | 54 | delete context.fn; 55 | 56 | return result; 57 | }; 58 | ``` 59 | 60 | `apply`: 61 | 62 | ```js 63 | Function.prototype.apply = function (context, arr) { 64 | context = context ? Object(context) : window; 65 | context.fn = this; 66 | 67 | if (!arr) { 68 | return context.fn(); 69 | } 70 | 71 | var args = []; 72 | for (let i = 0; i < arr.length; i++) { 73 | args.push('args[' + i + ']'); 74 | } 75 | 76 | var fn = new Function('ctx,args', `return ctx.fn(${args.join(',')})`); 77 | var result = fn(context, arr); 78 | 79 | delete context.fn; 80 | 81 | return result; 82 | }; 83 | ``` 84 | 85 | `bind`: 86 | 87 | https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/function-bind.js 88 | 89 | > 这里直接使用 ES6 语法,省去不重要的细节。 90 | 91 | ```js 92 | Function.prototype.bind = function (context, ...args1) { 93 | // 处理异常 94 | if (typeof this !== 'function') { 95 | throw new TypeError('error'); 96 | } 97 | 98 | const F = this; 99 | const P = F.prototype; 100 | 101 | const bound = function (...args2) { 102 | const args = args1.concat(args2); 103 | 104 | // 处理 new 和无 new 两种调用 105 | return this instanceof bound ? new F(...args) : F.apply(context, args); 106 | }; 107 | 108 | // FIXME: MDN 上提到“However, because a bound function does not have the prototype property”, 109 | // 但是 core-js 和网上所有文章中实现的 bind 都与 MDN 的这句话不符,并且在浏览器中测试可知 MDN 是对的, 110 | // 经过我的尝试,目前用 JS 模拟实现 bind 貌似没法完美兼容原生 bind 的所有表现,可见测试用例:../__test__/bind.test.js 111 | // 原生的 bind 可以通过上面所有测试用例,但是网上所有模拟实现的 bind(包括 MDN 推荐的 Polyfill: core-js)都没法完全通过 112 | // 处理原型链(有问题,和原生 bind 不一致) 113 | if (P) { 114 | bound.prototype = P; 115 | } 116 | 117 | // 处理 bind 后的函数名 118 | Object.defineProperty(bound, 'name', { 119 | value: 'bound ' + F.name 120 | }); 121 | 122 | return bound; 123 | }; 124 | ``` 125 | 126 | 测试用例:[`../__test__/bind.test.js`](../__test__/bind.test.js)(完全参考 [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) 总结的) 127 | 128 | 参考资料: 129 | 130 | - call/apply: https://github.com/sisterAn/JavaScript-Algorithms/issues/78 131 | - bind: https://github.com/yygmind/blog/issues/23 132 | -------------------------------------------------------------------------------- /JavaScript/手写系列/手写柯里化.md: -------------------------------------------------------------------------------- 1 | - [手写柯里化](#手写柯里化) 2 | - [简易版(不支持占位符,ES6 代码)](#简易版不支持占位符es6-代码) 3 | - [进阶版(支持占位符,ES6 代码)](#进阶版支持占位符es6-代码) 4 | 5 | # 手写柯里化 6 | 7 | ## 简易版(不支持占位符,ES6 代码) 8 | 9 | ```js 10 | function curry(fn) { 11 | return function curried(...args) { 12 | if (args.length >= fn.length) { 13 | return fn.apply(this, args); 14 | } 15 | 16 | return function (...args2) { 17 | return curried.apply(this, args.concat(args2)); 18 | }; 19 | }; 20 | } 21 | 22 | // 测试: 23 | 24 | const fn = curry(function (a, b, c) { 25 | console.log([a, b, c]); 26 | }); 27 | 28 | fn('a', 'b', 'c'); // ['a', 'b', 'c'] 29 | fn('a', 'b')('c'); // ['a', 'b', 'c'] 30 | fn('a')('b')('c'); // ['a', 'b', 'c'] 31 | fn('a')('b', 'c'); // ['a', 'b', 'c'] 32 | ``` 33 | 34 | ## 进阶版(支持占位符,ES6 代码) 35 | 36 | ```js 37 | function curry(fn, placeholder = curry.placeholder) { 38 | return function curried(...args) { 39 | if (args.length >= fn.length && !args.includes(placeholder)) { 40 | return fn.apply(this, args); 41 | } else { 42 | return function (...args2) { 43 | const newArgs = args 44 | .map((arg) => 45 | arg === placeholder && args2.length ? args2.shift() : arg 46 | ) 47 | .concat(args2); 48 | 49 | return curried.apply(this, newArgs); 50 | }; 51 | } 52 | }; 53 | } 54 | 55 | curry.placeholder = Symbol(); 56 | 57 | // 测试: 58 | const _ = {}; 59 | const log = function (a, b, c, d, e) { 60 | console.log([a, b, c, d, e]); 61 | }; 62 | const fn = curry(log, _); 63 | 64 | fn(1, 2, 3, 4, 5); // [ 1, 2, 3, 4, 5 ] 65 | fn(_, 2, 3, 4, 5)(1); // [ 1, 2, 3, 4, 5 ] 66 | fn(1, _, 3, 4, 5)(2); // [ 1, 2, 3, 4, 5 ] 67 | fn(1, _, 3)(_, 4)(2)(5); // [ 1, 2, 3, 4, 5 ] 68 | fn(1, _, _, 4)(_, 3)(2)(5); // [ 1, 2, 3, 4, 5 ] 69 | fn(_, 2)(_, _, 4)(1)(3)(5); // [ 1, 2, 3, 4, 5 ] 70 | ``` 71 | -------------------------------------------------------------------------------- /JavaScript/手写系列/手写防抖节流.md: -------------------------------------------------------------------------------- 1 | ## 防抖 2 | 3 | https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L10304-L10427 4 | 5 | ## 节流 6 | 7 | ## 参考资料 8 | 9 | - [JavaScript 专题之跟着 underscore 学节流](https://github.com/mqyqingfeng/Blog/issues/26) 10 | - [JavaScript 专题之跟着 underscore 学防抖](https://github.com/mqyqingfeng/Blog/issues/22) 11 | -------------------------------------------------------------------------------- /JavaScript/正则表达式总结.md: -------------------------------------------------------------------------------- 1 | - [正则表达式](#正则表达式) 2 | - [知识图谱](#知识图谱) 3 | - [书写风格](#书写风格) 4 | - [可以使用正则的方法](#可以使用正则的方法) 5 | - [参数](#参数) 6 | - [量词](#量词) 7 | - [定位符](#定位符) 8 | - [非打印字符](#非打印字符) 9 | - [非捕获元](#非捕获元) 10 | - [其他特殊字符](#其他特殊字符) 11 | 12 | # 正则表达式 13 | 14 | ## 知识图谱 15 | 16 | ![regexp_knowledge_map](./images/regexp_knowledge_map.png) 17 | 18 | ## 书写风格 19 | 20 | - JS 风格: `new RegExp(/a/, 'ig')` 21 | - Perl 风格: `/a/ig` 22 | 23 | ## 可以使用正则的方法 24 | 25 | - String 上的方法: 26 | 27 | - search `str.search(re)` -- 返回位置索引(失败返回 `-1`) 28 | - match `str.match(re)` -- 返回数组(失败返回 `null`) 29 | - replace `str.replace(re)` 30 | - split `str.split(re)` 31 | 32 | - RegExp 上的方法: 33 | - test `re.test(str)` -- 返回 `true / false` 34 | - exec `re.exec(str)` -- 返回数组(失败返回 `null`) 35 | 36 | > 注意几点: 37 | > 38 | > 1. `match` 不加参数 `g` 只会匹配一个值 39 | > 2. `exec` 一次匹配只会匹配到一个值,如果想要匹配多个值,需要循环多次匹配 40 | > 3. 如果只判断是否匹配,用 `test、search` 更方便 41 | 42 | ## 参数 43 | 44 | - `i` -- `ignoreCase` 忽略大小写 45 | - `g` -- `global` 全局匹配(贪婪匹配,默认是非贪婪匹配) 46 | - `m` -- `multiline` 多行匹配 47 | - `y` -- 执行“粘性”搜索(从 `lastIndex` 处开始匹配) 48 | 49 | 关于参数 `y` 的用法,举个例子: 50 | 51 | ```javascript 52 | var str = '##foo##'; 53 | var regex = /foo/y; 54 | 55 | regex.lastIndex = 2; 56 | console.log(regex.test(str)); // true (此例仅当 lastIndex = 1 时匹配成功,这就是 sticky 的作用) 57 | regex.lastIndex = 5; 58 | console.log(regex.test(str)); // false (lastIndex 被 sticky 标志考虑到,从而导致匹配失败) 59 | console.log(regex.lastIndex); // 0 (匹配失败后重置) 60 | ``` 61 | 62 | ## 量词 63 | 64 | - `+` -- 至少 1 个,等价于 `{1,}` 65 | - `*` -- 至少 0 个,等价于 `{0,}` 66 | - `?` -- 0 或 1 个,等价于 `{0,1}` 67 | - `{}` 68 | - `{n}` -- 匹配 `n` 个 69 | - `{n,m}` -- 匹配 `n` 到 `m` 个 70 | - `{n,}` -- 匹配大于等于 `n` 个 71 | 72 | > 注意: 73 | > `?` 跟在任何量词后面,都会使量词变得 **非贪婪** 74 | 75 | ## 定位符 76 | 77 | - `^` -- 字符串开头 78 | - `$` -- 字符串结尾 79 | - `\b` -- 匹配一个词的边界 80 | - `\B` -- 匹配一个非 **单词边界** 81 | 82 | > 注: 83 | > 84 | > - 不加参数 m(单行模式):`^$` 表示 **整体** 字符串的开头结尾 85 | > - 加上参数 m(多行模式):`^$` 表示 **每行** 字符串的开头结尾 86 | 87 | ## 非打印字符 88 | 89 | - `\f` -- 换页 90 | - `\n` -- 换行 91 | - `\r` -- 回车 92 | - `\t` -- 制表(水平) 93 | - `\v` -- 制表(垂直) 94 | - `\s` -- 空白字符,包括:`空格、制表符、换页符、换行符` 95 | - `\S` -- 非空白字符 96 | - `\cX` 97 | 98 | ## 非捕获元 99 | 100 | 使用 `()` 有一个副作用就是相关匹配会被缓存,可以在 `()` 的最前面加上非捕获元来清除缓存。 101 | 102 | - `?:` -- `(?:x)` 103 | - `?=` -- `x(?=y)` 104 | - `?!` -- `x(?!y)` 105 | 106 | ## 其他特殊字符 107 | 108 | - `.` -- 匹配除了 **换行符以外** 的所有单字符 109 | - `|` -- 或者 110 | - `\d` -- 数字,等价于 `[0-9]` 111 | - `\D` -- 非数字 112 | - `\w` -- 数字、字母、下划线,等价于 `[0-9a-zA-Z_]` 113 | - `\W` -- 非(数字、字母、下划线) 114 | - `[\b]` -- 匹配退格(U+0008),不要和 `\b` 混淆了(`\b` 是匹配词边界) 115 | - `\数字` -- 表示匹配阶段的捕获 116 | - `$数字` -- 表示替换阶段的捕获 117 | - `[]` 118 | - 或 -- `[abc]` 匹配 `a` 或 `b` 或 `c` 119 | - 范围 -- `[a-z]` 匹配 `a` 到 `z` 120 | - 排除 -- `[^0-9]` 匹配非数字 121 | - `()` -- 捕获括号,匹配结果会储存在 **捕获** 里。 122 | 匹配阶段,**捕获** 用 `\1、\2 ...` 表示;替换阶段,**捕获** 用 `$1、$2 ...` 表示。 123 | 也用于 **设置匹配优先级** 124 | 125 | 对于 **捕获**,举个例子: 126 | 127 | ```javascript 128 | console.log('abcd'.replace(/(b)(c)/, '$2$1')); // => acbd 129 | // 捕获2($2)会匹配 c,捕获1($1)会匹配 b 130 | ``` 131 | -------------------------------------------------------------------------------- /JavaScript/零碎知识笔记.md: -------------------------------------------------------------------------------- 1 | - [零碎知识(暂未分类)](#零碎知识暂未分类) 2 | - [isNaN 和 Number.isNaN](#isnan-和-numberisnan) 3 | - [import() 和 require()](#import-和-require) 4 | - [事件传播的三个阶段](#事件传播的三个阶段) 5 | - [格式化数字](#格式化数字) 6 | 7 | # 零碎知识(暂未分类) 8 | 9 | ## isNaN 和 Number.isNaN 10 | 11 | ```js 12 | // 判断是否等价于 NaN 13 | isNaN; 14 | // ES6 中的方法,先判断是否是 Number,再判断 isNaN 15 | Number.isNaN; 16 | 17 | // 判断是否等价于 isFinite 18 | isFinite; 19 | // ES6 中的方法,先判断是否是 Number,再判断 isFinite 20 | Number.isFinite; 21 | ``` 22 | 23 | ## import() 和 require() 24 | 25 | - `import()` 是编译时执行 26 | - `require()` 是运行时执行 27 | 28 | 因此,使用不同的模块规范,就会导致不同的代码执行顺序。举例如下: 29 | 30 | - ES6 规范 31 | 32 | ```js 33 | // index.js 34 | console.log('log 111'); 35 | import { log } from './log.js'; 36 | 37 | // log.js 38 | console.log('log 222'); 39 | ``` 40 | 41 | 输出顺序如下: 42 | 43 | ``` 44 | log 222 45 | log 111 46 | ``` 47 | 48 | - CommonJS 规范 49 | 50 | ```js 51 | // index.js 52 | console.log('log 111'); 53 | const log = require('./log.js'); 54 | 55 | // log.js 56 | console.log('log 222'); 57 | ``` 58 | 59 | 输出顺序如下: 60 | 61 | ``` 62 | log 111 63 | log 222 64 | ``` 65 | 66 | ES6 模块是引用,只读。CommonJS 模块是拷贝,可写。 67 | 68 | ## 事件传播的三个阶段 69 | 70 | **捕获 > 目标 > 冒泡** 71 | 72 | 在捕获阶段,事件通过父元素向下传递,到目标元素后,开始冒泡。 73 | 74 | ## 格式化数字 75 | 76 | - 每三位加个逗号 77 | 78 | ```js 79 | var num = 123456.789; 80 | // 将语言指定为英语(en) 81 | var formatNum = new Intl.NumberFormat('en').format(num); 82 | 83 | console.log(formatNum); // => "123,456.789" 84 | ``` 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-present liuyib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/README.md: -------------------------------------------------------------------------------- 1 | # Node 包教不包会 2 | 3 | 教程地址:[https://github.com/alsotang/node-lessons](https://github.com/alsotang/node-lessons) 4 | 5 | ## 用到的一些库,记录下部分 6 | 7 | - superagent([http://visionmedia.github.io/superagent/](http://visionmedia.github.io/superagent/)) 是个 http 方面的库,可以发起 get 或 post 请求。 8 | - cheerio([https://github.com/cheeriojs/cheerio](https://github.com/cheeriojs/cheerio)) 可以理解成一个 Node.js 版的 jquery,用来从网页中以 css selector 取数据,使用方式跟 jquery 一样。 9 | - eventproxy([https://github.com/JacksonTian/eventproxy](https://github.com/JacksonTian/eventproxy)) 避免回调函数的深度嵌套。 10 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson1/README.md: -------------------------------------------------------------------------------- 1 | ##### hello world 2 | 3 | 使用的库 / 框架: 4 | 5 | - express 6 | 7 | 在浏览器输出 hello world 8 | 9 | 代码: 10 | 11 | ```node 12 | var express = require('express'); 13 | var app = express(); // 创建一个 express 实例 14 | 15 | app.get('/', function(req, res) { 16 | res.send('Hello World'); 17 | }); 18 | 19 | app.listen(3000, function() { 20 | console.log('app is listening at port 3000'); 21 | }); 22 | ``` -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson1/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); // 创建一个 express 实例 3 | 4 | app.get('/', function (req, res) { 5 | res.send('Hello World'); 6 | }); 7 | 8 | app.listen(3000, function () { 9 | console.log('app is listening at port 3000'); 10 | }); -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson1", 3 | "version": "0.0.1", 4 | "description": "一个简单的 express 应用", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test splicified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson1" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson2/README.md: -------------------------------------------------------------------------------- 1 | ##### 获取 url 中的查询参数 2 | 3 | 使用的库 / 框架: 4 | 5 | - express 6 | - utility - 提供了很多有用的工具。这里用到了里面的 md5 加密 api 7 | 8 | 代码如下: 9 | 10 | ```node 11 | var express = require('express'); 12 | var utility = require('utility'); 13 | 14 | var app = express(); 15 | 16 | app.get('/', function(req, res) { 17 | // 从 req.query 获取查询参数 18 | // 如果是 post 传来的数据,那么查询参数在 req.body 里面。但是 express 默认不处理 body 里的信息 19 | // 需要引入中间件 https://github.com/expressjs/body-parser 20 | var q = req.query.q; 21 | 22 | // 调用utility.md5 方法,得到 md5 值 23 | var md5Value = ''; 24 | 25 | if (q) { 26 | md5Value = utility.md5(q); 27 | } else { 28 | md5Value = 'no query param'; 29 | } 30 | 31 | res.send(md5Value); 32 | }); 33 | 34 | app.listen(4000, function(req, res) { 35 | console.log('app is listen at port 4000'); 36 | }) 37 | ``` 38 | 39 | 效果图如下: 40 | 41 | 没有查询参数: 42 | 43 | ![](./imgs/no_query.png) 44 | 45 | 有查询参数: 46 | 47 | ![](./imgs/have_query.png) -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson2/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var utility = require('utility'); 3 | 4 | var app = express(); 5 | 6 | app.get('/', function (req, res) { 7 | // 从 req.query 获取查询参数 8 | // 如果是 post 传来的数据,那么查询参数在 req.body 里面。但是 express 默认不处理 body 里的信息 9 | // 需要引入中间件 https://github.com/expressjs/body-parser 10 | var q = req.query.q; 11 | 12 | // 调用utility.md5 方法,得到 md5 值 13 | var md5Value = ''; 14 | 15 | if (q) { 16 | md5Value = utility.md5(q); 17 | } else { 18 | md5Value = 'no query param'; 19 | } 20 | 21 | res.send(md5Value); 22 | }); 23 | 24 | app.listen(4000, function (req, res) { 25 | console.log('app is listen at port 4000'); 26 | }) -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson2/imgs/have_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson2/imgs/have_query.png -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson2/imgs/no_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson2/imgs/no_query.png -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson2", 3 | "version": "0.0.1", 4 | "description": "学习使用外部模块", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test splicified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson2" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson3/README.md: -------------------------------------------------------------------------------- 1 | ##### 爬取 cnode 社区 2 | 3 | 使用的库 / 框架: 4 | 5 | - express 6 | - superagent 7 | - cheerio 8 | 9 | 代码: 10 | 11 | ```node 12 | var express = require('express'); 13 | var superagent = require('superagent'); 14 | var cheerio = require('cheerio'); 15 | 16 | var app = express(); 17 | 18 | app.get('/', function(req, res, next) { 19 | // 使用 superagent 抓取 cnode 社区的内容 20 | superagent.get('https://cnodejs.org/') 21 | .end(function(err, sres) { 22 | if (err) { 23 | return next(err); 24 | } 25 | 26 | // sres.text 中储存了网页的 html 内容,将他传给 cheerio.load 之后 27 | // 就得到了一个实现 jquery 接口的变量。它和 jquery 的用法一样,我们习惯把它命名为 $ 28 | // 就下来就都是 jquery 的内容了 29 | var $ = cheerio.load(sres.text); 30 | var items = []; 31 | 32 | $('#topic_list .topic_title').each(function(index, elem) { 33 | var $elem = $(elem); 34 | 35 | items.push({ 36 | title: $elem.attr('title'), 37 | href: $elem.attr('href') 38 | }); 39 | }); 40 | 41 | res.send(items); 42 | }); 43 | }); 44 | 45 | app.listen(4000, function () { 46 | console.log('app is listening at port 4000'); 47 | }); 48 | ``` 49 | 50 | 浏览器中输入 localhost:4000 结果如下: 51 | 52 | ![](./show.png) -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson3/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var superagent = require('superagent'); 3 | var cheerio = require('cheerio'); 4 | 5 | var app = express(); 6 | 7 | app.get('/', function (req, res, next) { 8 | // 使用 superagent 抓取 cnode 社区的内容 9 | superagent.get('https://cnodejs.org/') 10 | .end(function (err, sres) { 11 | if (err) { 12 | return next(err); 13 | } 14 | 15 | // sres.text 中储存了网页的 html 内容,将他传给 cheerio.load 之后 16 | // 就得到了一个实现 jquery 接口的变量。它和 jquery 的用法一样,我们习惯把它命名为 $ 17 | // 就下来就都是 jquery 的内容了 18 | var $ = cheerio.load(sres.text); 19 | var items = []; 20 | 21 | $('#topic_list .topic_title').each(function (index, elem) { 22 | var $elem = $(elem); 23 | 24 | items.push({ 25 | title: $elem.attr('title'), 26 | href: $elem.attr('href') 27 | }); 28 | }); 29 | 30 | res.send(items); 31 | }); 32 | }); 33 | 34 | app.listen(4000, function () { 35 | console.log('app is listening at port 4000'); 36 | }); -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson3", 3 | "version": "0.0.0", 4 | "description": "使用 superagent 与 cheerio 完成简单的爬虫", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test splicified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson3" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson3/show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson3/show.png -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson4/README.md: -------------------------------------------------------------------------------- 1 | ##### 使用 eventproxy 控制并发 2 | 3 | 使用的库 / 框架: 4 | 5 | - express 6 | - superagent 7 | - cheerio 8 | - eventproxy 9 | 10 | 代码如下: 11 | 12 | ```node 13 | var eventproxy = require('eventproxy'); 14 | var superagent = require('superagent'); 15 | var cheerio = require('cheerio'); 16 | var url = require('url'); // node 里的 url 模块 17 | 18 | var CNODE_URL = 'https://cnodejs.org'; 19 | 20 | superagent.get(CNODE_URL) 21 | .end(function (err, res) { 22 | if (err) { 23 | return console.error(err); 24 | } 25 | 26 | var topicUrls = []; // 要抓取话题的 url 27 | var $ = cheerio.load(res.text); 28 | 29 | $('#topic_list .topic_title').each(function (index, elem) { 30 | var $elem = $(elem); 31 | var href = url.resolve(CNODE_URL, $elem.attr('href')); 32 | 33 | if (index < 5) { // 只抓取 5 个 url 的第一条评论(抓太多会被网站限制) 34 | topicUrls.push(href); 35 | } 36 | }); 37 | 38 | var ep = new eventproxy(); 39 | 40 | // 重复监听 5 次 topic_html 事件后,在执行行动 41 | // topics 是一个数组,它包含了 ep.emit('topic_html', pair) 中的那 5 个 pair 42 | ep.after('topic_html', 5, function (topics) { 43 | topics = topics.map(function (topicPair) { 44 | // ep 从 emit 中接受到的参数。分别为 url 和 html 45 | var topicUrl = topicPair[0]; 46 | var topicHtml = topicPair[1]; 47 | var $ = cheerio.load(topicHtml); 48 | 49 | return ({ 50 | title: $('.topic_full_title').text().trim(), 51 | href: topicUrl, 52 | comment1: $('.reply_content').eq(0).text().trim(), 53 | }); 54 | }); 55 | 56 | console.log('final:'); 57 | console.log(topics); 58 | }); 59 | 60 | // 同时抓取所有的 url 61 | topicUrls.forEach(function (topicUrl) { 62 | superagent.get(topicUrl) 63 | .end(function (err, res) { 64 | if (err) { 65 | console.log('error'); 66 | } 67 | 68 | console.log('fetch: ' + topicUrl + ', successful'); 69 | 70 | var _html = res.text; 71 | // 给 ep 发送 topic_html 事件 72 | ep.emit('topic_html', [topicUrl, _html]); 73 | }) 74 | }) 75 | }); 76 | ``` 77 | 78 | 效果如下: 79 | 80 | ![](./cnode_crawler.png) -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson4/app.js: -------------------------------------------------------------------------------- 1 | var eventproxy = require('eventproxy'); 2 | var superagent = require('superagent'); 3 | var cheerio = require('cheerio'); 4 | var url = require('url'); // node 里的 url 模块 5 | 6 | var CNODE_URL = 'https://cnodejs.org'; 7 | 8 | superagent.get(CNODE_URL) 9 | .end(function (err, res) { 10 | if (err) { 11 | return console.error(err); 12 | } 13 | 14 | var topicUrls = []; // 要抓取话题的 url 15 | var $ = cheerio.load(res.text); 16 | 17 | $('#topic_list .topic_title').each(function (index, elem) { 18 | var $elem = $(elem); 19 | var href = url.resolve(CNODE_URL, $elem.attr('href')); 20 | 21 | if (index < 5) { // 只抓取 5 个 url 的第一条评论(抓太多会被网站限制) 22 | topicUrls.push(href); 23 | } 24 | }); 25 | 26 | var ep = new eventproxy(); 27 | 28 | // 重复监听 5 次 topic_html 事件后,在执行行动 29 | // topics 是一个数组,它包含了 ep.emit('topic_html', pair) 中的那 5 个 pair 30 | ep.after('topic_html', 5, function (topics) { 31 | topics = topics.map(function (topicPair) { 32 | // ep 从 emit 中接受到的参数。分别为 url 和 html 33 | var topicUrl = topicPair[0]; 34 | var topicHtml = topicPair[1]; 35 | var $ = cheerio.load(topicHtml); 36 | 37 | return ({ 38 | title: $('.topic_full_title').text().trim(), 39 | href: topicUrl, 40 | comment1: $('.reply_content').eq(0).text().trim(), 41 | }); 42 | }); 43 | 44 | console.log('final:'); 45 | console.log(topics); 46 | }); 47 | 48 | // 同时抓取所有的 url 49 | topicUrls.forEach(function (topicUrl) { 50 | superagent.get(topicUrl) 51 | .end(function (err, res) { 52 | if (err) { 53 | console.log('error'); 54 | } 55 | 56 | console.log('fetch: ' + topicUrl + ', successful'); 57 | 58 | var _html = res.text; 59 | // 给 ep 发送 topic_html 事件 60 | ep.emit('topic_html', [topicUrl, _html]); 61 | }); 62 | }); 63 | }); -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson4/cnode_crawler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson4/cnode_crawler.png -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson4", 3 | "version": "0.0.0", 4 | "description": "使用 eventproxy 控制并发", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson4" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson5/README.md: -------------------------------------------------------------------------------- 1 | ##### 使用 async 控制并发 2 | 3 | > 对于什么时候用 eventproxy,什么时候用 async。一般是这样的:当你需要多个源(一般是小于 10 个)汇总数据的时候,用 eventproxy 方便; 4 | > 当你需要用到队列时, 需要控制并发数,或者你喜欢函数式的编程思维时,使用 async。大部分场景是使用 eventproxy。 5 | 6 | 使用的库 / 框架: 7 | 8 | - async 9 | 10 | 代码如下: 11 | 12 | ```node 13 | var async = require('async'); 14 | 15 | var concurrenceCount = 0; // 并发数计时器 16 | var fetchUrl = function(url, callback) { 17 | var delay = parseInt((Math.random() * 10000000) % 2000, 10); // 随即一个延迟时间 18 | 19 | concurrenceCount++; 20 | 21 | console.log('并发数是:' + concurrenceCount + ',正在抓取网页是:' + url + ',耗时:' + delay + '毫秒'); 22 | 23 | setTimeout(function () { 24 | concurrenceCount--; 25 | callback(null, url + ': html content'); 26 | }, delay); 27 | }; 28 | 29 | // 伪造一组链接 30 | var urls = []; 31 | for (var i = 0; i < 30; i++) { 32 | urls.push('http://datasource_' + i); 33 | } 34 | 35 | // 使用 async.mapLimit 来并发抓取,并获取结果。 36 | /** 37 | * mapLimit 38 | * 并发抓取网页 39 | * @param {Array} 一组网页 url 40 | * @param {Number} 并发连接的数量 41 | * @param {Function} 异步函数 42 | * @param {Function} 回调函数 43 | */ 44 | async.mapLimit(urls, 5, function (url, callback) { 45 | fetchUrl(url, callback); 46 | }, function (err, result) { 47 | if (err) { 48 | console.log('error'); 49 | } 50 | 51 | console.log('final: '); 52 | console.log(result); 53 | }); 54 | ``` 55 | 56 | 效果如下: 57 | 58 | ![](async_crewler.png) -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson5/app.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | var concurrenceCount = 0; // 并发数计时器 4 | var fetchUrl = function(url, callback) { 5 | var delay = parseInt((Math.random() * 10000000) % 2000, 10); // 随即一个延迟时间 6 | 7 | concurrenceCount++; 8 | 9 | console.log('并发数是:' + concurrenceCount + ',正在抓取网页是:' + url + ',耗时:' + delay + '毫秒'); 10 | 11 | setTimeout(function () { 12 | concurrenceCount--; 13 | callback(null, url + ': html content'); 14 | }, delay); 15 | }; 16 | 17 | // 伪造一组链接 18 | var urls = []; 19 | for (var i = 0; i < 30; i++) { 20 | urls.push('http://datasource_' + i); 21 | } 22 | 23 | // 使用 async.mapLimit 来并发抓取,并获取结果。 24 | /** 25 | * mapLimit 26 | * 并发抓取网页 27 | * @param {Array} 一组网页 url 28 | * @param {Number} 并发连接的数量 29 | * @param {Function} 异步函数 30 | * @param {Function} 回调函数 31 | */ 32 | async.mapLimit(urls, 5, function (url, callback) { 33 | fetchUrl(url, callback); 34 | }, function (err, result) { 35 | if (err) { 36 | console.log('error'); 37 | } 38 | 39 | console.log('final: '); 40 | console.log(result); 41 | }); -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson5/async_crewler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson5/async_crewler.png -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson5", 3 | "version": "0.0.0", 4 | "description": "使用 async 控制并发", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson5" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/READMD.md: -------------------------------------------------------------------------------- 1 | ##### 《测试用例:mocha,should,istanbul》 2 | 3 | 用到的库 / 框架: 4 | 5 | - should - 断言库 6 | - mocha - 测试框架 7 | - istanbul - 在测试中输出覆盖率 8 | 9 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson6/app.js -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/coverage.json: -------------------------------------------------------------------------------- 1 | {"/media/liuyibo/Data/myFolder/study-note/Node/alsotang-node-lessons/lesson6/main.js":{"path":"/media/liuyibo/Data/myFolder/study-note/Node/alsotang-node-lessons/lesson6/main.js","s":{"1":1,"2":181,"3":0,"4":181,"5":1,"6":180,"7":1,"8":179,"9":35,"10":144,"11":56,"12":88,"13":1,"14":0,"15":0,"16":1},"b":{"1":[0,181],"2":[1,180],"3":[1,179],"4":[35,144],"5":[56,88],"6":[0,1]},"f":{"1":181},"fnMap":{"1":{"name":"(anonymous_1)","line":1,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":29}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":19,"column":1}},"2":{"start":{"line":2,"column":2},"end":{"line":4,"column":3}},"3":{"start":{"line":3,"column":4},"end":{"line":3,"column":44}},"4":{"start":{"line":5,"column":2},"end":{"line":7,"column":3}},"5":{"start":{"line":6,"column":4},"end":{"line":6,"column":37}},"6":{"start":{"line":8,"column":2},"end":{"line":10,"column":3}},"7":{"start":{"line":9,"column":4},"end":{"line":9,"column":38}},"8":{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},"9":{"start":{"line":12,"column":4},"end":{"line":12,"column":13}},"10":{"start":{"line":14,"column":2},"end":{"line":16,"column":3}},"11":{"start":{"line":15,"column":4},"end":{"line":15,"column":13}},"12":{"start":{"line":18,"column":2},"end":{"line":18,"column":45}},"13":{"start":{"line":23,"column":0},"end":{"line":27,"column":1}},"14":{"start":{"line":24,"column":2},"end":{"line":24,"column":34}},"15":{"start":{"line":26,"column":2},"end":{"line":26,"column":57}},"16":{"start":{"line":29,"column":0},"end":{"line":29,"column":30}}},"branchMap":{"1":{"line":2,"type":"if","locations":[{"start":{"line":2,"column":2},"end":{"line":2,"column":2}},{"start":{"line":2,"column":2},"end":{"line":2,"column":2}}]},"2":{"line":5,"type":"if","locations":[{"start":{"line":5,"column":2},"end":{"line":5,"column":2}},{"start":{"line":5,"column":2},"end":{"line":5,"column":2}}]},"3":{"line":8,"type":"if","locations":[{"start":{"line":8,"column":2},"end":{"line":8,"column":2}},{"start":{"line":8,"column":2},"end":{"line":8,"column":2}}]},"4":{"line":11,"type":"if","locations":[{"start":{"line":11,"column":2},"end":{"line":11,"column":2}},{"start":{"line":11,"column":2},"end":{"line":11,"column":2}}]},"5":{"line":14,"type":"if","locations":[{"start":{"line":14,"column":2},"end":{"line":14,"column":2}},{"start":{"line":14,"column":2},"end":{"line":14,"column":2}}]},"6":{"line":23,"type":"if","locations":[{"start":{"line":23,"column":0},"end":{"line":23,"column":0}},{"start":{"line":23,"column":0},"end":{"line":23,"column":0}}]}}}} -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* dark red */ 156 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 157 | .low .chart { border:1px solid #C21F39 } 158 | /* medium red */ 159 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 160 | /* light red */ 161 | .low, .cline-no { background:#FCE1E5 } 162 | /* light green */ 163 | .high, .cline-yes { background:rgb(230,245,208) } 164 | /* medium green */ 165 | .cstat-yes { background:rgb(161,215,106) } 166 | /* dark green */ 167 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 168 | .high .chart { border:1px solid rgb(77,146,33) } 169 | /* dark yellow (gold) */ 170 | .medium .chart { border:1px solid #f9cd0b; } 171 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; } 172 | /* light yellow */ 173 | .medium { background: #fff4c2; } 174 | /* light gray */ 175 | span.cline-neutral { background: #eaeaea; } 176 | 177 | .cbranch-no { background: yellow !important; color: #111; } 178 | 179 | .cstat-skip { background: #ddd; color: #111; } 180 | .fstat-skip { background: #ddd; color: #111 !important; } 181 | .cbranch-skip { background: #ddd !important; color: #111; } 182 | 183 | 184 | .cover-fill, .cover-empty { 185 | display:inline-block; 186 | height: 12px; 187 | } 188 | .chart { 189 | line-height: 0; 190 | } 191 | .cover-empty { 192 | background: white; 193 | } 194 | .cover-full { 195 | border-right: none !important; 196 | } 197 | pre.prettyprint { 198 | border: none !important; 199 | padding: 0 !important; 200 | margin: 0 !important; 201 | } 202 | .com { color: #999 !important; } 203 | .ignore-none { color: #999; font-weight: normal; } 204 | 205 | .wrapper { 206 | min-height: 100%; 207 | height: auto !important; 208 | height: 100%; 209 | margin: 0 auto -48px; 210 | } 211 | .footer, .push { 212 | height: 48px; 213 | } 214 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for All files 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | / 20 |

21 |
22 |
23 | 81.25% 24 | Statements 25 | 13/16 26 |
27 |
28 | 83.33% 29 | Branches 30 | 10/12 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 81.25% 39 | Lines 40 | 13/16 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 | 72 | 73 | 74 | 75 |
FileStatementsBranchesFunctionsLines
lesson6/
81.25%13/1683.33%10/12100%1/181.25%13/16
76 |
77 |
78 | 82 | 83 | 84 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/lesson6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for lesson6/ 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | all files lesson6/ 20 |

21 |
22 |
23 | 81.25% 24 | Statements 25 | 13/16 26 |
27 |
28 | 83.33% 29 | Branches 30 | 10/12 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 81.25% 39 | Lines 40 | 13/16 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 | 72 | 73 | 74 | 75 |
FileStatementsBranchesFunctionsLines
main.js
81.25%13/1683.33%10/12100%1/181.25%13/16
76 |
77 |
78 | 82 | 83 | 84 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/lesson6/main.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for lesson6/main.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | all files / lesson6/ main.js 20 |

21 |
22 |
23 | 81.25% 24 | Statements 25 | 13/16 26 |
27 |
28 | 83.33% 29 | Branches 30 | 10/12 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 81.25% 39 | Lines 40 | 13/16 41 |
42 |
43 |
44 |
45 |

 46 | 
131 | 
1 47 | 2 48 | 3 49 | 4 50 | 5 51 | 6 52 | 7 53 | 8 54 | 9 55 | 10 56 | 11 57 | 12 58 | 13 59 | 14 60 | 15 61 | 16 62 | 17 63 | 18 64 | 19 65 | 20 66 | 21 67 | 22 68 | 23 69 | 24 70 | 25 71 | 26 72 | 27 73 | 28 74 | 29 75 | 181× 76 |   77 |   78 | 181× 79 | 80 |   81 | 180× 82 | 83 |   84 | 179× 85 | 35× 86 |   87 | 144× 88 | 56× 89 |   90 |   91 | 88× 92 |   93 |   94 |   95 |   96 | 97 |   98 |   99 |   100 |   101 |   102 |
var fibonacci = function (n) {
103 |   Iif (typeof n !== 'number') {
104 |     throw new Error('n should be a Number');
105 |   }
106 |   if (n < 0) {
107 |     throw new Error('n should >= 0');
108 |   }
109 |   if (n > 10) {
110 |     throw new Error('n should <= 10');
111 |   }
112 |   if (n === 0) {
113 |     return 0;
114 |   }
115 |   if (n === 1) {
116 |     return 1;
117 |   }
118 |  
119 |   return fibonacci(n - 1) + fibonacci(n - 2);
120 | }
121 |  
122 | // 如果是直接执行 main.js,则执行此处
123 | // 如果 main.js 被其他文件 require,则此处不会执行
124 | Iif (require.main === module) {
125 |   var n = Number(process.argv[2]);
126 |  
127 |   console.log('fibonacci(' + n + ') is ' + fibonacci(n));
128 | }
129 |  
130 | exports.fibonacci = fibonacci;
132 |
133 |
134 | 138 | 139 | 140 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuyib/note/5444ec18c3864ce593f1986107932aa5a8f7a1a8/Node/Node.js 包教不包会/lesson6/coverage/lcov-report/sort-arrow-sprite.png -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov-report/sorter.js: -------------------------------------------------------------------------------- 1 | var addSorting = (function () { 2 | "use strict"; 3 | var cols, 4 | currentSort = { 5 | index: 0, 6 | desc: false 7 | }; 8 | 9 | // returns the summary table element 10 | function getTable() { return document.querySelector('.coverage-summary'); } 11 | // returns the thead element of the summary table 12 | function getTableHeader() { return getTable().querySelector('thead tr'); } 13 | // returns the tbody element of the summary table 14 | function getTableBody() { return getTable().querySelector('tbody'); } 15 | // returns the th element for nth column 16 | function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } 17 | 18 | // loads all columns 19 | function loadColumns() { 20 | var colNodes = getTableHeader().querySelectorAll('th'), 21 | colNode, 22 | cols = [], 23 | col, 24 | i; 25 | 26 | for (i = 0; i < colNodes.length; i += 1) { 27 | colNode = colNodes[i]; 28 | col = { 29 | key: colNode.getAttribute('data-col'), 30 | sortable: !colNode.getAttribute('data-nosort'), 31 | type: colNode.getAttribute('data-type') || 'string' 32 | }; 33 | cols.push(col); 34 | if (col.sortable) { 35 | col.defaultDescSort = col.type === 'number'; 36 | colNode.innerHTML = colNode.innerHTML + ''; 37 | } 38 | } 39 | return cols; 40 | } 41 | // attaches a data attribute to every tr element with an object 42 | // of data values keyed by column name 43 | function loadRowData(tableRow) { 44 | var tableCols = tableRow.querySelectorAll('td'), 45 | colNode, 46 | col, 47 | data = {}, 48 | i, 49 | val; 50 | for (i = 0; i < tableCols.length; i += 1) { 51 | colNode = tableCols[i]; 52 | col = cols[i]; 53 | val = colNode.getAttribute('data-value'); 54 | if (col.type === 'number') { 55 | val = Number(val); 56 | } 57 | data[col.key] = val; 58 | } 59 | return data; 60 | } 61 | // loads all row data 62 | function loadData() { 63 | var rows = getTableBody().querySelectorAll('tr'), 64 | i; 65 | 66 | for (i = 0; i < rows.length; i += 1) { 67 | rows[i].data = loadRowData(rows[i]); 68 | } 69 | } 70 | // sorts the table using the data for the ith column 71 | function sortByIndex(index, desc) { 72 | var key = cols[index].key, 73 | sorter = function (a, b) { 74 | a = a.data[key]; 75 | b = b.data[key]; 76 | return a < b ? -1 : a > b ? 1 : 0; 77 | }, 78 | finalSorter = sorter, 79 | tableBody = document.querySelector('.coverage-summary tbody'), 80 | rowNodes = tableBody.querySelectorAll('tr'), 81 | rows = [], 82 | i; 83 | 84 | if (desc) { 85 | finalSorter = function (a, b) { 86 | return -1 * sorter(a, b); 87 | }; 88 | } 89 | 90 | for (i = 0; i < rowNodes.length; i += 1) { 91 | rows.push(rowNodes[i]); 92 | tableBody.removeChild(rowNodes[i]); 93 | } 94 | 95 | rows.sort(finalSorter); 96 | 97 | for (i = 0; i < rows.length; i += 1) { 98 | tableBody.appendChild(rows[i]); 99 | } 100 | } 101 | // removes sort indicators for current column being sorted 102 | function removeSortIndicators() { 103 | var col = getNthColumn(currentSort.index), 104 | cls = col.className; 105 | 106 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 107 | col.className = cls; 108 | } 109 | // adds sort indicators for current column being sorted 110 | function addSortIndicators() { 111 | getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; 112 | } 113 | // adds event listeners for all sorter widgets 114 | function enableUI() { 115 | var i, 116 | el, 117 | ithSorter = function ithSorter(i) { 118 | var col = cols[i]; 119 | 120 | return function () { 121 | var desc = col.defaultDescSort; 122 | 123 | if (currentSort.index === i) { 124 | desc = !currentSort.desc; 125 | } 126 | sortByIndex(i, desc); 127 | removeSortIndicators(); 128 | currentSort.index = i; 129 | currentSort.desc = desc; 130 | addSortIndicators(); 131 | }; 132 | }; 133 | for (i =0 ; i < cols.length; i += 1) { 134 | if (cols[i].sortable) { 135 | // add the click event handler on the th so users 136 | // dont have to click on those tiny arrows 137 | el = getNthColumn(i).querySelector('.sorter').parentElement; 138 | if (el.addEventListener) { 139 | el.addEventListener('click', ithSorter(i)); 140 | } else { 141 | el.attachEvent('onclick', ithSorter(i)); 142 | } 143 | } 144 | } 145 | } 146 | // adds sorting functionality to the UI 147 | return function () { 148 | if (!getTable()) { 149 | return; 150 | } 151 | cols = loadColumns(); 152 | loadData(cols); 153 | addSortIndicators(); 154 | enableUI(); 155 | }; 156 | })(); 157 | 158 | window.addEventListener('load', addSorting); 159 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/coverage/lcov.info: -------------------------------------------------------------------------------- 1 | TN: 2 | SF:/media/liuyibo/Data/myFolder/study-note/Node/alsotang-node-lessons/lesson6/main.js 3 | FN:1,(anonymous_1) 4 | FNF:1 5 | FNH:1 6 | FNDA:181,(anonymous_1) 7 | DA:1,1 8 | DA:2,181 9 | DA:3,0 10 | DA:5,181 11 | DA:6,1 12 | DA:8,180 13 | DA:9,1 14 | DA:11,179 15 | DA:12,35 16 | DA:14,144 17 | DA:15,56 18 | DA:18,88 19 | DA:23,1 20 | DA:24,0 21 | DA:26,0 22 | DA:29,1 23 | LF:16 24 | LH:13 25 | BRDA:2,1,0,0 26 | BRDA:2,1,1,181 27 | BRDA:5,2,0,1 28 | BRDA:5,2,1,180 29 | BRDA:8,3,0,1 30 | BRDA:8,3,1,179 31 | BRDA:11,4,0,35 32 | BRDA:11,4,1,144 33 | BRDA:14,5,0,56 34 | BRDA:14,5,1,88 35 | BRDA:23,6,0,0 36 | BRDA:23,6,1,1 37 | BRF:12 38 | BRH:10 39 | end_of_record 40 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/main.js: -------------------------------------------------------------------------------- 1 | var fibonacci = function (n) { 2 | if (typeof n !== 'number') { 3 | throw new Error('n should be a Number'); 4 | } 5 | if (n < 0) { 6 | throw new Error('n should >= 0'); 7 | } 8 | if (n > 10) { 9 | throw new Error('n should <= 10'); 10 | } 11 | if (n === 0) { 12 | return 0; 13 | } 14 | if (n === 1) { 15 | return 1; 16 | } 17 | 18 | return fibonacci(n - 1) + fibonacci(n - 2); 19 | } 20 | 21 | // 如果是直接执行 main.js,则执行此处 22 | // 如果 main.js 被其他文件 require,则此处不会执行 23 | if (require.main === module) { 24 | var n = Number(process.argv[2]); 25 | 26 | console.log('fibonacci(' + n + ') is ' + fibonacci(n)); 27 | } 28 | 29 | exports.fibonacci = fibonacci; -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson6", 3 | "version": "0.0.0", 4 | "description": "测试永立:mocha, should, istanbul", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson5" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson6/test/main.test.js: -------------------------------------------------------------------------------- 1 | var main = require('../main'); 2 | var should = require('should'); 3 | 4 | describe('test/main.test.js', function () { 5 | it('should equal 0 when n === 0', function () { 6 | main.fibonacci(0).should.equal(0); 7 | }); 8 | 9 | it('should equal 1 when n === 1', function () { 10 | main.fibonacci(1).should.equal(1); 11 | }); 12 | 13 | it('should equal 55 when n === 10', function () { 14 | main.fibonacci(10).should.equal(55); 15 | }); 16 | 17 | it('should throw when n > 10', function () { 18 | (function () { 19 | main.fibonacci(11); 20 | }).should.throw('n should <= 10'); 21 | }); 22 | 23 | it('should throw when n < 0', function () { 24 | (function () { 25 | main.fibonacci(-1); 26 | }).should.throw('n should >= 0'); 27 | }); 28 | 29 | if ('should throw when n isnt Number', function () { 30 | (function () { 31 | main.fibonacci('呵呵呵'); 32 | }).should.throw('n should be a Number'); 33 | }); 34 | }); 35 | 36 | // should 断言库的一些其他用法 37 | /* 38 | 1、测试一个数是不是大于 3: 39 | (5).should.above(3) 40 | 2、测试一个字符串是否有着特定的前缀: 41 | 'foo'.should.startWith('foo'); 42 | */ -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson7/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson7", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "mocha.js", 6 | "scripts": { 7 | "test": "mocha-phantomjs index.html --ssl-protocol=any --ignore-ssl-errors=true" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/liuyib/study-note/blob/master/Node/alsotang-node-lessons/lesson6" 12 | }, 13 | "keywords": [ 14 | "test", 15 | "study" 16 | ], 17 | "author": "liuyibo", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson7/vendor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 23 | 24 | 25 | 28 | 29 | 30 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson7/vendor/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | #mocha .test .html-error { 140 | overflow: auto; 141 | color: black; 142 | line-height: 1.5; 143 | display: block; 144 | float: left; 145 | clear: left; 146 | font: 12px/1.5 monaco, monospace; 147 | margin: 5px; 148 | padding: 15px; 149 | border: 1px solid #eee; 150 | max-width: 85%; /*(1)*/ 151 | max-width: -webkit-calc(100% - 42px); 152 | max-width: -moz-calc(100% - 42px); 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | max-height: 300px; 155 | word-wrap: break-word; 156 | border-bottom-color: #ddd; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-box-shadow: 0 1px 3px #eee; 159 | box-shadow: 0 1px 3px #eee; 160 | -webkit-border-radius: 3px; 161 | -moz-border-radius: 3px; 162 | border-radius: 3px; 163 | } 164 | 165 | #mocha .test .html-error pre.error { 166 | border: none; 167 | -webkit-border-radius: 0; 168 | -moz-border-radius: 0; 169 | border-radius: 0; 170 | -webkit-box-shadow: 0; 171 | -moz-box-shadow: 0; 172 | box-shadow: 0; 173 | padding: 0; 174 | margin: 0; 175 | margin-top: 18px; 176 | max-height: none; 177 | } 178 | 179 | /** 180 | * (1): approximate for browsers not supporting calc 181 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 182 | * ^^ seriously 183 | */ 184 | #mocha .test pre { 185 | display: block; 186 | float: left; 187 | clear: left; 188 | font: 12px/1.5 monaco, monospace; 189 | margin: 5px; 190 | padding: 15px; 191 | border: 1px solid #eee; 192 | max-width: 85%; /*(1)*/ 193 | max-width: -webkit-calc(100% - 42px); 194 | max-width: -moz-calc(100% - 42px); 195 | max-width: calc(100% - 42px); /*(2)*/ 196 | word-wrap: break-word; 197 | border-bottom-color: #ddd; 198 | -webkit-box-shadow: 0 1px 3px #eee; 199 | -moz-box-shadow: 0 1px 3px #eee; 200 | box-shadow: 0 1px 3px #eee; 201 | -webkit-border-radius: 3px; 202 | -moz-border-radius: 3px; 203 | border-radius: 3px; 204 | } 205 | 206 | #mocha .test h2 { 207 | position: relative; 208 | } 209 | 210 | #mocha .test a.replay { 211 | position: absolute; 212 | top: 3px; 213 | right: 0; 214 | text-decoration: none; 215 | vertical-align: middle; 216 | display: block; 217 | width: 15px; 218 | height: 15px; 219 | line-height: 15px; 220 | text-align: center; 221 | background: #eee; 222 | font-size: 15px; 223 | -webkit-border-radius: 15px; 224 | -moz-border-radius: 15px; 225 | border-radius: 15px; 226 | -webkit-transition:opacity 200ms; 227 | -moz-transition:opacity 200ms; 228 | -o-transition:opacity 200ms; 229 | transition: opacity 200ms; 230 | opacity: 0.3; 231 | color: #888; 232 | } 233 | 234 | #mocha .test:hover a.replay { 235 | opacity: 1; 236 | } 237 | 238 | #mocha-report.pass .test.fail { 239 | display: none; 240 | } 241 | 242 | #mocha-report.fail .test.pass { 243 | display: none; 244 | } 245 | 246 | #mocha-report.pending .test.pass, 247 | #mocha-report.pending .test.fail { 248 | display: none; 249 | } 250 | #mocha-report.pending .test.pass.pending { 251 | display: block; 252 | } 253 | 254 | #mocha-error { 255 | color: #c00; 256 | font-size: 1.5em; 257 | font-weight: 100; 258 | letter-spacing: 1px; 259 | } 260 | 261 | #mocha-stats { 262 | position: fixed; 263 | top: 15px; 264 | right: 10px; 265 | font-size: 12px; 266 | margin: 0; 267 | color: #888; 268 | z-index: 1; 269 | } 270 | 271 | #mocha-stats .progress { 272 | float: right; 273 | padding-top: 0; 274 | 275 | /** 276 | * Set safe initial values, so mochas .progress does not inherit these 277 | * properties from Bootstrap .progress (which causes .progress height to 278 | * equal line height set in Bootstrap). 279 | */ 280 | height: auto; 281 | -webkit-box-shadow: none; 282 | -moz-box-shadow: none; 283 | box-shadow: none; 284 | background-color: initial; 285 | } 286 | 287 | #mocha-stats em { 288 | color: black; 289 | } 290 | 291 | #mocha-stats a { 292 | text-decoration: none; 293 | color: inherit; 294 | } 295 | 296 | #mocha-stats a:hover { 297 | border-bottom: 1px solid #eee; 298 | } 299 | 300 | #mocha-stats li { 301 | display: inline-block; 302 | margin: 0 5px; 303 | list-style: none; 304 | padding-top: 11px; 305 | } 306 | 307 | #mocha-stats canvas { 308 | width: 40px; 309 | height: 40px; 310 | } 311 | 312 | #mocha code .comment { color: #ddd; } 313 | #mocha code .init { color: #2f6fad; } 314 | #mocha code .string { color: #5890ad; } 315 | #mocha code .keyword { color: #8a6343; } 316 | #mocha code .number { color: #2f6fad; } 317 | 318 | @media screen and (max-device-width: 480px) { 319 | #mocha { 320 | margin: 60px 0px; 321 | } 322 | 323 | #mocha #stats { 324 | position: absolute; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /Node/Node.js 包教不包会/lesson7/vendor/tests.js: -------------------------------------------------------------------------------- 1 | var should = chai.should(); 2 | 3 | describe('simple test', function () { 4 | it('should equal 0 when n === 0', function () { 5 | window.fibonacci(0).should.equal(0); 6 | }); 7 | }); -------------------------------------------------------------------------------- /Node/七天学会 Node.js/README.md: -------------------------------------------------------------------------------- 1 | # 七天学会 NodeJS 2 | 3 | 教程地址:[https://nqdeng.github.io/7-days-nodejs/](https://nqdeng.github.io/7-days-nodejs/) 4 | -------------------------------------------------------------------------------- /Node/七天学会 Node.js/cat.test/tests.js: -------------------------------------------------------------------------------- 1 | var cat = require('../cat'); 2 | 3 | console.log(cat.create('Tom')); -------------------------------------------------------------------------------- /Node/七天学会 Node.js/cat/body.js: -------------------------------------------------------------------------------- 1 | var create = function () { 2 | return 'This is body'; 3 | }; 4 | 5 | exports.create = create; -------------------------------------------------------------------------------- /Node/七天学会 Node.js/cat/head.js: -------------------------------------------------------------------------------- 1 | var create = function () { 2 | return 'This is head'; 3 | }; 4 | 5 | exports.create = create; -------------------------------------------------------------------------------- /Node/七天学会 Node.js/cat/index.js: -------------------------------------------------------------------------------- 1 | var head = require('./head'); 2 | var body = require('./body'); 3 | 4 | var create = function (name) { 5 | return { 6 | name: name, 7 | head: head.create(), 8 | body: body.create() 9 | }; 10 | }; 11 | 12 | exports.create = create; -------------------------------------------------------------------------------- /Node/七天学会 Node.js/myCopy/big-copy.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function copy(source, target) { 4 | fs.createReadStream(source).pipe(fs.createWriteStream(target)); 5 | } 6 | 7 | function main(src) { 8 | copy(src[0], src[1]); 9 | } 10 | 11 | main(process.argv.slice(2)); 12 | 13 | // 在命令行中执行命令 node big-copy.js /opt/aaa.pdf /tmp/test/bbb.pdf 14 | // 即可将 /opt/aaa.pdf 复制到 /tmp/test/bbb.pdf 中。(复制的时候会自动创建 bbb.pdf 文件) 15 | // 这个复制操作适用于大文件的拷贝 -------------------------------------------------------------------------------- /Node/七天学会 Node.js/myCopy/small-copy.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | /** 4 | * 复制文件 5 | * @param {String} source 复制源文件 6 | * @param {String} target 复制目标文件 7 | */ 8 | function copy(source, target) { 9 | fs.writeFileSync(target, fs.readFileSync(source)); 10 | } 11 | 12 | /** 13 | * 接收一个路径数组,进行复制文件 14 | * @param {Array} src 复制文件的路径 15 | */ 16 | function main(src) { 17 | copy(src[0], src[1]); 18 | } 19 | 20 | // process 是一个全局的变量。process.argv 用于获取命令行中的参数 21 | main(process.argv.slice(2)); 22 | 23 | // 命令行中执行 node small-copy.js /opt/aaa.txt /tmp/bbb.txt 24 | // 然后 /opt/aaa.txt 文件里的内容就会被复制到 /tmp/bbb.txt 里。(复制的时候会自动创建 bbb.txt 文件) 25 | // 这个复制操作只适用于小文件的拷贝 -------------------------------------------------------------------------------- /Node/七天学会 Node.js/node-echo.test/tests.js: -------------------------------------------------------------------------------- 1 | var echo = require('../node-echo'); 2 | 3 | console.log(echo('This is test')); -------------------------------------------------------------------------------- /Node/七天学会 Node.js/node-echo/README.md: -------------------------------------------------------------------------------- 1 | 工程目录: 2 | 3 | ```shell 4 | - node-echo/ # 工程目录 5 | - bin/ # 存放命令行相关代码 6 | node-echo 7 | + doc/ # 存放文档 8 | - lib/ # 存放API相关代码 9 | echo.js 10 | + node_modules/ # 存放三方包 11 | + tests/ # 存放测试用例 12 | package.json 13 | README.md 14 | ``` 15 | 16 | 这个部分文件示例如下: 17 | 18 | bin/node-echo.js: 19 | 输出命令行中的参数。 20 | ```node 21 | var echo = require('../lib/echo'); 22 | var argv = process.argv.slice(2); // 命令行中的参数 23 | 24 | console.log(echo(argv.join(' '))); 25 | 26 | // 例如,命令行中执行下面这条命令: 27 | $ node node-echo.js hello world 28 | // => hello world 29 | ``` 30 | 31 | lib/echo.js: 32 | 返回命令行中的参数。 33 | 34 | ```node 35 | module.exports = function (mess) { 36 | return mess; 37 | }; 38 | ``` -------------------------------------------------------------------------------- /Node/七天学会 Node.js/node-echo/bin/node-echo.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | var echo = require('../lib/echo'); 4 | var argv = process.argv.slice(2); // 命令行中的参数 5 | 6 | console.log(echo(argv.join(' '))); -------------------------------------------------------------------------------- /Node/七天学会 Node.js/node-echo/lib/echo.js: -------------------------------------------------------------------------------- 1 | module.exports = function (mess) { 2 | return mess; 3 | }; -------------------------------------------------------------------------------- /Node/七天学会 Node.js/node-echo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-echo", 3 | "version": "0.0.1", 4 | "description": "一个标准的 node 工程目录示例", 5 | "main": "./lib/echo.js", 6 | "bin": { 7 | "node-echo": "node-echo.js" 8 | }, 9 | "directories": { 10 | "doc": "doc", 11 | "test": "tests" 12 | }, 13 | "scripts": { 14 | "test": "echo \"no test specified\"" 15 | }, 16 | "keywords": [ 17 | "test", 18 | "study" 19 | ], 20 | "author": "liuyibo", 21 | "license": "MIT" 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 本仓库分为两部分,一是学习笔记,二是知识总结。 2 | 3 | 学习是一个重复的过程,很多时候学了一大堆知识,很快就会忘记,即使做了笔记。因此,对知识进行**系统的总结**显得尤为重要。 4 | 5 | 知识不成体系,也是基础薄弱的一个明显标志。记得有大佬说过 **“真正专业的人,不是获得了多厉害的奖,也不是学了多难的东西,而是脑海中有着成体系的知识”**。 6 | 7 | 当然,为了形成知识体系,并不建议从头到尾把所有内容重新学习一遍。这就陷入了“完美主义”的怪圈,**学习中“完成”胜过“完美”**。 8 | 9 | 那些制定完美计划的、为了巩固基础想要重新学习所有知识的,最终几乎都会以失败告终。正确的做法应该是:**在原来基础上,对知识点逐个击破,然后对知识进行系统的总结**(如果害怕自己总结不好,可以跟着系统的教程、视频进行总结)。 10 | 11 | ## 学习笔记 12 | 13 | - HTML 14 | 15 | - 《大厂 H5 开发实战》- 掘金小册 16 | 17 | - [基础页面开发](<./HTML(5)/HTML5/大厂%20H5%20开发实战/基础页面开发.md>) 18 | 19 | - CSS(3) 20 | 21 | - [CSS 重排与重绘笔记](<./CSS(3)/CSS%20重排与重绘笔记.md>) 22 | - [BEM 命名规范笔记](<./CSS(3)/BEM%20命名规范笔记.md>) 23 | - [深入理解 font-size 笔记](<./CSS(3)/深入理解%20font-size%20笔记.md>) 24 | - [CSS 实现等比缩放](<./CSS(3)/CSS%20实现等比缩放.md>) 25 | 26 | - JavaScript 27 | 28 | - [ES6 笔记](./JavaScript/ES6%20笔记/README.md) 29 | - [零碎知识笔记](./JavaScript/零碎知识笔记.md) 30 | 31 | - 数据结构 ==> 移步:[https://github.com/liuyib/fucking-algorithm](https://github.com/liuyib/fucking-algorithm) 32 | 33 | - Node 34 | 35 | - [Node.js 包教不包会](./Node/Node.js%20包教不包会/README.md) 36 | - [七天学会 Node.js](./Node/七天学会%20Node.js/README.md) 37 | 38 | - 打包工具 39 | 40 | - [Gulp 学习笔记](./打包工具/gulp/README.md) 41 | - [Rollup 学习笔记](./打包工具/rollup/README.md) 42 | - [webpack 学习笔记](./打包工具/webpack/README.md) 43 | 44 | - 读书笔记 45 | 46 | - [你不知道的 JS](./读书笔记/你不知道的%20JS/README.md) 47 | - [JS 高级程序设计](./读书笔记/JS%20高级程序设计/README.md) 48 | 49 | ## 系统总结 50 | 51 | - HTML(5) 52 | 53 | - [HTML5 知识总结](<./HTML(5)/HTML5/HTML5%20知识总结.md>) 54 | - [WebSocket 学习总结](<./HTML(5)/HTML5/WebSocket%20学习总结.md>) 55 | - [像素、分辨率相关概念](<./HTML(5)/HTML5/像素、分辨率相关概念.md>) 56 | - [探究移动端适配](<./HTML(5)/HTML5/探究移动端适配.md>) 57 | - [1px 的解决方案](<./HTML(5)/HTML5/1px%20的解决方案.md>) 58 | 59 | - CSS(3) 60 | 61 | - [七种水平垂直居中方法总结](<./CSS(3)/七种水平垂直居中方法总结.md>) 62 | - [CSS 面试题总结](<./CSS(3)/CSS面试题总结.md>) 63 | 64 | - JavaScript 65 | 66 | - [JS 基础知识点 1](./JavaScript/JS%20基础知识点1.md) 67 | - [JS 基础知识点 2](./JavaScript/JS%20基础知识点2.md) 68 | - 手写系列总结 69 | 70 | - [手写 Ajax](./JavaScript/手写系列/手写%20Ajax.md) 71 | - [手写 JSONP](./JavaScript/手写系列/手写%20JSONP.md) 72 | - [手写 XXX](./JavaScript/手写系列/手写%20XXX.md) 73 | 74 | - [正则表达式总结](./JavaScript/正则表达式总结.md) 75 | - [从 JS 高阶函数到柯里化](./JavaScript/从%20JS%20高阶函数到柯里化.md) 76 | - [深入理解 JS 事件循环机制(浏览器篇)](./浏览器相关/深入理解%20JS%20事件循环机制(浏览器篇).md) 77 | 78 | - 网络相关 79 | 80 | - [Web 安全总结](./网络相关/Web%20安全/Web%20安全总结.md) 81 | 82 | - [密码安全](./网络相关/Web%20安全/密码安全/README.md) 83 | - [Cookie 安全](./网络相关/Web%20安全/Cookie%20安全/README.md) 84 | - [HTML5 安全](./网络相关/Web%20安全/HTML5%20安全/README.md) 85 | - [XSS 攻击](./网络相关/Web%20安全/XSS%20攻击/README.md) 86 | - [CSRF 攻击](./网络相关/Web%20安全/CSRF%20攻击/README.md) 87 | - [点击劫持攻击](./网络相关/Web%20安全/点击劫持攻击/README.md) 88 | - [SQL 注入攻击](./网络相关/Web%20安全/SQL%20注入攻击/README.md) 89 | - [上传文件攻击](./网络相关/Web%20安全/上传文件攻击/README.md) 90 | 91 | - HTTP(S) 相关 92 | 93 | - [深入探究 TLS 协议](<./网络相关/HTTP(S)%20相关/深入探究%20TLS%20协议.md>) 94 | 95 | - TCP 相关 96 | 97 | - [深入探究 TCP 协议](./网络相关/TCP%20相关/深入探究%20TCP%20协议.md) 98 | 99 | - 浏览器相关 100 | 101 | - [跨域及其实现原理总结](./浏览器相关/跨域及其实现原理.md) 102 | 103 | - 性能优化 104 | 105 | - [浏览器缓存机制总结](./性能优化/浏览器缓存机制总结.md) 106 | - [Web 性能优化总结](./性能优化/Web%20性能优化总结.md) 107 | 108 | - 设计模式 109 | 110 | - [前端常用的设计模式](./设计模式/前端常用的设计模式.md) 111 | 112 | - 版本控制 113 | 114 | - [Git 相关总结](./版本控制/Git%20相关总结.md) 115 | 116 | - 代码规范 117 | 118 | - [Stranard 规范总结](./代码规范/Stranard%20规范总结.md) 119 | 120 | - UI 设计 121 | 122 | - [MEB 风格图标设计](./UI%20设计/MBE风格图标设计/README.md) 123 | - [Photoshop (PS)](<./UI%20设计/Photoshop%20(PS)/README.md>) 124 | 125 | ## 模仿面试 126 | 127 | - [面试前八股速查](./模仿面试/八股速查.md) 128 | - [面试前必刷算法题](https://github.com/liuyib/fucking-algorithm#面试前必刷题) 129 | -------------------------------------------------------------------------------- /React/egghead/start-learning-react/JSX编译器.md: -------------------------------------------------------------------------------- 1 | # JSX 编译器 2 | 3 | 使用 Babel 在线进行解析 React: 4 | 5 | ```js 6 | import React, { Component } from 'react'; 7 | import './App.css'; 8 | 9 | export class App extends Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.state = { 14 | input: 15 | 'const App = () => {\n return (\n
hello world
\n );\n}\nexport default App;\n', 16 | output: '', 17 | error: '' 18 | }; 19 | } 20 | 21 | update = e => { 22 | let code = e.target.value; 23 | 24 | try { 25 | this.setState({ 26 | output: window.Babel.transform(code, { presets: ['es2015', 'react'] }) 27 | .code, 28 | error: '' 29 | }); 30 | } catch (err) { 31 | this.setState({ 32 | error: err.message 33 | }); 34 | } 35 | }; 36 | 37 | render() { 38 | return ( 39 |
40 |
{this.state.error}
41 |