├── .gitignore
├── README.md
├── animation.md
├── assets
├── 0_-IMsNws_L1g0Syla.png
├── 0_-OqUfzpRtgDJQjXY.png
├── 0_1QVfw-_HSz22ZtJS.png
├── 0_3R6-AyKY1831P10d.jpeg
├── 0_3SRCEtv7rMhWpB5s.png
├── 0_60xiqW7kPsQg5ssn.png
├── 0_66kSmyuCNeD7Oiaq.png
├── 0_9KPehy4mUb8f-hSp.png
├── 0_A5ucCHOZsxXyHMfN.jpeg
├── 0_AsoHzlowoLItnUEL.png
├── 0_B4g8Jpu1dZOF1QMI.png
├── 0_Ci-Jl1Mlki03m3A-.png
├── 0_D586dpehfQtaG2wY.png
├── 0_EoQo41lZY6_RXAii.png
├── 0_GDU4GguTzk8cSAYk.png
├── 0_HP66Xm7oe9u8Ofk1.png
├── 0_IN688nPbgu8zYETe.png
├── 0_Nm9r_NLcAhJernmo.png
├── 0_PTDs1BkbMgekizit.png
├── 0_QbI-hozmSVibF6DW.png
├── 0_QhRSzkngh6Aty-nD.png
├── 0_QphcOVaiVC2YL7Jd.png
├── 0_RRgTDdRfLGEhuR7U.png
├── 0_SXRTlnVxy2-hE9ZX.png
├── 0_SufKRGfPZIDlw1OG.png
├── 0_VKQINIYfu2O7d7BH.png
├── 0_Vqa1yAeYJio0RF5a.png
├── 0_W4wHvVAeUmc45vA2.png
├── 0_XZ2U-ztABhWJOSky.png
├── 0_avLiOV_zXLxOgBee.png
├── 0_bIg_WMaJvjHWSc5J.png
├── 0_bN9YVBLw_tT1Xvte.png
├── 0_c2HpOiUYMimMXHv2.png
├── 0_eEArxn147Ev8xf5n.png
├── 0_g6rpKt5YynDCQ5n3.png
├── 0_hZbijxS0vXu8vUmz.png
├── 0_hxC_NUPNycUBhj-L.png
├── 0_j3zkSjnrL4fnCK3A.png
├── 0_jJuHfRMipW8PPcb0.png
├── 0_k0vSOmvdDkRJzcpW.png
├── 0_kGDQYE70_z58D7na.png
├── 0_l-QdpBfjwNfPDTyh.png
├── 0_mSOIiWpkctkD0Gfg.png
├── 0_ndU4N8xQF6QEQmSY.png
├── 0_nlOmrsfy-Y1XoR8B.png
├── 0_oWsMRoltriNxmRNe.png
├── 0_oqlhISJ0TrPjoG74.png
├── 0_pohqKvj9psTPRlOv.png
├── 0_qelaH45ZWG3UybLO.jpg
├── 0_v56OyrPtg_cZzzaZ.png
├── 0_wzuQ9LYv7CAUICOC.png
├── 0_z-A-JIe5OWFtgyd2.png
├── 1_-vHNuJsJVXvqq5dLHPt7cQ.png
├── 1_0B-dAUOH7NrcCDP6GhKHQw.jpeg
├── 1_0REL14sYPR34hY7yua6-PA.png
├── 1_0e8X3UTBpsiBSZKa3l1hXA.png
├── 1_1Mc_9bnkR74BnACCbP1Q5A.jpeg
├── 1_1NAeDnEv6DWFewX_C-L8mg.png
├── 1_2EduxBOtJ7ENgIna-aU4WA.png
├── 1_2v7G1ZJ1C-y_mWHOYQfQKQ.png
├── 1_48tGIboHxgLeKEjMTGkUGg.png
├── 1_4lHHyfEhVB0LnQ3HlhSs8g.png
├── 1_5YU1su2mdzHEQ5iDisKUyw.png
├── 1_5aBou4onl1B8xlgwoGTDOg.png
├── 1_6o2TRDmrJlS97vh1wEjLYw.png
├── 1_81mCsOzyJj-HfQ1lP_033w.png
├── 1_9b1uEMcZLWuGPuYcIn7ZXQ.png
├── 1_9fbOuFXJHwhqa6ToCc_v2A.png
├── 1_9ryMUEZhtbTg7lECHVz0fw.png
├── 1_AKKvE3QmN_ZQmEzSj16oXg.png
├── 1_AycFMDy9tlDmNoc5LXd9-g.png
├── 1_C1VWSKOx89vqdiSiflDRJw.png
├── 1_FbbOG9mcqWZtNajjDO6SaA.png
├── 1_GF3p99CQPZkX3UkgyVKSHw.png
├── 1_HIn-BxIP38X6mF_65snMKg.png
├── 1_Jyyot22aRkKMF3LN1bgE-w.png
├── 1_KGAppeqpUwv8OgPKkT0Ujw.jpeg
├── 1_KGBiAxjeD9JT2j6KDo0zUg.png
├── 1_M5htfOGgza04ISv_l-69zg.png
├── 1_McIb2yWGRG6PoDUSavp86g.jpeg
├── 1_NVT6WbNrH_mQL64--b-l1Q.png
├── 1_ONNxJHqmMTXB1Nuq3qTNXQ.png
├── 1_OnH_DlbNAPvB9KLxUCyMsA.png
├── 1_OsLmWRogydrW0gG1YlYklQ.jpeg
├── 1_P5nzyldL4rg36dZmt2RViQ.png
├── 1_PgclyCPqxWc1rENfAOesag.png
├── 1_Qm9OFPq3siW0tCKfa03DqQ.png
├── 1_QsVUE3snZD9abYXccg6Sgw.png
├── 1_SEZ_0CFc4zoHp_g7A0KKNw.png
├── 1_Sz4wI2ukt91vRrgf8UonWw.png
├── 1_T-W_ihvl-9rG4dn18kP3Qw.png
├── 1_TozSrkk92l8ho6d8JxqF_w.gif
├── 1_UwtM7DmK1BmlBOUUYEopGQ.png
├── 1_UxkSstuyCvmKkBTnjbezNw.png
├── 1_VDWQl67cmbyAFC5xL9Og4g.png
├── 1_WHR_08AD8APDITQ-4CFDgg.png
├── 1_WVtok3BV0NgU95mpxk9CNg.gif
├── 1_WlMXK3rs_scqKTRV41au7g.jpeg
├── 1_WqInzMPQGGcMX9AOONN76g.jpeg
├── 1_YFr59cEF2qxzjjleebvbcQ.png
├── 1_Yp1KOt_UJ47HChmS9y7KXw.png
├── 1_Zf4reZZJ9DCKsXf5CSXghg.png
├── 1__LdkAVTcSDkoIUbqanj2gg.jpeg
├── 1__nYLhoZPKD_HPhpJtQeErA.png
├── 1_a4lA5FYDkjA9mv53NPKtOg.png
├── 1_dvrghQCVQIZOfNC27Jrtlw.png
├── 1_e0nEd59RPKz9coyY8FX-uw.png
├── 1_eOj6NVwGI2N78onh6CuCbA.png
├── 1_eyaMLcORDVsCFIf5h_ygjA.png
├── 1_ezFoXqgf91umls9FqO0HsQ.png
├── 1_hTMGxzZrOmxxIfaQU7nKig.png
├── 1_hpyVeL1zsaeHaqS7mU4Qfw.png
├── 1_iBedryNbqtixYTKviPC1tA.png
├── 1_iHfI6MQ-YKQvWvo51J-P0w.png
├── 1_jQMQ9BEKPycs2wFC233aNg.png
├── 1_lMBu87MtEsVFqqbfMum-kA.png
├── 1_lvOtCg75ObmUTOxIS6anEQ.png
├── 1_lzOIevUBVy5eWyf2kHf--w.png
├── 1_mVOrpKC9pFTMg4EXPozoog.png
├── 1_oOcY2Gn-LVt1h-e9xOv5oA.jpeg
├── 1_pSh7IORJoUXbwCjyJ7fM9A.png
├── 1_pVnIrMZiB9iAz5sW28AixA.png
├── 1_qY-yRQWGI-DLS3zRHYHm9A.png
├── 1_rWh8YlBn8SypiMduLiYDhA.png
├── 1_rjBdCBwOx5Gp_A6b6FQgfw.png
├── 1_slxXgq_TO38TgtoKpWa_jQ.png
├── 1_spJ8v7GWivxZZzTAzqVPtA.png
├── 1_t2Btfb_tBbBxTvyVgKX0Qg.png
├── 1_tGXhNroe8KxGN7r4UTVSHw.png
├── 1_tLntRcKj-gIVaCcSusDpLQ.jpeg
├── 1_vd3X2O_qRfqaEpW4AfZM4w.png
├── 1_x8P3OcgcgKrEEDpgT2IKkQ.jpeg
├── 1_xhB8ceUNM6vb8s0ZQKMHNg.png
├── 1_ya4zMDfbNUflXhzKz9EBIw.png
├── 1_yn9Y4PXNP8XTz6mtCAzDZQ.png
├── 25_cover.jpeg
├── 26_cover.jpeg
├── 26_shapes.png
├── 27_cover.png
├── 28_cover.jpeg
└── 29_cover.jpeg
├── ast.md
├── cryptography.md
├── csrf.md
├── custom-element.md
├── deno.md
├── design-patterns.md
├── event-loop.md
├── exceptions.md
├── functional-programing.md
├── http.md
├── iterators.md
├── memory-management.md
├── mutation-observer.md
├── networking.md
├── overview.md
├── polymorphism.md
├── push-notifications.md
├── regexp.md
├── rendering.md
├── service-worker.md
├── shadow-dom.md
├── storage.md
├── transpiling.md
├── v8.md
├── webassembly.md
├── webrtc.md
├── worker.md
└── xss-attack.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Numerous always-ignore extensions
2 | *.diff
3 | *.err
4 | *.log
5 | *.orig
6 | *.rej
7 | *.swo
8 | *.swp
9 | *.vi
10 | *.zip
11 | *~
12 |
13 | # OS or Editor folders
14 | ._*
15 | .cache
16 | .DS_Store
17 | .idea
18 | .project
19 | .settings
20 | .tmproj
21 | *.esproj
22 | *.sublime-project
23 | *.sublime-workspace
24 | nbproject
25 | Thumbs.db
26 |
27 | # Folders to ignore
28 | node_modules
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # how-javascript-works
2 |
3 | > Contributors: [@Troland](https://github.com/Troland/) [@三月](https://github.com/heaven2049) [@chkaos](https://github.com/chkaos)。
4 | >
5 | > 本文是翻译介绍 JavaScript 的工作原理的,该系列原文还在更新中,原文见[这里](https://blog.sessionstack.com/tagged/tutorial)。
6 |
7 | ## 目录结构
8 |
9 | - 第一章:[语言引擎,运行时和调用栈概述](overview.md)
10 | - 第二章:[如何在 V8 引擎中书写最优代码的 5 条小技巧](v8.md)
11 | - 第三章:[内存管理及如何处理 4 类常见的内存泄漏](memory-management.md)
12 | - 第四章:[事件循环及异步编程的出现和 5 种更好的 async/await 编程方式](event-loop.md)
13 | - 第五章:[深入理解 WebSockets 和带有 SSE 机制的 HTTP/2 以及正确的使用姿势](http.md)
14 | - 第六章:[WebAssembly 对比 JavaScript 及其使用场景](webassembly.md)
15 | - 第七章:[Web Workers 分类及 5 个使用场景](worker.md)
16 | - 第八章:[Service Workers,生命周期及其使用场景](service-worker.md)
17 | - 第九章:[网页消息推送通知机制](push-notifications.md)
18 | - 第十章:[使用 MutationObserver 监测 DOM 变化](mutation-observer.md)
19 | - 第十一章:[渲染引擎及性能优化小技巧](rendering.md)
20 | - 第十二章:[网络层探秘及如何提高其性能和安全性](networking.md)
21 | - 第十三章:[CSS 和 JS 动画底层原理及如何优化其性能](animation.md)
22 | - 第十四章:[解析,语法抽象树及最小化解析时间的 5 条小技巧](ast.md)
23 | - 第十五章:[类和继承及 Babel 和 TypeScript 代码转换探秘](transpiling.md)
24 | - 第十六章:[存储引擎及如何选择合适的存储 API](storage.md)
25 | - 第十七章:[Shadow DOM 内部构造及如何构建独立组件](shadow-dom.md)
26 | - 第十八章:[WebRTC 及点对点网络通信机制](webrtc.md)
27 | - 第十九章:[自定义元素探秘及构建可复用组件最佳实践](custom-element.md)
28 | - 第二十章:[异常及同步/异步代码异常处理最佳实践](exceptions.md)
29 | - 第二十一章:[5 种跨站脚本攻击及防御措施](xss-attack.md)
30 | - 第二十二章:[跨站请求伪造攻击及 7 种缓解策略](csrf.md)
31 | - 第二十三章:[迭代器及生成器高阶控制技巧](iterators.md)
32 | - 第二十四章:[密码术及中间人攻击处理](cryptography.md)
33 | - 第二十五章:[函数式编程及与其他范式对比](functional-programing.md)
34 | - 第二十六章:[3 种多态](polymorphism.md)
35 | - 第二十七章:[正则表达式](regexp.md)
36 | - 第二十八章:[Deno 简介](deno.md)
37 | - 第二十八章:[创建型/结构型/行为型设计模式及 4 种最佳实践](design-patterns.md)
38 |
39 | ## 版权说明
40 |
41 | 未经允许禁止任何形式的转载。
42 |
43 | 除非另行注明,这个项目中的所有内容采用[知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/)共享,BY [Troland](https://github.com/Troland)。
44 |
45 | 欢迎通过 Issue 或者 Pull Request 提建议。
46 |
47 | ## 来杯咖啡
48 |
49 | [赞助我](https://user-images.githubusercontent.com/1475173/39091700-c211409e-462c-11e8-8531-90261c9a7b73.png)
50 |
--------------------------------------------------------------------------------
/animation.md:
--------------------------------------------------------------------------------
1 | # CSS 和 JS 动画底层原理及如何优化其性能
2 |
3 | > 原文请查阅[这里](https://blog.sessionstack.com/how-javascript-works-under-the-hood-of-css-and-js-animations-how-to-optimize-their-performance-db0e79586216),本文采用[知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/)共享,BY [Troland](https://github.com/Troland)。
4 |
5 | **这是 JavaScript 工作原理的第十三章。**
6 |
7 | ## 概述
8 |
9 | 正如你所知,动画在创建令人叹服的网络应用中扮演着一个关键角色。由于用户越来越注重用户体验,企业开始意识到完美,令人愉悦的用户体验的重要性,结果网络应用变得越来越臃肿并且拥有更多动态交互的功能。这就要求网络应用于用户使用过程中提供更加复杂的动画来实现平滑的状态过渡。现在,这已经司空见惯。用户变得越来越挑剔,他们默认期许可以体验高度响应能力和交互良好的用户界面。
10 |
11 | 然而,让界面具有动画效果不一定必须的。哪些需要使用动画,动画的时机,方面及采用何种动画效果都是复杂的问题。
12 |
13 | ## JavaScript 和 CSS 动画比较
14 |
15 | JavaScript 和 CSS 是创建网页动画的两条主要途径。两种不分好赖,看情况用吧。
16 |
17 | ## CSS 动画
18 |
19 | 使用 CSS 动画是让元素在屏幕上移动的最简单方法。
20 |
21 | 我们将会以如何让元素在 X 和 X 座标上移动元素 50 像素作为小示例开始。通过持续 1 秒的 CSS 过渡来移动元素。
22 |
23 | ```
24 | .box {
25 | -webkit-transform: translate(0, 0);
26 | -webkit-transition: -webkit-transform 1000ms;
27 |
28 | transform: translate(0, 0);
29 | transition: transform 1000ms;
30 | }
31 |
32 | .box.move {
33 | -webkit-transform: translate(50px, 50px);
34 | transform: translate(50px, 50px);
35 | }
36 | ```
37 |
38 | 当为元素添加 `move` 类的时候,改变 `transform` 的值然后开发发生过渡效果。
39 |
40 | 除了过渡持续时间,还有 **easing** 参数,它主要负责动画体验。该参数会在之后详细介绍。
41 |
42 | 如果通过以上的代码片段可以创建单独的样式类来操作动画,那么也可以使用 JavaScript 来切换每个动画。
43 |
44 | 如下元素:
45 |
46 | ```
47 |
48 | Sample content.
49 |
50 | ```
51 |
52 | 然后,使用 JavaScript 来切换每个动画。
53 |
54 | ```
55 | var boxElements = document.getElementsByClassName('box'),
56 | boxElementsLength = boxElements.length,
57 | i;
58 |
59 | for (i = 0; i < boxElementsLength; i++) {
60 | boxElements[i].classList.add('move');
61 | }
62 | ```
63 |
64 | 以上代码片段为每个包含 `box` 类的元素添加 `move` 类来触发动画。
65 |
66 | 这样做可以很好为你的网络应用提供很好的平衡。你就可以专注于使用 JavaScript 来操作应用状态,然后只需为目标元素设置合适的类,让浏览器来处理动画。如若你选择这么处理,就可以监听元素的 `transitionend` 事件,不过须放弃支持 IE 老版本浏览器。
67 |
68 | 
69 |
70 | 如下监听 `transitioned` 事件,该事件会在动画结束时触发。
71 |
72 | ```
73 | var boxElement = document.querySelector('.box'); // 获取第一个包含 box 类的元素
74 | boxElement.addEventListener('transitionend', onTransitionEnd, false);
75 |
76 | function onTransitionEnd() {
77 | // Handle the transition finishing.
78 | }
79 | ```
80 |
81 | 除了使用 CSS 过渡,还可以使用 CSS 动画,CSS 动画可以让你更好地控制单独的动画关键帧,持续时间以及循环次数。
82 |
83 | > 关键帧是用来通知浏览器在规定的时间点上应有的 CSS 属性值,然后填充空白。
84 |
85 | 看一个例子:
86 |
87 | ```
88 | /**
89 | * 该示例是没有包含浏览器前缀的精简版。加上以后会更加准确些。
90 | *
91 | */
92 | .box {
93 | /* 选择动画名称 */
94 | animation-name: movingBox;
95 |
96 | /* 动画时长 */
97 | animation-duration: 2300ms;
98 |
99 | /* 动画循环次数 */
100 | animation-iteration-count: infinite;
101 |
102 | /* 每次奇数次循环时反转动画方向 */
103 | animation-direction: alternate;
104 | }
105 |
106 | @keyframes movingBox {
107 | 0% {
108 | transform: translate(0, 0);
109 | opacity: 0.4;
110 | }
111 |
112 | 25% {
113 | opacity: 0.9;
114 | }
115 |
116 | 50% {
117 | transform: translate(150px, 200px);
118 | opacity: 0.2;
119 | }
120 |
121 | 100% {
122 | transform: translate(40px, 30px);
123 | opacity: 0.8;
124 | }
125 | }
126 | ```
127 |
128 | 效果示例-
129 |
130 | 通过使用 CSS 动画定义独立于目标元素的动画本身,然后设置元素的 animation-name 属性来使用想要的动画效果。
131 |
132 | CSS 动画仍然是需要加浏览器前缀的,在 Safari, Safari 移动浏览器和 Android 端添加 `-webkit-` 前缀。Chrome, Opera, Internet Explorer, and Firefox 端全部不需要添加前缀。有很多工具可以用来创建包含任意前缀的样式,这样就不需要在源文件中带样式前缀。
133 |
134 | **可以使用 autoprefixer 或者 cssnext 来自动为样式添加前缀。**
135 |
136 | ## JavaScript 动画
137 |
138 | 和 CSS 过渡或者 CSS 动画相比,使用 JavaScript 来创建动画要更加复杂些,但是一般而言,它会为开发者提供引人注目且强大的功能。
139 |
140 | 一般情况下,可以内联 JavaScript 动画作为代码的一部分。也可以把它们封装在其它对象之中。以下为复现之前描述的 CSS 过渡的 JavaScript 代码:
141 |
142 | ```
143 | var boxElement = document.querySelector('.box');
144 | var animation = boxElement.animate([
145 | {transform: 'translate(0)'},
146 | {transform: 'translate(150px, 200px)'}
147 | ], 500);
148 | animation.addEventListener('finish', function() {
149 | boxElement.style.transform = 'translate(150px, 200px)';
150 | });
151 | ```
152 |
153 | 默认情况下,网页动画只是修改了元素的展示效果。如果想要让元素停留在指定的移动位置,那么就得在动画结束的时候修改其底层样式。这也是为什么在以上的示例中监听 `finish` 事件然后设置` box.style.transform` 属性为 `translate(150px, 200px)` 的原因,该属性值和 CSS 动画执行的第二个样式转换是一样的。
154 |
155 | 通过使用 JavaScript 动画,开发者可以完全控制每一步元素的样式。这意味着可以随心所欲地减速,暂停,停止或者翻转动画以控制目标元素。由于可以适当地封装动画行为,所以当在构建复杂的、面向对象的应用程序的时候会特别有用。
156 |
157 | ## Easing 定义
158 |
159 | 自然平滑地移动会让网络应用拥有更好的用户交互体验。
160 |
161 | 自然条件下,没有事物可以线性地从一个点运动到另一个点。现实生活中,在我们周围的物理世界中物体在移动的时候会加速或减速,因为我们并不生活在真空状态下且有不同的因素来影响事物的运行状态。人类的大脑会本能地感受到这样的移动,所以当为网络应用制作动画的时候,利用此类知识会有好处。
162 |
163 | 这是你所应该理解的术语:
164 |
165 | * 『ease in』-开始移动缓慢而后加速
166 | * 『ease out』-开始移动迅速而后减速
167 |
168 | 可以合并两个动画,比如 『ease in out』。
169 |
170 | Easing 可以使得动画更加自然平滑。
171 |
172 | ### Easing 关键字
173 |
174 | 可以为 CSS 过渡和动画选择任意的 easing 方法。不同的关键字会影响动画的 easing。你也可以完全自定义 easing 方法。
175 |
176 | 以下为可以选择用来控制 easing 的 CSS 关键字:
177 |
178 | * `linear`
179 | * `ease-in`
180 | * `ease-out`
181 | * `ease-in-out`
182 |
183 | 让我们深入了解并查看他们的效果。
184 |
185 | ### Linear 动画
186 |
187 | 不使用任何的 easing 方法的动画即为 **linear**。
188 |
189 | 以下为 linear 过渡效果的图示:
190 |
191 | 
192 |
193 | 值随着时间流逝,值等比增加。使用 linear 动效,会让动画不自然。一般来说,避免使用 linear 动效。
194 |
195 | 使用如下代码实现一个简单的线性动画:
196 |
197 | `transition: transform 500ms linear;`
198 |
199 | ### Ease-out 动画
200 |
201 | 正如前所述,和 linear 对比,easing out 让动画快速启动,结束时会减速。以下为图示:
202 |
203 | 
204 |
205 | 总之,easing out 是最适合做界面体验的,因为快速地启动会给人以快速响应的动画的感觉,而结束时由于不一致的移动速度让人感觉平滑。
206 |
207 | **打个比喻,比如那些跑车,首先启动速度相当的快,这就给人以愉悦的感觉。这个就比较符合人类对于动画的感知。**
208 |
209 | 有很多的方法来实现 ease out 动画效果,而最简单的即为 CSS 中的 `ease-out` 关键字。
210 |
211 | `transition: transform 500ms ease-out;`
212 |
213 | ### Ease-in 动画
214 |
215 | 和 ease-out 动画相反-其启动慢然后结束时变快。图示如下:
216 |
217 | 
218 |
219 | 和 ease-out 动画比较,由于他们启动缓慢给人以反应卡顿的感觉,所以 ease-in 让人感觉动画不自然。动画结束时很快给人一种奇怪的感觉,因为整个动画一直在加速,而现实世界中当事物突然停止运动的时候会减速而不是加速。
220 |
221 | 和 ease-out 和 linear 动画类似,使用 CSS 关键字来实现 ease-in 动画:
222 |
223 | `transition: transform 500ms ease-in;`
224 |
225 | ### Ease-in-out 动画
226 |
227 | 该动画为 ease-in 和 ease-out 的合集。图示如下:
228 |
229 | 
230 |
231 | 不要设置动画持续时间过长,否则会给人一种界面不响应的感觉。
232 |
233 | 使用 `ease-in-out` CSS 关键字来实现 ease-in-out 动画:
234 |
235 | `transition: transform 500ms ease-in-out;`
236 |
237 | ### 自定义 easing
238 |
239 | 你可以自定义自己的 easing 曲线,这样就更有效地控制项目中的动画。
240 |
241 | 实际上, `ease-in`,`linear` 及 `ease` 关键字映射到预定义[贝塞尔曲线 ](https://en.wikipedia.org/wiki/B%C3%A9zier_curve),可以在 [CSS transitions specification](http://www.w3.org/TR/css3-transitions/) 和 [Web Animations specification](https://w3c.github.io/web-animations/#scaling-using-a-cubic-bezier-curve) 中查找更多关于贝塞尔曲线的内容。
242 |
243 | ## 贝塞尔曲线
244 |
245 | 让我们看一下贝塞尔曲线的运行原理。一条贝塞尔曲线包含四个点,或者准确地说是包含两组数值。每一对数值内包含表示三次贝塞尔曲线控制点的 X 和 Y 坐标。贝塞尔曲线的起点坐标为 (0, 0) ,终点坐标为 (1, 1)。可以设置两组数值对。每个控制点的 X 轴值必须在 [0, 1] 之间,而 Y 轴值可以超过 [0, 1],虽然规范并没有明确允许超过的数值。
246 |
247 |
248 |
249 | 即使每个控制点的 X 和 Y 值的微小差异都会输出完全不同的贝塞尔曲线。
250 |
251 | 查看维基百科关于[贝塞尔曲线](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)的说明,通俗一点讲即,现在所说的即三次贝塞尔曲线,该曲线由四个点组成,P0, P1, P2, P3 组成,那么,P0 和 P1 组成一对,P2 和 P3 组成一对,P1 和 P2 即为控制点,P0 和 P3 即为起始和结束节点。如下图所示:
252 |
253 | 
254 |
255 | 看下两张拥有相近但不同坐标的控制结点的贝塞尔曲线图。
256 |
257 | 
258 |
259 | 和
260 |
261 | 
262 |
263 | 如你所见,两张图有很大不同。第一个控制点向量差为 (0.045, 0.183),而第二个控制点向量差为 (-0.427, -0.054)。
264 |
265 | 第二条曲线的样式为:
266 |
267 | `transition: transform 500ms cubic-bezier(0.465, 0.183, 0.153, 0.946);`
268 |
269 | 第一组数值为起始控制点的 X 和 Y 坐标而第二组数值为第二个控制点的 X 和 Y 坐标。
270 |
271 | ## 性能优化
272 |
273 | 你得维持动画帧数为 60 帧每秒,否则会影响到用户体验。
274 |
275 | 和世界上其它事物一样,动画会有性能开销。一些属性的动画性能开销相比其它属性要小。比如,为元素的 `width` 和 `height` 做动画会更改其几何结构并且可能会造成页面上的其它元素移动或者大小的改变。这一过程被称为布局。之前的[文章](https://github.com/Troland/how-javascript-works/blob/master/rendering.md)中有详细介绍过布局和渲染。
276 |
277 | 总之,应该尽量避免为会引起布局和绘制的属性做动画。对于大多数现代浏览器而言,即把动画局限于 `opacity` 和 `transform` 属性。
278 |
279 | ## Will-change
280 |
281 | 可以使用 [will-change](https://dev.w3.org/csswg/css-will-change/) 来通知浏览器将会更改某个元素的属性。这会允许浏览器当更改某个元素属性的时候,事先进行最合理的优化。但不要滥用 `will-change`,因为这样做会适得其反,使得浏览器浪费更多的资源,从而造成更多的性能问题。
282 |
283 | 为 transforms 和 opacity 添加 `will-change` 代码如下:
284 |
285 | ```
286 | .box {
287 | will-change: transform, opacity;
288 | }
289 | ```
290 |
291 | 该属性在 Chrome, Firefox,Opera 得到很好的兼容。
292 |
293 | 
294 |
295 | ## 如何选择 JavaScript 和 CSS 来执行动画
296 |
297 | 这个问题是无解的。只需谨记以下原则:
298 |
299 | * 基于 CSS 的动画和原生支持的网页动画一般都是由被称为『合成线程』的线程来处理的。这不同于浏览器的主线程,主线程是用来执行计算样式,布局,绘制及 JavaScript 代码的。这即意味着如果浏览器在主线程上运行耗时的任务,不会中断动画的运行。
300 | * 很多时候,也可以由合成线程来处理 `transforms` 和 `opacity` 属性值的更改。
301 | * 如果有任何动画触发绘制,布局或者同时触发两者,『主线程』将不得不来进行处理。事实是基于 CSS 和 JavaScript 的动画和布局或者绘制的性能开销将很有可能会阻塞所有和 CSS 或者 JavaScript 运行相关的工作,致命动画问题变得毫无意义。
302 |
303 | ## 正确使用动画
304 |
305 | 良好的动画为项目添加一层令人愉快和互动的用户体验。你可以随意使用动画,不管是宽度,调试,定位,颜色或背景色,但必须注意潜在的性能瓶颈。糟糕的动画选择会影响用户体验,所以动画必须是高效且合理的。尽可能减少使用动画。只使用动画来让用户体验流畅自然而不是滥用。
306 |
307 | ## 使用动画进行交互
308 |
309 | 不要因为只是为了用而去使用动画。相反,有策略性地使用动画来加强用户交互体验。避免使用不必要的动画来打断或者阻碍用户的使用。
310 |
311 | ## 避免为性能开销大的属性做动画
312 |
313 | 比糟糕的动画使用更糟的是那些会引起页面卡顿的动画。这类动画让用户感到懊丧和不快。
314 |
315 | ## 引用资源
316 |
317 | -
318 | -
319 | -
320 |
321 | ## 疑问
322 |
323 | 可以看下网上的这篇介绍[贝塞尔曲线](http://www.html-js.com/article/1628)的文章,那么可以如何使用贝塞尔曲线来做出令人惊叹的动画呢?
--------------------------------------------------------------------------------
/assets/0_-IMsNws_L1g0Syla.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_-IMsNws_L1g0Syla.png
--------------------------------------------------------------------------------
/assets/0_-OqUfzpRtgDJQjXY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_-OqUfzpRtgDJQjXY.png
--------------------------------------------------------------------------------
/assets/0_1QVfw-_HSz22ZtJS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_1QVfw-_HSz22ZtJS.png
--------------------------------------------------------------------------------
/assets/0_3R6-AyKY1831P10d.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_3R6-AyKY1831P10d.jpeg
--------------------------------------------------------------------------------
/assets/0_3SRCEtv7rMhWpB5s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_3SRCEtv7rMhWpB5s.png
--------------------------------------------------------------------------------
/assets/0_60xiqW7kPsQg5ssn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_60xiqW7kPsQg5ssn.png
--------------------------------------------------------------------------------
/assets/0_66kSmyuCNeD7Oiaq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_66kSmyuCNeD7Oiaq.png
--------------------------------------------------------------------------------
/assets/0_9KPehy4mUb8f-hSp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_9KPehy4mUb8f-hSp.png
--------------------------------------------------------------------------------
/assets/0_A5ucCHOZsxXyHMfN.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_A5ucCHOZsxXyHMfN.jpeg
--------------------------------------------------------------------------------
/assets/0_AsoHzlowoLItnUEL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_AsoHzlowoLItnUEL.png
--------------------------------------------------------------------------------
/assets/0_B4g8Jpu1dZOF1QMI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_B4g8Jpu1dZOF1QMI.png
--------------------------------------------------------------------------------
/assets/0_Ci-Jl1Mlki03m3A-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_Ci-Jl1Mlki03m3A-.png
--------------------------------------------------------------------------------
/assets/0_D586dpehfQtaG2wY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_D586dpehfQtaG2wY.png
--------------------------------------------------------------------------------
/assets/0_EoQo41lZY6_RXAii.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_EoQo41lZY6_RXAii.png
--------------------------------------------------------------------------------
/assets/0_GDU4GguTzk8cSAYk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_GDU4GguTzk8cSAYk.png
--------------------------------------------------------------------------------
/assets/0_HP66Xm7oe9u8Ofk1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_HP66Xm7oe9u8Ofk1.png
--------------------------------------------------------------------------------
/assets/0_IN688nPbgu8zYETe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_IN688nPbgu8zYETe.png
--------------------------------------------------------------------------------
/assets/0_Nm9r_NLcAhJernmo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_Nm9r_NLcAhJernmo.png
--------------------------------------------------------------------------------
/assets/0_PTDs1BkbMgekizit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_PTDs1BkbMgekizit.png
--------------------------------------------------------------------------------
/assets/0_QbI-hozmSVibF6DW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_QbI-hozmSVibF6DW.png
--------------------------------------------------------------------------------
/assets/0_QhRSzkngh6Aty-nD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_QhRSzkngh6Aty-nD.png
--------------------------------------------------------------------------------
/assets/0_QphcOVaiVC2YL7Jd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_QphcOVaiVC2YL7Jd.png
--------------------------------------------------------------------------------
/assets/0_RRgTDdRfLGEhuR7U.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_RRgTDdRfLGEhuR7U.png
--------------------------------------------------------------------------------
/assets/0_SXRTlnVxy2-hE9ZX.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_SXRTlnVxy2-hE9ZX.png
--------------------------------------------------------------------------------
/assets/0_SufKRGfPZIDlw1OG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_SufKRGfPZIDlw1OG.png
--------------------------------------------------------------------------------
/assets/0_VKQINIYfu2O7d7BH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_VKQINIYfu2O7d7BH.png
--------------------------------------------------------------------------------
/assets/0_Vqa1yAeYJio0RF5a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_Vqa1yAeYJio0RF5a.png
--------------------------------------------------------------------------------
/assets/0_W4wHvVAeUmc45vA2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_W4wHvVAeUmc45vA2.png
--------------------------------------------------------------------------------
/assets/0_XZ2U-ztABhWJOSky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_XZ2U-ztABhWJOSky.png
--------------------------------------------------------------------------------
/assets/0_avLiOV_zXLxOgBee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_avLiOV_zXLxOgBee.png
--------------------------------------------------------------------------------
/assets/0_bIg_WMaJvjHWSc5J.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_bIg_WMaJvjHWSc5J.png
--------------------------------------------------------------------------------
/assets/0_bN9YVBLw_tT1Xvte.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_bN9YVBLw_tT1Xvte.png
--------------------------------------------------------------------------------
/assets/0_c2HpOiUYMimMXHv2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_c2HpOiUYMimMXHv2.png
--------------------------------------------------------------------------------
/assets/0_eEArxn147Ev8xf5n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_eEArxn147Ev8xf5n.png
--------------------------------------------------------------------------------
/assets/0_g6rpKt5YynDCQ5n3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_g6rpKt5YynDCQ5n3.png
--------------------------------------------------------------------------------
/assets/0_hZbijxS0vXu8vUmz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_hZbijxS0vXu8vUmz.png
--------------------------------------------------------------------------------
/assets/0_hxC_NUPNycUBhj-L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_hxC_NUPNycUBhj-L.png
--------------------------------------------------------------------------------
/assets/0_j3zkSjnrL4fnCK3A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_j3zkSjnrL4fnCK3A.png
--------------------------------------------------------------------------------
/assets/0_jJuHfRMipW8PPcb0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_jJuHfRMipW8PPcb0.png
--------------------------------------------------------------------------------
/assets/0_k0vSOmvdDkRJzcpW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_k0vSOmvdDkRJzcpW.png
--------------------------------------------------------------------------------
/assets/0_kGDQYE70_z58D7na.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_kGDQYE70_z58D7na.png
--------------------------------------------------------------------------------
/assets/0_l-QdpBfjwNfPDTyh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_l-QdpBfjwNfPDTyh.png
--------------------------------------------------------------------------------
/assets/0_mSOIiWpkctkD0Gfg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_mSOIiWpkctkD0Gfg.png
--------------------------------------------------------------------------------
/assets/0_ndU4N8xQF6QEQmSY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_ndU4N8xQF6QEQmSY.png
--------------------------------------------------------------------------------
/assets/0_nlOmrsfy-Y1XoR8B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_nlOmrsfy-Y1XoR8B.png
--------------------------------------------------------------------------------
/assets/0_oWsMRoltriNxmRNe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_oWsMRoltriNxmRNe.png
--------------------------------------------------------------------------------
/assets/0_oqlhISJ0TrPjoG74.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_oqlhISJ0TrPjoG74.png
--------------------------------------------------------------------------------
/assets/0_pohqKvj9psTPRlOv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_pohqKvj9psTPRlOv.png
--------------------------------------------------------------------------------
/assets/0_qelaH45ZWG3UybLO.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_qelaH45ZWG3UybLO.jpg
--------------------------------------------------------------------------------
/assets/0_v56OyrPtg_cZzzaZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_v56OyrPtg_cZzzaZ.png
--------------------------------------------------------------------------------
/assets/0_wzuQ9LYv7CAUICOC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_wzuQ9LYv7CAUICOC.png
--------------------------------------------------------------------------------
/assets/0_z-A-JIe5OWFtgyd2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/0_z-A-JIe5OWFtgyd2.png
--------------------------------------------------------------------------------
/assets/1_-vHNuJsJVXvqq5dLHPt7cQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_-vHNuJsJVXvqq5dLHPt7cQ.png
--------------------------------------------------------------------------------
/assets/1_0B-dAUOH7NrcCDP6GhKHQw.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_0B-dAUOH7NrcCDP6GhKHQw.jpeg
--------------------------------------------------------------------------------
/assets/1_0REL14sYPR34hY7yua6-PA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_0REL14sYPR34hY7yua6-PA.png
--------------------------------------------------------------------------------
/assets/1_0e8X3UTBpsiBSZKa3l1hXA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_0e8X3UTBpsiBSZKa3l1hXA.png
--------------------------------------------------------------------------------
/assets/1_1Mc_9bnkR74BnACCbP1Q5A.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_1Mc_9bnkR74BnACCbP1Q5A.jpeg
--------------------------------------------------------------------------------
/assets/1_1NAeDnEv6DWFewX_C-L8mg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_1NAeDnEv6DWFewX_C-L8mg.png
--------------------------------------------------------------------------------
/assets/1_2EduxBOtJ7ENgIna-aU4WA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_2EduxBOtJ7ENgIna-aU4WA.png
--------------------------------------------------------------------------------
/assets/1_2v7G1ZJ1C-y_mWHOYQfQKQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_2v7G1ZJ1C-y_mWHOYQfQKQ.png
--------------------------------------------------------------------------------
/assets/1_48tGIboHxgLeKEjMTGkUGg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_48tGIboHxgLeKEjMTGkUGg.png
--------------------------------------------------------------------------------
/assets/1_4lHHyfEhVB0LnQ3HlhSs8g.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_4lHHyfEhVB0LnQ3HlhSs8g.png
--------------------------------------------------------------------------------
/assets/1_5YU1su2mdzHEQ5iDisKUyw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_5YU1su2mdzHEQ5iDisKUyw.png
--------------------------------------------------------------------------------
/assets/1_5aBou4onl1B8xlgwoGTDOg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_5aBou4onl1B8xlgwoGTDOg.png
--------------------------------------------------------------------------------
/assets/1_6o2TRDmrJlS97vh1wEjLYw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_6o2TRDmrJlS97vh1wEjLYw.png
--------------------------------------------------------------------------------
/assets/1_81mCsOzyJj-HfQ1lP_033w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_81mCsOzyJj-HfQ1lP_033w.png
--------------------------------------------------------------------------------
/assets/1_9b1uEMcZLWuGPuYcIn7ZXQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_9b1uEMcZLWuGPuYcIn7ZXQ.png
--------------------------------------------------------------------------------
/assets/1_9fbOuFXJHwhqa6ToCc_v2A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_9fbOuFXJHwhqa6ToCc_v2A.png
--------------------------------------------------------------------------------
/assets/1_9ryMUEZhtbTg7lECHVz0fw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_9ryMUEZhtbTg7lECHVz0fw.png
--------------------------------------------------------------------------------
/assets/1_AKKvE3QmN_ZQmEzSj16oXg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_AKKvE3QmN_ZQmEzSj16oXg.png
--------------------------------------------------------------------------------
/assets/1_AycFMDy9tlDmNoc5LXd9-g.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_AycFMDy9tlDmNoc5LXd9-g.png
--------------------------------------------------------------------------------
/assets/1_C1VWSKOx89vqdiSiflDRJw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_C1VWSKOx89vqdiSiflDRJw.png
--------------------------------------------------------------------------------
/assets/1_FbbOG9mcqWZtNajjDO6SaA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_FbbOG9mcqWZtNajjDO6SaA.png
--------------------------------------------------------------------------------
/assets/1_GF3p99CQPZkX3UkgyVKSHw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_GF3p99CQPZkX3UkgyVKSHw.png
--------------------------------------------------------------------------------
/assets/1_HIn-BxIP38X6mF_65snMKg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_HIn-BxIP38X6mF_65snMKg.png
--------------------------------------------------------------------------------
/assets/1_Jyyot22aRkKMF3LN1bgE-w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_Jyyot22aRkKMF3LN1bgE-w.png
--------------------------------------------------------------------------------
/assets/1_KGAppeqpUwv8OgPKkT0Ujw.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_KGAppeqpUwv8OgPKkT0Ujw.jpeg
--------------------------------------------------------------------------------
/assets/1_KGBiAxjeD9JT2j6KDo0zUg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_KGBiAxjeD9JT2j6KDo0zUg.png
--------------------------------------------------------------------------------
/assets/1_M5htfOGgza04ISv_l-69zg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_M5htfOGgza04ISv_l-69zg.png
--------------------------------------------------------------------------------
/assets/1_McIb2yWGRG6PoDUSavp86g.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_McIb2yWGRG6PoDUSavp86g.jpeg
--------------------------------------------------------------------------------
/assets/1_NVT6WbNrH_mQL64--b-l1Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_NVT6WbNrH_mQL64--b-l1Q.png
--------------------------------------------------------------------------------
/assets/1_ONNxJHqmMTXB1Nuq3qTNXQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_ONNxJHqmMTXB1Nuq3qTNXQ.png
--------------------------------------------------------------------------------
/assets/1_OnH_DlbNAPvB9KLxUCyMsA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_OnH_DlbNAPvB9KLxUCyMsA.png
--------------------------------------------------------------------------------
/assets/1_OsLmWRogydrW0gG1YlYklQ.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_OsLmWRogydrW0gG1YlYklQ.jpeg
--------------------------------------------------------------------------------
/assets/1_P5nzyldL4rg36dZmt2RViQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_P5nzyldL4rg36dZmt2RViQ.png
--------------------------------------------------------------------------------
/assets/1_PgclyCPqxWc1rENfAOesag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_PgclyCPqxWc1rENfAOesag.png
--------------------------------------------------------------------------------
/assets/1_Qm9OFPq3siW0tCKfa03DqQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_Qm9OFPq3siW0tCKfa03DqQ.png
--------------------------------------------------------------------------------
/assets/1_QsVUE3snZD9abYXccg6Sgw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_QsVUE3snZD9abYXccg6Sgw.png
--------------------------------------------------------------------------------
/assets/1_SEZ_0CFc4zoHp_g7A0KKNw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_SEZ_0CFc4zoHp_g7A0KKNw.png
--------------------------------------------------------------------------------
/assets/1_Sz4wI2ukt91vRrgf8UonWw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_Sz4wI2ukt91vRrgf8UonWw.png
--------------------------------------------------------------------------------
/assets/1_T-W_ihvl-9rG4dn18kP3Qw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_T-W_ihvl-9rG4dn18kP3Qw.png
--------------------------------------------------------------------------------
/assets/1_TozSrkk92l8ho6d8JxqF_w.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_TozSrkk92l8ho6d8JxqF_w.gif
--------------------------------------------------------------------------------
/assets/1_UwtM7DmK1BmlBOUUYEopGQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_UwtM7DmK1BmlBOUUYEopGQ.png
--------------------------------------------------------------------------------
/assets/1_UxkSstuyCvmKkBTnjbezNw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_UxkSstuyCvmKkBTnjbezNw.png
--------------------------------------------------------------------------------
/assets/1_VDWQl67cmbyAFC5xL9Og4g.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_VDWQl67cmbyAFC5xL9Og4g.png
--------------------------------------------------------------------------------
/assets/1_WHR_08AD8APDITQ-4CFDgg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_WHR_08AD8APDITQ-4CFDgg.png
--------------------------------------------------------------------------------
/assets/1_WVtok3BV0NgU95mpxk9CNg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_WVtok3BV0NgU95mpxk9CNg.gif
--------------------------------------------------------------------------------
/assets/1_WlMXK3rs_scqKTRV41au7g.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_WlMXK3rs_scqKTRV41au7g.jpeg
--------------------------------------------------------------------------------
/assets/1_WqInzMPQGGcMX9AOONN76g.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_WqInzMPQGGcMX9AOONN76g.jpeg
--------------------------------------------------------------------------------
/assets/1_YFr59cEF2qxzjjleebvbcQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_YFr59cEF2qxzjjleebvbcQ.png
--------------------------------------------------------------------------------
/assets/1_Yp1KOt_UJ47HChmS9y7KXw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_Yp1KOt_UJ47HChmS9y7KXw.png
--------------------------------------------------------------------------------
/assets/1_Zf4reZZJ9DCKsXf5CSXghg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_Zf4reZZJ9DCKsXf5CSXghg.png
--------------------------------------------------------------------------------
/assets/1__LdkAVTcSDkoIUbqanj2gg.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1__LdkAVTcSDkoIUbqanj2gg.jpeg
--------------------------------------------------------------------------------
/assets/1__nYLhoZPKD_HPhpJtQeErA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1__nYLhoZPKD_HPhpJtQeErA.png
--------------------------------------------------------------------------------
/assets/1_a4lA5FYDkjA9mv53NPKtOg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_a4lA5FYDkjA9mv53NPKtOg.png
--------------------------------------------------------------------------------
/assets/1_dvrghQCVQIZOfNC27Jrtlw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_dvrghQCVQIZOfNC27Jrtlw.png
--------------------------------------------------------------------------------
/assets/1_e0nEd59RPKz9coyY8FX-uw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_e0nEd59RPKz9coyY8FX-uw.png
--------------------------------------------------------------------------------
/assets/1_eOj6NVwGI2N78onh6CuCbA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_eOj6NVwGI2N78onh6CuCbA.png
--------------------------------------------------------------------------------
/assets/1_eyaMLcORDVsCFIf5h_ygjA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_eyaMLcORDVsCFIf5h_ygjA.png
--------------------------------------------------------------------------------
/assets/1_ezFoXqgf91umls9FqO0HsQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_ezFoXqgf91umls9FqO0HsQ.png
--------------------------------------------------------------------------------
/assets/1_hTMGxzZrOmxxIfaQU7nKig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_hTMGxzZrOmxxIfaQU7nKig.png
--------------------------------------------------------------------------------
/assets/1_hpyVeL1zsaeHaqS7mU4Qfw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_hpyVeL1zsaeHaqS7mU4Qfw.png
--------------------------------------------------------------------------------
/assets/1_iBedryNbqtixYTKviPC1tA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_iBedryNbqtixYTKviPC1tA.png
--------------------------------------------------------------------------------
/assets/1_iHfI6MQ-YKQvWvo51J-P0w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_iHfI6MQ-YKQvWvo51J-P0w.png
--------------------------------------------------------------------------------
/assets/1_jQMQ9BEKPycs2wFC233aNg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_jQMQ9BEKPycs2wFC233aNg.png
--------------------------------------------------------------------------------
/assets/1_lMBu87MtEsVFqqbfMum-kA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_lMBu87MtEsVFqqbfMum-kA.png
--------------------------------------------------------------------------------
/assets/1_lvOtCg75ObmUTOxIS6anEQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_lvOtCg75ObmUTOxIS6anEQ.png
--------------------------------------------------------------------------------
/assets/1_lzOIevUBVy5eWyf2kHf--w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_lzOIevUBVy5eWyf2kHf--w.png
--------------------------------------------------------------------------------
/assets/1_mVOrpKC9pFTMg4EXPozoog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_mVOrpKC9pFTMg4EXPozoog.png
--------------------------------------------------------------------------------
/assets/1_oOcY2Gn-LVt1h-e9xOv5oA.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_oOcY2Gn-LVt1h-e9xOv5oA.jpeg
--------------------------------------------------------------------------------
/assets/1_pSh7IORJoUXbwCjyJ7fM9A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_pSh7IORJoUXbwCjyJ7fM9A.png
--------------------------------------------------------------------------------
/assets/1_pVnIrMZiB9iAz5sW28AixA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_pVnIrMZiB9iAz5sW28AixA.png
--------------------------------------------------------------------------------
/assets/1_qY-yRQWGI-DLS3zRHYHm9A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_qY-yRQWGI-DLS3zRHYHm9A.png
--------------------------------------------------------------------------------
/assets/1_rWh8YlBn8SypiMduLiYDhA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_rWh8YlBn8SypiMduLiYDhA.png
--------------------------------------------------------------------------------
/assets/1_rjBdCBwOx5Gp_A6b6FQgfw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_rjBdCBwOx5Gp_A6b6FQgfw.png
--------------------------------------------------------------------------------
/assets/1_slxXgq_TO38TgtoKpWa_jQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_slxXgq_TO38TgtoKpWa_jQ.png
--------------------------------------------------------------------------------
/assets/1_spJ8v7GWivxZZzTAzqVPtA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_spJ8v7GWivxZZzTAzqVPtA.png
--------------------------------------------------------------------------------
/assets/1_t2Btfb_tBbBxTvyVgKX0Qg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_t2Btfb_tBbBxTvyVgKX0Qg.png
--------------------------------------------------------------------------------
/assets/1_tGXhNroe8KxGN7r4UTVSHw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_tGXhNroe8KxGN7r4UTVSHw.png
--------------------------------------------------------------------------------
/assets/1_tLntRcKj-gIVaCcSusDpLQ.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_tLntRcKj-gIVaCcSusDpLQ.jpeg
--------------------------------------------------------------------------------
/assets/1_vd3X2O_qRfqaEpW4AfZM4w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_vd3X2O_qRfqaEpW4AfZM4w.png
--------------------------------------------------------------------------------
/assets/1_x8P3OcgcgKrEEDpgT2IKkQ.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_x8P3OcgcgKrEEDpgT2IKkQ.jpeg
--------------------------------------------------------------------------------
/assets/1_xhB8ceUNM6vb8s0ZQKMHNg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_xhB8ceUNM6vb8s0ZQKMHNg.png
--------------------------------------------------------------------------------
/assets/1_ya4zMDfbNUflXhzKz9EBIw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_ya4zMDfbNUflXhzKz9EBIw.png
--------------------------------------------------------------------------------
/assets/1_yn9Y4PXNP8XTz6mtCAzDZQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/1_yn9Y4PXNP8XTz6mtCAzDZQ.png
--------------------------------------------------------------------------------
/assets/25_cover.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/25_cover.jpeg
--------------------------------------------------------------------------------
/assets/26_cover.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/26_cover.jpeg
--------------------------------------------------------------------------------
/assets/26_shapes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/26_shapes.png
--------------------------------------------------------------------------------
/assets/27_cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/27_cover.png
--------------------------------------------------------------------------------
/assets/28_cover.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/28_cover.jpeg
--------------------------------------------------------------------------------
/assets/29_cover.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Troland/how-javascript-works/a01d67580d007e07844e2564a541639a169a8af0/assets/29_cover.jpeg
--------------------------------------------------------------------------------
/ast.md:
--------------------------------------------------------------------------------
1 | # 解析,抽象语法树及最小化解析时间的 5 条小技巧
2 |
3 | > 原文请查阅[这里](https://blog.sessionstack.com/how-javascript-works-parsing-abstract-syntax-trees-asts-5-tips-on-how-to-minimize-parse-time-abfcf7e8a0c8),本文采用[知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/)共享,BY [Troland](https://github.com/Troland)。
4 |
5 | **这是 JavaScript 工作原理的第十四章。**
6 |
7 | ## 概述
8 |
9 | 我们都知道运行一大段 JavaScript 代码性能会变得很糟糕。代码不仅仅需要在网络中传输而且还需要解析,编译为字节码,最后运行。之前的文章讨论了诸如 JS 引擎,运行时及调用栈,还有为 Google Chrome 和 NodeJS 广泛使用的 V8 引擎的话题。它们都在整个 JavaScript 的运行过程中扮演着重要的角色。
10 |
11 | 今天所讲的主题也非常重要:了解到大多数的 JavaScript 引擎是如何把文本解析为机器能够理解的代码,转换之后发生的事情以及开发者如何利用这一知识。
12 |
13 | ## 编程语言原理
14 |
15 | 那么,首先让我们回顾一下编程语言原理。无论使用何种编程语言,你经常需要一些软件来处理源码以便让计算机按照代码实际去做些事情。该软件可以是解释器或编译器。不管是使用解释型语言(JavaScript, Python, Ruby) 或者编译型语言(C#, Java, Rust),它们都有一个共同点:把源码作为纯文本解析为抽象语法树(AST)的数据结构。AST 不仅要以结构化地方式展示源码,而且在语义分析中扮演了重要的角色,编译器检查验证程序和语言元素的语法使用是否正确及适当。之后, 使用 AST 来生成实际的字节码或者机器码。
16 |
17 | ## AST 程序
18 |
19 | AST 不止应用于语言解释器和编译器,在计算机世界中,还有其它用途。最为常见的用途之一即静态代码分析。静态代码分析并不会运行输入的代码。但是,它们仍然需要理解代码的结构。比如,实现一个工具来找出常见的代码结构以便用来代码重构减少重复代码。或许你可以使用字符串比较来实现,但是工具会相当简单且有局限性。当然了,如果你有兴趣实现这样的工具,你不必自己动手去编写解析器,有许多完美兼容于 Ecmascript 规范的开源项目。Esprima 和 Acorn 即是黄金搭档。还有其它工具可以用来帮助解析器输出代码,即 ASTs.ASTs 被广泛应用于代码转换。举个栗子,你可能想实现一个转换器用来转换 Python 代码为 JavaScript.大致的思路即使用 Python 代码转换器来生成 AST,然后使用该 AST 来生成 JavaScript 代码。你可能会觉得难以置信。事实是 ASTs 只是部分语言的不同表示法。在解析之前,它表现为文本,该文本遵守着构成语言的一些语法规则。解析之后,它表现为一种树状结构,该结构所包含的信息和输入文本几乎一样。因此,也可以进行反向解析然后回到文本。
20 |
21 | ## JavaScript 解析
22 |
23 | 让我们看一下 AST 的构造。以如下一个简单 JavaScript 函数为例子:
24 |
25 | ```
26 | function foo(x) {
27 | if (x > 10) {
28 | var a = 2;
29 | return a * x;
30 | }
31 |
32 | return x + 10;
33 | }
34 | ```
35 |
36 | 解析器会产生如下的 AST。
37 |
38 | 
39 |
40 | 请注意,这里为了展示用只是解析器输出的简化版本。实际的 AST 要更加复杂。然而,这里的意思即了解一下运行源码之前的第一个步骤。可以访问 [AST Explorer](https://astexplorer.net/) 来查看实际的 AST 树。这是一个在线工具,你可以在上面写 JavaScript 代码,然后网站会输出目标代码的 AST。
41 |
42 | 也许你会问为什么我得学习 JavaScript 解析器的工作原理。反正,浏览器会负责运行 JavaScript 代码。你有那么一丁点是正确的。以下图表展示了 JavaScript 运行过程中不同阶段的耗时。瞪大眼睛瞅瞅,也许你可以发现点有趣的东西。
43 |
44 | 
45 |
46 | 发现没?通常情况下,浏览器大概消耗了 15% 到 20% 的总运行时间来解析 JavaScript.我没有具体统计过这些数值。这些统计数据来自于现实世界中程序和网站的各种 JavaScript 使用方式。 现在也许 15% 看起来不是很多,但相信我,很多的。一个典型的单页程序会加载大约 0.4M 的 JavaScript 代码,然后消耗掉浏览器大概 370ms 的时间来进行解析。也许你会又说,这也不是很多嘛。本身花费的时间并不多。但记住了,这只是把 JavaScript 代码转化为 ASTs 所消耗的时间。其中不包含运行本身的时间或者页面加载期间其它诸如 [CSS 和 HTML](https://blog.sessionstack.com/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance-7b95553baeda) 渲染的过程的耗时。这仅仅只是桌面浏览器所面临的问题。移动浏览器的情况会更加复杂。一般情况下,手机移动浏览器解析代码的时间是桌面浏览器的 2-5 倍。
47 |
48 | 
49 |
50 | 以上图表展示了不同移动和桌面浏览器解析 1MB JavaScript 代码所消耗的时间。
51 |
52 | 另外,为了获得更多类原生的用户体验而把越来越多的业务逻辑堆积在前端,网页程序变得越来越复杂。网页程序越来越胖,都快走不动了。你可以轻易地想到网络应用受到的性能影响。只需打开浏览器开发者工具,然后使用该工具来检测解析,编译及其它发生于浏览器中直到页面完全加载所消耗的时间。
53 |
54 | 
55 |
56 | 不幸的是,移动浏览器没有开发者工具来进行性能检测。不用担心。因为有 [DeviceTiming](https://github.com/danielmendel/DeviceTiming) 工具。它可以用来帮助检测受控环境中脚本的解析和运行时间。它通过植入代码来封装本地代码,这样每当从不同设备访问的时候,可以本地测量解析和运行时间。
57 |
58 | 好事即 JavaScript 引擎做了大量的工作来避免冗余工作及更加高效。以下为主流浏览器使用的技术。
59 |
60 | 例如,V8 实现了 script 流和代码缓存技术。Script 流即当脚本开始下载的时候,async 和 deferred 的脚本在单独的线程中进行解析。这意味着解析会在脚本下载完成时立即完成。这会提升 10% 的页面加载速度。
61 |
62 | 每当访问页面的时候,JavaScript 代码通常会被编译为字节码。但是,当用户访问另一个页面的时候,该字节码会作废。这是因为编译的代码严重依赖于编译阶段机器的状态和上下文。从 Chrome 42 开始带来了字节码缓存。该技术会本地缓存编译过的代码,这样当用户返回到同一页面的时候,诸如下载,解析和编译等所有步骤都会被跳过。这样就会为 Chrome 节约大概 40% 的代码解析和编译时间。另外,这同样会节省手机电量。
63 |
64 | Opera 中,[Carakan](https://dev.opera.com/blog/carakan/) 引擎可以复用另一个程序最近编译过的输出。不要求代码在同一页面或是相同域名下。该缓存技术非常高效且可以完全跳过编译步骤。它依赖于典型的用户行为和浏览场景:每当用户在程序/网站上遵循某个用户浏览习惯,则会加载相同的 JavaScript 代码。然而,Carakan 早就被谷歌 V8 引擎所取代。
65 |
66 | Firefox 使用的 [SpiderMonkey](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey) 引擎没有使用任何的缓存技术。它可以过渡到监视阶段,在那里记录脚本运行次数。基于此计算,它推导出频繁使用而可以被优化的代码部分。
67 |
68 | 很明显地,一些人选择不做任何处理。Safari 首席开发者 [Maciej Stachowiak](http://en.wikipedia.org/wiki/Maciej_Stachowiak) 指出 Safari 不缓存编译的字节码。他们可能已经想到了缓存技术但并没付诸实施,因为生成代码的耗时小于总运行时间的 2%。
69 |
70 | 这些优化措施没有直接影响 JavaScript 源码的解析时间,但是会尽可能完全避免。毕竟了胜于无。
71 |
72 | 有许多方法可以用来减少程序的初始化加载时间。最小化加载的 JavaScript 数量:代码越少,解析耗时越少,运行时间越少。为了达到此目的,可以用特殊的方法加载必需的代码而不是一股劳地加载一大坨代码。比如,[PRPL](https://developers.google.com/web/fundamentals/performance/prpl-pattern/) 模式即表示该种代码传输类型。或者,可以检查依赖然后查看是否有无用、冗余的依赖导致代码库的膨胀。然而,这些东西需要很大的篇幅来进行讨论。
73 |
74 | 本文的目标即开发者如何帮助加快 JavaScript 解析器的解析速度。现代 JavaScript 解析器使用 heuristics(启发法) 来决定是否立即运行指定的代码片段或者推迟在未来的某个时候运行。基于这些 heuristics,解析器会进行立即或者懒解析。立即解析会运行需要立即编译的函数。其主要做三件事:构建 AST,构建作用域层级,然后检查所有的语法错误。而懒解析只用于未编译的函数,它不构建 AST和检查任何语法错误。只构建作用域层级,这样相对于立即解析会节省大约一半的时间。
75 |
76 | **React即是使用的启发式来进行 AST diff,从而获得性能的提升。**
77 |
78 | 显然,这并不是一个新概念。甚至像 IE9 这样老掉牙的浏览器也支持该优化技术,虽然和现代解析器的工作方式相比是以一种简陋的方式实现的。
79 |
80 | 举个栗子吧。假设有如下代码片段:
81 |
82 | ```
83 | function foo() {
84 | function bar(x) {
85 | return x + 10;
86 | }
87 |
88 | function baz(x, y) {
89 | return x + y;
90 | }
91 |
92 | console.log(baz(100, 200));
93 | }
94 | ```
95 |
96 | 和之前代码类似,把代码输入解析器进行语法分析然后输出 AST。这样表述如下:
97 |
98 | 声明 bar 函数接收 x 参数。有一个返回语句。函数返回 x 和 10 相加的结果。
99 |
100 |
101 |
102 | 声明 baz 函数接收两个参数(x 和 y)。有一个返回语句。函数函数 x 和 y 相加结果。
103 |
104 |
105 |
106 | 调用 bar 函数传入 40 和 2。
107 |
108 |
109 |
110 | 调用 console.log 参数为之前函数调用的返回值。
111 |
112 | 
113 |
114 | 那么期间发生了什么呢?解析器发现了 foo 函数声明, baz 函数声明,调用 baz 函数及 console.log 函数。然而,解析器做了完全不相关的额外无用功即解析 bar 函数。为何不相关?因为函数 bar 从未被调用(或者至少不是在对应时间点上)。这只是一个简单示例及可能有些不同寻常,但是在现实生活的许多程序中,许多函数声明从未被调用过。
115 |
116 | 这里不解析 bar 函数,该函数声明了却没有指出其用途。只在需要的时候在函数运行前进行真正的解析。懒解析仍然只需要找出整个函数体然后为其声明。它不需要语法树因其将不会被处理。另外,它不从内存堆中获得内存,而这通常会消耗相当一部分系统资源。简而言之,跳过这些步骤可以有巨大的性能提升。
117 |
118 | 所以之前的例子,解析器实际上会像如下这样解析:
119 |
120 | 
121 |
122 | 注意到这里仅仅只是确认函数 bar 声明。没有进入 bar 函数体。当前情况下,函数体只有一句简单的返回语句。然而,正如现代世界中的大多数程序那样,函数体可能会更加庞大,包含多个返回语句,条件语句,循环,变量声明甚至嵌套函数声明。由于函数从未被调用,这完全是在浪费时间和系统资源。
123 |
124 | 实际上这是一个相当简单的概念,然而其实现是非常难的。不局限于以上示例。整个方法还可以应用于函数,循环,条件语句,对象等等。一般情况下,所有代码都需要解析。
125 |
126 | 例如,以下是一个实现 JavaScript 模块的相当常见的模式。
127 |
128 | ```
129 | var myModule = (function() {
130 | // 整个模块的逻辑
131 | // 返回模块对象
132 | })();
133 | ```
134 |
135 | 该模式可以被大多数现代 JavaScript 解析器识别且标识里面的代码需要立即解析。
136 |
137 | 那么为何解析器不都使用懒解析呢?如果懒解析一些代码,而该代码必须立即运行,这样实际上会降低代码运行速度。需要运行一次懒解析之后进行另一个立即解析。和立即解析相比,运行速度会降低 50%。
138 |
139 | 现在,对解析器底层原理有了大致的理解,是时候考虑如何帮助提高解析器的解析速度了。可以以这样的方式编写代码,这样就可以在正确的时间解析函数。这里有一个为大多数解析器所识别的模式:使用括号封装函数。这样会告诉解析器需要立即函数。如果解析器看到一个左括号且之后为函数声明,它会立即解析该函数。可以通过显式声明立即运行函数来帮助解析器加快解析速度。
140 |
141 | 假设有一个 foo 函数
142 |
143 | ```
144 | function foo(x) {
145 | return x * 10;
146 | }
147 | ```
148 |
149 | 因为没有明显地标识表明需要立即运行该函数所以浏览器会进行懒解析。然而,我们确定这是不对的,那么可以运行两个步骤。
150 |
151 | 首先,把函数存储为一变量。
152 |
153 | ```
154 | var foo = function foo(x) {
155 | return x * 10;
156 | };
157 | ```
158 |
159 | 注意,在 function 关键字和函数参数的左括号之间的函数名。这并不是必要的,但推荐这样做,因为当抛出异常错误的时候,堆栈追踪会包含实际的函数名而不是 。
160 |
161 | 解析器仍然会做懒解析。可以做一个微小的改动来解决这一问题:用括号封装函数。
162 |
163 | ```
164 | var foo = (function foo(x) {
165 | return x * 10;
166 | });
167 | ```
168 |
169 | 现在,解析器看见 function 关键字前的左括号便会立即进行解析。
170 |
171 | 因需要知道解析器在何种情况下懒解析或者立即解析代码,进行手动操作会相当困难。同样地,开发者需要花时间考虑指定的函数是否需要立即解析。肯定没人想费力地这么做。最后同样重要的是,这肯定会让代码难以阅读和理解。可以使用 Optimize.js 来处理此类情况。该工具只是用来优化 JavaScript 源代码的初始加载时间。他们对代码运行静态分析,然后通过使用括号封装需要立即运行的函数以便浏览器立即解析并准备运行它们。
172 |
173 | 那么,可以如平常杂编码然后一小段代码如下:
174 |
175 | ```
176 | (function() {
177 | console.log('Hello, World!');
178 | })();
179 | ```
180 |
181 | 一切看起来很美好,因为在函数声明前添加了左括号。当然,在进入生产环境之前需要进行代码压缩。以下为压缩工具的输出:
182 |
183 | ```
184 | !function(){console.log('Hello, World!')}();
185 | ```
186 |
187 | 看起来一切正常。代码如期运行。然而好像少了什么。压缩工具移除了封装函数的括号代之以一个感叹号。这意味着解析器会跳过该代码且将会运行懒解析。总之,为了运行该函数,解析器会在懒解析之后进行立即解析。这会导致代码运行变慢。幸运的是,可以利用 Optimize.js 来解决此类问题。传给 Optimize.js 压缩过的代码会输出如下代码:
188 |
189 | ```
190 | !(function(){console.log('Hello, World!')})();
191 | ```
192 |
193 | 现在,充分利用了各自的优势:压缩代码且解析器正确地识别懒解析和立即解析的函数。
194 |
195 | ## 预编译
196 |
197 | 但是为何不在服务端进行这些工作呢?总之,比强制各个客户端重复做该项事情更好的做法是只在服务端运行一次并在客户端输出结果。那么,有一个正在进行的讨论即引擎是否需要提供一个运行预编译代码的功能以节省浏览器的运行时间。本质上,该思路即使用服务端工具来生成字节码,这样就只需要传输字节码并在客户端运行。之后,将会看到启动时间上的一些主要差异。这听起来很有诱惑性但实现起来会很难。可能会有反效果,因为它将会很庞大且由于安全原因很有可能需要进行签名和处理。例如,V8 团队已经在内部解决重复解析问题,这样预编译有可能实际上没啥鸟用。
198 |
199 | ## 一些提升网络应用速度的建议
200 |
201 | * 检查依赖。减少不必要的依赖。
202 | * 分割代码为更小的块而不是一整块。如 webpack 的 code-spliting 功能。
203 | * 尽可能延迟加载 JavaScript 代码。可以只加载当前路由所要求的代码片段。比如只在点击某个元素的时候引入 某段代码模块。
204 | * 使用开发者工具和 DeviceTiming 来检测性能瓶颈。
205 | * 使用像 Optimize.js 的工具来帮助解析器选择立即解析或者懒解析以加快解析速度。
206 |
207 | ## 拓展
208 |
209 | 有时候,特别是手机端浏览器,比如当你点击前进/后退按钮的时候,浏览器会进行缓存。但是在有些场景下,你可能不需要浏览器的这种功能。有如下解决办法:
210 |
211 | ```
212 | window.addEventListener('pageshow', (event) => {
213 | // 检查前进/后退缓存,是否从缓存加载页面
214 | if (event.persisted || window.performance &&
215 | window.performance.navigation.type === 2) {
216 | // 进行相应的逻辑处理
217 | }
218 | };
219 | ```
--------------------------------------------------------------------------------
/csrf.md:
--------------------------------------------------------------------------------
1 | # 跨站请求伪造攻击及7种缓解策略
2 |
3 | > 原文请查阅[这里](https://blog.sessionstack.com/how-javascript-works-csrf-attacks-7-mitigation-strategies-757dfb08e7a6),本文采用[知识共享署名 4.0 国际许可协议](http://creativecommons.org/licenses/by/4.0/)共享,BY [Troland](https://github.com/Troland)。
4 |
5 | **这是 JavaScript 工作原理第二十二章。**
6 |
7 | ## 概述
8 |
9 | 跨站请求伪造(CSRF:Cross-site request forgery, 发音为“sea-surf”),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是 Web 应用程序或网页的一种恶意攻击方法。在这类型的攻击中,攻击者伪装成受害者执行恶意请求。
10 |
11 | 恶意 Web 程序可以通过多种方式发起请求,例如特制图像标签,隐藏表单,AJAX 请求等。它们可以在用户不参与甚至不知情的情况下运行。
12 |
13 | 与[跨站脚本](https://blog.sessionstack.com/how-javascript-works-5-types-of-xss-attacks-tips-on-preventing-them-e6e28327748a)(XSS)相比,XSS 利用的是用户对特定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
14 |
15 | ## 探索跨站请求伪造攻击
16 |
17 | 当 CSRF 攻击工作时,受害者会提交他们不知情的恶意请求。这可能会导致 Web 应用程序执行一些操作,包括客户端或服务器数据泄漏,会话状态的更改和操纵终端用户帐户等。
18 |
19 | CSRF 攻击是针对浏览器的责任混淆攻击的一个例子,因为低权限的攻击者欺骗了浏览器从而提交伪造的请求。
20 |
21 | CSRF 通常具有以下特征:
22 |
23 | * 涉及依赖用户认证体系的网站或 Web 应用。
24 | * 利用网站对该认证的信任。
25 | * 欺骗用户浏览器向目标站点发送 HTTP 请求。
26 | * 涉及存在副作用的 HTTP 请求。
27 |
28 | 跨站请求伪造攻击步骤如下:
29 | * 受害者执行由攻击者控制的动作,例如访问网页,点击链接等。
30 | * 此操作伪装成受害者的身份发送 HTTP 请求到 Web 应用。
31 | * 如果受害者在 Web 应用上具有通过身份验证的活动会话,则该请求将作为合法请求进行处理。
32 |
33 | 重要的是,受害者与网络应用间必须保持有效会话,而该会话就是 CSRF 攻击的目标。
34 |
35 | 大多数情况下,CSRF 攻击并不会窃取私人信息,而是触发与受害者帐户相关的某种形式上的变更,例如修改凭据甚至进行购物行为。强迫受害者从服务器获取数据不会对攻击者产生受益,因为攻击方没有接收到服务端的响应。但受害人可以,正因如此,CSRF攻击的目标主要锁定在执行更改的请求上。
36 |
37 | Web 应用程序中的会话管理通常是基于 cookies。每次向服务器发送请求时,浏览器都为请求携带上相关 cookie,用于标识当前用户的会话。即使请求源来自其他 Web 应用和域,通常也会发生这种情况。这正是攻击者利用的漏洞。
38 |
39 | 尽管我们通常将 CSRF 描述为基于 cookie 会话处理相关,它也出现在应用程序自动向请求添加一些用户凭据等其他情况下,例如 HTTP [基本认证](https://en.wikipedia.org/wiki/Basic_access_authentication)和基于证书的认证。
40 |
41 | ### 例子
42 |
43 | 让我们看下面的示例,为社交网络应用上的一个简单的“个人资料页面”:
44 |
45 | ```html
46 |
47 |
48 |
49 |
50 |
56 |
57 |
58 |
70 |
71 |
72 |
73 | 该页面只是简单从服务器加载用户配置文件数据,并填充到表单中。如果表单数据被编辑,则发送请求到服务器更新数据。
74 |
75 | 只有当前用户通过身份验证时,服务器才会接受提交的数据。
76 |
77 | 让我们看一下执行 CSRF 攻击的恶意页面。页面由攻击者创建,并且位于一个不同的域上。该页面的目标是根据通过身份验证的受害者向社交网络应用发送请求:
78 |
79 | ```html
80 |
81 |
82 |
83 |
84 |
88 |
89 |
92 |
93 |
94 | ```
95 |
96 | 该页面包含一个带有隐藏字段的表单。其操作指向与社交网络的个人资料页面相同的端点。
97 |
98 | 一旦受害者打开了恶意网站,该表单就会通过页面脚本自动将数据提交至服务器。
99 |
100 | 当社交应用的用户未通过身份验证时表单没有危害。由于缺少身份验证数据,易受攻击的 Web 应用程序将拒绝更改用户的个人资料。但如果用户通过了身份验证,则修改将被应用认为是合法请求。
101 |
102 | 由于浏览器上的 Cookie 跟踪了用户在社交网络上的会话。当易受攻击的 Web 程序收到更新请求时,由于它具有正确的会话 cookie,因此是合法的。
103 |
104 | 
105 |
106 | 因此,即使攻击者无法直接访问到 Web 应用程序,也可以利用受害者和 CSRF 漏洞来执行未经授权的操作。实际上,与 XSS 攻击不同,攻击者不会直接读取 cookie 并窃取。
107 |
108 | 此示例是一种过于简单但成熟的攻击,实际上它可能更复杂且不易察觉。例如,CSRF 攻击可以嵌入 iframe,且受害者根本不会意识到攻击的发生。
109 |
110 | 为了降低受到 CSRF 攻击的风险,下面是应遵守的一系列方法。
111 |
112 | ## 基于令牌的措施
113 |
114 | 这种防御措施是缓解 CSRF 攻击的最受欢迎和推荐的方法之一。可以通过两种方法来实现:
115 |
116 | * 有状态—同步器令牌模式
117 | * 无状态-基于加密或散列的令牌模式
118 |
119 | 许多流行框架提供了现成的实现技术。
120 |
121 | ### CSRF 内置实现
122 |
123 | 强烈建议开发者在尝试构建自定义系统前,先研究所使用的框架是否内置 CSRF 保护的选项。
124 |
125 | 即使有这样的系统,仍有一些配置需要开发者正确处理,例如密钥管理和令牌管理。
126 |
127 | 如果使用的框架中没有内置的 CSRF 保护机制,才考虑造轮子。
128 |
129 | 让我们看一下 `Express` 中的内置 CSRF 实现。`Express` 提供了一个名为 `csurf` 的中间件,仅此而已。
130 |
131 | 在本文中,我们不会讨论有关 `Express` 的知识或安装软件包的详细步骤。
132 |
133 | 这是我们的 `index.js` 文件:
134 |
135 | ```js
136 | const express = require('express');
137 | const bodyParser = require('body-parser');
138 | const csrf = require('csurf')
139 | const cookieParser = require('cookie-parser')
140 | const app = express();
141 | const csrfProtection = csrf({ cookie: true });
142 |
143 | app.use(cookieParser());
144 | app.use(bodyParser.urlencoded({ extended: true }));
145 | app.set('view engine', 'ejs');
146 |
147 | app.get('/', csrfProtection, (req, res) => {
148 | res.render('index', { csrfToken: req.csrfToken() });
149 | });
150 |
151 | app.post('/profile', csrfProtection, (req, res, next) => {
152 | res.send(req.body.name);
153 | });
154 |
155 | app.listen(3000);
156 | ```
157 |
158 | 然后在 `views` 目录下添加 `index.ejs`,如下所示:
159 |
160 | ```html
161 |
167 | ```
168 |
169 | 根路由将使用模板中的 `csrfToken` 变量来渲染 `index.ejs` 模板。在 `index.ejs` 中,`csrfToken` 将被设置为隐藏字段的值。
170 |
171 | 提交表单后,请求将发送至受 CSRF 保护的`/profile`路由。如果没有令牌,将抛出无效的`CSRF`令牌错误。
172 |
173 | ### 同步器令牌模式
174 |
175 | 同步器令牌模式允许服务器验证请求并确保它们来自合法的源。该模式的工作原理是在服务器上为每个用户会话或每个请求生成令牌。
176 |
177 | 客户端发送请求时,服务器必须对比用户会话中的令牌并验证请求中令牌的存在和有效性。
178 |
179 | 大多数 Web 应用中,服务器使用 HTTP 会话对象来标识已登录用户。这种情况下服务器先生成会话,并将会话 ID 传递给客户端。该会话 ID 大部分时间都保存在客户端 cookie 中。
180 |
181 | 如果存储会话 ID 的 cookie 不受高级配置(httponly、samesite、secure 等)的保护,则可以从浏览器中打开的另一个页面访问此 cookie。
182 |
183 | 每个请求生成令牌的方法更安全,因为攻击者有较少的时间来干扰和利用令牌。但这种方法会降低用户体验。如果用户单击浏览器中的“后退”按钮,则上一页可能包含失效令牌。这意味着与上一页的交互将导致服务器无法验证令牌导致请求失败。
184 |
185 | CSRF 令牌应具有以下特征:
186 | * 每个会话具有唯一性
187 | * 难以预测-安全生成的随机值
188 |
189 | CSRF 令牌可以缓解 CSRF 攻击,如果没有令牌,攻击者将无法创建能在服务器上执行的有效请求。
190 |
191 | 由于攻击者可能会拦截或访问 CSRF 令牌,因此请勿使用 cookies 进行传输。
192 |
193 | 另外,也不建议通过 `GET` 请求传输 CSRF 令牌,因为如果受保护的站点链接到外部站点,则可能会在多个位置泄漏,例如浏览器历史记录,日志文件,`Referrer headers` 等。
194 |
195 | CSRF 令牌应通过以下方式传输:
196 |
197 | * 表单中使用的隐藏字段
198 | * AJAX 请求头
199 |
200 | 将 CSRF 令牌添加到表单的方法如下:
201 |
202 | ```html
203 |
206 | ```
207 |
208 | 与上面 `Express` 示例类似,在服务器上生成作为输入字段值的令牌。
209 |
210 | ### 基于加密或散列的令牌模式
211 |
212 | 顾名思义,基于加密的令牌模式基于加密。对于现代 Web 应用,这是一种更合适的方法,程序在服务器上不维护任何状态。
213 |
214 | 令牌由服务器生成的,一般是由用户的会话 ID 和时间戳组成。这使用密钥对其加密。令牌生成后,将其返回给客户端。与同步令牌一样,基于加密的令牌既可以存储在隐藏字段中,也可以添加到 AJAX 请求头。
215 |
216 | 使用令牌发出请求后,服务器将尝试使用密钥对其进行解密。如果服务器无法解密令牌,则意味着存在某种形式的入侵,并且该请求被视为恶意或无效。
217 |
218 | 如果服务器成功解码,则将提取会话 ID 和时间戳。首先比较会话 ID 与当前经过身份验证的用户,并将时间戳与当前服务器时间进行比较,以确认其未超出预设到期时间。
219 |
220 | 如果会话 ID 与当前用户匹配,并且时间戳未过期,则该请求被视为安全请求。
221 |
222 | ## SameSite Cookie
223 |
224 | Cookie 的 `SameSite` 属性用来限制第三方 Cookie,从而减少安全风险。
225 |
226 | 此属性使浏览器可以决定是否与跨站点请求一起发送cookie。可选值为:
227 |
228 | * **Strict**- Cookies 仅在第一方上下文中发送,不会与第三方网站发起的请求一起发送。这意味着,如果某个网站上有指向 GitHub 私仓的链接,则单击链接后 GitHub 不会收到会话 cookie,用户将无法访问该存储库。
229 | * **Lax**- Cookies 不会通过 CSRF 倾向的请求方法(例如 POST)发送。当用户导航到原始站点时,将发送 Cookie。如果在最近的浏览器版本中未明确指定 SameSite,则这是默认的 cookie 值。如果某个网站上有指向私有GitHub存储库的链接,则 GitHub将收到会话 cookie,且用户将能够访问该存储库。
230 | * **None**-Cookies将在所有情况下(例如第一方请求和跨域请求)发送。此外,将需要 `secure flag`(SSL/HTTPS)。
231 |
232 | 所有台式机浏览器和绝大部分移动端浏览器都支持 SameSite 属性。
233 |
234 | 此属性不是用来完全替代 CSRF 令牌。相反,应该与令牌共存,以便以更可靠的方式保护用户。
235 |
236 | ## Origins 校验
237 |
238 | 此措施包括两个步骤,这些步骤依赖于检查 HTTP 请求头的值:
239 |
240 | * 确定来源域 - 可以通过 `Origin` 或者 `Referer` 头判断请求来自哪里。
241 | * 确定目标域 - 请求发送至哪里。
242 |
243 | 服务器必须验证来源域和目标域是否匹配。如果存在匹配项,则该请求被视为合法并被接受。如果不匹配,由于跨域该请求将被丢弃。
244 |
245 | 可以默认这些头信息是可靠的,因为它们属于[禁止头信息](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name)(只能由浏览器修改),无法通过 JavaScript 对其进行修改。
246 |
247 | ## 双重 Cookie 验证
248 |
249 | 双重 Cookie 验证是 CSRF 令牌的替代方法。这是一种无状态的方法。
250 |
251 | 当用户访问应用时,应生成加密强度高的伪随机值,并将其设置为用户计算机上的一个 cookie,与会话 ID 分开。
252 |
253 | 然后,服务器要求每个请求都包含该值(通过隐藏的表单或请求参数)。如果它们在服务器端都匹配,则服务器将其接受为合法请求,否则,服务器将拒绝该请求。
254 |
255 | > 补充:作用较弱。任何可以设置 cookie 的攻击者都可以破解此方式(通过漏洞注入 cookie 或者中间人攻击 MITM)。
256 |
257 | ## 自定义请求头
258 |
259 | 这种方法非常适合于大量使用 AJAX 请求并依赖 API 访问的 Web 应用程序。
260 |
261 | 此方法使用[同源策略](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy),该策略限制了仅 JavaScript 可以添加自定义头信息,并且只能在同源内使用。默认情况下,浏览器不允许 JavaScript 使用自定义请求头进行跨域请求。
262 |
263 | 此解决方案的效率性要求具有鲁棒性的 CORS 配置,因为来自其他域的自定义请求头会触发 CORS 预检查(用于获知服务端是否允许该跨源请求)。
264 |
265 | 这允许开发者向请求中添加自定义头信息,只需在服务器验证其存在和值即可。
266 |
267 | 该技术适用于 AJAX 请求,但`