├── BOOK.assets ├── 1552904604507.png ├── 1552905378968.png ├── 1552912887244.png ├── 1552999312562.png ├── 1552999360878.png ├── 1552999390567.png ├── 1553079227178.png ├── 1556836336765.png ├── 1558824325517.png ├── 1563330252149.png ├── 1563331271632.png ├── 1565929449193.png ├── 1565930786117.png ├── 1565932209076.png ├── 1565932212738.png ├── 1800-census.jpg ├── 2237281220-5898949183cc3_articlex.png ├── 640.webp ├── F9B33FF24D7418356F52703587ED9922.jpg ├── numbers-table.png └── swimming-timetable.png ├── BOOK.md ├── BOOK └── image_165.png ├── MOTTO.md ├── README.assets └── image_165.png ├── README.md ├── REFERENCE.md ├── SCRATCH.md ├── TODO.md ├── 事件 ├── DOM事件.md ├── DOM操作.md ├── HTML控件.md ├── assets │ ├── 1558824824647.png │ ├── 1562599371222.png │ ├── 1562626507612.png │ ├── 1562998447733.png │ ├── 1562999615730.png │ ├── 1563063890497.png │ ├── 1563326586026.png │ ├── 1563326700807.png │ ├── 1563326712428.png │ ├── 1563327353017.png │ ├── 1563327355516.png │ ├── 1563327994789.png │ ├── 1565500624871.png │ ├── 1565500672733.png │ ├── 1565500784247.png │ ├── 1565501845034.png │ ├── 1565503235616.png │ ├── 1565505025525.png │ ├── 1565506008086.png │ ├── 1565562799087.png │ ├── 1565563216094.png │ ├── 1565584162296.png │ ├── 1565584238135.png │ ├── 1565586248914.png │ ├── 1565590070635.png │ ├── 1565590445363.png │ ├── 1565600046914.png │ ├── 1565679212423.png │ ├── 1565682021275.png │ ├── 1565767500359.png │ ├── 1565771987739.png │ ├── 1565772422179.png │ ├── 1565853058826.png │ ├── 1565854628479.png │ ├── 1565854636619.png │ ├── 1566463506773.png │ ├── javascript_sensors_yaw.jpg │ └── old-radio.jpg ├── 事件初探.md ├── 事件的细节.md ├── 定时器.md ├── 文档对象模型.md ├── 移动设备事件.md └── 第六章.md ├── 信息处理 ├── 二进制数据.md ├── 反应式编程.md ├── 案例研究:编程语言.md ├── 模板.md ├── 正则表达式.md └── 算术表达式解析.md ├── 函数 ├── assets │ ├── 1555713818684.png │ ├── 1555713844215.png │ ├── 1555713869213.png │ ├── 1556862479426.png │ ├── th-1555712687236.jpg │ └── th.jpg ├── 函数初步.md ├── 函数和算法.md ├── 方法.md ├── 生成器函数.md ├── 第五章.md ├── 第五章练习.md ├── 递归.md └── 高阶函数.md ├── 分布式计算 ├── AJAX.md ├── 互联网与信息传输.md ├── 分布式应用程序.md ├── 安全性.md ├── 客户端存储技术.md ├── 异步操作.md └── 数据交换格式.md ├── 启程 ├── HTML.md ├── Hello-world.md ├── assets │ ├── 1552904604507.png │ ├── 1552905378968.png │ ├── 1552912887244.png │ ├── 1552999312562.png │ ├── 1552999360878.png │ ├── 1552999390567.png │ ├── 1553079227178.png │ ├── 1553079359387.png │ └── 1558824325517.png ├── 命名的值.md ├── 更进一步.md ├── 第二章.assets │ ├── 1552904604507.png │ ├── 1552905378968.png │ ├── 1552912887244.png │ ├── 1552999312562.png │ ├── 1552999360878.png │ ├── 1552999390567.png │ ├── 1553079227178.png │ ├── 1558824325517.png │ ├── 1563330252149.png │ └── 1563331271632.png ├── 第二章.md └── 表达式与值.md ├── 图形与动画 ├── Canvas.md ├── WebGL.md ├── 可伸缩矢量图形.md ├── 案例研究·波浪进度球.md ├── 案例研究:画图工具.md ├── 级联样式表.md └── 计算机图形.md ├── 导出 ├── BOOK.docx ├── BOOK.pdf ├── JavaScript 艺术之旅.docx ├── ~$第五章.docx ├── ~$第四章.docx ├── 第三章.docx ├── 第五章.docx ├── 第五章.pdf ├── 第四章.docx └── 第四章.pdf ├── 数据 ├── assets │ ├── 1565929449193.png │ ├── 1565930786117.png │ ├── 1565932209076.png │ ├── 1565932212738.png │ ├── 2237281220-5898949183cc3_articlex.png │ ├── bg2014121121-1.png │ └── ieee754.jpg ├── 字符串.md ├── 对象.md ├── 数值.md ├── 数据类型.md ├── 数组.md ├── 第三章.md ├── 第三章练习.md ├── 解构赋值.md └── 逻辑.md ├── 新的征途 ├── Chrome 扩展.md ├── Electron.md ├── Node.js.md ├── 案例研究:聊天室.md ├── 案例研究:音乐播放器.md └── 自动化工作流.md ├── 练习答案 └── 第四章.md ├── 绪论 └── 绪论.md ├── 设计原理 ├── JavaScript标准对象.md ├── Symbol和迭代器.assets │ └── 0054F331.png ├── Symbol和迭代器.md ├── assets │ └── freezeSealAndPreventExtensions.png ├── 代理对象.md ├── 可见性和属性描述符.md ├── 求值策略.md ├── 类和原型.md └── 装饰器.md ├── 语句 ├── assets │ ├── 1556836336765.png │ └── 640.webp ├── for-in和for-of语句.md ├── for语句.md ├── if语句.md ├── switch语句.md ├── while和do-while语句.md ├── 异常处理.md ├── 第四章.md ├── 第四章练习.md └── 语句优化.md ├── 软件开发 ├── reference.md ├── 使用JavaScript库.md ├── 兼容性.md ├── 国际化.md ├── 性能.md ├── 模块化.md ├── 测试.md └── 风格与质量.md ├── 附录 ├── JavaScript语言参考.md ├── Unicode指南.md └── 键码映射表.md └── 魔法世界 ├── WebRTC.md ├── Web幻灯片.md ├── 再探HTML5.md ├── 图像处理.md ├── 多线程.md ├── 数据可视化.md └── 音频处理.md /BOOK.assets/1552904604507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1552904604507.png -------------------------------------------------------------------------------- /BOOK.assets/1552905378968.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1552905378968.png -------------------------------------------------------------------------------- /BOOK.assets/1552912887244.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1552912887244.png -------------------------------------------------------------------------------- /BOOK.assets/1552999312562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1552999312562.png -------------------------------------------------------------------------------- /BOOK.assets/1552999360878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1552999360878.png -------------------------------------------------------------------------------- /BOOK.assets/1552999390567.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1552999390567.png -------------------------------------------------------------------------------- /BOOK.assets/1553079227178.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1553079227178.png -------------------------------------------------------------------------------- /BOOK.assets/1556836336765.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1556836336765.png -------------------------------------------------------------------------------- /BOOK.assets/1558824325517.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1558824325517.png -------------------------------------------------------------------------------- /BOOK.assets/1563330252149.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1563330252149.png -------------------------------------------------------------------------------- /BOOK.assets/1563331271632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1563331271632.png -------------------------------------------------------------------------------- /BOOK.assets/1565929449193.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1565929449193.png -------------------------------------------------------------------------------- /BOOK.assets/1565930786117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1565930786117.png -------------------------------------------------------------------------------- /BOOK.assets/1565932209076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1565932209076.png -------------------------------------------------------------------------------- /BOOK.assets/1565932212738.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1565932212738.png -------------------------------------------------------------------------------- /BOOK.assets/1800-census.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/1800-census.jpg -------------------------------------------------------------------------------- /BOOK.assets/2237281220-5898949183cc3_articlex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/2237281220-5898949183cc3_articlex.png -------------------------------------------------------------------------------- /BOOK.assets/640.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/640.webp -------------------------------------------------------------------------------- /BOOK.assets/F9B33FF24D7418356F52703587ED9922.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/F9B33FF24D7418356F52703587ED9922.jpg -------------------------------------------------------------------------------- /BOOK.assets/numbers-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/numbers-table.png -------------------------------------------------------------------------------- /BOOK.assets/swimming-timetable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK.assets/swimming-timetable.png -------------------------------------------------------------------------------- /BOOK/image_165.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/BOOK/image_165.png -------------------------------------------------------------------------------- /MOTTO.md: -------------------------------------------------------------------------------- 1 | # Motto 2 | 3 | --- 4 | 5 | 一些会在书中引用到以抒发思想感情的诗句、格言和梦话。 6 | 7 | 8 | 9 | 路漫漫其修远兮,吾将上下而求索。 10 | 11 | ——屈原《离骚》 12 | 13 | 14 | 15 | 新的转机和闪闪星斗,正在缀满无遮拦的天空。那是五千年的象形文字,那是未来人们凝视的眼睛。 16 | 17 | ——北岛《回答》 18 | 19 | 20 | 21 | 黑夜给了我黑色的眼睛,我却用它来寻找光明。 22 | 23 | ——顾城《一代人》 24 | 25 | 26 | 27 | 老玉米,金黄黄,养活了一国的小儿郎。我捧着玉米回头望,地里站着的是我的娘。 28 | 29 | ——《中国妈妈》 30 | 31 | 32 | 33 | 此后如竟没有炬火,我便是那唯一的光。 34 | 35 | ——鲁迅 36 | 37 | 38 | 39 | 魂兮归来!北方不可以止些。增冰峨峨,飞雪千里些。 40 | 41 | ——《招魂》 42 | 43 | 44 | 45 | 走过的人说树枝低了,走过的人说树枝在长。 46 | 47 | ——顾城《墓床》 48 | 49 | 50 | 51 | 原野上有一股好闻的淡淡焦味,太阳把一切成熟的东西焙得更成熟,黄透的枫叶杂着赭尽的橡叶,一路艳烧到天边。 52 | 53 | ——余光中 54 | 55 | 56 | 57 | 外面下着雨,已经下了许多天,点点滴滴,歪歪斜斜,像我的抄不完的草稿。 58 | 59 | ——张爱玲 60 | 61 | 62 | 63 | 城市是一粒粒精致的银扣,缀在旷野的黑绿色大氅上,不分昼夜地熠熠闪光。 64 | 65 | ——毕淑敏 66 | 67 | 68 | 69 | 我不知道,天为什么无端落起雨来了。薄薄的水雾把山和树隔到更远的地方去,我的窗外遂只剩下一片辽阔的空茫了。 70 | 71 | ——张晓风 72 | 73 | 74 | 75 | 寒不累时,则霜不降;温不兼日,则冰不释。 76 | 77 | ——王充 78 | 79 | 80 | 81 | 一条路是桃花,一条路是雪。开满了桃花的路上,云蒸霞蔚,前程似锦,不由得你不想往前走。 82 | 83 | ——季羡林 84 | 85 | 86 | 87 | 午夜醒来,后庭的月光正在涨潮,满园的林木都淹没在发亮的波澜里。 88 | 89 | ——张晓风 90 | 91 | 92 | 93 | 除非通过黑暗之径,人们不能抵达黎明。 94 | 95 | ——纪伯伦 96 | 97 | -------------------------------------------------------------------------------- /README.assets/image_165.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/README.assets/image_165.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript 艺术之旅 2 | 3 | --- 4 | 5 | ![](https://img.shields.io/badge/JavaScript-Art--Tour-brightgreen.svg) 6 | 7 | ![](https://img.shields.io/badge/Tanpero-book-ff69b4.svg) 8 | 9 | 10 | 11 | ![image_165](README.assets/image_165.png) 12 | 13 | --- 14 | 15 | **绪论需要细细斟酌,因此它的内容暂时空缺。** 16 | 17 | 本书的写作计划和章节划分可参看目录,其中添加了链接的章节名称代表着已经完成,或正在写作、可预览的内容。 18 | 19 | --- 20 | 21 | 待办事项列表:[TODO](TODO.md) 22 | 23 | --- 24 | 25 | 目录 26 | ======= 27 | - Station 1 绪论 28 | 29 | - Day 0 30 | - 这片土地上的旧时光 31 | 32 | - Station 2 启程 33 | - Day 1 34 | 35 | - [千里之行,始于足下](启程/Hello-world.md) 36 | 37 | - [辟自鸿蒙](启程/表达式与值.md) 38 | 39 | - [请叫阮的名](启程/命名的值.md) 40 | 41 | - [听风知雨·更进一步](启程/更进一步.md) 42 | - Day 2 43 | 44 | - [互联网的礼服·HTML](启程/HTML.md) 45 | 46 | - Station 3 数据山谷 47 | - Day 3 48 | - [万物原动力·逻辑](数据/逻辑.md) 49 | - [宇宙谐和论·数值](数据/数值.md) 50 | - Day 4 51 | 52 | - [交流的载体·字符串](数据/字符串.md) 53 | - Day 5 54 | 55 | - [道生万物·对象](数据/对象.md) 56 | 57 | - [时空列车·数组](数据/数组.md) 58 | 59 | - [物以类聚·数据类型](数据/数据类型.md) 60 | - [数据山谷的驿站](数据/第三章练习.md) 61 | 62 | - Station 4 语句河畔 63 | - Day 6 64 | - [正义的准绳·if 语句](语句/if语句.md) 65 | - [逐一排查·switch 语句](语句/switch语句.md) 66 | - Day 7 67 | - [以车代步·while 和 do-while 语句](语句/while和do-while语句.md) 68 | - [以梦为马·for 语句](语句/for语句.md) 69 | - [见微知著·for-in 和 for-of 语句](语句/for-in和for-of语句.md) 70 | - Day 8 71 | - [欲工先利器·语句优化](语句/语句优化.md) 72 | - [人以群分·解构赋值](数据/解构赋值.md) 73 | - Day 9 74 | - [安全的保障·异常处理](语句/异常处理.md) 75 | - [语句河畔的旅馆](语句/第四章练习.md) 76 | 77 | - Station 5 函数小镇 78 | - Day 10 79 | - [化繁为简·函数初步](函数/函数初步.md) 80 | - [手可摘星辰·函数和算法](函数/函数和算法.md) 81 | - [调兵遣将·方法](函数/方法.md) 82 | - Day 11 83 | - [大道至简·高阶函数](函数/高阶函数.md) 84 | - Day 12 85 | - [聚沙成塔·递归](函数/递归.md) 86 | - Day 13 87 | - 原子和宇宙·高级主题:函数式编程 88 | - [函数小镇的时光邮局](函数/第五章练习.md) 89 | 90 | - Station 6 事件博览会 91 | - Day 14 92 | - [电光火石·事件初探](事件/事件初探.md) 93 | - [山川相缪·文档对象模型](事件/文档对象模型.md) 94 | - Day 15 95 | - [高屋建瓴·HTML 控件](事件/HTML控件.md) 96 | - [硕果可采撷·DOM 操作](事件/DOM操作.md) 97 | - Day 16 98 | - 风声鹤唳·DOM 事件和定时器 99 | - 芥子纳须弥·事件的细节 100 | - Day 17 101 | - 行走在人间·移动设备事件 102 | - Day 18 103 | - 案例研究:富文本编辑器 104 | - 事件博览会的休息室 105 | 106 | - Station 7 原理图书馆 107 | - Day 19 108 | - [本是同根生·类和原型](设计原理/类和继承.md) 109 | - Day 20 110 | - [思考自我·可见性和属性描述符](设计原理/可见性和属性描述符.md) 111 | - [迷雾清泉·求值策略](设计原理/求值策略.md) 112 | - Day 21 113 | - [人间烟火·Symbol 和迭代器](设计原理/Symbol和迭代器.md) 114 | - Day 22 115 | - 白马非马·代理对象 116 | - Day 23 117 | - [物种起源·JavaScript 标准对象](设计原理/JavaScript标准对象.md) 118 | - 原理图书馆的会客厅 119 | 120 | - Station 8 软件开发公园 121 | - Day 24 122 | - 集思广益·使用 JavaScript 库 123 | - Day 25 124 | - 百炼成钢·测试 125 | - 分秒必争·性能 126 | - Day 26 127 | - 风雨无阻·兼容性 128 | - Day 27 129 | - [山中无岁月·模块化](软件开发/模块化.md) 130 | - Day 28 131 | - [山川异域,风月同天·国际化](软件开发/国际化.md) 132 | - Day 29 133 | - .[见微知著·风格与质量](软件开发/风格与质量.md) 134 | - 软件开发公园的沉思角 135 | 136 | - Station 9 分布式广场 137 | - Day 30 138 | - 让世界相连·互联网和信息传输 139 | - 殊途同归·数据交换格式 140 | - Day 31 141 | - 世界灯火·AJAX 142 | - Day 32 143 | - 鱼跃龙门·异步操作 144 | - Day 33 145 | - 光阴逆旅·分布式应用程序 146 | - 避风港湾·安全性 147 | - Day 34 148 | - 百代过客·客户端存储技术 149 | - 分布式广场的地下室 150 | 151 | - Station 10 图形和动画乐园 152 | - Day 35 153 | - 眼见为实·计算机图形 154 | - 绝世伴侣·级联样式表 155 | - Day 36 156 | - 游刃有余·可伸缩矢量图形 157 | - 案例研究:波浪进度球 158 | - Day 37 159 | - 挥洒青春·Canvas 160 | - 案例研究:画图工具 161 | - Day 38 162 | - 俯仰自如·WebGL 163 | - 图形和动画乐园的餐厅 164 | 165 | - Station 11 忍者道场 166 | - Day 39 167 | - 计研心算·算术表达式解析 168 | - 案例研究:编程语言 169 | - Day 40 170 | - 见素抱朴·正则表达式 171 | - Day 41 172 | - 水落石出·模板 173 | - Day 42 174 | - 闻风而兴·反应式编程 175 | - Day 43 176 | - 计算机的内心·二进制数据 177 | - 忍者道场的寝室 178 | 179 | - Station 12 魔法地下河 180 | - Day 44 181 | - 互通有无·再探 HTML 5 182 | - Day 45 183 | - 让数据说话·数据可视化 184 | - Day 46 185 | - 鹰瞵鹗视·图像处理 186 | - Day 47 187 | - 绕梁三日·音频处理 188 | - Day 48 189 | - 与时俱进·智能化 Web 应用 190 | - Day 49 191 | - 似水流年·WebRTC 192 | - 魔法地下河的码头 193 | 194 | - Station 13 星辰大海 195 | - Day 50 196 | - Node.js 服务器开发 197 | - 案例研究:聊天室 198 | - Day 51 199 | - 自动化工作流 200 | - Day 52 201 | - Electron 桌面开发 202 | - 案例研究:音乐播放器 203 | - Day 53 204 | - React Native 移动开发 205 | - 案例研究:天气日历 206 | - Day 54 207 | - WebAssembly 208 | - Day 55 209 | - 起点,终点:征途无尽 210 | - 星辰大海的山洞 211 | 212 | - 旅行手册 213 | - 指南 A 214 | JavaScript 语言参考 215 | - 指南 B 216 | Unicode 指南 217 | - 指南 C 218 | [键码映射表](附录/键码映射表.md) 219 | 220 | --- 221 | 222 | ## 关于本书 223 | 224 | 六年级某节平常的信息课上,隔壁座位的男生在翻一本 JavaScript 的书。 225 | 226 | 我借过来翻了翻,从此就踏上了一条不归路。 227 | 228 | 我还记得那本书平易浅显到一个小学生掌握 JavaScript 的基础知识毫无问题, 229 | 230 | 能够做出好玩的简单 Web 应用,在 DOM 和 Canvas 上挥洒自如。 231 | 232 | 甚至连分布式计算和 WebGL 等离一个小学生太远太抽象的知识,都在头脑中烙下印记, 233 | 234 | 成为一切的起点。 235 | 236 | 本书是一本从头开始的JavaScript书, 237 | 238 | 为了纪念我小学六年级时那段温暖祥和的日子, 239 | 240 | 为了向当年的那本入门书致敬。 241 | 242 | 243 | 244 | ## 关于作者 245 | 246 | 作者目前准高一,就读于广州市第六中学。本书主要写于初二至初三前期,之后暂时停滞。 247 | 248 | 对未来有一定的打算,但是长路漫漫,行止难至。 249 | 250 | 惟有一个人不眠的夜空里,总有繁星如昼。 251 | 252 | -------------------------------------------------------------------------------- /REFERENCE.md: -------------------------------------------------------------------------------- 1 | - .Jeremy Keith, Jeffrey Sambells. *DOM Scripting: Web Design with JavaScript and the Document Object Model, Second Edition*. Berkeley. Apress L.P.. 2010. 2 | - John Resig, Bear Bibeault, Josip Maras. *JavaScript Ninja, Second Edition*. Greenwich. Manning Publications Co. .2016. 3 | - Eric S. Roberts. *Programming Abstractions in C++*. Standford. Pearson Education, Inc.. 2014. 4 | - Steve Fulton, Jeff Fulton. *HTML5 Canvas, Second Edition*, 8bitrocket Studios. O`Reilly. 2013 5 | - Mark E. Daggett. *Expert JavaScript*. Apress Media. 2013. 6 | - Den Odell. *Pro JavaScript Development: Coding, Capabilities, and Tooling*. Berkeley. Apress L.P.. 2014. 7 | - Lü Zhihua. *Pro D3.js: High-level programming of interactive Data visualization*. Beijing. Electronic Industry Press. 2015. 8 | 9 | -------------------------------------------------------------------------------- /SCRATCH.md: -------------------------------------------------------------------------------- 1 | # Scratch 2 | 3 | --- 4 | 5 | 记录一些想法。 6 | 7 | 8 | 9 | 绪论会从互联网的诞生开始,经历洪荒时代、JavaScript 的诞生、浏览器大战、W3C 标准、ECMAScript 标准的发展、一直到当下最前沿的 JavaScript 周边技术,包括 WebGL、React Native、WebAssembly 等都会浮光掠影一番。 10 | 11 | 全书零散添加一些历史小故事。 12 | 13 | - HTML 的发展历程。 14 | - JavaScript 服务端的历史和 Node.js 的成功故事。 15 | - JavaScript Linter 的演变。 16 | - 前端库和框架的演变。 17 | - JavaScript 引擎的进化。 18 | 19 | 20 | 21 | 附录里添加一篇编辑器和开发环境选择指南。 22 | 23 | - Visual Studio Code 24 | - Atom 25 | - Bracket 26 | - WebStorm 27 | - Sublime Text 28 | - Notepad++ 29 | - EditPlus 30 | - Vim 31 | - Emacs 32 | - Nano 33 | 34 | 35 | 36 | 第九章 37 | 38 | - 介绍了客户端与服务器的概念后,快速搭建一个简易服务器以进行后续示例。 39 | 40 | 41 | 42 | 第十四章 43 | 44 | - Node.js:编写一个聊天应用。 45 | - Electron:编写一个音乐播放器。 46 | - Ionic:编写一个天气应用。 47 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # TODO 4 | 5 | --- 6 | 7 | 暂时保留的待办事项列表。 8 | 9 | 10 | 11 | --- 12 | 13 | - 全局 14 | 15 | - [ ] 为每一章添加课后练习 16 | 17 | - [ ] 为每一章添加参考文献 18 | 19 | - [ ] 为每一节添加小结 20 | 21 | - 第二章 22 | 23 | - [ ] 更新第二章的运行器截图 24 | 25 | - [ ] 在第二章的 表达式 一节中加入关于十六进制和二进制的概念 26 | 27 | - [ ] 完成第二章的 HTML 一节相关内容 28 | 29 | 简要介绍 HTML 和在 HTML 中使用 JavaScript 的方式 30 | 31 | - 第三章 32 | 33 | - [x] 完成第三章的 数值 一节剩余内容 34 | - [x] 完成第三章的 模式匹配 一节 35 | - [x] 完成第三章的 字符串 一节中关于字符串操作的剩余内容 36 | - [x] 完成第三章的 数组 一节中关于数组操作的剩余内容 37 | - [x] 添加标签模板字符串相关内容(新的函数调用形式) 38 | - [x] 添加第三章中 对象 一节中有关 `this` 的说明 39 | - [ ] 添加 ECMAScript 6 的设置对象字面量中属性名的新语法 40 | 41 | - 第四章 42 | 43 | - [x] 在第四章的 for语句 一节中,增加数组遍历相关内容 44 | - [ ] 在第四章中添加有关 块级作用域 的内容 45 | 46 | - 第五章 47 | - [ ] 完成 生成器函数 一节 48 | - [ ] 完成 高级主题:函数式编程 一节 49 | - [ ] 完成 递归 一节剩余内容 50 | - [ ] 完成 高阶函数 一节中关于数组方法 `reduce`、`filter` 的解释 51 | - [x] 完成 高阶函数 一节中关于闭包的阐述 52 | 53 | - 第七章 54 | 55 | - [ ] 将 迭代协议 一节中有关迭代器的内容移到可迭代对象之前 56 | -------------------------------------------------------------------------------- /事件/DOM事件.md: -------------------------------------------------------------------------------- 1 | ## DOM 事件 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /事件/HTML控件.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## HTML 控件 4 | 5 | --- 6 | 7 | ### 控件的概念 8 | 9 | *控件*(widget)是一个很广泛的概念,它的字面含义是“可以由用户控制的组件”,包括你在使用桌面和 Web 应用时看到的那些按钮、文本框、滑动条、图片、菜单等。它们为用户和应用程序提供了一个便捷的交流途径,合适的控件使应用程序的意图得以良好传达,给用户以良好的使用体验。所有页面上的元素,只要能对用户进行的一定操作进行响应,改变状态或做出其它反馈,或专门负责传达某一类信息,都可以被称为控件。 10 | 11 | 开发者在使用控件时,应当考虑两大因素,一是**语义清晰**,而是**用户友好**。按钮就是一类语义非常清楚的控件,当用户看见它时,就会知道应该点击它来达到一定目的。所有的按钮都是基于这种固有经验。在此基础上,承担不同任务的按钮可能会被赋予不同的样式或提示信息。按钮中间可以显示点说明作用的文本。 12 | 13 | ```html 14 | 17 | ``` 18 | 19 | 在大多数 Web 浏览器上,一个默认的按钮都会呈现为灰色的矩形方块,静止状态、鼠标移上、点击时、点击后对应的效果都会发生改变,体现了按钮操作时的质感。(在作者所使用的的浏览器上只会发生光影变化) 20 | 21 | ![1563326586026](assets/1563326586026.png)![1563326700807](assets/1563326700807.png)![1563326712428](assets/1563326712428.png) 22 | 23 | 另一对例子是*单选按钮*和*下拉菜单*。单选按钮与普通的按钮有所不同,相信你一定见过它: 24 | 25 | ![1565600046914](assets/1565600046914.png) 26 | 27 | 如果要让用户在三四个选项里做出唯一选择,显然单选按钮是很合适的。当用户改变要改变选择时,只需要点击另一个选项前的圆框。但是选项和内容一多,全部平铺堆积在页面上,看上去显得杂乱无章。当用户不需要做出选择时,还会带给用户冗余的视觉信息。 28 | 29 | ![1563327353017](assets/1563327353017.png)![1563327355516](assets/1563327355516.png) 30 | 31 | 而下拉菜单默认状态下只会显示一行文本,当我们单击它时,就会弹出所有的选项——我们知道它弹出的内容不是通知或别的什么东西,而是供我们做出选择的选项。显示选项让用户做出选择,是单选按钮和下拉带单共有的语义。但当用户不需要做出选择时,下拉菜单会隐藏大部分内容,等到时机合适时再显示,不会给用户做出视觉上的困扰,这时下拉菜单就比单选按钮更加用户友好。而只有少量选项时,单选按钮要求用户做的事情更加一目了然,省去了单击才能显示所有选项的步骤,这时单选按钮比下拉菜单更加用户友好。 32 | 33 | 编写 HTML 是显示控件的一种基本方式。HTML 页面上可以使用的控件有十几种,我们将在下文中了解到大部分控件的具体使用。 34 | 35 | 36 | 37 | 38 | 39 | ### 生成控件的元素 40 | 41 | **1. input** 42 | 43 | HTML 中有许多元素和属性可以生成特定的控件。其中一个重要的元素是 ``,它本身在默认情况下会显示为一个空白的单行纯文本输入框(简称*文本输入框*),我们通常将它的 `type` 属性设置为 `"text"` 来强调它是一个“输入纯文本”的控件。通过将 `type` 属性设置为不同的值,`` 元素所产生的控件外观与作用也各不相同。在前面的温度转换器示例中,我们使用了文本输入框与数字输入框两类控件用于接收用户输入。其中,数字输入框在点击后,右侧会显示一对上下箭头,我们可以借助于此对输入的数值进行微调。 44 | 45 | ![1565500624871](assets/1565500624871.png)![1565500672733](assets/1565500672733.png) 46 | 47 | 文本输入框的一种变体专门用于输入密码,它是 ``,称为*密码输入框*。它和文本输入框一样接受文本输入,同时将停留在框内的文本显示为遮盖黑点。 48 | 49 | 另一个我们见过的控件是按钮。它也可以被视为输入控件的一种,因此 50 | 51 | ```html 52 | 53 | ``` 54 | 55 | 会被渲染为: 56 | 57 | ![1565500784247](assets/1565500784247.png) 58 | 59 | 不过更方便的写法还是直接使用 ` 63 | ``` 64 | 65 | 在互联网中用户交互对控件的需求远不止触发按钮与文本和数字输入。通过*颜色选取控件*,我们可以在游戏中选择今天穿的衣服的颜色,抑或是给个人主页换个背景色。 66 | 67 | ```html 68 | 今天用什么颜色的口红呢?当然是 69 | ``` 70 | 71 | 它在我的 Chrome 浏览器上呈现为一个颜色按钮,单击它后会打开一个对话框来让我们选择颜色。 72 | 73 | ![1565501845034](assets/1565501845034.png) 74 | 75 | 控件获取到的颜色以*HTML颜色格式*存放,关于颜色与颜色格式的相关知识我们会在第十章详细了解。 76 | 77 | 日期和时间输入同样是一类非常常见的需求。以前,页面只能提供给用户一个简陋的文本输入框,让用户自己来输入日期和时间,再根据规定的格式进行解析,如果格式写错了还会遇到意想不到的麻烦。现在 HTML 提供了一类日期和时间输入控件,它们外观上呈现为一个文本框,其中包含了预定的日期或时间格式。为了适应具体的需要,它们还分为不同的类型。 78 | 79 | ```html 80 | 我的生日: 81 | ``` 82 | 83 | 还可以用 `min` 和 `max` 属性为日期范围添加限制。 84 | 85 | ```html 86 | 请输入一个建国以前的日期: 87 | 请输入一个21世纪的日期: 88 | ``` 89 | 90 | 我们可以用类似于数字输入框的上下箭头,对年月日等每一项进行微调,也可以在控件里找到一个图标,触发浏览器提供的简单日历以自由选择。 91 | 92 | ![1565503235616](assets/1565503235616.png) 93 | 94 | 还有几种日期和时间控件,可以根据需要进行使用。 95 | 96 | - ` ` 提供一个类似上面的控件,不过它会忽略时区,只考虑本地时间。 97 | 98 | - `` 可以用上下箭头选择一个时刻。 99 | 100 | (它也可能会提供一个时刻选择器,不过我的浏览器上没有 D-:) 101 | 102 | - `` 精确到具体月份。 103 | 104 | - `` 要求用户选择某一周。 105 | 106 | `` 提供了一个滑块,可以让我们在规定范围内选择一个值,`min` 和 `max` 属性分别确定了这个范围的最小值和最大值,而 `step` 属性还可以控制每次递增或递减的程度。 107 | 108 | ```html 109 | 对 TFBoys 的好感度(0-100): 110 | ``` 111 | 112 | 我们对 TFboys 的好感度默认从 `50` 开始,每次至少增加或减少 `2` ,上限为 `100`,下限为 `0`。 113 | 114 | ![1565505025525](assets/1565505025525.png) 115 | 116 | 如果我们的数据很多,写在文件里,怎么告诉页面呢?打开文件复制内容并粘贴到输入框里吗? 117 | 118 | 当然不用!我们有*文件选择控件*:`` 119 | 120 | 默认状态下,控件的外观是这样的: 121 | 122 | ![1565506008086](assets/1565506008086.png) 123 | 124 | 标有“选择文件”的按钮和其后的文本都包含在控件里,单击一下,浏览器会唤起一个文件选择对话框,就像我们在 Office Word 等文档编辑软件里做的那样,选择文件并确定,控件便会显示出刚才读取到的文件的文件名。读取文件后进行的处理过程超出了本章范畴,我们将在后续有关数据处理的章节中详细了解。 125 | 126 | 以上控件能大致满足我们的基本需求,但是 Web 标准的制定者们在人性化方面做得更好,一些控件使原本需要用户“输入”的内容,变为只需“选择”,为 Web 应用程序的交互提供了莫大方便。我们在上一小节中见过的单选按钮、复选框和下拉菜单就是例子,它们已经在无数应用程序中得到了历久弥新的实践,也让网络投票与问卷这类特定需求变得轻松快捷。 127 | 128 | 单选按钮不是通常所说的按钮(专用于触发特定操作),而是列出数据以供用户进行唯一选择,它是 `type` 类型为 `"radio"` 的 `` 元素。一组单选按钮的 `name` 属性相同,也就是说,如果一些单选按钮的 `name` 属性相同,那么它们会被划分为同一组,一组单选按钮中最多有一项被选中。不同 `name` 属性的单选按钮互不影响。 129 | 130 | ```html 131 |

你喜欢吃哪种水果?

132 | 苹果 133 | 西瓜 134 | 柑橘 135 | 葡萄 136 |

你喜欢吃哪种干果?

137 | 核桃 138 | 杏仁 139 | 榛子 140 | ``` 141 | 142 | 效果如图所示。 143 | 144 | ![1565562799087](assets/1565562799087.png) 145 | 146 | 这样,选择被区分在水果与干果之间。假如把它们的 `name` 属性都变成同一个,或者原本要分成两组却用了三个 `name` ,意义就截然不同了。 147 | 148 | 另一种常见的选择控件是*复选框*,它也可以像单选按钮那样在逻辑上划分为一个个组,只不过做出的选择彼此互不干扰,我们可以单击这个复选框来*激活*它,也可以*取消激活*另一个已经激活了的复选框。复选框是 `type` 属性为 `"checkbox"` 的 `` 元素。 149 | 150 | ```html 151 |

今日份计划

152 | 打游戏 153 | 逛街 154 | 学习 155 | 看电影 156 | 画画 157 | ``` 158 | 159 | 你的选择是什么呢? 160 | 161 | ![1565563216094](assets/1565563216094.png) 162 | 163 | 164 | 165 | --- 166 | 167 | Note: 168 | 169 | 单选按钮是 `type` 属性为 `"radio"` 的 `` 元素,因为它们的外观和操作方式与老式无线电收音机(radio)上的按钮类似,如下图所示。 170 | 171 | ![Shows what radio buttons looked like in the olden days.](assets/old-radio.jpg) 172 | 173 | --- 174 | 175 | 176 | 177 | **2. select** 178 | 179 | HTML `` 标签中。每个选项用一对 ` 188 | 189 | 190 | 191 | 192 | ``` 193 | 194 | 默认状态下下拉菜单只会显示第一个选项的内容,选中其他选项后则会显示其他选项。如果需要预先指定某个选项已经选中,那么就需要设置那个 ` 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | ``` 231 | 232 | 这时虽然选项较多,却没有直接堆砌,而是按照所属类别(大洲)进行分组,其中亚洲类的印度选项和美洲类被暂时禁用,不能进行选择。 233 | 234 | ![1565586248914](assets/1565586248914.png) 235 | 236 | **3. textarea** 237 | 238 | 当我们只是在登录网站时输入一下用户名和密码,或者填写手机号码和邮箱,那么 `` 元素提供的单行文本输入框足以满足需求。但是,如果我们正在线编辑一篇新闻稿,或者给某人发一封邮件,就不会想在一个小小的文本输入框里塞进所有东西。术业有专攻,编辑长文本应当使用*文本编辑框*,顾名思义是一个专用于专门用来写字的区域,拥有足够的视觉空间并可以方便地进行浏览。` 244 | ``` 245 | 246 | 这里我们做了一个 100 列宽、20 行高的文本编辑框,其中包含的内容不会被当做 HTML 进行渲染,而是直接以纯文本显示,我们可以在编辑框中对原有内容进行编辑。 247 | 248 | ![1565590070635](assets/1565590070635.png) 249 | 250 | 如果需要显示一点文本提示用户这里需要输入什么,可以使用 `placeholder` 属性。当文本编辑框中包含文本的时候,提示文本会藏起来。微博和 Twitter 等软件对于输入文本有字数限制,这时就可以用 `placeholder` 属性发挥出提醒用户又不在编辑时影响美观的作用,同时用 `maxlength` 属性规定文本框所容纳的最大字符数量。 251 | 252 | ```html 253 | 254 | ``` 255 | 256 | 我们可以尝试着往文本框里写字或复制一首诗歌、一篇文章,会发现字数确实被限制在了 300。 257 | 258 | ![1565590445363](assets/1565590445363.png) 259 | 260 | 261 | 262 | --- 263 | 264 | Note: 265 | 266 | `placeholder` 和 `maxlength` 属性对于 `` 元素同样适用。 267 | 268 | --- -------------------------------------------------------------------------------- /事件/assets/1558824824647.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1558824824647.png -------------------------------------------------------------------------------- /事件/assets/1562599371222.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1562599371222.png -------------------------------------------------------------------------------- /事件/assets/1562626507612.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1562626507612.png -------------------------------------------------------------------------------- /事件/assets/1562998447733.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1562998447733.png -------------------------------------------------------------------------------- /事件/assets/1562999615730.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1562999615730.png -------------------------------------------------------------------------------- /事件/assets/1563063890497.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563063890497.png -------------------------------------------------------------------------------- /事件/assets/1563326586026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563326586026.png -------------------------------------------------------------------------------- /事件/assets/1563326700807.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563326700807.png -------------------------------------------------------------------------------- /事件/assets/1563326712428.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563326712428.png -------------------------------------------------------------------------------- /事件/assets/1563327353017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563327353017.png -------------------------------------------------------------------------------- /事件/assets/1563327355516.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563327355516.png -------------------------------------------------------------------------------- /事件/assets/1563327994789.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1563327994789.png -------------------------------------------------------------------------------- /事件/assets/1565500624871.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565500624871.png -------------------------------------------------------------------------------- /事件/assets/1565500672733.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565500672733.png -------------------------------------------------------------------------------- /事件/assets/1565500784247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565500784247.png -------------------------------------------------------------------------------- /事件/assets/1565501845034.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565501845034.png -------------------------------------------------------------------------------- /事件/assets/1565503235616.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565503235616.png -------------------------------------------------------------------------------- /事件/assets/1565505025525.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565505025525.png -------------------------------------------------------------------------------- /事件/assets/1565506008086.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565506008086.png -------------------------------------------------------------------------------- /事件/assets/1565562799087.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565562799087.png -------------------------------------------------------------------------------- /事件/assets/1565563216094.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565563216094.png -------------------------------------------------------------------------------- /事件/assets/1565584162296.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565584162296.png -------------------------------------------------------------------------------- /事件/assets/1565584238135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565584238135.png -------------------------------------------------------------------------------- /事件/assets/1565586248914.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565586248914.png -------------------------------------------------------------------------------- /事件/assets/1565590070635.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565590070635.png -------------------------------------------------------------------------------- /事件/assets/1565590445363.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565590445363.png -------------------------------------------------------------------------------- /事件/assets/1565600046914.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565600046914.png -------------------------------------------------------------------------------- /事件/assets/1565679212423.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565679212423.png -------------------------------------------------------------------------------- /事件/assets/1565682021275.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565682021275.png -------------------------------------------------------------------------------- /事件/assets/1565767500359.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565767500359.png -------------------------------------------------------------------------------- /事件/assets/1565771987739.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565771987739.png -------------------------------------------------------------------------------- /事件/assets/1565772422179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565772422179.png -------------------------------------------------------------------------------- /事件/assets/1565853058826.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565853058826.png -------------------------------------------------------------------------------- /事件/assets/1565854628479.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565854628479.png -------------------------------------------------------------------------------- /事件/assets/1565854636619.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1565854636619.png -------------------------------------------------------------------------------- /事件/assets/1566463506773.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/1566463506773.png -------------------------------------------------------------------------------- /事件/assets/javascript_sensors_yaw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/javascript_sensors_yaw.jpg -------------------------------------------------------------------------------- /事件/assets/old-radio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/assets/old-radio.jpg -------------------------------------------------------------------------------- /事件/事件初探.md: -------------------------------------------------------------------------------- 1 | ## 事件初探 2 | 3 | --- 4 | 5 | ### 事件的概念 6 | 7 | 我们已经在学习初期接触到了 HTML 的概念,如前文所述,HTML 是 Web 页面的基本组成,接下来我们的任务将变得新奇有趣:抛开运行器窗口的拘束吧!让我们在自己的 HTML 页面中展现出 JavaScript 的力量。 8 | 9 | 在前面的一些示例中,我们简单了解了温度转换的算法,现在可以结合 HTML 写出一个温度转换器的小程序。 10 | 11 | ![1558824824647](assets/1558824824647.png) 12 | 13 | 还记得怎样使用 HTML 文件吗?我们在 HTML 文件中输入以下代码: 14 | 15 | ```html 16 | 17 | 18 | 19 | 20 | 温度转换器 21 | 22 | 23 |

温度转换器

24 | 摄氏度: 25 | 华氏度: 26 | 27 |

28 | 29 | 30 | ``` 31 | 32 | 接下来保存并在浏览器中打开它,一个简单的温度转换器就呈现在了我们眼前。 33 | 34 | 这个 HTML 页面比我们在前面所看到的要复杂一些,因为我们将在这一章中了解到更多实用的概念。这里我们务必认识几个新的 HTML 标签: 35 | 36 | - `

温度转换器

` 这是一个 `

` 标签,它的作用是在页面上产生一个*一级标题*——就像我们在文档处理软件中所用到的那样。 37 | - ` ` 这是一个 `` 标签,它的*类型*是 `text`,这会在页面上显示一个*文本输入框*。这里我们将它的 `id` 属性设置为 `"c"` ,说明它将会用来输入摄氏度的值。后面的华氏度输入框与此类似。 38 | - `` 是一个*按钮*,就像它的外观所呈现的那样,用于传达用户的操作要求。 39 | 40 | 可以看到,上面的 HTML 元素组成了一个温度转换器的页面,我们可以在输入框中输入数字。但是,我们点击“转换”的按钮时,却不会产生任何效果。显然,我们还需要添加相应的 JavaScript 代码,才能使这个简单的**应用程序**运行起来。加入 JavaScript 后的代码如下: 41 | 42 | ```html 43 | 44 | 45 | 46 | 47 | 温度转换器 48 | 49 | 50 |

温度转换器

51 | 摄氏度: 52 | 华氏度: 53 | 54 |

55 | 66 | 67 | 68 | ``` 69 | 70 | 看起来有许多新的东西!不要着急,它们的含义都十分简单 ;-) 71 | 72 | 在 HTML 中,我们已经有了一个 `id` 为 `c` 的输入框,但是 JavaScript 并不知道这件事情!所以我们要在 JavaScript 代码中呼唤它的名字(`id`),召唤它闪亮登场。这个召唤的法术交给 `document.getElementById` 这个方法来做。`getElementById` 就是 “get element by id” 的*驼峰式*拼写形式,意为“通过 `id` 来获取元素。这样,我们就通过元素的 `id` 得到了这个元素。其后的 `button` 和 `result` 也是同理。 73 | 74 | 在第三章中,我们见过了摄氏度转换为华氏度的公式: 75 | 76 | $$ F = C \times1.8 +32 $$ 77 | 78 | 函数 `c2f` 根据这个公式完成计算。 79 | 80 | --- 81 | 82 | Note: 83 | 84 | 英语中,把 A “转换” 为 B 可以写作 “A to B”。由于英语单词 "two" 和 "to" 发音相同,因此数字 2 是单词 to 的常见替代形式,“c2f” 即 “c to f”。 85 | 86 | --- 87 | 88 | 接下来是这个小程序最核心的部分: 89 | 90 | ```javascript 91 | button.onclick = () => { 92 | result.innerHTML = c2f(parseFloat(c.value)).toFixed(2); 93 | }; 94 | ``` 95 | 96 | 我们可以从用途猜测它的含义:当我们*点击*(click)一个按钮时,会期望按钮做一些事情。需要做的事情写在一个函数里,当这个按钮被单击时(`onclick`),就执行这个函数。函数所做的事情自然也不足为奇:首先获取到摄氏度输入框的里的值(`c.value`),由于它是一个字符串,所以把它转换为数字方便进行计算,再用 `c2f` 转换为相应的华氏度值并取两位小数。最后,让它在 `result` 上显示出结果。像这样**当按钮被点击后执行对应操作**的技术,称为*事件*,我们可以给事件*绑定*一个函数,也就是*事件处理函数*。 97 | 98 | 在这里,`id` 为 `"c-to-f"` 的按钮被触发*单击事件*时,会调用我们绑定的事件处理函数,执行相应操作。这个过程称为*响应*。 99 | 100 | 显然,这个小程序还未达到我们预期的效果,还需要相应的把摄氏度转化为华氏度的功能。我们已经了解了原理,非常容易办到。我们先给页面添加一个对应的操作按钮: 101 | 102 | ```html 103 | 104 | ``` 105 | 106 | ` 152 | 153 | 154 |

温度转换器

155 | 摄氏度: 156 | 华氏度: 157 | 158 | 159 |

160 | 161 | 162 | ``` 163 | 164 | 现在保存并刷新页面。咦?这个小程序似乎不像原来那样正常工作了! 165 | 166 | 原因出在浏览器解析 HTML 的机制上:`` 元素包含 HTML 文件的基本信息,因此它会比 `` 更早解析。在解析 JavaScript 脚本时,由于还没有解析 `` 中的内容,浏览器不知道 `` 有哪些需要的东西,JavaScript 中无法获取到相应的元素,自然无法正常运行。 167 | 168 | 那么解决的办法是什么呢?让我们的思路随浏览器一起往下走: 169 | 170 | `` 和 `` 中的内容先后解析完毕后(即“加载完毕”),将会触发当前窗口对象的*加载事件*,加载事件标志着页面中所需要的一切已经准备完毕。那么,我们就可以在加载事件的处理函数中做我们需要做的事情! 171 | 172 | ```javascript 173 | /* 174 | * temperature.js 175 | */ 176 | // 这两个函数不需要页面加载成功 177 | const c2f = (value) => value * 1.8 + 32; 178 | const f2c = (value) => (value - 32) / 1.8; 179 | 180 | window.onload = () => { 181 | const c = document.getElementById("c"); 182 | const f = document.getElementById("f"); 183 | const buttonC2F = document.getElementById("c-to-f"); 184 | const buttonF2C = document.getElementById("f-to-c"); 185 | const result = document.getElementById("result"); 186 | 187 | buttonC2F.onclick = () => { 188 | result.innerHTML = `${c.value} ℃ = ${c2f(parseFloat(c.value)).toFixed(2)} ℉`; 189 | }; 190 | 191 | buttonF2C.onclick = () => { 192 | result.innerHTML = `${f.value} ℉ = ${f2c(parseFloat(f.value)).toFixed(2)} ℃`; 193 | }; 194 | }; 195 | ``` 196 | 197 | 虽然也可以把 JavaScript 直接写在所有页面元素的下面,但这样做不太优雅。出于关注点分离的思想,把 JavaScript 写在单独的文件里并在页面中引用的方式更为推荐,具有更好的灵活性。加载事件使我们可以在合适的时间做合适的事情,不必关注 JavaScript 文件被引入的具体位置。 198 | 199 | 200 | 201 | 202 | 203 | ### 一切皆事件 204 | 205 | 页面中可以被触发的事件实际上非常多,每个都代表了一种可能的操作状态。有些事件是特定于对象的,如只属于当前窗口的 `load` 事件,而 HTML 元素则拥有许多与用户相关的事件。现在让我们一起来看一个简单的应用程序:每当用户移动鼠标、单击或双击页面时,就会显示一条消息。 206 | 207 | ```html 208 | 211 | 212 | 213 | 214 | 215 | 事件响应 App 216 | 217 | 218 | 219 | 220 | < 221 | ``` 222 | 223 | ```javascript 224 | /* 225 | * app.js 226 | */ 227 | const addMessage = (message) => { 228 | document.write(`
  • ${message}
  • `); 229 | } 230 | 231 | window.onload = () => { 232 | addMessage("触发页面加载事件"); 233 | document.body.onmousemove = () => addMessage("触发鼠标移动事件"); 234 | document.body.onclick = () => addMessage("触发单击事件"); 235 | document.body.ondblclick = () => addMessage("触发双击事件"); 236 | }; 237 | ``` 238 | 239 | 我们定义了一个 `addMessage` 函数,每当调用该函数都会在页面上打印一个新的列表项,显示所需的信息。 240 | 241 | 然后应当响应窗口的 `load` 事件: 242 | 243 | ```javascript 244 | window.onload = () => addMessage("触发页面加载事件"); 245 | ``` 246 | 247 | 窗口加载后,还要一并为页面添加 `mousemove`、`click` 和 `dblclick` 事件的处理函数。每当用户移动鼠标,就会触发 `mousemove` 事件,双击则是 `dblclick` 事件。这个应用的运行结果和交互看起来像下面这样。 248 | 249 | ![1562626507612](assets/1562626507612.png) 250 | 251 | -------------------------------------------------------------------------------- /事件/事件的细节.md: -------------------------------------------------------------------------------- 1 | ## 芥子纳须弥·事件的细节 2 | 3 | --- 4 | 5 | ### 事件捕获与冒泡 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ### 默认操作 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ### 事件队列 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /事件/定时器.md: -------------------------------------------------------------------------------- 1 | ## 定时器 2 | 3 | --- 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /事件/移动设备事件.md: -------------------------------------------------------------------------------- 1 | ## 移动设备事件 2 | 3 | --- 4 | 5 | ### 触摸和手势事件 6 | 7 | “移动设备”这个词的历史比我们所想象的还要悠久,它最初始于移动的车辆之间的通信实验,那个时候,更确切的叫法是“移动电话”。1926 年,在柏林和汉堡之间的路线上,德国国家铁路上的头等舱乘客获得了首个成功的移动电话服务。1973 年,摩托罗拉的 Martin Cooper 博士在重量为 1.1kg 的设备上进行了首次公开移动电话通话。它是世界上第一部真正意义上的“移动设备”,是我们口中的“手机”的先驱。其后的三十年里,人类在移动设备的洪荒中开不断开辟着将为后世带来深远影响的新技术,包括短信、铃声下载、表情符号等。2003 年,3G 标准开始在全球范围内采用,“移动互联网”这个词诞生了。移动设备的前置技术,逐渐在数十年的静默发展中积蓄了足够的力量。 8 | 9 | 在本章中,我们所说的移动设备,都是指从 2007 年开始所有继承了远古时代第一台 iPhone 的产品,这些产品更广为人知的名字叫**智能手机**。多年以后,我们仍然会记得,2007 年的 Apple 发布会上,iPhone 首次亮相的那一天。在乔布斯先生的手上,我们看到了一个崭新的时代。智能手机成为了移动设备真正的发展方向, 10 | 11 | 在移动设备上,浏览器会我们提供一类全新的事件,用以发挥出移动设备自身的强大特性。移动设备所拥有的触摸传感器可以让用户以一种简单、自然的方式来操控移动设备的界面。安装在屏幕底下的触摸传感器可以检测一只或一只以上的手指对屏幕的触碰,当检测到手指与屏幕发生触摸、移动或离开动作时,就会触发*触摸事件*(touch event)。触摸事件主要分为以下三种类型: 12 | 13 | - `touchstart` 事件:当手指接触屏幕时触发,相当于桌面设备上的 `mousedown`。 14 | - `touchmove` 事件:当手指在屏幕上开始移动时触发,相当于桌面设备上的 `mousemove`。 15 | - `touchend` 事件:当手指从屏幕上移走的时候触发,相当于桌面设备上的 `mouseup`。 16 | 17 | 触摸事件的处理函数可以接受一个事件对象,它包含许多有用的信息,包括: 18 | 19 | - 触摸点在屏幕上的位置; 20 | - 页面上被触摸的元素; 21 | - 以*接触对象数组*形式提供的当前屏幕上所有其他手指的触摸情况(`touches`); 22 | - 特定事件目标元素内的接触情况(`targetTouches`); 23 | - 上次触摸事件发生以来出现改变的接触(`changeTouches`)。 24 | 25 | 26 | 27 | 28 | 29 | ### 姿态和方向事件 30 | 31 | 移动设备朝向的改变可以被*姿态传感器*捕捉到,进而触发*姿态事件*。通过姿态传感器提供的信息,我们可以知道设备的哪一面朝上摆放。姿态传感器还可以检测出设备相对于三条坐标轴的旋转情况,就如同一个陀螺仪一样。一些设备,如 Apple iPhone 和 iPad,还配置了*磁力传感器*,它可以提供设备摆放的精确方向。围绕着 $$x$$轴、$$y$$轴和$$z$$轴的旋转,可以分别表示移动设备的前后倾斜角度(beta)、左右旋转角度(gamma)和平面朝向(alpha)。 32 | 33 | > 图片来源:Hillcrest Labs 34 | 35 | ![mobile axes](assets/javascript_sensors_yaw.jpg) 36 | 37 | 38 | 39 | 40 | 41 | ### 运动事件 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ### 网络状态事件 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /事件/第六章.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/事件/第六章.md -------------------------------------------------------------------------------- /信息处理/二进制数据.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/信息处理/二进制数据.md -------------------------------------------------------------------------------- /信息处理/反应式编程.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/信息处理/反应式编程.md -------------------------------------------------------------------------------- /信息处理/案例研究:编程语言.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/信息处理/案例研究:编程语言.md -------------------------------------------------------------------------------- /信息处理/模板.md: -------------------------------------------------------------------------------- 1 | ## 模板 2 | 3 | --- 4 | 5 | ### 模板引擎的概念 6 | 7 | 8 | 9 | 10 | 11 | ### Handlebars.js 模板引擎 12 | 13 | 14 | 15 | 16 | 17 | ### Dust.js 模板引擎 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /信息处理/正则表达式.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/信息处理/正则表达式.md -------------------------------------------------------------------------------- /信息处理/算术表达式解析.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /函数/assets/1555713818684.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/函数/assets/1555713818684.png -------------------------------------------------------------------------------- /函数/assets/1555713844215.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/函数/assets/1555713844215.png -------------------------------------------------------------------------------- /函数/assets/1555713869213.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/函数/assets/1555713869213.png -------------------------------------------------------------------------------- /函数/assets/1556862479426.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/函数/assets/1556862479426.png -------------------------------------------------------------------------------- /函数/assets/th-1555712687236.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/函数/assets/th-1555712687236.jpg -------------------------------------------------------------------------------- /函数/assets/th.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/函数/assets/th.jpg -------------------------------------------------------------------------------- /函数/函数和算法.md: -------------------------------------------------------------------------------- 1 | ## 手可摘星辰·函数和算法 2 | 3 | --- 4 | 5 | ### 算法的基础 6 | 7 | 函数不仅是一种管理程序复杂性的工具,更为算法的实现提供了基础性的保障。算法在人类历史长河的智慧中诞生,历史上最著名的一个数学算法是以希腊数学家欧几里德的名字命名的。在欧几里德的数学著作《几何原本》中,他描述了一个求两个整数 x 和 y *最大公约数*(greatest common divisor, gcd)的过程,即一个可以同时整除 x 和 y 的最大整数的算法。例如,49 和 35 的 gcd 是 7,6 和 18 的 gcd 是 6,32 和 33 的 gcd 是 1.欧几里德算法可以描述如下: 8 | 9 | 1. 用 x 除以 y 并计算余数 r。 10 | 2. 若 r 等于 0,则算法结束,最大公约数是 y。 11 | 3. 若 r 不等于 0,则令 x 的值为 y,y 的值为 r。 12 | 4. 回到步骤 1。 13 | 14 | 这个算法可以很容易地用 JavaScript 代码描述: 15 | 16 | ```javascript 17 | let gcd = (x, y) => { 18 | let r = x % y; 19 | while (r != 0) { 20 | x = y; 21 | y = r; 22 | r = x % y; 23 | } 24 | return y; 25 | } 26 | ``` 27 | 28 | 这个算法实现起来相当简单,但相比你自己可能发现的任何计算策略显得更有效率。事实上,欧几里德算法至今在包括网络安全的加密协议实现等很多情况下都得到了广泛的应用。 29 | 30 | 同时,我们很难清晰明确地看出该算法为什么会得到正确的结果。它的正确性已经在《几何原本》第七章命题二中得到了证明。虽然并不是总有证据来证明算法对计算机应用的驱动作用,但这些证据能让你对程序的正确性更有信心。 31 | 32 | 除了计算最大公约数以外,生活中我们还会不可避免的遇到一些算法问题,它们往往代表了我们需要解决和思考的一些实际问题。例如,当我们有了收入之后,会有意识地对财产进行储蓄或投资。银行或理财机构对于存款或投资的收益都做了详细的规划。如果我们要手动计算这些问题,不仅非常繁琐,还要冒着可能出错的风险。作为人类,我们只需要思考解决问题的方式,而重复性的繁杂工作都应该交给计算机来处理,使它根据我们的指挥更好地为我们服务。 33 | 34 | 定期存款的本金与总利息之和的计算遵循下面的公式: 35 | $$ 36 | 本利和=本金(1 + 利率(\frac{存期}{12}))^{(时长/存期)} 37 | $$ 38 | 我们可以根据这个公式直观地写出用于计算的函数: 39 | 40 | ```javascript 41 | let sum = (principal, rate, savingperiod, lastperiod) => { 42 | let value = principal * (Math.pow( 43 | (1 + rate * ( 44 | savingperiod / 12 45 | )), 46 | Math.floor(lastperiod / savingperiod))); 47 | return value.toFixed(2); 48 | }; 49 | ``` 50 | 51 | 这个函数具有四个参数,反映了实际应用中的复杂性,而函数起到的作用就是隐藏复杂性。`sum` 函数隐藏公式的算法细节细节,向外提供一个统一的计算方式。在进行计算时,我们应当且只应当关注每次计算时的值,而不必将精力放在实际的计算过程中,就像计算器提供的方便的数学函数,我们只需将它当成和四则运算没有什么区别的简单运算,当需要用到的值发生改变时,就改变它的参数——函数的意义就是这样简单。 52 | 53 | - `principal` :本金 54 | - `rate`:利率 55 | - `savingperiod`:存期 56 | - `lastperiod`:时长 57 | 58 | 它的核心实际上只有一行表达式,但是它有些复杂,因此我们将其拆分成多行,以便于阅读。计算完成后,我们将得到的值取两位小数,就是最后的结果。 59 | 60 | 使用一些值来测试这个函数: 61 | 62 | ```javascript 63 | alert(sum(100, 0.06, 5, 16)); // 107.69 64 | alert(sum(1000, 0.01, 12, 19)); // 1010.00 65 | alert(sum(520, 0.5, 3, 17)); // 937.06 66 | alert(sum(10, 0.1, 6, 24)); // 12.16 67 | alert(sum(6622, 0.087, 9, 31)); // 8004.68 68 | ``` 69 | 70 | 就像我们在中学时学到的数学函数一样,`sum` 这样的函数可以简单地看做是结果与一组值之间的映射。它单纯地进行一些计算并返回一个值。 71 | 72 | 73 | 74 | --- 75 | 76 | Note: 77 | 78 | *纯函数*可以理解为一种**相同参数必定有相同返回值**的函数,而不会产生任何可观察到的副作用(如改变状态)。 79 | 80 | 纯函数诸如:`Math.cos` `sum` `gcd` 81 | 82 | 而 `Math.random` `prompt` `alert` 则是非纯函数。 83 | 84 | --- 85 | 86 | 87 | 88 | 89 | 90 | ### 组合函数 91 | 92 | 想象一下,假如我们在玩一个掷骰子游戏,我们手中有两枚骰子,每次同时抛掷,那么它们的随机点数共有多少种组合呢? 93 | 94 | ![骰子](assets/th-1555712687236.jpg) 95 | 96 | 上图显示了 6 + 4 这一点数组合,而实际上我们也可能会得到 2 + 3,4 + 1, 6 + 3……共有十五种可能组合。作为一个编程者,应该思考一个更加普遍的问题:给定一个含有 $$ n $$ 个元素的集合,可以从中得到多少个包含 $$ k $$ 元素的子集?可以通过如下的*组合函数*(combinations function) `C(n, k)` 来得到答案: 97 | $$ 98 | C(n, k) = \frac{N!}{k \times (n - k)!} 99 | $$ 100 | 其中,感叹号代表了阶乘函数,表明为从 1 到所指定的值中所有整数的乘积。我们可能还想查看对于更大的 $$ n $$ 和 $$ k $$ 而言有多少种组合,因此可以写出这样的程序来方便地计算。 101 | 102 | ![1555713818684](assets/1555713818684.png) 103 | 104 | ![1555713844215](assets/1555713844215.png) 105 | 106 | ![1555713869213](assets/1555713869213.png) 107 | 108 | 这个程序分别向用户请求 $$ n $$ 和 $$ k $$ 的值,然后显示函数 `C(n, k)` 的值,代码实现如下。 109 | 110 | ```javascript 111 | let fact = (n) => { 112 | let result = 1; 113 | for (let i = 1; i <= n; i += 1) { 114 | result *= i; 115 | } 116 | return result; 117 | } 118 | 119 | let combinations = (n, k) => { 120 | return Math.floor( 121 | fact(n) / (fact(k) * fact(n - k)) 122 | ); 123 | } 124 | 125 | let n = +parseInt(prompt("请输入组合物体的总数:")); 126 | let k = +parseInt(prompt("请输入每次组合的个数:")); 127 | let c = combinations(n, k); 128 | alert(`C(k, n) = ${c}`); 129 | ``` 130 | 131 | 正如你所看到的, Combinations 程序划分为两个函数,借用第四章中定义的 `fact` 函数来计算所需要的阶乘结果,再利用 `combinations` 函数计算 `C(n, k)` 的值。 132 | 133 | 134 | 135 | 136 | 137 | ### 回文识别 138 | 139 | *回文*(palindrome)是指其字母排列正序与倒序均一致的词语,例如单词“level”或“noon”,本节的目的是编写一个判断函数以检测一个字符串是否属于回文。调用 `isPalindrome("level")` 应该返回 `true`;调用 `isPalindrome("xyz")` 应返回 `false`。 140 | 141 | 和大多数编程问题一样,这里有解决该问题的几种合理策略。根据我们的经验,可能首先尝试的方法是使用一个 `for` 循环依次读取字符串前半部分每一个索引位置上的字符。在每个位置上,代码将检测该字符是否与出现在字符串末尾对应对称位置的字符匹配。采取这种策略的代码如下: 142 | 143 | ```javascript 144 | let isPalindrome = (str) => { 145 | let n = str.length; 146 | for (let i = 0; i < n / 2; i += 1) { 147 | if (str[i] !== str[n - i - 1]) { 148 | return false; 149 | } 150 | } 151 | return true; 152 | } 153 | ``` 154 | 155 | 简单测试一下它是否与我们的预期一致: 156 | 157 | ```javascript 158 | alert(isPalindrome("level")); // true 159 | alert(isPalindrome("xyz")); // false 160 | alert(isPalindrome("noon")); // true 161 | alert(isPalindrome("")); // true 162 | alert(isPalindrome(123)); // true 163 | ``` 164 | 165 | 只有一个字符的字符串和空串本身就是回文,但是为什么数字 `123` 也会被检测为回文?这是因为数字的 `length` 属性为 `undefined`(不存在),对它进行除以二的运算会得到 0,因此 `for` 循环不会执行,直接返回 `true`,便有了这个怪异的结果。毫无疑问,我们应该对输入的数据进行检查,以确认它一定是我们需要的数据,这是编写任何函数都很重要的一个环节。 166 | 167 | ```javascript 168 | let isPalindrome = (str) => { 169 | if (typeof str !== "string") { 170 | return false; 171 | } 172 | 173 | let n = str.length; 174 | for (let i = 0; i < n / 2; i += 1) { 175 | if (str[i] !== str[n - i - 1]) { 176 | return false; 177 | } 178 | } 179 | return true; 180 | } 181 | ``` 182 | 183 | 这个函数看起来有些长,我们可以考虑使用第三章中遇到的字符串与数组方法,用下面更简捷的形式编写 `isPalindrome` 函数: 184 | 185 | ```javascript 186 | let isPalindrome = (str) => { 187 | if (typeof str !== "string") { 188 | return false; 189 | } 190 | return str === str.reverse(); 191 | } 192 | ``` 193 | 194 | 最后一行的字面意义就是:如果字符串与它的逆序形式完全相等,那么它就是一个回文。不过,JavaScript 并没有为字符串提供 `reverse` 方法,我们只好先将其拆分为数组,使用数组的逆序方法,再组合成字符串,这样得到的就是字符串的逆序形式。 195 | 196 | ```javascript 197 | let isPalindrome = (str) => { 198 | if (typeof str !== "string") { 199 | return false; 200 | } 201 | return str === str.split("").reverse().join(""); 202 | } 203 | ``` 204 | 205 | 现在我们来测试一下。 206 | 207 | ```javascript 208 | alert(isPalindrome("level")); // true 209 | alert(isPalindrome("xyz")); // false 210 | alert(isPalindrome("noon")); // true 211 | alert(isPalindrome("")); // true 212 | alert(isPalindrome(123)); // false 213 | ``` 214 | 215 | 嗯!一切都好。 216 | 217 | 在上述两种实现方式中,第一个版本更为有效。第二个版本必须构造一个数组和新的字符串,且其中的字符与源字符串中的字符顺序是相反的。更糟糕的是,它通过一个一个字符地拆分字符串、一个一个地逆序排列再一个一个地组合起来,创建了两个临时字符串和一个临时数组。第一个版本不需要创建任何字符串。它通过选择和比较字符串中的字符完成功能,这被证明是一种低代价的运算。 218 | 219 | 除了二者在效率上的不同外,第二种编写方式也有许多优点,特别是对于编程新手而言,可将其作为参考范例。其主要优点为:一方面,它通过使用 `reverse` 方法重用了已有的代码;另一方面,第一个版本需要字符串中字符的索引位置,而第二个版本则隐藏了涉及这方面的编程复杂性。对于大部分初学者,至少要花费一分钟或两分钟弄明白为什么代码中要包含下标访问表达式 `str[n - i - 1]` ,或者为什么它要在 `for` 循环检验中使用 `<` 操作符而不是 `<=` 。相比之下以下这一行代码: 220 | 221 | ```javascript 222 | return str === str.split("").reverse().join(""); 223 | ``` 224 | 225 | 读起来几乎就和英语一样流畅:如果一个字符串,和它拆分成的数组的逆序形式看起来一样,则它是一个回文。 226 | 227 | 尤其是当我们正在学习编程时,致力于程序的简洁性比关注其执行效率更为重要。鉴于现在计算机的速度,牺牲几个 CPU 周期来使程序更易于理解是值得的。 228 | 229 | 230 | 231 | 232 | 233 | ### 中文数字 234 | 235 | 本节将讨论一个面向文本处理的简单算法应用,用于将阿拉伯数字转换为其汉字形式。 236 | 237 | 根据《五经算术》的记载,黄帝将数字分为“十等三法”。 238 | 239 | > 十等者,谓“亿、兆、京、垓、秭、穰、沟、涧、正、载”也。 240 | > 241 | > 三法者,谓“上、中、下”也。 242 | > 243 | > 下数者,十十变之。若言十万曰亿,**十亿曰兆**,十兆曰京也。 244 | > 245 | > 中数者,万万变之。若言万万曰亿,**万亿曰兆**,万兆曰京也。 246 | > 247 | > 上数者,数穷则变。若言万万曰亿,**亿亿曰兆**、兆兆曰京也。 248 | > 249 | 250 | 也就是说,汉语的大数单位有三种递进法则。 251 | 252 | 其中上法为自乘系统:,万万为亿,亿亿为兆,兆兆为京,以此类推。这种方式,希腊的阿基米德也采用过,写成科学计数法即:10^4^ => 万,10^8^ => 亿,10^16^ => 兆,10^32^ => 京。 253 | 254 | 二是中法,为万进系统,以万递进:万万为亿,万亿为兆,万兆为京,以此类推。写成科学计数法就是:10^4^ => 万,10^8^ => 亿,10^12^ => 兆,10^16^ => 京。 255 | 256 | 三是下法,为十进系统,以十递进:十万为亿,十亿为兆,十兆为京,以此类推。写成科学计数法即:10^4^ = >万,10^5^ => 亿,10^6^ => 兆,10^7^ => 京。 257 | 258 | 本节将采用中法来作为数字单位递进准则。因此,一个中文数字可以通过以下方式由阿拉伯数字构造: 259 | 260 | - 将阿拉伯数字每四位为一组,每组从低到高的单位分别为 “”(个位),“万”,“亿”,“兆”,以此类推。 261 | - 每组内的转换方法是一样的,比如 1234,就是"一千二百三十四",加上对应的单位,如“万”,就是“一千二百三十四万”。 262 | 263 | 那么要做的第一件事就是将单位和基本数字存起来,按需取用。 264 | 265 | ```javascript 266 | const nums = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]; 267 | const units = ["", "十", "百", "千"]; 268 | const sections = ["", "万", "亿", "兆", "京", "垓", "秭", "穰", "沟", "涧", "正", "载"]; 269 | ``` 270 | 271 | 其中我们要注意一些细节: 272 | 273 | - 结尾的零都忽略,如 1200,就是“一千二百”。 274 | - 中间的零,只需要用一个零表示,如 1004,是“一千零四”。 275 | 276 | - 如果整组都是0,忽略其单位。 277 | 278 | - 如果一组在 10 到 19 之间,则十位可以省略一,即不写成“一十”;否则,十位上的“一”都要添加。如 12 就是“十二”,312就是“三百一十二”。 279 | 280 | 一个要点是:如果一个数字除以递进基数的余数不为 0,那么这个余数就是递进位后的数字,例如 ` 12 % 10 === 2`,那么 12 的次位就为 2,相应地转为为汉字“二”。大数的单位递进基数是 10000,因此我们将它除以 10000 并取余数,如 `12345 % 10000 === 2345`,那么“两千三百四十五”就是递进位“万“后的数字。 281 | 282 | 下面简要说明一下大致转换流程: 283 | 284 | 1. 我们首先会判断它是否为 0,如为 0 则直接得到 0,显然并不是。 285 | 2. 因此我们将它除以 10000(一万)并得到余数,它不会大于 9999,因此将其视为一组。 286 | 3. 使用另一个转换函数对这一组进行专门转换: 287 | 1. 如果它大于 0,取其整除余数,如果余数为 0 那么填充“零”,并用一个标记记录,如果前面已跟着“零”那么就不会填充。 288 | 2. 根据余数判断其汉字形式,然后根据数位决定单位,依次累积。 289 | 4. 一组转换完毕后,回到步骤 2,直到剩下的数向下取整为 0。 290 | 291 | 这里给出算法的完整实现。 292 | 293 | ```javascript 294 | const nums = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]; 295 | const units = ["", "十", "百", "千"]; 296 | const sections = ["", "万", "亿", "兆", "京", "垓", "秭", "穰", "沟", "涧", "正", "载"]; 297 | 298 | // 将每一组四位数字单独进行转换 299 | const sectionToChinse = (section) => { 300 | let ins = "", str = ""; 301 | let unitPos = 0, zero = true; 302 | while (section > 0) { 303 | let v = section % 10; 304 | if (v === 0) { 305 | if (!zero) { 306 | zero = true; 307 | str = nums[v] + str; 308 | } 309 | } else { 310 | zero = false; 311 | ins = nums[v]; 312 | ins += units[unitPos]; 313 | str = ins + str; 314 | } 315 | unitPos += 1; 316 | section = Math.floor(section / 10); 317 | } 318 | return str; 319 | } 320 | 321 | // 将数字分组并进行转换 322 | const numberToChinse = (number) => { 323 | let unitPos = 0; 324 | let ins = "", str = ""; 325 | let needZero = false; 326 | if (number === 0) { 327 | return nums[0]; 328 | } 329 | 330 | while (number > 0) { 331 | let section = number % 10000; 332 | if (needZero) { 333 | str = nums[0] + str; 334 | } 335 | ins = sectionToChinse(section); 336 | ins += (section !== 0) ? sections[unitPos] : sections[0]; 337 | str = ins + str; 338 | needZero = section < 1000 && section > 0; 339 | number = Math.floor(number / 10000); 340 | unitPos += 1; 341 | } 342 | return str; 343 | } 344 | 345 | // 测试代码 346 | let num = +parseInt(prompt("请输入一个数字:")); 347 | alert(numberToChinese(num)); 348 | ``` 349 | 350 | 注意:由于算法中使用了除法,而 JavaScript 的除法具有精度限制,因此当数字太大时会因溢出给出错误的结果。如果这个整数不大于 16 位,那么结果应当是准确的。如果你还记得第三章中的相关内容,你应该会知道为什么。 351 | 352 | 来吧,使用一些数字来验证我们的算法吧。这个程序目前不支持负数,但我们可以做一些小小的改进来使转换函数对于负数依然有效。这是一个小练习。 353 | 354 | 355 | 356 | --- 357 | 358 | 练习 5.2 359 | 360 | 1. 编写一个函数,接受一个数组作为参数,找出其中最小值和最大值并返回。尝试用对象同时传递两个值。 361 | 2. 编写一个函数,接受一个数组作为参数,找出其中的中位数、众数,计算出方差和极差,并返回。 362 | 363 | --- -------------------------------------------------------------------------------- /函数/方法.md: -------------------------------------------------------------------------------- 1 | ## 调兵遣将·方法 2 | 3 | --- 4 | 5 | ### 作为成员的函数 6 | 7 | 我们在上一节中已经了解过,JavaScript 中的函数也是一种值,它们可以被赋给其他标识符,自然,函数也可以成为对象属性的值。当一个函数成为一个对象的成员时,我们可以叫它*成员函数*,更常见一点的叫法是*方法*。 8 | 9 | 按照我们的理解,对象中的方法看起来应该是这样的: 10 | 11 | ```javascript 12 | let person = { 13 | name: "Jason", 14 | say: () => alert("Hello world"); 15 | } 16 | ``` 17 | 18 | 那么我们就可以调用这个方法: 19 | 20 | ```javascript 21 | person.say(); // "Hello world" 22 | ``` 23 | 24 | 我们也可以让方法的定义与这个对象的其他属性相关,例如,在打招呼时进行自我介绍。 25 | 26 | ```javascript 27 | let person = { 28 | name: "Jason", 29 | say: (who) => alert(`Hello ${who}, my name is ${person.name}.`) 30 | }; 31 | 32 | person.say("Eric"); // "Hello Eric, my name is Jason." 33 | ``` 34 | 35 | 在 JavaScript 的语法中,我们可以使用更加简便的方式来定义一个方法,不必写出箭头与冒号,不过这时,大括号是必需的。 36 | 37 | ```javascript 38 | let person = { 39 | name: "Jason", 40 | say (who) { 41 | alert(`Hello ${who}, my name is ${person.name}.`); 42 | } 43 | }; 44 | person.say("Eric"); // "Hello Eric, my name is Jason." 45 | ``` 46 | 47 | 当我们要使用 `person.name` 时,我们会写出它的完整形式,但是在对象的方法中,访问这个对象的其它成员,是否也有什么简便的方法呢?事实上,你只需要使用关键字 `this` 指代这个对象本身。这样就带来了很大的便利性:无论我们的对象叫什么名字,名字有多长,在它方法中遇到它自己都只需要用 `this` 表示。 48 | 49 | ```javascript 50 | let person = { 51 | name: "Jason", 52 | say (who) { 53 | alert(`Hello ${who}, my name is ${this.name}.`); 54 | } 55 | }; 56 | person.say("Eric"); // "Hello Eric, my name is Jason." 57 | ``` 58 | 59 | 60 | 61 | 62 | 63 | ### 对外接口 64 | 65 | 作为“定义在特定对象中的函数”,方法与我们上一节中所接触到的通常的函数有什么不一样的意义呢? 66 | 67 | 对于对象而言,方法实际上是一个对外沟通的渠道,使对对象的操作便于管理与理解,并且隐藏一些细节。我们将通过一些实际的例子来说明对象中方法的作用和意义。 68 | 69 | 例如,我们有一辆车,并且知道一些关于它的基本信息,作为一个对象大概是这样子的: 70 | 71 | ```javascript 72 | let car = { 73 | model: "Honda Civic", 74 | year: 2009, 75 | miles: 20000 76 | }; 77 | ``` 78 | 79 | 现在,如果我们要得到一段文本,用于将车的信息片段连贯成一句话,自然可以这样做: 80 | 81 | ```javascript 82 | let info = `${car.model} built in ${car.year}, it has done ${car.miles} miles`; 83 | alert(info); // Honda Civic built in 2009, it has done 20000 miles 84 | ``` 85 | 86 | 由于我们已经了解过了函数的使用,因此可以将拼接字符串的操作放在一个函数中,隐藏实现细节,同时使得我们也可以应用于其它的车对象: 87 | 88 | ```javascript 89 | let toString = (car) => { 90 | // 形参的这个 car 可以指代任何车对象 91 | let info = `${car.model} built in ${car.year}, it has done ${car.miles} miles`; 92 | return info; 93 | } 94 | alert(toString(car)); // Honda Civic built in 2009, it has done 20000 miles 95 | ``` 96 | 97 | 这样看起来似乎没问题,但是如果我们不单单有小汽车,还有轮船、飞机、火车……每种东西都需要一个自己的 `toString` 方法来输出专属于自身类型的信息,但是名为 `toString` 的函数只能有一个,我们找到了一个解决办法:将 `toString` 函数加上它所用于的对象名称,例如,用于车对象的 `toString` 可以写成 `toStringOfCar`。 98 | 99 | ```javascript 100 | let toStringOfCar = (car) => { 101 | // 形参的这个 car 可以指代任何车对象 102 | let info = `${car.model} built in ${car.year}, it has done ${car.miles} miles`; 103 | return info; 104 | } 105 | alert(toStringOfCar(car)); // Honda Civic built in 2009, it has done 20000 miles 106 | ``` 107 | 108 | 但我们很快就发现了新的问题:这样的命名方式繁琐啰嗦。一个对象使用怎样的描述信息,应该由它自己决定,这样我们就可以把 `toString` 写成 `car` 对象的方法,而其他的对象也可以有自己的 `toString` 方法,这样互不冲突,每个对象专属的 `toString` 方法实现时只需关注自身所属对象的需求。 109 | 110 | ```javascript 111 | let car = { 112 | model: "Honda Civic", 113 | year: 2009, 114 | miles: 20000, 115 | toString () { 116 | let info = `${this.model} built in ${this.year}, it has done ${this.miles} miles`; 117 | return info; 118 | } 119 | }; 120 | alert(car.toString()); // Honda Civic built in 2009, it has done 20000 miles 121 | ``` 122 | 123 | 同时,我们可以把一些成员操作的细节隐藏在特定的方法里,这样我们可以使用统一而简洁的方式达到某些目的,不需要关心具体的实现过程,方法应该有怎样的细节,由对象自己负责,而使用时无需考虑,这种策略称为*封装*。例如,假如我们有一些书,每本书都有一个独一无二的数字编号,并且它应该是一个正整数,自然会想到: 124 | 125 | 创建一些 `book` 对象,每个对象都拥有一个名为 `no` 的数字属性用于记录编号。 126 | 127 | ```javascript 128 | let book = { 129 | name: "Design Patterns", 130 | author: "Addy Osmani", 131 | no: 10 132 | }; 133 | // 现在我们要修改数字编号 134 | book.no = 20; 135 | ``` 136 | 137 | 我们需要一个数字编号,但如果不小心用了一个 `undefined` 当做编号,抑或使用了非正数、浮点数,这与我们的要求不符。那么我们可以写一个方法,专门用来设置编号的值,并进行相应检查。 138 | 139 | ```javascript 140 | let book = { 141 | name: "Design Patterns", 142 | author: "Addy Osmani", 143 | no: 10, 144 | setNo (n) { 145 | if (n === undefined || n < 1 || n % 2 !== 0) { 146 | return; 147 | } 148 | this.no = n; 149 | } 150 | }; 151 | ``` 152 | 153 | 使用时只需要调用 `book.setNo` 来代替原本的赋值操作即可。 154 | 155 | ```javascript 156 | book.setNo(5); // OK 157 | book.setNo(-1); // 不符合要求,book.no 没有被改变 158 | book.setNo(); // 不符合要求,book.no 没有被改变 159 | alert(book.no); // 5 160 | ``` 161 | 162 | 使用这种策略的好处显而易见。我们可以按照需要对编号进行更多约束,也可以在设置值过程中顺便做其它的事情,还可以进行自动编号——只要稍微改变一下方法的细节。 163 | 164 | ```javascript 165 | let book = { 166 | name: "Design Patterns", 167 | author: "Addy Osmani", 168 | no: 0, // 编号的初始值 169 | setNo () { 170 | this.no++; 171 | } 172 | }; 173 | book.setNo(); 174 | alert(book.no); // 1 175 | ``` 176 | 177 | `set` 开头的方法具有“设置(某属性)”的含义,与之相对,我们也可以为“获取(值)”这一操作封装一个 `get` 开头的方法: 178 | 179 | ```javascript 180 | let book = { 181 | name: "Design Patterns", 182 | author: "Addy Osmani", 183 | no: 0, // 编号的初始值 184 | setNo () { 185 | this.no++; 186 | }, 187 | getNo () { 188 | return this.no; 189 | } 190 | }; 191 | alert(book.getNo()); // 0 192 | book.setNo(); 193 | alert(book.getNo()); // 1 194 | ``` 195 | 196 | 在一个对象中,`get` 开头的方法一般与 `set` 成对出现,它们的作用也是相对的。关于方法的命名方式有许多具体的细节与习惯,我们将在下一小节了解到。 197 | 198 | 不同类型的对象可能会拥有不同的方法以完成相应的工作,例如设置和获取数据,这类方法实际上承担了一个信息传递渠道的作用,就像可插拔的 USB 接口,我们也可以将这些方法称为*接口*。在 JavaScript 中,对象间的通信通过信息发送和请求来实现,我们将传递的这些信息统称为*消息*。对象间的消息发送通常理解为一个对象调用属于另一个对象的方法,例如 `book.getNo()` 就是调用了 `book` 方法,即“接收了 `book.no` 这一信息”。 199 | 200 | 我们应该尽可能地用特定的方法去封装原始的操作细节,并进行充分的检查以规避可能出现的问题。 201 | 202 | ```javascript 203 | let book = { 204 | name: "Design Patterns", 205 | author: "Addy Osmani", 206 | no: 0, // 编号的初始值 207 | 208 | getName () { 209 | return this.name; 210 | }, 211 | setName (name) { 212 | if (typeof name !== "string") { 213 | return; 214 | } 215 | this.name = name; 216 | }, 217 | 218 | getAuthor () { 219 | return this.author; 220 | }, 221 | 222 | setAuthor (author) { 223 | if (typeof author !== "string") { 224 | return; 225 | } 226 | this.author = author; 227 | }, 228 | 229 | setNo () { 230 | this.no++; 231 | }, 232 | getNo () { 233 | return this.no; 234 | } 235 | }; 236 | 237 | alert(book.getName()); // "Design Patterns" 238 | alert(book.getAuthor()); // "Addy Osmani" 239 | book.setName("JavaScript Design Patterns"); 240 | alert(book.getName()); // "JavaScript Design Patterns" 241 | book.setAuthor(null); 242 | alert(book.getAuthor()); // "Addy Osmani" 243 | book.setNo(); 244 | book.setNo(); 245 | alert(book.getNo()); // 2 246 | ``` 247 | 248 | 249 | 250 | 有时,我们需要在一个对象中存放一组函数,这个对象承担的功能仅仅是一个“工具箱”,例如 `Math` 对象,专门用于提供各类数学函数。我们可以把这类本身属性不需要改变,专门用于提供单独操作的对象称为*模块*,一个模块意味着一组实用的功能,这些以方法形式提供的功能称为*库接口*。除了 `Math` 之外,JavaScript 还提供了其它一些模块,例如 `JSON` `Reflect` `Intl` 等,**它们都是一些用于特定操作的函数的集合**,我们将在后面的章节接触到它们。 251 | 252 | 253 | 254 | 255 | 256 | ### 命名约定 257 | 258 | 我们在 JavaScript 中定义方法时,一般会遵循一些特殊的命名法则,以便于理解和记忆,增强程序的*可读性*。 259 | 260 | 一个习惯是,当一个方法需要操作或访问对象的特定属性时,一般会写成一个动词 + 名词的形式,例如 `getName` 的意义就是“获取 `name` 属性的值。获取与访问一般是成对出现的,因此当我们需要用到 `getXXX` 时,我们还需要写一个相应的 `setXXX` 。同时,方法的拼写遵循*驼峰式大小写*,即首字母小写,其它单词的首字母大写。 261 | 262 | 当我们需要一个名字很长的方法,比如“如果下雨了就提醒哥哥回来”,用驼峰式大小写拼写,看起来是这样的: 263 | 264 | `remindBrotherToBackWhenRains` 265 | 266 | 当然实际生活中我们不可能会写出这样冗长的方法名,这里只是说明驼峰式大小写的效果。为了保持方法名的简洁,我们应该尽量只使用动词 + 名词的形式,有特殊需要时在其后加上用于说明的修饰词。 267 | 268 | 我们已经见过了 `toString` 这一方法名,它的语义就是“转换为字符串”。这类用于将对象或对象的特定属性转换为所需要的形式的方法一般写作 `toAnything` 的形式,例如 `toString`、 `toNumber` 、`toArray` 等。 269 | 270 | -------------------------------------------------------------------------------- /函数/生成器函数.md: -------------------------------------------------------------------------------- 1 | ## 生成器函数 2 | 3 | --- 4 | 5 | 请想象一台机器,一台只存在于我们深邃思想之中的机器。你问它一个问题,它就会悄悄运作一番,给我们一个答案,然后就在静默中无声地等待着,直到我们再一次唤醒它,向它提问。和其他机器不同,每次向它提问时,它都留存着上一次思索的记忆。每一个回答都可能独一无二,每一个回答也一定有一个内在的秩序,这秩序以精妙的设计存在于机器的思想中,只有它和创造它的我们知晓。 6 | 7 | 这台机器已经在 JavaScript 世界中被制造出来了,它的名字叫*生成器函数*(Generator Function),这个名字使人想起工厂里流水化的生产线,批量地制造新产品。而生成器函数的确就像流水线上的机器,每次运作,都生成一个值,然后这个值被送到我们面前,然后经过一些时刻生成下一个值。生成器函数在本质上,是一种调用一次而多次返回一个值的函数,而普通函数调用一次只能返回一次。 8 | 9 | 因此,生成器函数是一种特殊类型的函数。当我们从头到尾运行标准函数时,我们最多只会得到一次值,这个值保存了函数在这次运行中所留给我们的所有东西。然而生成器函数不同的地方在于,它是可以在运行过程中暂停的,每次暂停都可以返回一个值。生成器函数能生成一组值的序列,当我们“要求”它生成一个值的时候,它就生成一个值,然后在运行过程中暂停在所处的位置。如果它不再打算生成新的值,就会告诉我们它的运行周期已经结束了。 10 | 11 | 12 | 13 | --- 14 | 15 | Note: 16 | 17 | 生成器函数是 JavaScript 中一个相对较新的概念和特性,其实它已经在 Python、PHP 和 C# 等编程语言中存在很长时间了,它常常被用来实现一种称为*协程*的语言机制。生成器函数的本质是*状态机*。 18 | 19 | --- 20 | 21 | -------------------------------------------------------------------------------- /函数/第五章练习.md: -------------------------------------------------------------------------------- 1 | ## 练习题 2 | 3 | --- 4 | 5 | 1. 编写一个函数,用于将浮点数取整为与其最接近的整数,类似于 `Math.round`。例如,假设我们有一个浮点数变量 `x`,我们可以通过将变量加上 0.5 并舍去小数部分来得到其最接近的整数。因为数值四舍五入取整时会朝着数轴上 0 的方向,所以负数取整时需要将其数值减去 0.5,而不是加上 0.5。 6 | 7 | 2. 我们已经知道 JavaScript 可以使用二进制、八进制、十进制和十六进制的数字形式,也可以自己动手,将数字转换为其它进制的字符串形式。假设要将数字 n 转换为以 b 为基数的数字,实现转换的算法如下。 8 | 1. 设置最高位为 n % b,存入一个数组。 9 | 2. 使用 n / b 代替 n。 10 | 3. 重复步骤 1 和 2,直到 n 为 0,且没有余数。 11 | 4. 将整个数组顺序逆转,连接成的字符串就是转换后的 b 进制数字字符串。 12 | 13 | 3. 如果你碰上寒冷、大风的天气,你对温度的感觉不仅取决于温度,还取决于风速。风速越高,越觉得寒冷。为了量化风速对于温度感觉的影响,美国国家气象局提供了风寒指数报告,其官网上的说明如下: 14 | 15 | ![风寒的图像ç"“æžœ](assets/th.jpg) 16 | 17 | 正如你在图片底部所看到的,气象局使用以下公式计算风寒指数: 18 | 19 | $$ 35.74 + 0.6215 t - 35.75 v ^ {0.16} + 0.4725t v ^ {0.16} $$ 20 | 21 | 其中,$$t$$是华氏温度,$$v$$是风速,单位是$$ 英里/小时 $$。 22 | 23 | 编写一个函数 `windChill`,参数为 t 和 v,返回风寒指数。为了达到这一目的,你的函数必须考虑以下情况: 24 | 25 | - 如果没有风,`windChill` 必须返回原始温度 t。 26 | - 如果温度高于 $$40 ℉ $$,风寒指数未定义,此时你应当考虑如何输出错误信息。 27 | 28 | 29 | 30 | 4. 世界上的大部分人们使用摄氏度而非华氏度,包括我们。因此为了适应我们的习惯,编写一个华氏度转到摄氏度的函数,并基于此来计算风寒指数。我们在第三章中了解过温度转换的方法,现在是发挥作用的时候。 31 | 32 | 5. 实现一个将英语基数词转换为序数词的函数。 33 | 34 | 我们小学时学过这样的规则口诀: 35 | 36 | > 基变序,有规律,词尾加上“th”; 37 | > 38 | > 一,二,三(first、second、third)特殊记; 39 | > 40 | > 从 4 开始上 th,5 变 ve 改f(fifth),8 去 t(eighth),9 减 e(ninth); 41 | > 42 | > 几十尾巴 ty 变 tie,后面跟上th(twentieth); 43 | > 44 | > 几十几,头不变,尾巴变,中间记得加个杠(twenty-fourth); 45 | > 46 | > 遇到特殊要除外(twenty-first)。 47 | 48 | 49 | 50 | 6. 实现一个将英语动词转换为现在分词形式的函数。 51 | 52 | 规则: 53 | 54 | 1. 一般情况下,直接在动词原形后面加 -ing。 55 | 2. 结尾是辅音+一个元音+一个辅音的,重复最后一个辅音字母,再加 -ing。 56 | 3. 以不发音的 -e 结尾的动词,去 -e ,再加 -ing。 57 | 58 | 7. 实现一个将阿拉伯数字转换为罗马数字的函数。 59 | 60 | 罗马数字共有 7 个,即 I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和M(1000)。按照下述的规则可以表示任意正整数。 61 | 62 | - 一个罗马数字重复几次,就表示这个数的几倍。 63 | 64 | - 在较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字。 65 | 66 | - 在较大的罗马数字的左边记上较小的罗马数字,表示大数字减小数字。 67 | - 左减的数字有限制,仅限于I、X、C。比如45不可以写成VL,只能是XLV 68 | - 但是,左减时不可跨越一个位值。比如,99不可以用IC($100-1$)表示,而是用 XCIX($ [100-10]+[10-1] $)表示(等同于阿拉伯数字每位数字分别表示。) 69 | 70 | 8. 改进之前的罗马数字函数。 71 | 72 | 罗马数字具有以下限制: 73 | 74 | - 左减数字必须为一位,比如8写成VIII,而非IIX。 75 | - 右加数字不可连续超过三位,比如14写成XIV,而非XIIII。(见下方“数码限制”一项。) 76 | - 同一数码最多只能连续出现三次,如40不可表示为XXXX,而要表示为XL。 77 | -------------------------------------------------------------------------------- /分布式计算/AJAX.md: -------------------------------------------------------------------------------- 1 | ## 世界灯火·AJAX 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /分布式计算/互联网与信息传输.md: -------------------------------------------------------------------------------- 1 | # 让世界相连·互联网和信息传输 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /分布式计算/分布式应用程序.md: -------------------------------------------------------------------------------- 1 | ## 光阴逆旅·分布式应用程序 2 | 3 | --- 4 | 5 | ### 统一资源标识符 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ### 表征状态传输 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ### GraphQL 22 | 23 | -------------------------------------------------------------------------------- /分布式计算/安全性.md: -------------------------------------------------------------------------------- 1 | ## 避风港·安全策略 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /分布式计算/客户端存储技术.md: -------------------------------------------------------------------------------- 1 | ## 百代之过客·客户端存储技术 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /分布式计算/异步操作.md: -------------------------------------------------------------------------------- 1 | ## 鱼跃龙门·异步操作 2 | 3 | --- 4 | 5 | ### 初探 Promise 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ### 深入 Promise 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ### async 和 await 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ### 使用异步操作的 API -------------------------------------------------------------------------------- /分布式计算/数据交换格式.md: -------------------------------------------------------------------------------- 1 | ## 殊途同归·数据交换格式 2 | 3 | --- 4 | 5 | ### 纯文本 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ### XML 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ### JSON 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ### YAML 30 | 31 | -------------------------------------------------------------------------------- /启程/HTML.md: -------------------------------------------------------------------------------- 1 | ## 互联网的礼服·HTML 2 | 3 | --- 4 | 5 | ### 初识 HTML 6 | 7 | 我们已经初步接触了 JavaScript 中的表达式、值、变量、常量、输入输出等一系列最基本的内容,现在让我们把目光从运行器暂时移开,为将来的长远发展做打算。 8 | 9 | 如前文所说,JavaScript 是一种运行在浏览器中的编程语言。假如离开了这个运行器,它究竟是怎样运行的呢?我们该做些什么呢? 10 | 11 | 讨论这个之前,让我们先了解一下 HTML(HyperText Markup Language,超文本标记语言)的概念。 12 | 13 | HTML 是你所看到的每一个 Web 页面的原始形式。浏览器将获得的 HTML 代码进行处理,就成了你上网时浏览到的每一行文字、每一张图片、每一个链接,每一个按钮、菜单、输入框。现在我们来编写一个最简单的 HTML 文档。 14 | 15 | ```html 16 | 17 | 18 | 19 | 20 | Hello world 21 | 22 | 23 |

    Hello world

    24 | 25 | 26 | ``` 27 | 28 | 我们将这个文档的内容保存一个文本文件里,可以叫它 `hello.html`,并尝试双击它,或者将它拖到浏览器窗口里。浏览器会显示以下画面: 29 | 30 | ![1558824325517](assets/1558824325517.png) 31 | 32 | 看,来自我们的问候,被浏览器显示在了屏幕上。成功了!这个最简单的 HTML 的事情就是告诉浏览器:我们要将 `Hello world` 这行文本显示在屏幕上。就这么简单!那么我们刚刚写下的东西,与我们看到的问候之间,究竟有什么关系呢?不妨再来看看这个文档的内容。 33 | 34 | 首先,第一行 `` 被称为 *DTD 标记*。它的作用是告诉浏览器:我们这个文档遵循最新的 HTML 5 标准,这样我们就可以享受一些便利。其后的 `` 是一个*标签*,而最后一行的 `` 则是一个*闭合标签*。相对地,`` 被称为*开放标签*。`` 和 `` 是一个 HTML 文档的*根元素*,这对元素中包含了整个 HTML 文档的实际内容。 35 | 36 | 第三行的 `` 与 `` 标签类似,它也有一个闭合标签 ``。`` 被称为*头元素*,用于说明文档的一些基本信息。这个文档的头元素中包含了两行内容: 37 | 38 | - `` ,它告诉浏览器这个文档应该根据 UTF-8 编码方式被解析。我们将在第三章了解编码方式的相关内容。它是一个*孤立标签*,因此我们把斜杠 `/` 写在它的后面。 39 | - `Hello world` 将浏览器顶部的页面标题更改为 Hello world,这样我们在浏览多个页面的时候,就会知道每个标签页是什么内容。 40 | 41 | `` 标签后的 `` 标签包含了我们实际看见的页面内容,在这里写下的任何东西,都会以某种方式被浏览器呈现出来。我们不需要显示其他什么东西,因此我们就添加了一个*段落*,内容是 Hello world,用一对 `

    ` 标签包裹起来。 42 | 43 | 44 | 45 | --- 46 | 47 | Story: 48 | 49 | 1980年,物理学家蒂姆·伯纳斯-李在欧洲核子研究中心(CERN)在承包工程期间,为使 CERN 的研究人员使用并共享文档,他提出并创建原型系统 ENQUIRE。1989年,伯纳斯-李在一份备忘录中提出一个基于互联网的超文本系统。他规定HTML 并在 1990 年底写出浏览器和服务器软件。同年,伯纳斯-李与 CERN 的数据系统工程师罗伯特·卡里奥联合为项目申请资助,但未被 CERN 正式批准。在他的个人笔记中,伯纳斯-李列举“一些使用超文本的领域”,并把网络百科全书列为首位。 50 | 51 | HTML 的首个公开描述出现于一个名为“HTML 标签”的文件中,由蒂姆·伯纳斯-李于 1991 年底提及。它描述 18 个元素,包括 HTML 初始的、相对简单的设计。除了超链接标签外,其他设计都深受 CERN 内部一个以标准通用标记语言(SGML)为基础的文件格式 SGMLguid 的影响。这些元素在 HTML 4 中仍有 11 个存在。 52 | 53 | 伯纳斯-李认为 HTML 是 SGML 的一个应用。1993年中期互联网工程任务组(IETF)发布首个 HTML 规范的提案:“超文本标记语言(HTML)”互联网草案,由伯纳斯-李与丹·康纳利撰写。其中包括一个 SGML 文档类型定义来定义语法。草案于 6 个月后过期,但是它带来了由 Masaic 创造的在页面上显示图片的特性。同时,戴夫·拉格特在 1993 年末提出的与之竞争的互联网草案“HTML+(超文本标记格式)”,最终引入了诸如表格与填写表单的功能。 54 | 55 | 在 HTML 和 HTML+ 的草案于 1994 年初到期后,IETF 创建了一个 HTML 工作组,并在 1995 年完成“HTML 2.0”,这是第一个旨在成为对其后续实现标准的依据的 HTML 规范。 56 | 57 | 在 IETF 的主持下,HTML 标准的进一步发展因竞争利益而遭受停滞。自 1996 年起,HTML 规范一直由万维网联盟(W3C)维护,并由商业软件厂商出资支持。不过在 2000 年,HTML 也成为国际标准(ISO/ IEC 15445:2000)。HTML 4.01 于1 999 年末发布,进一步的勘误版本于 2001 年发布。2004 年,网页超文本应用技术工作小组(WHATWG)开始开发 HTML 5,并在 2008 年与 W3C 共同交付,2014 年 10 月 28 日完成标准化。 58 | 59 | --- 60 | 61 | 62 | 63 | 64 | 65 | ### 表格 66 | 67 | 表格是由行和列组成的*结构化数据集*,能够使我们简便迅捷地查找某个值的位置或是一组值的某种关系,例如姓名与年龄的对应关系、学校课程表、游泳池的时刻表等。 68 | 69 | ![A sample table showing names and ages of some people - Chris 38, Dennis 45, Sarah 29, Karen 47.](https://mdn.mozillademos.org/files/14583/numbers-table.png) 70 | 71 | > 图片来源:https://mdn.mozillademos.org/files/14583/numbers-table.png,Mozilla Developer Network 72 | 73 | ![A swimming timetable showing a sample data table](https://mdn.mozillademos.org/files/14587/swimming-timetable.png) 74 | 75 | > 图片来源:https://mdn.mozillademos.org/files/14587/swimming-timetable.png,Mozilla Developer Network 76 | 77 | 表格在人类社会中很常见,而且已经存在很长时间了,下面这张 1800 年美国的人口普查表就足以反映。 78 | 79 | ![A very old parchment document; the data is not easily readable, but it clearly shows a data table being used.](https://mdn.mozillademos.org/files/14585/1800-census.jpg) 80 | 81 | > 图片来源:https://mdn.mozillademos.org/files/14585/1800-census.jpg,Mozilla Developer Network 82 | 83 | 常见的文档处理程序,如 Microsoft Office Word 等,都提供了电子表格的支持,HTML 也拥有一套方法来处理和呈现 Web 上表格形式的数据。 84 | 85 | 表格的一大特点是**严格**,通过在行和列的标题之间进行*视觉关联*的方法,使大量信息得以被快速有效地解读与判断。我们观察如下表格,当我们要快速查询特定的单复数、人称、性别所对应的人称时,表格的结构化就提供了极大地便利。 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 |
    SubjectObject
    单数第一人称Ime
    第二人称youyou
    第三人称hehim
    sheher
    otheritit
    复数第一人称weus
    第二人称youyou
    第三人称theythem
    139 | 140 | 表格在信息技术领域所承担的重要作用超越了本书的范围。除了文字处理程序提供的便捷的表格操作,在 HTML 里纯手工“画”一个表格也并非什么难事。下面,我们将动手创建一个可爱的 HTML 表格。 141 | 142 | - 所有的表格都包含在 `
    ` 这对标签中,我们首先在 HTML 源代码里添加这些内容。 143 | 144 | - 我们知道,表格是由一个个单元格构成的。一个单元格就是一对 `` 标签,其中包含单元格将会呈现的内容。我们把下面的内容添加到 `` 标签内。 145 | 146 | ```html 147 | 148 | ``` 149 | 150 | 如果我们想要一行四个单元格,我们需要把这组标签复制三次。 151 | 152 | ```html 153 | 154 | 155 | 156 | 157 | ``` 158 | 159 | 四个单元格呈现出这样的效果: 160 | 161 | ![1563330252149](C:\Users\alphaxuan\AppData\Roaming\Typora\typora-user-images\1563330252149.png) 162 | 163 | 你会看到, 单元格不会放置在彼此的下方, 而是自动与同一行上的其他单元格对齐. 每个 `` 元素。 166 | 167 | 1. 把已经创建好的 4 个单元格放入 `` 标签, 就像这样: 168 | 169 | ```html 170 | 171 | 172 | 173 | 174 | 175 | 176 | ``` 177 | 178 | 2. 现在我们已经拥有了完整的一行,可以继续增加至两行、三行。每一行都需要包裹在专属的 `` 标签中,`` 标签内只能存放 `
    你好!我是第一个单元格~你好!我是第一个单元格~我是第二个单元格我是第三个单元格我是第四个单元格` 元素 创建一个单独单元格,它们共同组成了第一行。同时,表格并没有呈现出我们通常所见的分隔线。在后续的章节中,我们将了解到如何使用*样式*,来使表格看起来更像是“经典”的表格。 164 | 165 | - 如果想停止在这一行添加单元格,并开启新的一行,我们需要使用 `
    你好!我是第一个单元格~我是第二个单元格我是第三个单元格我是第四个单元格
    ` 单元格。 179 | 180 | 3. 我们得到了一个类似这样的表格: 181 | 182 | | 你好!我是第一个单元格~ | 我是第二个单元格 | 我是第三个单元格 | 我是第四个单元格 | 183 | | ----------------------- | ---------------- | ---------------- | ---------------- | 184 | | 第二行第一个单元格 | 呜呜呜 | 好寂寞 | 谁来陪我鸭 | 185 | 186 | 而实际呈现的效果大致是如下: 187 | 188 | ![1563331271632](C:\Users\alphaxuan\AppData\Roaming\Typora\typora-user-images\1563331271632.png) 189 | 190 | - 现在,让我们把注意力转向表格标题,表格中的标题是特殊的单元格,通常在行或列的开始处,定义行或列包含的数据类别。我们可以看一下这个例子(来自 Mozilla Developer Network): 191 | 192 | ```html 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 |
    KnockyFlorEllaJuan
    BreedJack RussellPoodleStreetdogCocker Spaniel
    Age169105
    OwnerMother-in-lawMeMeSister-in-law
    Eating HabitsEats everyone's leftoversNibbles at foodHearty eaterWill eat till he explodes
    230 | ``` 231 | 232 | 以上源代码大致呈现出这样的效果: 233 | 234 | | | Knocky | Flor | Ella | Juan | 235 | | ------------- | ------------------------- | --------------- | ------------ | ------------------------- | 236 | | Breed | Jack Russell | Poodle | Streetdog | Cocker Spaniel | 237 | | Age | 16 | 9 | 10 | 5 | 238 | | Owner | Mother-in-law | Me | Me | Sister-in-law | 239 | | Eating Habits | Eats everyone's leftovers | Nibbles at food | Hearty eater | Will eat till he explodes | 240 | 241 | 我们也可以使用专门的 `
    ` 标签来定义表格标题。 242 | 243 | ------ 244 | 245 | Note: 246 | 247 | ``、``、`
    `、`
    ` 这些标签都是什么意思? 248 | 249 | `table` 在英语中除了有桌子的含义,也用于指代表格。 250 | 251 | `td` 是*表格数据*(table data)的缩写。 252 | 253 | `tr` 是*表格行*(table raw)的缩写。 254 | 255 | `th` 是*表格标题*(table header)的缩写。 256 | 257 | ------ 258 | 259 | -------------------------------------------------------------------------------- /启程/Hello-world.md: -------------------------------------------------------------------------------- 1 | ## 千里之行,始于足下·Hello world 2 | 3 | --- 4 | 5 | 你可能曾有过学习编程语言的打算,但是最艰难的恐怕就是那关键的第一步吧——如何上手?要做什么,才能做好写下第一行代码的准备,又该怎样让它成为一个精巧的“程序”呢?事实上,当你读完本章后,你恐怕就不会再有这样的迷惑了。 6 | 7 | 因为,我们将要学习的是这种可以在**任何地方**运行的编程语言——JavaScript!从每一台桌面或移动设备的浏览器,到服务器和多媒体领域,甚至嵌入式和正逐步成为现实的物联网。事实上,JavaScript 是如此地容易上手和学习,因为踏出第一步便如此简单:如我所说,JavaScript 是一种可以在浏览器上运行的编程语言。你只需要准备一个好用的现代浏览器,诸如 Google Chrome ,Mozilla Firefox 或 Microsoft Edge 等。我们将会用非常多的时间与浏览器打交道,因此,选择一个适合的浏览器,将会使我们的学习之路变得得心应手且愉快顺利。 8 | 9 | 编写和运行 JavaScript 代码最简单的方式是在浏览器地址栏中。是的,就是那个用于输入网址以便你去到因特网的任何地方的地址栏。如图所示: 10 | 11 | ![1552904604507](./assets/1552904604507.png) 12 | 13 | 我所使用的是运行在 Microsoft Windows 10 上的 Google Chrome 浏览器,如果你的浏览器类型或版本与我不同,可能会呈现出不一样的外观。这是没关系的。当然,为了我们接下来的愉快旅程考虑——使用一些 Chrome 或 Firefox 之类的**流行**浏览器会得到更一致的操作体验,更好的兼容性(关于兼容性的问题我们会在第八章详细谈到)。最重要的是,我们所学习的 JavaScript 语言遵循一个2015年正式发布的国际标准:ECMAScript 6。如果您的浏览器版本较低或“*没有跟上时代的步伐*”,将不能很好地运行我们所写的代码。这非常令人不愉快。无论如何,**请不要使用 Internet Explorer**。 14 | 15 | 回到正题。我们在浏览器地址栏中这样一句代码: `javascript:alert("Hello world")`,接下来按下 Enter 键,会发生什么有趣的事情呢? 16 | 17 | 看!一个弹出来的窗口!上面写着 Hello world 。我们已经成功运行了第一个 JavaScript 程序。 18 | 19 | ![1552905378968](./assets/1552905378968.png) 20 | 21 | Hello world 上方的“cn.bing.com”指我输入这行代码时页面所处的域名,你的应该会和我不一样。不必在意这个。 22 | 23 | 现在来分析一下这一行最简单的 JavaScript 代码: 24 | 25 | ```javascript 26 | javascript:alert("Hello world") 27 | ``` 28 | 29 | 前面的 ```javascript: ```标记是告诉浏览器把随后的文本当做 JavaScript 代码来执行,而不是一个网址或搜索内容。它只是一个说明性的标记,不是 JavaScript 语言所规定的。 30 | 31 | 其后的 ```alert``` 代表一个名为 ```alert``` 的函数。函数可以用来做一些事情,你只需要用它们的名字来召唤它。例如, ```alert``` 这个函数的作用是使浏览器弹出一个对话框,在上面显示一些信息。我们将会在第五章中详细讨论函数。 32 | 33 | ```(``` 和 ```)``` 两个括号表示使用 ```alert``` 这个函数,它们像一个大嘴巴,里面装着喂给函数的东西,这对小括号承担了*调用函数*的作用,因此称为*函数调用运算符*。其中包含的 ```"Hello world"``` 是一个*字符串*,它被函数调用运算符包住了,会被喂给 ```alert``` 函数。```alert``` 函数得到了 ```"Hello world"``` 这个字符串值,便会使浏览器弹出一个对话框,对话框的信息内容是我们传入的 ```"Hello world"``` 这个字符串。包裹```Hello world```的双引号是一对*字符串标记*,这个字符串的实际内容是双引号之间的内容。 34 | 35 | 输入之后,按一下 Enter,这行代码便被浏览器运行了。就是这样! 36 | 37 | 现在我们已经了解了这行最简单的 JavaScript 代码的结构与作用,现在我们可以做一点小练习,复习我们刚才所讨论的东西。 38 | 39 | --- 40 | 41 | **练习 2.1** 42 | 43 | 1. 在浏览器地址栏中输入并运行以下代码: 44 | 45 | ```javascript 46 | javascript:alert(1 + 1 + 1 + 1) 47 | ``` 48 | 49 | 2. 在浏览器地址栏中输入并运行以下代码: 50 | 51 | ```javascript 52 | javascript:alert("1" + "1" + "1" + "1") 53 | ``` 54 | 55 | 3. 观察运行结果,试猜想为什么会这样。 56 | 57 | --- 58 | 59 | 60 | 61 | 显然,我们不可能一直呆在浏览器地址栏中。我们总不可能在浏览器地址栏中写出一个“愤怒的小鸟”游戏,也不可能写出一个 QQ 或者微信。有个地方可以让我们更加方便地编写和运行 JavaScript 代码,它的链接如下: 62 | 63 | https://art-book.github.io/runner 64 | 65 | 在这个代码输入框里,你可以这样输入 Hello world 代码: 66 | 67 | ```javascript 68 | alert("Hello world"); 69 | ``` 70 | 71 | 点击运行按钮,会得到与刚才相同的效果。 72 | 73 | ![1552912887244](./assets/1552912887244.png) 74 | 75 | 嘿,你注意到了吗:我们写的是 ```alert("Hello world");```,后面多了一个分号! 76 | 77 | 分号表示一个语句的结尾,刚才我们写下的 ```alert("Hello world");``` 是一个独立的语句,需要用分号来结束。 78 | 79 | 你已经了解了编写 JavaScript 代码的基本感觉,现在让我们来尝试一些简单有趣的代码片段,在这个 JavaScript 运行器中好好玩耍! 80 | 81 | 1)这行代码每次运行都会得到一个位于 1 和 6 之间的不一样的骰子点数。 82 | 83 | ```javascript 84 | alert("你的骰子点数是" + Math.ceil(Math.random() * 6)); 85 | ``` 86 | 87 | 2)这行代码会以 ISO 标准格式显示当前时间。 88 | 89 | ```javascript 90 | alert(new Date()); 91 | ``` 92 | 93 | 3)这行代码会弹出一个输入框,并判断你所输入的数字是奇数还是偶数。它过于简单了,如果不是数字,它也会显示“偶数”。 94 | 95 | ```javascript 96 | alert(+prompt() % 2 ? "奇数" : "偶数"); 97 | ``` 98 | 99 | 4)这行代码会把运行器页面背景调成粉色。 100 | 101 | ```javascript 102 | document.body.style.backgroundColor = "pink"; 103 | ``` 104 | 105 | 5)这行代码可以让你随意更改页面内容(提示:快捷键 Ctrl-Z 可以撤销更改) 106 | 107 | ```javascript 108 | document.body.contentEditable = true; 109 | ``` 110 | 111 | 6)这行代码会更改运行按钮的行为。当你再次点击运行按钮时,它不会再执行其它代码,而是弹出一个 “Hi” 的对话框。刷新浏览器页面即可恢复原来的样子。 112 | 113 | ```javascript 114 | document.getElementById("run").onclick = () => alert("Hi"); 115 | ``` 116 | 117 | --- 118 | 119 | **练习 2.2** 120 | 121 | 1. 尝试将运行器的页面背景改为其他颜色。总共成功尝试了几种颜色? 122 | 2. 尝试把代码 1 进行扩展,把点数可能的范围扩大到1-30 。 123 | 3. 结合代码 3 和 代码 5,尝试把运行器按钮的背景颜色调成绿色。 124 | 125 | --- 126 | 127 | -------------------------------------------------------------------------------- /启程/assets/1552904604507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1552904604507.png -------------------------------------------------------------------------------- /启程/assets/1552905378968.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1552905378968.png -------------------------------------------------------------------------------- /启程/assets/1552912887244.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1552912887244.png -------------------------------------------------------------------------------- /启程/assets/1552999312562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1552999312562.png -------------------------------------------------------------------------------- /启程/assets/1552999360878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1552999360878.png -------------------------------------------------------------------------------- /启程/assets/1552999390567.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1552999390567.png -------------------------------------------------------------------------------- /启程/assets/1553079227178.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1553079227178.png -------------------------------------------------------------------------------- /启程/assets/1553079359387.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1553079359387.png -------------------------------------------------------------------------------- /启程/assets/1558824325517.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/assets/1558824325517.png -------------------------------------------------------------------------------- /启程/命名的值.md: -------------------------------------------------------------------------------- 1 | ## 请叫阮的名·命名的值 2 | 3 | --- 4 | 5 | ### 变量与常量 6 | 7 | 人类发明了各种各样的容器来盛放东西,而 JavaScript 的世界也有容器,用来存放表达式的值。这种容器,叫变量。 8 | 9 | **变量** 10 | 11 | 在 JavaScript 中,我们可以使用*变量*来存放一个值,这个值随时可以被改变。变量的定义像下面这样: 12 | 13 | ```javascript 14 | let a; 15 | a = 10; 16 | ``` 17 | 18 | 我们使用关键字 `let` 来表示一个变量声明的开始。`let a` 会产生一个名为 `a` 的变量,我们将这一步叫做**变量声明**,随后的 `a = 10;` 会把 10 这个值放进变量 `a` 中,这个过程叫**赋值**。 19 | 20 | 变量声明与赋值是两个过程,一个变量只能被声明一次,但是可以被多次赋值。正如它的名称所示,它的值是可以变的。第一次的声明与赋值可以被写到一行语句中: 21 | 22 | ```javascript 23 | let a = 10; 24 | ``` 25 | 26 | 我们可以像这样改变它的值: 27 | 28 | ```javascript 29 | a = 100; 30 | a = 12 * 3 + 20; 31 | a = "Hello world"; 32 | ``` 33 | 34 | 为变量赋值之后,我们可以像通常的表达式那样使用它: 35 | 36 | ```javascript 37 | let a = 10; 38 | alert(a); 39 | a = a + 1; 40 | alert(a); 41 | alert(a + 2); 42 | ``` 43 | 44 | 显示的结果分别为 10,11 和 13。 45 | 46 | 假如我们使用了一个从未出现的变量,会产生一个错误: 47 | 48 | ![1552999312562](./assets/1552999312562.png) 49 | 50 | 如果我们已经给 `hahaha` 赋值,那么可以使用它,不会产生错误,但是我们非常不推荐这种做法,使用变量前一定要记得声明。 51 | 52 | ![1552999360878](./assets/1552999360878.png) 53 | 54 | 如果你早就已经接触了 JavaScript,你或许曾在别的地方见过另一种声明变量的方式: 55 | 56 | ```javascript 57 | var a = 10; 58 | ``` 59 | 60 | `var` 关键字是 JavaScript 曾经使用的变量声明方式,但是这种方式存在一些缺陷,在2015年发布的新标准中被 `let` 所取代,一些细节我们会在后面谈到。我们理解它的含义即可,不建议使用它。 61 | 62 | **常量** 63 | 64 | 顾名思义,*常量*与变量类似,但是它的值是不可改变的。也就是说,只能赋值一次,之后如果尝试改变它的值,会产生一个错误。 65 | 66 | ![1552999390567](./assets/1552999390567.png) 67 | 68 | 第一次调用 `alert` 会正常显示 `name` 的值 `"Jim"` 。随后的赋值语句会带来如图所示的错误,并中断代码的执行,因此最后一行语句不会被执行。 69 | 70 | 常量的使用场景一般是强制某些不应改变的值**不可改变**,可以避免失误造成原有值被覆盖的情况。为了带来明显的视觉效果以与通常的变量区分,我们一般会使用大写字母来书写常量名称,并使用下划线“_”来连接常量名中包含的单词。 71 | 72 | ```javascript 73 | const MY_BIRTHDAY = "2005-2-6"; 74 | alert(MY_BIRTHDAY); 75 | ``` 76 | 77 | **标识符** 78 | 79 | JavaScript 中的变量名或常量名必须是合法的*标识符*。一个合法的标识符应当以以下字符开头: 80 | 81 | ``` 82 | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 83 | a b c d e f g h i j k l m n o p q r s t u v w x v z $ _ 84 | ``` 85 | 86 | 即二十六个英文大小写字母以及美元符号和下划线。 87 | 88 | 而其后跟随的字符可以是: 89 | 90 | ``` 91 | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 92 | a b c d e f g h i j k l m n o p q r s t u v w x v z $ _ 93 | 0 1 2 3 4 5 6 7 8 9 94 | ``` 95 | 96 | 即二十六个英文大小写字母、美元符号、下划线和十个阿拉伯数字。 97 | 98 | 除此之外的字符原则上可以使用,但是容易造成难以预料的错误。我们不推荐使用。关于 JavaScript 中的字符,我们将在第三章详细了解。 99 | 100 | 一个合法的标识符不能与已有的保留字发生冲突。保留字一些 JavaScript 语言内预留的、具有特殊含义的单词,如果你使用它们作为标识符,会产生语法错误。 101 | 102 | **** 103 | 104 | | | | | | | 105 | | :------: | :-------: | :--------: | :-------: | :----------: | 106 | | `abstract` | `arguments` | `boolean` | `break` | byte | 107 | | `case` | `catch` | `char` | `class` | const | 108 | | `continue` | `debugger` | `default` | `delete` | `do` | 109 | | ` double` | `else` | `enum` | `eval` | `export` | 110 | | `extends` | `false` | `final` | `finally` | `float` | 111 | | `for` | `function` | `goto` | `if` | `implements` | 112 | | `import` | `in` | `instanceof` | `int` | `interface` | 113 | | `let` | `long` | `native` | `new` | `null` | 114 | | `package` | `private` | `protected` | `public` | `return` | 115 | | `short` | `static` | `super` | `switch` | `synchronized` | 116 | | `this` | `throw` | `throws` | `transient` | `true` | 117 | | `try` | `typeof` | `var` | `void` | `volatile` | 118 | | `while` | `with` | `yield` | | | 119 | 120 | 除此之外,有一些 JavaScript 中预定义的函数、常量等也不应当被用作标识符,包括 `Object` `Ininity` `undefined` `isNaN` 等。 -------------------------------------------------------------------------------- /启程/更进一步.md: -------------------------------------------------------------------------------- 1 | ## 听风知雨·更进一步 2 | 3 | --- 4 | 5 | ### 输入与输出 6 | 7 | 提供输入与输出数据的功能,是任何一种编程语言的基本设施。试想一下,假如我们的程序无法使用输入输出的功能——确切地说,无法与外界进行数据交换,那么它能做什么呢?什么也做不了!它也许可以静静地计算一个表达式的值,可以执行一个循环与条件判断,但是我们将始终无从得知它的最终工作,这个程序也就不会有存在的意义。输入与输出非常重要,也充满灵活性。 8 | 9 | JavaScript 的语言标准(即 ECMAScript)并没有规定用于输入与输出的方式。浏览器为我们提供了两个简单的函数:`alert` 和 `prompt` ,以便进行基本的信息输出和输入。 10 | 11 | 12 | 13 | **`alert` 和 `prompt`** 14 | 15 | `alert` 我们已经见过很多次了。当我们简单地想要输出一条信息,比如显示一条表达式的计算结果,或者干其他的什么事情,我们可以使用它。在实际应用中我们有更复杂和标准的信息输出方式,但是现在我们使用 `alert` 就足以满足我们需要了。 16 | 17 | `prompt` 这个函数我们已经在前面的代码示例中见过了,它可以弹出一个对话框,要求用户输入一点内容,然后得到这个内容,它的使用形式如下: 18 | 19 | `let xxx = prompt();` 20 | 21 | 变量 `xxx` 中存放的就是我们获取到的内容。 22 | 23 | 但是它弹出的对话框什么提示也没有,只有一个简单的输入框。试想一下,假如别人使用你的程序,突然就看到这么一个框,什么也没有提示,会觉得丈二和尚摸不着头脑,对吧?更有可能,我们想要一个表示年龄的数字,用户却稀里糊涂地输入了一个手机号码。 24 | 25 | 我们可以给 `prompt` 的对话框加一条简单的提示语,来表明我们要干的事情,像这样: 26 | 27 | `let xxx = prompt("请输入你的年龄:");` 28 | 29 | 这是使用 `prompt` 函数的另一种形式,变量 `xxx` 即是我们获取到的内容。 30 | 31 | ![1553079227178](assets/1553079227178.png) 32 | 33 | 34 | 35 | 我们可以用一些简单的示例来尝试我们刚刚了解到的东西: 36 | 37 | ```javascript 38 | alert("我们将收集您的身高数据以计算您的标准体重。"); 39 | let height = parseFloat(prompt("请输入您的身高(单位:厘米):")); 40 | let weight = (height - 100) * 0.9; 41 | alert("您的标准体重是:" + weight); 42 | ``` 43 | 44 | 其中 `parseFloat` 函数会从文本中获取一个数字,将在第三章讲到。 45 | 46 | 你可以自己试着测测你的标准体重哦! 47 | 48 | 49 | 50 | 这是另一个实用性的例子,更好地练习我们刚刚所学内容: 51 | 52 | ```javascript 53 | // 计算圆的面积与周长。 54 | let r = parseFloat(prompt("请输入圆的半径:")); 55 | alert("圆的面积是:" + r * r * Math.PI); 56 | alert("圆的周长是:" + r * 2 * Math.PI); 57 | ``` 58 | 59 | 60 | 61 | 62 | 63 | ### 注释 64 | 65 | 在编写 JavaScript 代码时,你可以在代码周围插入一些说明性的文字,它们被称为*注释*。 66 | 67 | 以下是一个在代码中应用注释的例子: 68 | 69 | ```javascript 70 | let a = +prompt(); // 获取第一个数字 71 | let b = +prompt(); // 获取第二个数字 72 | alert(a + b) // 计算并展示 a + b 的值 73 | ``` 74 | 75 | 在代码中, `//` 标记被用于说明一个*单行注释*的开始。它意味着之后的整一行代码都会被忽略掉,因此你可以在里面写上任何东西,当然更多情况下我们只是用于说明该行代码的功能。 76 | 77 | 假如我们需要写很多行东西来解释和说明我们的代码,或者是充当一个临时备忘录,我们可以相应地使用*多行注释*,如下所示: 78 | 79 | ```javascript 80 | /* 交换两个变量的值。 81 | 作者:杨雨露 82 | 日期:2019-3-19 83 | 备注:无 84 | */ 85 | let a = prompt(); 86 | let b = prompt(); 87 | let temp = a; 88 | a = b; 89 | b = temp; 90 | alert("a = " + a + ", b = " + b); 91 | ``` 92 | 93 | 在一对 `/*` 与 `*/` 之间的所有东西都会被忽略掉,同样,你可以在里面写上任何东西。从第一个 `/*` 开始,一旦遇到一个 `*/` ,就意味着注释内容已经结束了,其后如果再出现单独的 `*/` ,就会被认为是一个语法错误。 94 | 95 | 如下所示: 96 | 97 | ```javascript 98 | /* 第一行注释 99 | 第二行注释 100 | /* 第三行注释(这行开头的 /* 同样会被忽略) 101 | 第四行注释 102 | 现在多行注释结束了: */ 103 | 104 | */ // 嘿,多余的 */ 会被当成语法错误! 105 | ``` 106 | 107 | **` //` 注释用于一行里的注释,`/* */` 注释用于一段完整的注释。** -------------------------------------------------------------------------------- /启程/第二章.assets/1552904604507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1552904604507.png -------------------------------------------------------------------------------- /启程/第二章.assets/1552905378968.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1552905378968.png -------------------------------------------------------------------------------- /启程/第二章.assets/1552912887244.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1552912887244.png -------------------------------------------------------------------------------- /启程/第二章.assets/1552999312562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1552999312562.png -------------------------------------------------------------------------------- /启程/第二章.assets/1552999360878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1552999360878.png -------------------------------------------------------------------------------- /启程/第二章.assets/1552999390567.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1552999390567.png -------------------------------------------------------------------------------- /启程/第二章.assets/1553079227178.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1553079227178.png -------------------------------------------------------------------------------- /启程/第二章.assets/1558824325517.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1558824325517.png -------------------------------------------------------------------------------- /启程/第二章.assets/1563330252149.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1563330252149.png -------------------------------------------------------------------------------- /启程/第二章.assets/1563331271632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/启程/第二章.assets/1563331271632.png -------------------------------------------------------------------------------- /启程/表达式与值.md: -------------------------------------------------------------------------------- 1 | ## 辟自鸿蒙·表达式与值 2 | 3 | --- 4 | 5 | 从最初有理性的记忆里,我们便与表达式打交道。小时候我们在数学课上称为“算式”的东西,就是一种表达式。$$1 + 1 = 2$$ 这样的算式,与我们最初理解的现实世界的概念相伴而生,与物体的颜色、形状、名字一道,扎根在每个人最深层的理性思维中。有一个苹果,又得到了一个苹果,那么我们有两个苹果——这是我们对这种来自于现实而又超脱于一切现实事物的人类智慧的最初印象,从最简单的算术开始,一点一点窥见数学这一人类智慧的曙光。人们把这种纯粹、不掺杂一点杂念的智慧带进了计算机最根本的生命中。 6 | 7 | 人们问,$$1 + 1 = $$ ?,然后电子管和继电器所组成的巨大机器便开始轰鸣运作,渐渐就能解答人们越来越复杂的问题。人类有关数学的理想,在用晦涩的低级程序语言编写以前,是怎样表达的?是数学表达式。很多年以后,在 JavaScript 这样的高级编程语言中,人们可以直接写下数学表达式,刹那间便可以从计算机那里得到答案。 8 | 9 | ```javascript 10 | alert(1 + 1); 11 | ``` 12 | 13 | 我们知道了答案:$$2$$。不需要有任何等待,一切都在电光火石中进行着。 14 | 15 | 为了让计算机中所蕴含的人类智慧不被如此简单的问题小觑,我们可以来个更难的: 16 | 17 | $$(147 + 258) × 369 - (550 + 233) ÷ 29$$ 18 | 19 | 在 JavaScript 中,这个数学表达式将以略微不同的方式被写下,然后用 `alert` 输出答案。 20 | 21 | ```javascript 22 | alert((147 + 258) * 369 - (550 + 233) / 29); 23 | ``` 24 | 25 | 答案是 $$149418$$。计算机不会让我们失望。 26 | 27 | 这个表达式被包裹在 `alert` 函数和一对小括号中。加号 `+`,减号 `-`,小括号 `( )` 都是本来的意思。`*` 负责在 JavaScript 中进行乘法运算,`/` 负责除法。这些标点符号被称为**运算符(Operator)**,负责执行各类算术运算。 28 | 29 | ------ 30 | 31 | Note: 32 | 33 | 为什么 JavaScript 的乘号和除号,与它们在日常生活中所使用的的形式不同? 34 | 35 | 一个简单的答案是因为你的键盘上没有所谓的乘号($$\times$$)与除号($$\div$$),而键盘布局的历史需要读者自行查阅资料。 36 | 37 | 几十年最初使用键盘进行编程的程序员们脑洞大开,将星号(`*`)作为乘号使用。而使用斜杠作为除号的原因还有一个:在数学中,除法还可以用分数的形式表示,所以这个斜杠其实就是横着写的分数线——左侧的被除数相当于分子,右侧的除数也就相当于分母。 38 | 39 | ------ 40 | 41 | 常见的 JavaScript 数学运算符有以下几种。 42 | 43 | | 符号 | 运算 | 44 | | ----- | -------------- | 45 | | `+` | 加法 | 46 | | `-` | 减法或取相反数 | 47 | | `*` | 乘法 | 48 | | `/` | 除法 | 49 | | `%` | 求整除后的余数 | 50 | | `**` | 乘方 | 51 | | `( )` | 改变优先级 | 52 | 53 | 如果没有 `alert` 函数来输出,计算仍然会进行,会算出一个结果,然后这个结果就被丢弃到了虚空中。为了让我们的眼睛能看见,`alert` 函数就担负了输出信息的作用,虽然它更常见的用途是输出一段警告信息。 54 | 55 | 我们可以编写并运行以下程序,重温小学数学课上的感觉。 56 | 57 | ```javascript 58 | alert(18 + 14 * 5); 59 | alert(24 - 112); 60 | alert(1 / 3); 61 | alert(-10 * 15); 62 | alert(Math.PI); 63 | alert(0xf + 1); 64 | alert(20 % 8); 65 | alert(20 ** 3); 66 | ``` 67 | 68 | 其中,`Math.PI` 是一个预定义的值,它表示圆周率 $π$ 近似值;最后一行代码中的 `0xf` 是一个*十六进制数*,它的值就是 `16`,我们会在第三章讲到它。 69 | 70 | 人们说,计算机不但要拥有人类的理性,还要有人类的情感,于是人类文字就以某种精巧的方式就进入了计算机的生命。我们需要某种优雅的方式,向计算机吐露我们的心声,计算机的答案也要以更优雅、合乎我们思维的方式让我们知晓,于是一种叫**字符串**的东西诞生了,它是人类文字的载体,让人类以自己熟悉的方式写下话语并传达给计算机和未来的读者成为可能。 71 | 72 | 我们也可以用 `+` 符号来拼接两段文本,使它成为我们想要的形式。如: 73 | 74 | ```javascript 75 | alert("Hello " + "world"); 76 | alert("我今年" + 120 + "岁了!"); 77 | alert("十六进制数 0xefef 的值是" + 0xefef); 78 | ``` 79 | 80 | 另外,在 JavaScript 中,`alert(xxx)` 这样的*函数调用*也是表达式,像其它的一样,它也会得到一个值。`alert()` 得到的是 `undefined`,我们会在后面的章节中慢慢了解。 81 | 82 | 我们已经初步了解了 JavaScript 中的表达式,接下来的我们将学到给值命名的技巧,这样我们就可以把表达式的值存放起来,需要的时候再用。 83 | 84 | -------------------------------------------------------------------------------- /图形与动画/Canvas.md: -------------------------------------------------------------------------------- 1 | ## 案例研究:小黄人动画 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /图形与动画/WebGL.md: -------------------------------------------------------------------------------- 1 | ## 俯仰自如·WebGL 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /图形与动画/可伸缩矢量图形.md: -------------------------------------------------------------------------------- 1 | ## 游刃有余·可伸缩矢量图形 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /图形与动画/案例研究·波浪进度球.md: -------------------------------------------------------------------------------- 1 | ## 案例研究:波浪进度球 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /图形与动画/案例研究:画图工具.md: -------------------------------------------------------------------------------- 1 | ## 案例研究:画图工具 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /图形与动画/级联样式表.md: -------------------------------------------------------------------------------- 1 | ## 绝世伴侣·级联样式表 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /图形与动画/计算机图形.md: -------------------------------------------------------------------------------- 1 | ## 眼见为实·计算机图形 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /导出/BOOK.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/BOOK.docx -------------------------------------------------------------------------------- /导出/BOOK.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/BOOK.pdf -------------------------------------------------------------------------------- /导出/JavaScript 艺术之旅.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/JavaScript 艺术之旅.docx -------------------------------------------------------------------------------- /导出/~$第五章.docx: -------------------------------------------------------------------------------- 1 | alphaxuan alphaxuan  -------------------------------------------------------------------------------- /导出/~$第四章.docx: -------------------------------------------------------------------------------- 1 | alphaxuan alphaxuan  -------------------------------------------------------------------------------- /导出/第三章.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/第三章.docx -------------------------------------------------------------------------------- /导出/第五章.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/第五章.docx -------------------------------------------------------------------------------- /导出/第五章.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/第五章.pdf -------------------------------------------------------------------------------- /导出/第四章.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/第四章.docx -------------------------------------------------------------------------------- /导出/第四章.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/导出/第四章.pdf -------------------------------------------------------------------------------- /数据/assets/1565929449193.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/1565929449193.png -------------------------------------------------------------------------------- /数据/assets/1565930786117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/1565930786117.png -------------------------------------------------------------------------------- /数据/assets/1565932209076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/1565932209076.png -------------------------------------------------------------------------------- /数据/assets/1565932212738.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/1565932212738.png -------------------------------------------------------------------------------- /数据/assets/2237281220-5898949183cc3_articlex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/2237281220-5898949183cc3_articlex.png -------------------------------------------------------------------------------- /数据/assets/bg2014121121-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/bg2014121121-1.png -------------------------------------------------------------------------------- /数据/assets/ieee754.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/数据/assets/ieee754.jpg -------------------------------------------------------------------------------- /数据/对象.md: -------------------------------------------------------------------------------- 1 | ## 道生万物·对象 2 | 3 | --- 4 | 5 | ### 对象的概念 6 | 7 | **构造器与字面量** 8 | 9 | *字面量*是一种值的表示法。通过直接写出一个值所包含的内容,来创建这个值,这种**形式**被称为字面量。 10 | 11 | 如:"Hello world" 就是一个字符串字面量,1234.5678 就是一个数字字面量。 12 | 13 | 字面量与*创建实例*相对。在 JavaScript 中,每种类型的值都有一个对应的*构造器*,原则上你可以使用构造器来构造一个值,构造器写成函数调用的形式,可以接受一些值,作为创建新值的所需信息,这种创建值的形式称为*创建实例*。如: 14 | 15 | ```javascript 16 | const a = new Number(); // a = 0 17 | const b = new Number(10); // b = 10 18 | const c = new String(); // c = "" 19 | const d = new String(100); // c = "100" 20 | ``` 21 | 22 | 当然,JavaScript 中提供了方便的*字面量*方式来让我们创建值,我们不需要为值一个个创建实例。 23 | 24 | 除了我们已经了解过的布尔值、数值、字符串之外,有一大类值被称为*对象*。对象是一个包含了一些值的结构。 25 | 26 | ```javascript 27 | const o = new Object(); 28 | o.name = "John"; 29 | o.age = 12; 30 | ``` 31 | 32 | 现在我们创建了一个对象,并将它赋给常量 `o` 。我们为 `o` 提供了两个信息,分别是名称和年龄。这个信息被称作*属性*,我们可以通过属性来访问对象中所存放的具体的值。 33 | 34 | ```javascript 35 | const cat = new Object(); 36 | cat.name = "Lily"; 37 | cat.age = 3; 38 | alert(cat.age); // 3 39 | ``` 40 | 41 | 我们赋予了这个“猫对象”两个属性,一个是它的名字,一个是年龄。每个属性都对应一个值。当我们需要改变或得到某个属性的值时,我们只需要写下 `<对象名>.<属性名>` 即可。你可以把它当做一个对象内部的变量。 42 | 43 | 如果访问了未设置值的属性,你会得到值 `undefined` ,——因为“未定义”啊! 44 | 45 | 和基本的值一样,对象也可以用简便的字面量方式创建,这种形式被称为*对象字面量*。 46 | 47 | 它的特征是一对大括号,标志着这个对象字面量的起始与结束。在大括号中你可以写下一个属性和一个值,二者用冒号分隔,值后面写一个逗号,表示这一项*成员*结束了。 48 | 49 | ```javascript 50 | const dog = {name: "Peter", age: 4}; 51 | alert(dog.name); // "Peter" 52 | ``` 53 | 54 | 现在我们拥有了一只小狗,并给它起了名字。我们可以利用合适的换行,让对象字面量看起来舒服一些: 55 | 56 | ```javascript 57 | const dog = { 58 | name: "Peter", 59 | age: 4, 60 | }; 61 | alert(dog.name); // "Peter" 62 | ``` 63 | 64 | 最后一项成员的逗号是可选的,你可以自由决定是否添加。 65 | 66 | 你可以重复定义对象中的一个属性,最后一次定义的值会覆盖掉先前的值。但是我们没必要这样做,如果要改变一个属性的值,随时可以通过访问它的方式来改变。 67 | 68 | ```javascript 69 | // 不要这样做 70 | const object = { 71 | a: 1, 72 | a: 2 73 | }; 74 | alert(object.a); // 2 75 | 76 | // 你可以像这样 77 | const object2 = { 78 | a: 1 79 | }; 80 | object2.a = 2; 81 | alert(object2.a); // 2 82 | ``` 83 | 84 | 85 | 86 | 对象字面量中所包含的信息构成了这个对象本身,自身也是值,因此你可以在对象字面量中将一个属性的值设置为另一个对象字面量。当一个对象成为属性的值时,你可以通过属性访问这个对象,进一步访问它的属性只需要在它后面接着写 `.<属性名>`即可。这种访问属性的方式被称为*点号访问法。* 87 | 88 | ```javascript 89 | const person = { 90 | name: "Venn", 91 | age: 18, 92 | foods: { 93 | apple: 5, 94 | pear: 3 95 | } 96 | }; 97 | alert(person.name); // Venn 98 | alert(person.foods.apple); // 5 99 | ``` 100 | 101 | 当然,你不能直接查看一个对象,否则你会得到一个看起来有些奇怪的字符串值: 102 | 103 | ```javascript 104 | alert(person); // "[object Object]" 105 | alert(person.foods); // "[object Object]" 106 | ``` 107 | 108 | 109 | 110 | **属性名** 111 | 112 | 如前文所说,对象的属性名可以看做是一个变量名,一个合法的标识符也可以直接用做属性名。但事实上,你可以用**任何字符**作为属性名,包括空格等。你只需要用双引号或单引号将属性名括起: 113 | 114 | ```javascript 115 | const person = { 116 | "first name": "John", 117 | "last name": "Doe", 118 | }; 119 | ``` 120 | 121 | 然后,可以这样访问属性值: 122 | 123 | ```javascript 124 | alert(person["first name"]); // John 125 | alert(person["last name"]); // Doe 126 | ``` 127 | 128 | 这种方法被称为*方括号访问法*。如果属性名是合法的标识符,你也可以写成完全等价的点号访问法的形式。 129 | 130 | ```javascript 131 | const o = { 132 | "aaa": "bbb" 133 | }; 134 | alert(o["aaa"]); // "bbb" 135 | alert(o.aaa); // "bbb" 136 | ``` 137 | 138 | 139 | 140 | --- 141 | 142 | 练习 3.4.1 143 | 144 | 1. 编写一个程序,提示用户输入一些个人信息,并将它存储到一个对象里。 145 | 2. 在 3.4.1.1 的基础上,从对象中访问某个信息,并告诉用户某个它的值。 146 | 147 | --- 148 | 149 | 150 | 151 | 152 | 153 | ### 成员操作 154 | 155 | JavaScript 提供了一些方式来操作对象中的属性和值。我们已经讨论了访问和修改成员值的方式,添加一个成员也很简单——就像修改它一样,直接赋值即可。 156 | 157 | ```javascript 158 | const cat = { 159 | name: "Kitty", 160 | age: 4, 161 | kind: "unknown" 162 | }; 163 | 164 | // 现在我知道它是波斯猫 165 | cat.kind = "Persian cat"; 166 | alert(cat.kind); // "Persian cat" 167 | ``` 168 | 169 | 你也可以删除对象中的属性,JavaScript 提供了 `delete` 操作符。 170 | 171 | ```javascript 172 | const cat = { 173 | name: "Kitty", 174 | age: 4, 175 | kind: "unknown" 176 | }; 177 | 178 | // 现在我不需要关心它的品种 179 | delete cat.kind; 180 | alert(cat.kind); // undefined 181 | ``` 182 | 183 | 这时候我们或许会有疑问:当一个属性被删除之后,再访问它就会得到 `undefined`。那么这跟直接把属性的值设为 `undefined` 有什么区别吗? 184 | 185 | 当然有!当属性值为 `undefined` 时,它只是一个值为 `undefined` 的属性,依然存在于对象中。但是,当你用 `delete` 删除一个属性的时候,这个对象中就不存在这个属性了,只不过是访问了不存在的属性会得到 `undefined` 而已。 186 | 187 | 你可以使用 `in` 操作符来查看对象中究竟是否存在某一个属性: 188 | 189 | ```javascript 190 | const cat = { 191 | name: "Kitty", 192 | age: 4, 193 | kind: "unknown" 194 | }; 195 | 196 | // 现在我不需要关心它的品种 197 | delete cat.kind; 198 | alert("kind" in cat); // false 199 | alert("name" in cat); // true 200 | ``` 201 | 202 | 为什么当我们查看某个属性是否存在时,这个属性名要写成字符串?这是因为在 JavaScript 的语法中,如果直接写成 `kind in cat` 这样,`kind` 会被理解为一个标识符,然而我们并没有定义这个标识符,因此会得到一个错误。 203 | 204 | 事实上,JavaScript 中的对象的属性名都是以字符串方式存储的,只不过在对象字面量中,如果是合法的标识符,就不需要写成字符串的形式,JavaScript 会理解它。如果是点号标记法,由于它**只能用于访问写成合法标识符的属性名称**,因此属性名也不需要写成字符串的形式。但是在中括号标记法中,就应该写成字符串的形式。因为 JavaScript 会**将中括号中的内容当做表达式进行求值**,如果是一个字符串,那么就是按这个字符串对应的属性名进行访问,而如果认为是一个标识符,就会查找这个标识符本身的值所对应的属性名。 205 | 206 | 下面用一个示例来说明。 207 | 208 | ```javascript 209 | let a = "123"; 210 | const object = { 211 | a: 456, 212 | "123": 123 213 | }; 214 | alert(object.a); // 456 215 | alert(object[a]); // 123(标识符 a 的值是 "123",那么访问了名为 "123" 的属性) 216 | alert(object["a"]); // 456(访问名为"a"的属性) 217 | ``` 218 | 219 | 220 | 221 | --- 222 | 223 | 练习 3.4.2 224 | 225 | 1. 在 3.4.1.1 的基础上,提示用户输入一个名称,告诉用户这个对象中是否能找到名称对应的值。 226 | 227 | (提示:使用前面学习的逻辑相关知识) 228 | 229 | --- 230 | 231 | 232 | 233 | 234 | 235 | ### 枚举 236 | 237 | 上文所提供的访问对象成员的方式,都是通过属性名进行的。假如我们不知道某个属性的名称,或者说想一次性访问到所有的值,JavaScript 也提供了一些方便的函数来进行这类操作。 238 | 239 | 第一个是 `Object.values` 函数。我们可以从它得到一个列表,列表中会依次存放它所有的值。由于我们可以直接显示数组中的内容,因此它的所有值也就一目了然。 240 | 241 | ```javascript 242 | const cat = { 243 | name: "Kitty", 244 | age: 4, 245 | kind: "Cheshire Cat" 246 | }; 247 | 248 | const values = Object.values(cat); 249 | alert(values); // Kitty,4,Cheshire Cat 250 | ``` 251 | 252 | 第二个是 `Object.keys` 函数,它得到一个对象中所有属性名称的列表。 253 | 254 | ```javascript 255 | const cat = { 256 | name: "Kitty", 257 | age: 4, 258 | kind: "Cheshire Cat" 259 | }; 260 | 261 | const keys = Object.keys(cat); 262 | alert(keys); // name,age,kind 263 | ``` 264 | 265 | 此外,我们还可以使用 `Object.entries` 函数,它同样是得到一个列表,列表中交替保存属性名和值。 266 | 267 | ```javascript 268 | const cat = { 269 | name: "Kitty", 270 | age: 4, 271 | kind: "Cheshire Cat" 272 | }; 273 | 274 | const entries = Object.entries(cat); 275 | alert(entries); // name,Kitty,age,4,kind,Cheshire Cat 276 | ``` 277 | 278 | 我们会在下一节中详细了解如何使用列表中的值。 279 | 280 | 281 | 282 | 283 | 284 | ### 全局对象 285 | 286 | 我们在这里需要明确一个之前没有提及的概念: 287 | 288 | > 在 JavaScript 中,所有变量和常量都是一个*全局对象*的属性。 289 | 290 | 这个全局对象叫 `window` 。它在语义上指浏览器的窗口对象,同时也是 JavaScript 运行环境的一个“兜底”对象。当我们访问 `window` 对象的属性的时候,我们实际上是在访问这样一个变量/常量。 291 | 292 | ```javascript 293 | let hello = "Hello world"; 294 | alert(window.hello); // "Hello world" 295 | alert(window.hello === hello); // true 296 | ``` 297 | 298 | 由于 `window` 自身也是一个变量,因此它也是它自己的属性。 299 | 300 | ```javascript 301 | alert(window === window.window); // true 302 | alert(window.window === window.window.window); // true 303 | ``` 304 | 305 | 一般情况下我们不需要直接使用 `window` 对象,不过我们可以使用它来判断一个标识符是否存在于当前环境中——换句话说,只要知道 `window` 对象是否有这个 A 属性,就可以是否声明过一个叫 A 的标识符。 306 | 307 | --- 308 | 309 | 练习 3.4.3 310 | 311 | 1. 编写一个程序,判断当前环境中是否存在一个变量或常量。 312 | 313 | 2. 编写一个程序,在 3.4.3.1 的基础上判断某个标识符是否为变量或常量。 314 | 315 | (提示:`window` 对象的属性值可改变) 316 | 317 | --- 318 | 319 | -------------------------------------------------------------------------------- /数据/数据类型.md: -------------------------------------------------------------------------------- 1 | ## 数据类型 2 | 3 | --- 4 | 5 | ### JavaScript 中的数据类型 6 | 7 | JavaScript 所有的值(数据)都有着自己的**数据类型**。目前 JavaScript 中共有以下的数据类型: 8 | 9 | - `number`(数值) 10 | - `BigInt` (任意精度整数,2019年发布) 11 | - `string`(字符串) 12 | - `object`(对象) 13 | - `function`(函数) 14 | - `undefined` 15 | - `regexp`(正则表达式) 16 | - `symbol`(符号) 17 | 18 | 我们已经接触了 `number` 用于数值计算,`string` 用于处理文本内容,`object` 作为存放其他数据的结构,还有 `undefined`,它自身也是一种独立的数据类型。你可能会为看不见 `array` 而感到奇怪,事实上,数组是一种特殊的对象,它被精心设计以用于处理数据,但是我们仍需将它归类为 `object`。至于另外三种数据类型,我们会在后面逐渐接触到。 19 | 20 | JavaScript 提供了 `typeof` 操作符来确定一个值的数据类型,它得到类型的字符串名称,即以上七种之一。 21 | 22 | ```javascript 23 | alert(typeof 123); // "number" 24 | alert(typeof NaN); // "number 25 | alert(typeof Infinity); // "number" 26 | alert(typeof "Hello world"); // "string" 27 | alert(typeof {}); // "object" 28 | alert(typeof [1, 2, 3]); // "object" 29 | alert(typeof null); // "object" 30 | alert(typeof alert); // "function" 31 | alert(typeof undefined); // "undefined" 32 | ``` 33 | 34 | 35 | 36 | --- 37 | Note: 为什么 `typeof null === "object"` ? 38 | 39 | 在 JavaScript 第一个版本的实现中,每个值在内部存储时都会用一个标记来记录它的数据类型。由于 `object` 是 JavaScript 中的“第一等类型”,它的类型标记为 `0`,而 `null` 表示为 *`NULL` 指针*,在大多数平台上, `NULL` 指针的实际值是 `0x00`,那么 `null` 的数据类型标记实际上与 `object` 相同。因此 `typeof` 运算符在获取 `null` 的类型标记的时候,得到的是 `0` ,便会将它判断为 `"object"`。 40 | 41 | --- 42 | 43 | 44 | 45 | 46 | 47 | ### 判断数据类型的技巧 48 | 49 | `typeof` 的首要作用自然是判断数据类型,以便了解到的数据类型做出可能的操作。它具有明显的局限性:它对数据类型的判断仅限于以上七种;无法区分 `array` 和一般的 `object`;当我们明确地需要一个**对象**时它却会将 `null` 混为一谈。我们需要在 `typeof` 的判断的基础上使用一些辅助方法,以便在各类数据类型之间游刃有余。 50 | 51 | **区分数组与对象** 52 | 53 | `typeof` 会将数组判定为 `"object"`,没关系,JavaScript 提供了 `Array.isArray` 函数来判断一个值是不是数组。那么我们只需要: 54 | 55 | ```javascript 56 | alert(Array.isArray([1, 2, 3])); // true 57 | alert(Array.isArray([])); // true 58 | alert(Array.isArray(new Array())); // true 59 | alert(Array.isArray({})); // false 60 | ``` 61 | 62 | 63 | 64 | **精确地判断对象** 65 | 66 | 我们在第一节(逻辑)中了解过,`null` 会被当做一个假值,因此对它进行非运算会得到 `true`。 67 | 68 | 我们可以采取这样的策略: 69 | 70 | - 使用`typeof` 进行判断,如果它不是 `"object"`,得到 `false`。 71 | - 如果它是 `"object"`,判断它是否为数组,若是,得到 `false`。 72 | - 如果它是 `null`,得到 `false`,否则为 `true`。可以对它进行 `!!`操作,若为 `null` ,我们就会得到 `false`,否则我们就会得到 `true`。 73 | 74 | 示例: 75 | 76 | ```javascript 77 | let value = "Hello world"; 78 | alert( // 对 "Hello" 进行判断 79 | typeof value !== "object" ? false : 80 | Array.isArray(value) ? false : 81 | !!value 82 | ); // false 83 | // value = true -> false 84 | // value = 123 -> false 85 | // value = null -> false 86 | // value = [1, 2, 3] -> false 87 | // value = {a: 1, b: 2} -> true 88 | // value = window -> true 89 | ``` 90 | 91 | **精确判断数字** 92 | 93 | `NaN` 和 `±Infinity` 都属于 `number` 类型,但是我们在进行数学运算的时候并不希望将它们参与到运算中。我们只需要进行一些附加判断即可将它们与真正的数字区分开来。 94 | 95 | ```javascript 96 | let n = 100; 97 | alert( 98 | typeof n !== "number" ? false : 99 | isNaN(n) ? false : isFinite(n) 100 | ); // true 101 | // n = "123" -> false 102 | // n = NaN -> false 103 | // n = Infinity -> false 104 | // n = Number.MAX_VALUE -> true 105 | // n = Number.MAX_VALUE * 10 -> false 106 | ``` 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /数据/第三章练习.md: -------------------------------------------------------------------------------- 1 | ## 练习题 2 | 3 | --- 4 | 5 | 1. 世界范围内人们使用两种温度计量方式,一种是摄氏度($$^{\circ}C$$),它的意义是:在标准大气压下,将水结冰的温度记为 0 ℃,而水沸腾的温度则为 100 ℃。另一种是华氏度(℉),其意义是:以水银为测温介质,将氯化铵和冰水的混合物的开始结冰的温度作为 0 ℉,人体温度为 100 ℉。 6 | 7 | 设摄氏度为 $$C$$,华氏度为 $$F$$,则它们的相互转换公式如下: 8 | 9 | $$ F = C \times1.8 +32 $$ 10 | 11 | $$ C=(F-32) \div 1.8 $$ 12 | 13 | 写一个程序,提示用户输入温度值,并将其进行转换。 14 | 15 | 16 | 17 | 2. 18 | 19 | 20 | -------------------------------------------------------------------------------- /数据/解构赋值.md: -------------------------------------------------------------------------------- 1 | ## 人以群分·解构赋值 2 | 3 | --- 4 | 5 | JavaScript 提供了一类语法以简化赋值操作,可以根据一组数据,一次定义一组变量,像这样: 6 | 7 | ```javascript 8 | let [a, b] = [1, 2]; 9 | alert(a); // 1 10 | alert(b); // 2 11 | ``` 12 | 13 | 等号右边是一个通常的对象/数组字面量,而等号左边就写成与右边相匹配的格式,即一个*模式*,以满足我们的需要。 14 | 15 | 它实际上是如下方式的替代: 16 | 17 | ```javascript 18 | let arr = [1, 2]; 19 | let a = arr[0]; 20 | let b = arr[1]; 21 | ``` 22 | 23 | 这类语法被称为*解构赋值*,按字面意义上来理解,就是“解析一个结构,并进行赋值”。 24 | 25 | 26 | 27 | 28 | 29 | ### 数组解构 30 | 31 | 你可以将一组变量在声明时同时进行赋值操作。 32 | 33 | ```js 34 | let arr = ["one", "two", "three"]; 35 | let [one, two, three] = arr; 36 | alert(one); // "one" 37 | alert(two); // "two" 38 | alert(three); // "three" 39 | ``` 40 | 41 | 也可以将声明与赋值分离。 42 | 43 | ```javascript 44 | let a, b; 45 | [a, b] = [1, 2]; 46 | alert(a); // 1 47 | alert(b); // 2 48 | ``` 49 | 50 | 如果一个模式没有找到相匹配的值,相应的变量就会得到 `undefined`,因为不知道到该给它赋什么值,只能说明它是“未定义”的。 51 | 52 | ```javascript 53 | let [a, b, c] = [1, 2]; 54 | alert(a); // 1 55 | alert(b); // 2 56 | alert(c); // undefined 57 | ``` 58 | 59 | 为了避免发生这种情况,可以在模式中为变量设置一个默认值。如果找不到匹配值,就使用变量的默认值。 60 | 61 | ```javascript 62 | let [a = 3, b = 4, c = 5] = [1, 2]; 63 | alert(a); // 1 64 | alert(b); // 2 65 | alert(c); // 5 66 | ``` 67 | 68 | 在一个解构赋值的表达式中我们还可以交换两个已有变量的值,以避免使用*临时变量*。 69 | 70 | ```javascript 71 | let a = 3, b = 5; 72 | [a, b] = [b, a]; 73 | alert(a); // 5 74 | alert(b); // 3 75 | 76 | // 不使用解构赋值 77 | let temp = a; 78 | a = b; 79 | b = temp; 80 | alert(a); // 5 81 | alert(b); // 3 82 | ``` 83 | 84 | 你也可以忽略你不感兴趣的值: 85 | 86 | ```js 87 | let [a, , b] = [1, 2, 3]; 88 | alert(a); // 1 89 | alert(b); // 3 90 | 91 | // 甚至忽略所有值,但是没必要这样 92 | [, ,] = [1, 2, 3]; 93 | ``` 94 | 95 | 当解构一个数组时,可以使用*剩余模式*,将数组的“剩余部分”交给一个变量。 96 | 97 | ```js 98 | let [a, ...b] = [1, 2, 3]; 99 | alert(a); // 1 100 | alert(b); // [2, 3] 101 | ``` 102 | 103 | 注意:如果剩余模式右侧有逗号,会得到一个语法错误,因为剩余元素必须是数组的最后一个元素。 104 | 105 | ```js 106 | let [a, ...b,] = [1, 2, 3]; // SyntaxError: rest element may not have a trailing comma 107 | ``` 108 | 109 | 同时,使用剩余模式赋值的变量不能被设置默认值。 110 | 111 | ```javascript 112 | let [a, ...b = 2] = [3, 4] // SyntaxError: Invalid destructuring assignment target 113 | alert("a = " + a); // 不会执行 114 | alert("b = " + b); // 不会执行 115 | ``` 116 | 117 | 用于实现剩余模式的三个点号称为*Spread 操作符*。 118 | 119 | 120 | 121 | 122 | 123 | ### 对象解构 124 | 125 | 除了数组以外,你还可以对一个对象进行解构赋值。在数组解构中,变量被赋予的值取决于它的排列顺序,而在对象解构中,变量根据对象中对应的属性名被赋予值。 126 | 127 | ```js 128 | let o = {p: 42, q: true}; 129 | let {p, q} = o; 130 | alert(p); // 42 131 | alert(q); // true 132 | ``` 133 | 134 | 通过解构,无需声明即可赋值一个变量。 135 | 136 | ```js 137 | let a, b; 138 | ({a, b} = {a: 1, b: 2}); 139 | alert(a); // 1 140 | alert(b); // 2 141 | ``` 142 | 143 | 赋值语句周围的 `( ... )` 是使用对象字面解构赋值时不需要声明的语法。`{a, b} = {a: 1, b: 2}` 不是有效的独立语法,因为左边的 `{a, b}` 被认为是一个**块语句**而不是对象字面量。然而,`({a, b} = {a: 1, b: 2})` 是有效的,相当于`let {a, b} = {a: 1, b: 2}` 144 | 145 | 146 | 147 | --- 148 | 149 | Note: 150 | 151 | 你的`( ... )` 表达式需要一个分号在它前面,否则它也许会被当成上一行中的函数来执行。 152 | 153 | --- 154 | 155 | 156 | 157 | 我们也可以根据属性,将值赋给我们需要的变量名,而不一定与属性名重名。 158 | 159 | ```js 160 | let obj = {p: 42, q: true}; 161 | let {p: a, q: b} = obj; 162 | // 相当于 163 | // a = obj.p; 164 | // b = obj.q 165 | alert(a); // 42 166 | alert(b); // true 167 | ``` 168 | 169 | 变量也可以先赋予默认值。当要提取的对象没有对应的属性,变量的值就是它的默认值。 170 | 171 | ```js 172 | let {a = 10, b = 5} = {a: 3}; 173 | alert(a); // 3 174 | alert(b); // 5 175 | ``` 176 | 177 | 可以将上述方法结合起来。 178 | 179 | ```js 180 | let {a: first = 10, b: second = 5} = {a: 3}; 181 | alert(first); // 3 182 | alert(second); // 5 183 | ``` 184 | 185 | 模式中可以像普通对象字面量那样使用计算属性名,来确定要从对象中匹配的属性值。 186 | 187 | ```js 188 | let key = "name"; 189 | let { [key]: theName } = { name: "Pluto" }; 190 | alert(theName); // "Pluto" 191 | ``` 192 | 193 | 事实上,我们还可以像数组解构那样,对对象解构应用剩余模式: 194 | 195 | ```js 196 | let {a, b, ...others} = {a: 10, b: 20, c: 30, d: 40} 197 | alert(a); // 10 198 | alert(b); // 20 199 | // others = { c: 30, d: 40 } 200 | alert(others.c); // 30 201 | alert(others.d); // 40 202 | ``` 203 | 204 | 显然,如果一个属性不能作为合法的标识符名称,就需要用一个等价的方式来代替。 205 | 206 | ```js 207 | const obj = { "hello-world": "Oh!" }; 208 | const { "hello-world": helloWorld } = obj; 209 | 210 | alert(helloWorld) // "Oh!" 211 | ``` -------------------------------------------------------------------------------- /数据/逻辑.md: -------------------------------------------------------------------------------- 1 | ## 万物原动力·逻辑 2 | 3 | --- 4 | 5 | ### 布尔值 6 | 7 | 小的时候,我们认为一张白纸只有两面,一个人只有好人坏人的区别,这个世界只有黑白两种颜色,说的话只有真假之分。现在,我们也可以说:逻辑——所有的逻辑,本质上都是一个靠真与假驱动的世界。真,则一往直前;假,则回归起点。JavaScript 提供了 `true` 来表示所有的“真”,`false` 来表示所有的“假”。他们便是一个真与假的二元世界。`true` 和 `false` 被称为布尔值,用以纪念 19 世纪为逻辑学做出杰出贡献的 George Boole。 8 | 9 | 在 JavaScript 中,布尔值属于 `boolean` 类型。你可以直接使用它们来明确地表示真假、是非,也时常会隐藏在一串逻辑表达式中,作为它背后的力量。 10 | 11 | ```javascript 12 | alert(true); // true 13 | alert(false); // false 14 | ``` 15 | 16 | 17 | 18 | 19 | 20 | ### undefined 和 null 21 | 22 | 先来看四个句子: 23 | 24 | 杨雨露有个姐姐,她叫杨雨晴。 25 | 26 | 杨雨露没有姐姐。 27 | 28 | 杨雨露不知道自己有没有姐姐。 29 | 30 | 杨雨露有姐姐,但是不知道姐姐在哪。 31 | 32 | 33 | 34 | JavaScript 为第二种情况提供了 `null`,为第三、四种情况提供了 `undefined`。 35 | 36 | 37 | 38 | `null` 和 `undefined` 是 JavaScript 定义的两个特殊值,分别表示 39 | 40 | 1. 一个空值。 41 | 42 | 这个可能需要一个值,但是明确地知道”这是空的“,用 `null` 来表示空值。 43 | 44 | 2. 未发现需要的值。 45 | 46 | 这个地方不知道有没有值,用 `undefined` 来表示”未定义“。 47 | 48 | `null` 是 JavaScript 的*关键字*,如果对它进行声明或赋值操作会产生错误。 49 | 50 | ```javascript 51 | let null; // SyntaxError: Unexpected token null 52 | null = 1; // ReferenceError: Invalid left-hand side in assignment 53 | ``` 54 | 55 | `undefined` 不是一个明确定义的保留字,如果尝试对它赋值不会产生错误,但它的值也不会改变。 56 | 57 | ```javascript 58 | alert(undefined); // undefined 59 | undefined = 0; // 不会产生错误 60 | alert(undefined); // undefined 61 | ``` 62 | 63 | 由于 `undefined` 在使用前已经存在,如果对它进行重复声明或赋值,可能会产生错误,也可能不产生错误,可能会赋值成功也可能失败,这是一个语言缺陷。请避免重定义 `undefined`。 64 | 65 | 66 | 67 | --- 68 | 69 | Note: 70 | 71 | 在全局上下文中 `undefined` 不允许被重复声明。如果重复赋值,`undefined`的行为是未定义的。而在局部作用域上下文中,`undefined` 可以被重新声明和赋值,它的表现像一个预定义但没有声明的变量,存在暂时性死区。 72 | 73 | 但很显然,这没什么用。 74 | 75 | 我们的运行器提供了一个特制的局部上下文,因此: 76 | 77 | ```javascript 78 | alert(undefined); // ReferenceError: Cannot access 'undefined' before initialization 79 | let undefined = 0; 80 | alert(undefined); // 0 81 | undefined = 3; 82 | alert(undefined); // 3 83 | ``` 84 | 85 | --- 86 | 87 | 88 | 89 | 我们可以使用表达式 `void 0` 来得到最“纯粹”的 `undefined` 值。并且我们也推荐这种方法——它写起来更简短! 90 | 91 | ```javascript 92 | alert(void 0); // undefined 93 | undefined = 0; 94 | alert(undefined); // 0 95 | alert(void 0); // undefined 96 | ``` 97 | 98 | 如果我们声明了一个变量却没有给它赋予任何值,那么它的默认值就是 `undefined` ——即“未定义”。 99 | 100 | ```javascript 101 | let a; 102 | alert(a); // undefined 103 | ``` 104 | 105 | 106 | 107 | --- 108 | 109 | 练习 3.1.1 110 | 111 | 1. 举出生活中可以分别用 `null` 和 `undefined` 描述的例子。 112 | 2. 尝试了解在 JavaScript 的创造过程中,`null` 和 `undefined` 分别是怎样出现的。 113 | 114 | --- 115 | 116 | 117 | 118 | 119 | 120 | ### 逻辑运算 121 | 122 | 很多人觉得逻辑冰冷而机械死板,正是如此。因此,它才有用。人类易被感情左右,但计算机不同。正是因为冰冷且机械死板,计算机才会一直稳定运行,为我们所用。 123 | 124 | 逻辑的本质是真与假的组合。在 JavaScript 中,以下值都会被视为“假”: 125 | 126 | `false NaN 0 "" '' null undefined` 127 | 128 | 除了以上的“假”值,其他自然都是“真”值。关于 0 、特殊数值 NaN、字符串的概念将在下文中讲到。 129 | 130 | 真值都可以被看做 `true`,假值都可以被看做 `false`。这两个布尔值是逻辑的基本组成部分,简单的逻辑自然也可以组合成更复杂的逻辑,这个组合的过程我们称为*逻辑运算*。与、或、非是三个基本的逻辑运算,JavaScript 提供了它们的运算符 `&&` `||` 和 `!`。这三个运算得到的值与参与运算的值有关,但是得到的还是参与运算的值本身,而不一定是布尔值。 131 | 132 | 133 | 134 | 1. **&& (与)** 135 | 136 | - 如果两个条件都为 `true`,那么得到 `true`,否则得到 `false`。 137 | 138 | 它和我们平时说话时“如果……并且……”是类似的,即判断两个条件是否都*成立*, 139 | 140 | ```javascript 141 | alert(true && false); // false 142 | alert(true && true); // true 143 | alert(false && false); // false 144 | ``` 145 | 146 | 很容易理解,对吧!但事实上,`&&` 不一定会得到一个布尔值。它得到的值与用来运算的值有关,如果运算的值不是布尔值,它也不一定得到一个布尔值,而是根据值本身被看做“真”或被看做“假”来决定得到什么值。 147 | 148 | 它的具体运算方式如下: 149 | 150 | - 如果第一个条件被视为 `true`,而第二个条件被视为 `false` ,那么得到第二个条件的值。 151 | - 如果两个条件都被视为 `true`,那么得到第二个条件的值。 152 | - 如果第一个条件被视为 `false`,那么得到第一个条件的值。 153 | 154 | 示例: 155 | 156 | ```javascript 157 | alert(0 && true); // 0 158 | alert(true && 0); // 0 159 | alert(0 && false); // 0 160 | alert(false && 0); // false 161 | alert(100 && 0); // 0 162 | alert("Hello" && "") // "" 163 | alert(null && undefined) // null 164 | alert(100 && NaN) // NaN 165 | ``` 166 | 167 | 168 | 169 | 2. **||(或)** 170 | 171 | - 两个条件中只要有一个为 `true`,那么得到 `true`,否则为 `false`。 172 | 173 | 它和我们平时所说的“如果……或者……”是等价的。 174 | 175 | ```javascript 176 | alert(true || false); // true(第一个条件的值) 177 | alert(true || true); // true(第一个条件的值) 178 | alert(false || false); // false(第二个条件的值) 179 | ``` 180 | 181 | 和 `&&` 类似,`||` 也不一定得到一个布尔值,而是根据它所运算的值被看做“真”还是看做“假”来得到值。 182 | 183 | 它的具体运算方式如下: 184 | 185 | - 如果第一个条件被视为 `true`,那么直接得到第一个条件的值。 186 | - 如果第一个条件被视为`false`,那么得到第二个条件的值。 187 | 188 | 示例: 189 | 190 | ```javascript 191 | alert(0 || true); // true 192 | alert(true || 0); // true 193 | alert(0 || false); // false 194 | alert(false || 0); // 0 195 | alert(100 || 0); // 100 196 | alert("Hello" || "") // "Hello" 197 | alert(null || undefined) // undefined 198 | alert(100 || NaN) // 100 199 | ``` 200 | 201 | 换句话说,如果第一个条件为“真”,那么就符合“或”的条件了,不必再判断下一个。如果第一个条件为假,就需要将第二个条件作为整个运算得到的值。 202 | 203 | 204 | 205 | 3. **!(非)** 206 | 207 | - 如果值为 `true`,那么得到 `false`,否则得到 `true`。 208 | 209 | 它确实得到一个布尔值,具体运算方式如下: 210 | 211 | - 如果条件被视为 `true`,那么得到 `false`。 212 | - 如果条件被视为 `false`,那么得到 `true`。 213 | 214 | 示例: 215 | 216 | ```javascript 217 | alert(!true); // false 218 | alert(!false); // true 219 | alert(!0); // true 220 | alert(!100); // false 221 | alert(!NaN); // true 222 | alert(!""); // true 223 | alert(!undefined); // true 224 | alert(!!0); // false 225 | alert(!!null); // true 226 | ``` 227 | 228 | 因此,!! 两个非运算重复进行,得到的值就是条件本身的布尔值描述形式,即被看做“真”还是“假”。 229 | 230 | 231 | 232 | **运算符优先级** 233 | 234 | 当 `&&` `||` `!` 三个运算符同时在一个表达式中,运算过程遵循**操作符优先级**。! 操作符具有最高的优先级,即在一个表达式中它所属的式子总是被最先计算,其次是 `&&`,`||` 的优先级最低。 235 | 236 | ```javascript 237 | alert(10 && !5); // false 238 | alert(!5 && 10); // false 239 | alert(!5 || 6 && 7); // 7 240 | alert(5 || 6 && 7 || 8); // 5 241 | alert(5 && 6 || 7 && 8); // 6 242 | alert(!5 || !6 || 7 && 8); // 8 243 | ``` 244 | 245 | 在第三行代码中: 246 | 247 | 1. `!5` 由于具有最高的优先级,被最先计算。由于它是 `false`,且是 `||` 的第一个条件,因此会继续计算位于 `||` 右侧的第二个条件。 248 | 2. 由于 `&&` 的优先级大于 `||` ,因此会计算 `6 && 7`,结果为 `7`,那么 `||` 的第二个条件就是 `7`。 249 | 3. 因此整个逻辑表达式的结果就是 7。 250 | 251 | 在第四行代码中: 252 | 253 | 1. 5 被视作 `true`, 因此 || 运算符不会查看第二个条件。 254 | 2. 结果为 5。 255 | 256 | 在第五行代码中,首先从左往右运算,`5 && 6` 的值为 6,则 `||` 的第一个条件为 6,最后结果为 6。 257 | 258 | 在第六行代码中: 259 | 260 | 1. !5 为 `false`,`||` 运算符会查看第二个条件。 261 | 2. 由于 ! 具有最高的优先级,!6 会先得到计算,结果为 `false`。 262 | 3. 那么 `!5 || !6` 的值为 `false`。 263 | 4. 第二个 `||` 操作符会查看右边的条件。 264 | 5. 由于 `&&` 的优先级大于 `||` ,会先计算 `7 && 8` 的值,值为 `8`。 265 | 6. 那么右边的条件为 8。 266 | 7. `false || 8` 的值为 8。 267 | 268 | 在实际应用中,我们可以使用括号 `(` `)` 来更改默认的运算符优先级。使用了括号的示例如下: 269 | 270 | ```javascript 271 | alert((5 || 6) && (7 || 8)); // 8 272 | alert(5 && (6 || 7) && 8); // 8 273 | alert(5 && 6 && (7 && 8)); // 8 274 | ``` 275 | 276 | 277 | 278 | 一个操作符后使用括号括起来的内容是一个整体,会先计算括号中表达式的值,这个值作为该操作符的条件进行下一步计算。因此,在第一行代码中: 279 | 280 | 1. `(5 || 6)` 作为一个整体会被计算,值为 6。 281 | 2. 6 成为了 `&&` 的第一个条件。`&&` 会查看第二个条件。 282 | 3. 第二个条件是 `(7 || 8)`,值为 8,所以第二个条件是 8。 283 | 4. `6 && 8` 结果为 8。 284 | 285 | 你可以尝试演算另两行代码的运算过程。 286 | 287 | --- 288 | 289 | 练习 3.1.2 290 | 291 | 1. 计算如下代码的值: 292 | 293 | `(18 || 24) && (15 && 0) || 6 || !12` 294 | 295 | 2. 计算如下代码的值: 296 | 297 | `!(15 || 0) && !(12 && !12) ` 298 | 299 | 3. 计算如下代码的值: 300 | 301 | `18 && (!(15 || 10) && (15 && 10))` 302 | 303 | --- 304 | 305 | 306 | 307 | 308 | 309 | ### 条件表达式 310 | 311 | 条件表达式是一种*三目运算符*,它需要三个操作数。格式如下: 312 | 313 | `a ? b : c` 314 | 315 | 如果 `a` 被视作 `true`,则这个表达式的值为 `b` ,否则为 `c`。 316 | 317 | 示例: 318 | 319 | ```javascript 320 | let a = 0; 321 | alert(a ? "Hello" : "Hi"); // "Hi" 322 | ``` 323 | 324 | 条件表达式的运算符具有最低的优先级。也就是说,如果 `a` `b` `c` 都是其它表达式,那么一定会先计算出 `a` `b` `c` 的值,再得到条件表达式的值。一般来说,如果 `a` `b` `c` 都是表达式,我们推荐给用括号进行包裹以避免混淆。 325 | 326 | --- 327 | 328 | 练习 3.1.3 329 | 330 | 1. 计算下列条件表达式的值: 331 | 332 | `(!(15 || 10) && (15 && 10)) ? "Hello" : "world"` 333 | 334 | 2. 计算下列条件表达式的值: 335 | 336 | `(15 || (true && NaN) || !Infinity && (!NaN || 12))) ? "Jim" : "Tom"` 337 | 338 | --- 339 | 340 | -------------------------------------------------------------------------------- /新的征途/Chrome 扩展.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/新的征途/Chrome 扩展.md -------------------------------------------------------------------------------- /新的征途/Electron.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/新的征途/Electron.md -------------------------------------------------------------------------------- /新的征途/Node.js.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/新的征途/Node.js.md -------------------------------------------------------------------------------- /新的征途/案例研究:聊天室.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/新的征途/案例研究:聊天室.md -------------------------------------------------------------------------------- /新的征途/案例研究:音乐播放器.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/新的征途/案例研究:音乐播放器.md -------------------------------------------------------------------------------- /新的征途/自动化工作流.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/新的征途/自动化工作流.md -------------------------------------------------------------------------------- /练习答案/第四章.md: -------------------------------------------------------------------------------- 1 | 5. ```javascript 2 | let n = parseInt(prompt("请输入一个正整数:")); 3 | let info = "你输入的数是:" + n + "\n"; 4 | while (n !== 1) { 5 | if (n % 2 === 0) { 6 | info += n + " 是一个偶数,将它除以 2 得到 "; 7 | n /= 2; 8 | info += n + "\n"; 9 | } else { 10 | info += n + " 是一个奇数,将它乘以 3 再加 1 得到 "; 11 | n *= 3; 12 | n += 1; 13 | info += n + "\n"; 14 | } 15 | } 16 | alert(info); 17 | ``` 18 | 19 | 6. -------------------------------------------------------------------------------- /绪论/绪论.md: -------------------------------------------------------------------------------- 1 | ## 绪论 2 | 3 | --- 4 | 5 | > 路漫漫其修远兮,吾将上下而求索。 6 | > 7 | > ——屈原 8 | 9 | 10 | 11 | ### JavaScript 的诞生 12 | 13 | 现代国际互联网(Internet)最早起源于美国国防部高级研究计划署(Defence Advanced Research Projects Agency,DARPA)所打造的一个早期计算机网络系统 ARPAnet ,该网络于1969年投入使用。由此,ARPAnet 成为现代计算机网络诞生的标志。 14 | 15 | 1980年,瑞士欧洲核子研究组织(CERN)的科学家蒂姆·博纳斯·李爵士建立了INQUIRE,作为一套基于计算机网络的数据库,同时实践了超文本的概念:INQUIRE 中的每一个页面都必须链接到另一个页面。1989年,蒂姆开始构想一套将超文本与互联网完美融合的项目,由于并没有人做过这样的尝试,他开始着手实现一些必要工具,1990年圣诞节,蒂姆创造了一种用于传输信息的协议——HTTP ,一个非常原始的浏览器,称为 World-Wide-Web,以及用于提供支持的服务器。这个项目最初被用来向核子研究组织的工作人员提供服务,后来向公众开放,主要提供一些文献和数据。这个项目被命名为 World Wide Web,即万维网,研究人员可以从万维网上浏览网页。最早的网页只能在操作系统的终端里浏览,网页都是在字符窗口中显示,这当然非常不方便。 16 | 17 | JavaScript 因为互联网而生,紧跟着浏览器的出现而问世。回顾它的历史,就要从浏览器的历史讲起。 18 | 19 | 1992年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做 Mosaic。这是人类历史上第一个真正的浏览器,从此网页可以在图形界面的窗口浏览。 20 | 21 | 1994年10月,NCSA 的一个主要程序员 Marc Andreessen 联合风险投资家 Jim Clark,成立了 Mosaic 通信公司(Mosaic Communications),不久后改名为 Netscape。这家公司的方向,就是在 Mosaic 的基础上,开发面向普通用户的新一代的浏览器 Netscape Navigator。 22 | 23 | 1994年12月,Navigator 发布了 1.0 版,市场份额一举超过90%。 24 | 25 | Netscape 公司很快发现,Navigator 浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为。当时,网速很慢而且上网费很贵,有些操作不宜在服务器端完成。比如,如果用户忘记填写“用户名”,就点了“发送”按钮,到服务器再发现这一点就有点太晚了,最好能在用户发出数据之前,就告诉用户“请填写用户名”。这就需要在网页中嵌入一些小型程序,让浏览器检查每一栏是否都填写了。 26 | 27 | 管理层对这种浏览器脚本语言的设想是:功能不需要太强,语法较为简单,容易学习和部署。那一年,正逢 Sun 公司的 Java 语言问世,市场推广活动非常成功。Netscape 公司决定与 Sun 公司合作,浏览器支持嵌入 Java 小程序(后来称为 Java applet)。但是,浏览器脚本语言是否就选用 Java,则存在争论。后来,还是决定不使用 Java,因为Java 的语法较为笨重,且需要一个庞大的运行时环境提供支持,与快速、迅捷的网页开发初衷相去甚远。但是,同时也决定脚本语言的语法要接近 Java,并且可以支持与 Java 程序合作。这些设想直接排除了使用现存语言,比如 Perl、Python 和 TCL。 28 | 29 | 1995年,Netscape 公司雇佣了程序员 Brendan Eich 开发这种网页脚本语言。Brendan Eich 有很强的函数式编程背景,希望以 Scheme 语言(函数式语言鼻祖 LISP 语言的一种方言)为蓝本,实现这种新语言。 30 | 31 | 1995年5月,Brendan Eich 只用了10天,就设计完成了这种语言的第一版。它是一个大杂烩,语法有多个来源。为了保持简单,这种脚本语言缺少一些关键的功能,比如块级作用域、模块、子类型(subtyping)等等,但是可以利用现有功能找出解决办法。这种功能的不足,直接导致了后来 JavaScript 的一个显著特点:对于其他语言,你需要学习语言的各种功能,而对于 JavaScript,你常常需要学习各种解决问题的模式。而且由于来源多样,从一开始就注定,JavaScript 的编程风格是函数式编程和面向对象编程的一种混合体。 32 | 33 | Netscape 公司的这种浏览器脚本语言,最初名字叫做 Mocha,1995年9月改为 LiveScript。12月,Netscape 公司与 Sun 公司(Java 语言的发明者和所有者)达成协议,后者允许将这种语言叫做 JavaScript。这样一来,Netscape 公司可以借助 Java 语言的声势,而 Sun 公司则将自己的影响力扩展到了浏览器。 34 | 35 | 之所以起这个名字,并不是因为 JavaScript 本身与 Java 语言有多么深的关系。它们的实际关系更类似于口语中“老婆”与“老婆饼”那样。 36 | 37 | 那时,因为 Netscape 公司已经决定,使用 Java 语言开发网络应用程序,而 JavaScript 可以像胶水一样,将各个部分连接起来。当然,后来的历史是 Java 语言的计划失败了,JavaScript 反而发扬光大。 38 | 39 | 1995年12月4日,Netscape 公司与 Sun 公司联合发布了 JavaScript 语言,对外宣传 JavaScript 是 Java 的补充,属于轻量级的 Java,专门用来操作网页。 40 | 41 | 1996年3月,Navigator 2.0 浏览器正式内置了 JavaScript 脚本语言。 42 | 43 | 44 | 45 | 46 | 47 | ### ECMAScript 48 | 49 | 1996年8月,微软模仿 JavaScript 开发了一种相近的语言,取名为JScript(JavaScript 是 Netscape 的注册商标,微软不能用),首先内置于IE 3.0。Netscape 公司面临丧失浏览器脚本语言的主导权的局面。 50 | 51 | 1996年11月,Netscape 公司决定将 JavaScript 提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScript 能够成为国际标准,以此抵抗微软。ECMA 的39号技术委员会(Technical Committee 39)负责制定和审核这个标准,成员由业内的大公司派出的工程师组成,目前共25个人。该委员会定期开会,所有的邮件讨论和会议记录,都是公开的。 52 | 53 | 1997年7月,ECMA 组织发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript。这个版本就是 ECMAScript 1.0 版。之所以不叫 JavaScript,一方面是由于商标的关系,Java 是 Sun 公司的商标,根据一份授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 已经被 Netscape 公司注册为商标,另一方面也是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。 54 | 55 | ECMAScript 只用来标准化 JavaScript 这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如 DOM 的标准就是由 W3C组织(World Wide Web Consortium)制定的。 56 | 57 | ECMA-262 标准后来也被另一个国际标准化组织 ISO(International Organization for Standardization)批准,标准号是 ISO-16262。 58 | 59 | 60 | 61 | 62 | 63 | ### JavaScript 版本年表 64 | 65 | 1997年7月,ECMAScript 1.0发布。 66 | 67 | 1998年6月,ECMAScript 2.0版发布。ECMAScript 2.0 与 1.0 的修改主要是编辑完善性的。 68 | 69 | 1999年12月,ECMAScript 3.0版发布,成为 JavaScript 的通行标准,得到了广泛支持。这个版本加入了一项非常重要的功能——正则表达式。 70 | 71 | 2007年10月,ECMAScript 4.0版草案发布,对3.0版做了大幅升级,预计次年8月发布正式版本。草案发布后,由于4.0版的目标过于激进,各方对于是否通过这个标准,发生了严重分歧。以 Yahoo、Microsoft、Google 为首的大公司,反对 JavaScript 的大幅升级,主张小幅改动;以 JavaScript 创造者 Brendan Eich 为首的 Mozilla 公司,则坚持当前的草案。 72 | 73 | 2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激进,ECMA 开会决定,中止 ECMAScript 4.0 的开发(即废除了这个版本),将其中涉及现有功能改善的一小部分,发布为 ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为 Harmony(和谐)。会后不久,ECMAScript 3.1 就改名为 ECMAScript 5。 74 | 75 | 2009年12月,ECMAScript 5.0版 正式发布。Harmony 项目则一分为二,一些较为可行的设想定名为 JavaScript.next 继续开发,后来演变成 ECMAScript 6;一些不是很成熟的设想,则被视为 JavaScript.next.next,在更远的将来再考虑推出。TC39 的总体考虑是,ECMAScript 5 与 ECMAScript 3 基本保持兼容,较大的语法修正和新功能加入,将由 JavaScript.next 完成。当时,JavaScript.next 指的是ECMAScript 6。第六版发布以后,将指 ECMAScript 7。TC39 预计,ECMAScript 5 会在2013年的年中成为 JavaScript 开发的主流标准,并在此后五年中一直保持这个位置。 76 | 77 | 2011年6月,ECMAScript 5.1版发布,并且成为 ISO 国际标准(ISO/IEC 16262:2011)。到了2012年底,所有主要浏览器都支持 ECMAScript 5.1版的全部功能。 78 | 79 | 2013年3月,ECMAScript 6 草案冻结,不再添加新功能。新的功能设想将被放到 ECMAScript 7。 80 | 81 | 2013年12月,ECMAScript 6 草案发布。然后是12个月的讨论期,听取各方反馈。 82 | 83 | 2015年6月,ECMAScript 6 正式发布,并且更名为“ECMAScript 2015”。这是因为 TC39 委员会计划,以后每年发布一个 ECMAScript 的版本,下一个版本在2016年发布,称为“ECMAScript 2016”,2017年发布“ECMAScript 2017”,以此类推。 84 | 85 | 截止本书写作时(2019年),JavaScript 的最新版本是 ECMAScript 10,。由于它太新了,很多浏览器并没有提供相应支持,因此本书主要遵循 2015 年发布的 ECMAScript 6,同时涉及了一些 7 和 8 中的新特性。 86 | 87 | 88 | 89 | 90 | 91 | ### JavaScript 周边技术年表 92 | 93 | JavaScript 伴随着互联网的发展一起发展。互联网周边技术的快速发展,刺激和推动了 JavaScript 语言的发展。下面,回顾一下 JavaScript 的周边应用发展。 94 | 95 | 1996年,样式表标准 CSS 第一版发布。 96 | 97 | 1997年,DHTML(Dynamic HTML,动态 HTML)发布,允许动态改变网页内容。这标志着 DOM 模式(Document Object Model,文档对象模型)正式应用。 98 | 99 | 1998年,Netscape 公司开源了浏览器,这导致了 Mozilla 项目的诞生。几个月后,美国在线(AOL)宣布并购 Netscape。 100 | 101 | 1999年,IE 5部署了 XMLHttpRequest 接口,允许 JavaScript 发出 HTTP 请求,为后来大行其道的 Ajax 应用创造了条件。 102 | 103 | 2000年,KDE 项目重写了浏览器引擎 KHTML,为后来的 WebKit 和 Blink 引擎打下基础。这一年的10月23日,KDE 2.0发布,第一次将 KHTML 浏览器包括其中。 104 | 105 | 2001年,微软公司时隔5年之后,发布了 IE 浏览器的下一个版本 Internet Explorer 6。这是当时最先进的浏览器,它后来统治了浏览器市场多年。 106 | 107 | 2001年,Douglas Crockford 提出了 JSON 格式,用于取代 XML 格式,进行服务器和网页之间的数据交换。JavaScript 可以原生支持这种格式,不需要额外部署代码。 108 | 109 | 2002年,Mozilla 项目发布了它的浏览器的第一版,后来起名为 Firefox。 110 | 111 | 2003年,苹果公司发布了 Safari 浏览器的第一版。 112 | 113 | 2004年,Google 公司发布了 Gmail,促成了互联网应用程序(Web Application)这个概念的诞生。由于 Gmail 是在4月1日发布的,很多人起初以为这只是一个玩笑。 114 | 115 | 2004年,Dojo 框架诞生,第一次为不同浏览器提供了统一的接口,并为主要功能提供了便利的调用方法。这标志着 JavaScript 编程框架的时代开始来临。 116 | 117 | 2004年,WHATWG 组织成立,致力于加速 HTML 的标准化进程。 118 | 119 | 2005年,苹果公司在 KHTML 引擎基础上,建立了 WebKit 引擎。 120 | 121 | 2005年,Ajax 方法(Asynchronous JavaScript and XML)正式诞生,Jesse James Garrett 发明了这个词汇。它开始流行的标志是,2月份发布的 Google Maps 项目大量采用该方法。它几乎成了新一代网站的标准做法,促成了 Web 2.0时代的来临。 122 | 123 | 2005年,Apache 基金会发布了 CouchDB 数据库。这是一个基于 JSON 格式的数据库,可以用 JavaScript 函数定义视图和索引。它在本质上有别于传统的关系型数据库,标识着 NoSQL 类型的数据库诞生。 124 | 125 | 2006年,jQuery 函数库诞生,作者为John Resig。jQuery 为操作网页 DOM 结构提供了非常强大易用的接口,成为了使用最广泛的函数库,并且让 JavaScript 语言的应用难度大大降低,推动了这种语言的流行。 126 | 127 | 2006年,微软公司发布 IE 7,标志重新开始启动浏览器的开发。 128 | 129 | 2006年,Google推出 Google Web Toolkit 项目(缩写为 GWT),提供 Java 编译成 JavaScript 的功能,开创了将其他语言转为 JavaScript 的先河。 130 | 131 | 2007年,Webkit 引擎在 iPhone 手机中得到部署。它最初基于 KDE 项目,2003年苹果公司首先采用,2005年开源。这标志着 JavaScript 语言开始能在手机中使用了,意味着有可能写出在桌面电脑和手机中都能使用的程序。 132 | 133 | 2007年,Douglas Crockford 发表了名为《JavaScript: The good parts》的演讲,次年由 O'Reilly 出版社出版。这标志着软件行业开始严肃对待 JavaScript 语言,给予了它应有的重视和思考。 134 | 135 | 2008年,V8 引擎诞生。这是 Google 公司为 Chrome 浏览器而开发的,它使 JavaScript 的运行效率变得非常高,极大地提高了 JavaScript 程序的性能,推动了语法的改进和标准化,改变了外界对 JavaScript 的不佳印象。同时,V8 是开源的,任何人想要一种快速的嵌入式脚本语言,都可以采用 V8,这拓展了 JavaScript 的应用领域。 136 | 137 | 2009年,Node.js 项目诞生,创始人为 Ryan Dahl,它标志着 JavaScript 可以用于服务器端编程,从此网站的前端和后端可以使用同一种语言开发。并且,Node.js 可以承受很大的并发流量,使得开发某些互联网大规模的实时应用变得容易。 138 | 139 | 2009年,Jeremy Ashkenas 发布了 CoffeeScript 的最初版本。CoffeeScript 可以被转换为 JavaScript 运行,但是语法要比 JavaScript 简洁。这开启了其他语言转译为 JavaScript 的风潮。 140 | 141 | 2009年,PhoneGap 项目诞生,它将 HTML5 和 JavaScript 引入移动设备的应用程序开发,主要针对 iOS 和 Android 平台,使得 JavaScript 可以用于跨平台的应用程序开发。 142 | 143 | 2009,Google 发布 Chrome OS,号称是以浏览器为基础发展成的操作系统,允许直接使用 JavaScript 编写应用程序。类似的项目还有 Mozilla 的 Firefox OS。 144 | 145 | 2010年,三个重要的项目诞生,分别是 NPM、BackboneJS 和 RequireJS,标志着 JavaScript 进入模块化开发的时代。 146 | 147 | 2011年,微软公司发布 Windows 8操作系统,将 JavaScript 作为应用程序的开发语言之一,直接提供系统支持。 148 | 149 | 2011年,Google 发布了 Dart 语言,目的是为了结束 JavaScript 语言在浏览器中的垄断,提供更合理、更强大的语法和功能。Chromium浏览器有内置的 Dart 虚拟机,可以运行 Dart 程序,但 Dart 程序也可以被编译成 JavaScript 程序运行。 150 | 151 | 2011年,微软工程师 Scott Hanselman 提出,JavaScript 将是互联网的汇编语言。因为它无所不在,而且正在变得越来越快。其他语言的程序可以被转成 JavaScript 语言,然后在浏览器中运行。 152 | 153 | 2012年,单页面应用程序框架(single-page app framework)开始崛起,AngularJS 项目和 Ember 项目都发布了1.0版本。 154 | 155 | 2012年,微软发布 TypeScript 语言。该语言被设计成 JavaScript 的超集,这意味着所有 JavaScript 程序,都可以不经修改地在 TypeScript 中运行。同时,TypeScript 添加了很多新的语法特性,主要目的是为了开发大型程序,然后还可以被编译成 JavaScript 运行。 156 | 157 | 2012年,Mozilla 基金会提出 [asm.js](http://asmjs.org/) 规范。asm.js 是 JavaScript 的一个子集,所有符合 asm.js 的程序都可以在浏览器中运行,它的特殊之处在于语法有严格限定,可以被快速编译成性能良好的机器码。这样做的目的,是为了给其他语言提供一个编译规范,使其可以被编译成高效的 JavaScript 代码。同时,Mozilla 基金会还发起了 [Emscripten](https://github.com/kripken/emscripten/wiki) 项目,目标就是提供一个跨语言的编译器,能够将 LLVM 的位代码(bitcode)转为 JavaScript 代码,在浏览器中运行。因为大部分 LLVM 位代码都是从 C / C++ 语言生成的,这意味着 C / C++ 将可以在浏览器中运行。此外,Mozilla 旗下还有 [LLJS](http://mbebenita.github.io/LLJS/) (将 JavaScript 转为 C 代码)项目和 [River Trail](https://github.com/RiverTrail/RiverTrail/wiki) (一个用于多核心处理器的 ECMAScript 扩展)项目。目前,可以被编译成 JavaScript 的[语言列表](https://github.com/jashkenas/coffee-script/wiki/List-of-languages-that-compile-to-JS),共有将近40种语言。 158 | 159 | 2013年,Mozilla 基金会发布手机操作系统 Firefox OS,该操作系统的整个用户界面都使用 JavaScript。 160 | 161 | 2013年,ECMA 正式推出 JSON 的[国际标准](http://www.ecma-international.org/publications/standards/Ecma-404.htm),这意味着 JSON 格式已经变得与 XML 格式一样重要和正式了。 162 | 163 | 2013年5月,Facebook 发布 UI 框架库 React,引入了新的 JSX 语法,使得 UI 层可以用组件开发,同时引入了网页应用是状态机的概念。 164 | 165 | 2014年,微软推出 JavaScript 的 Windows 库 WinJS,标志微软公司全面支持 JavaScript 与 Windows 操作系统的融合。 166 | 167 | 2014年11月,由于对 Joyent 公司垄断 Node 项目、以及该项目进展缓慢的不满,一部分核心开发者离开了 Node.js,创造了 io.js 项目,这是一个更开放、更新更频繁的 Node.js 版本,很短时间内就发布到了2.0版。三个月后,Joyent 公司宣布放弃对 Node 项目的控制,将其转交给新成立的开放性质的 Node 基金会。随后,io.js 项目宣布回归 Node,两个版本将合并。 168 | 169 | 2015年3月,Facebook 公司发布了 React Native 项目,将 React 框架移植到了手机端,可以用来开发手机 App。它会将 JavaScript 代码转为 iOS 平台的 Objective-C 代码,或者 Android 平台的 Java 代码,从而为 JavaScript 语言开发高性能的原生 App 打开了一条道路。 170 | 171 | 2015年4月,Angular 框架宣布,2.0 版将基于微软公司的TypeScript语言开发,这等于为 JavaScript 语言引入了强类型。 172 | 173 | 2015年5月,Node 模块管理器 NPM 超越 CPAN,标志着 JavaScript 成为世界上软件模块最多的语言。 174 | 175 | 2015年5月,Google 公司的 Polymer 框架发布1.0版。该项目的目标是生产环境可以使用 WebComponent 组件,如果能够达到目标,Web 开发将进入一个全新的以组件为开发基础的阶段。 176 | 177 | 2015年6月,ECMA 标准化组织正式批准了 ECMAScript 6 语言标准,定名为《ECMAScript 2015 标准》。JavaScript 语言正式进入了下一个阶段,成为一种企业级的、开发大规模应用的语言。这个标准从提出到批准,历时10年,而 JavaScript 语言从诞生至今也已经20年了。 178 | 179 | 2015年6月,Mozilla 在 asm.js 的基础上发布 WebAssembly 项目。这是一种 JavaScript 引擎的中间码格式,全部都是二进制,类似于 Java 的字节码,有利于移动设备加载 JavaScript 脚本,执行速度提高了 20+ 倍。这意味着将来的软件,会发布 JavaScript 二进制包。 180 | 181 | 2016年6月,ECMAScript 2016 (即 ECMAScript 7)发布。与前一年发布的版本相比,它只增加了两个较小的特性。 182 | 183 | 2017年6月,ECMAScript 2017(即 ECMAScript 8)发布,正式引入了 async 函数,使得异步操作的写法出现了根本的变化。 184 | 185 | 2017年11月,所有主流浏览器的新版本全部支持 WebAssembly,这意味着任何语言都可以编译成 JavaScript,在浏览器运行。 186 | 187 | -------------------------------------------------------------------------------- /设计原理/JavaScript标准对象.md: -------------------------------------------------------------------------------- 1 | ## JavaScript 标准对象 2 | 3 | --- 4 | 5 | ### 内置对象 6 | 7 | *内置对象*(internal object)指的是 JavaScript 核心语言中所包含的类与对象。它们直接由 ECMAScript 标准定义,与运行环境没有关系,但任何 JavaScript 环境都必须预先按照标准实现这些对象,无论是台式计算机,还是移动设备,抑或是手掌大小的单片机。内置对象与语法本身,构成了 JavaScript 的核心语言。 8 | 9 | 内置对象既包含真真正正的对象(如 `Math`),又包括基本类型所对应的类(如 `Number`)和建立其他一些对象的类(如 `Promise`),还有一些就是单纯的函数和变量(如 `parseInt` 和 `NaN`)。内置对象如同构成 JavaScript 强大力量的基础元件,因此也叫*内置基元*。 10 | 11 | **1. 内置变量和普通函数** 12 | 13 | 我们之前已经介绍了三个内置变量:`Infinity`,`NaN` 和 `undefined`,还有四个内置函数:`parseInt`,`parseFloat`,`isNaN`,`isFinite`。除此之外还有一些函数: 14 | 15 | - `encodeURI` 和 `encodeURIComponent` 根据将一段普通文本根据*统一资源标识符*的要求编码为对应的*安全版本*。这些函数将在第九章介绍。 16 | - `decodeURI` 和 `decodeURIComponent` 执行相应编码的逆操作。这些函数将在第九章介绍。 17 | - `eval` 将一段字符串视为 JavaScript 代码并执行。如果代码是一个表达式,那么 `eval` 会求出它的值。由于必须对字符串进行编译和解释,因此 `eval` 执行的速度很慢。 18 | 19 | 以下是 `eval` 函数的示例。 20 | 21 | ```javascript 22 | let code = prompt("请输入一个算式"); 23 | if (/[^\d()+*/-%\s]/.test(code)) { 24 | alert("这不是一个算式!"); 25 | } else { 26 | alert(`算式的值是:${eval(code)}`); 27 | } 28 | ``` 29 | 30 | `eval` 函数具有诸多争议,它有时被称为 evil(邪恶的)。当编程语言的某项特性很容易被错误使用,这项特性就被视为邪恶的。使用 `eval` 的唯一原因是动态地执行运行时才产生的代码,但这些代码从何而来呢?如果代码的来源不受信任,就会使应用程序有被注入恶意代码的风险,甚至破坏应用程序。 31 | 32 | 根据人们的经验,如果我们需要使用 `eval` 来解析一些东西,我们先考虑一下是否有其它替代品可以达成同样目的。一些策略是单纯地解析而不执行,例如 `JSON.parse`。如果我们必须要使用 `eval`,就要保证代码一定是安全的,没有无限循环,没有奇怪的赋值或函数调用等。在上面的示例中,我们使用正则表达式对用户输入的内容进行检查,确保文本仅包含数字、括号和几个运算符,才会调用 `eval` 来求值。我们可以在实践中思考怎样为 `eval` 创建一个安全的执行环境。 33 | 34 | 35 | 36 | **2. Math** 37 | 38 | 39 | 40 | **3. Object** 41 | 42 | 43 | 44 | **4. Array** 45 | 46 | 47 | 48 | **5. Boolean、Number 和 String** 49 | 50 | 51 | 52 | **6. Function** 53 | 54 | 55 | 56 | **7. Date** 57 | 58 | 59 | 60 | **8. 错误对象** 61 | 62 | 技术上讲,我们可以使用任何东西来作为一个异常对象。甚至可以是基础类型,比如数字或者字符串。但是更好的方式是用对象,尤其是有 `name` 和 `message` 属性的对象(某种程度上和内置的异常有可比性)。 63 | JavaScript 有很多内置的标准异常构造器,我们也可以用它们来构造标准的异常对象。 64 | 65 | | JavaScript 标准异常构造器 | 描述 | 66 | | ------------------------- | --------------------------------------------------- | 67 | | `Error` | 默认或自定义的错误。 | 68 | | `EvalError` | 用错误的方式使用 `eval` 函数。 | 69 | | `InternalError` | JavaScript 引擎遇到的内部错误,如:“递归嵌套太多”。 | 70 | | `RangeError` | 数值变量或参数超出其有效范围。 | 71 | | `ReferenceError` | 无效的引用、求值过程。 | 72 | | `SyntaxError` | JavaScript 引擎在解析代码时遇到的语法错误。 | 73 | | `TypeError` | 变量或参数不属于有效类型。 | 74 | | `URIError` | 给 `encodeURI` 或 `decodeURI` 传递的参数无效。 | 75 | 使用异常构造器的方式如下: 76 | ```javascript 77 | let error = new Error(message); 78 | // 或者 79 | let error = new SyntaxError(message); 80 | let error = new ReferenceError(message); 81 | // ... 82 | ``` 83 | 对于内置的异常对象(不是对于其他的对象,而是对于异常对象),`name` 属性刚好是构造器的名字。`message` 则来自于参数所提供的异常信息。例如: 84 | ```javascript 85 | let error = new Error("不知道发生了什么 (O_o)??"); 86 | alert(error.name); // "Error" 87 | alert(error.message); // "不知道发生了什么 (O_o)??" 88 | ``` 89 | 我们可以使用任何东西来作为一个异常对象。甚至可以是基础类型,比如数字或者字符串。但是更好的方式是用对象,尤其是有 `name` 和 `message` 属性的对象。而内置的异常构造器同时为我们设定好了异常所属的类型,因此尽量使用具体的异常构造器。如果异常不是特定的,那么可以直接用 `Error` 构造器。 90 | 异常构造器可以通过 `new` 运算符建立新的异常对象,包含下列属性: 91 | - `message` —— 我们能阅读的异常提示信息。 92 | - `name` —— 异常名称(异常对象的构造函数的名称)。 93 | - `stack` —— 异常发生时的调用栈。 94 | 95 | 96 | 97 | **9. JSON** 98 | 99 | 100 | 101 | **10. Map 和 WeakMap ** 102 | 103 | 104 | 105 | **11. Set 和 WeakSet** 106 | 107 | 108 | 109 | **12. Proxy** 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | ### 宿主对象 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /设计原理/Symbol和迭代器.assets/0054F331.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/设计原理/Symbol和迭代器.assets/0054F331.png -------------------------------------------------------------------------------- /设计原理/Symbol和迭代器.md: -------------------------------------------------------------------------------- 1 | ## 人间烟火·Symbol 和迭代器 2 | 3 | --- 4 | 5 | ### Symbol 是什么 6 | 7 | 发布于2015年的 ECMAScript 6 标准为 JavaScript 带来了一种新的原始数据类型——Symbol。就像早已存在的 `Boolean`、`Number` 和 `String` ,JavaScript 提供了一个 `Symbol` 函数来创造新的 Symbol 类型的值。但与其他原始类型不同,`Symbol` 不是类,不能用 `new` 来创建,也没有相应的字面量形式。要想使用 Symbol 值,我们唯一的方法是直接调用 `Symbol` 函数: 8 | 9 | ```javascript 10 | alert(Symbol()); // Symbol() 11 | alert(typeof Symbol()); // "symbol" 12 | alert(new Symbol()); // TypeError: Symbol is not a constructor 13 | ``` 14 | 15 | Symbol 是什么呢? 16 | 17 | 简单来说,Symbol 相当于一种*唯一标识符*,每个 Symbol 值诞生后就是独一无二的,就像枝头一片叶子长出,世界上就再也不会有另一片与它完全相同的叶子。我们可以给 Symbol 赋予一个标识,但即便是使用相同标识创建的两个 Symbol 值,它们彼此之间也是不相等的。 18 | 19 | ```javascript 20 | let s1 = Symbol(); 21 | let s2 = Symbol(); 22 | alert(s1 === s1); // true 23 | alert(s1 === s2); // false 24 | alert(s1 === Symbol()); // false 25 | 26 | let s3 = Symbol("love"); 27 | let s4 = Symbol("love"); 28 | alert(s3 === s4); // false 29 | ``` 30 | 31 | Symbol 的一大用途就是:创建独一无二的对象键名。就像一把指纹锁,当用一个 Symbol 值作为对象的键时,这个键所对应的值只有用同一个 Symbol 值才能访问到。 32 | 33 | ```javascript 34 | const NAME = Symbol(); 35 | let person = { 36 | [NAME]: "Modi" 37 | }; 38 | 39 | alert(person[NAME]); // "Modi" 40 | 41 | const AnotherNAME = Symbol(); 42 | person[AnotherNAME] = () => alert("Hello Modi"); 43 | person[AnotherNAME](); // "Hello Modi" 44 | ``` 45 | 46 | 47 | 48 | 49 | 50 | ### 迭代器 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | ### 迭代协议 59 | 60 | Symbol 值本身带来的意义几乎只是为 JavaScript 增加了一种“创建唯一无二的值”的方式,但是,用于构造 Symbol 值的 `Symbol` 函数,其本身的用途却不仅仅是构造 Symbol 值这么简单。事实上,JavaScript 中许多原本保留的内部细节,都通过 `Symbol` 的一批属性暴露给了我们,这使得 `Symbol ` 包含一组非常强大的工具,能帮助我们在探究 JavaScript 的语言构造和内部设计上如虎添翼。 61 | 62 | ECMAScript 标准定义了一批用来在语言内部实现某些特性的属性,这些属性以前缀 `@@` 标记,在 ECMAScript 6 以前,它们是无法被我们直接访问到的。一个例子是`@@iterator` 方法,它会使对象成为*可迭代对象*。当需要对一个对象进行迭代时(例如在 `for...of` 语句中),它的 `@@iterator` 方法会在不传参情况下被调用,并返回一个迭代器,这个迭代器提供每次迭代所需要的值。 63 | 64 | 一些内置类拥有默认的 `@@iterator` 方法,以展现出可供迭代的性质,例如 `Array`、`String` 等,而其他类(如 `Object`)则没有。 65 | 66 | `@@iterator` 方法代表 JavaScript 中的*可迭代协议*,凡是实现了正确的 `@@iterator` 方法的对象,就符合可迭代协议,那么这个对象就是*可迭代的*。可迭代协议允许 JavaScript 对象去定义或定制它们的迭代行为,例如在 `for...of` 语句中被遍历时,有什么值会被访问到。可迭代对象就是符合迭代协议、能够展现出正确的迭代行为的对象,它们必然拥有 `@@iterator` 。 67 | 68 | 来整理一下这些术语的关系:**一些对象拥有定义正确的 `@@iterator` 内部方法,它们符合可迭代协议,是可迭代对象。它们的 `@@iterator` 方法实际上是迭代器函数,它返回一个迭代器,每次迭代时会自动得到一个值。** 69 | 70 | 为了在每次迭代时提供一个新值, `@@iterator` 方法可以被实现为一个**生成器函数**。我们不能直接访问 `@@iterator` 这样的内部方法,但是可以通过 **`Symbol.iterator`** 访问到,这是一个预定义的 `Symbol` 值。因此,我们可以像这样定义一个最简单的可迭代对象。 71 | 72 | ```javascript 73 | let it = { 74 | *[Symbol.iterator]() { 75 | yield 1; 76 | yield 2; 77 | yield 3; 78 | } 79 | }; 80 | ``` 81 | 82 | 我们看到, `it` 已经定义了正确的 `@@iterator` 方法,它现在是一个符合迭代协议的可迭代对象了。一些 JavaScript 的语法特性,在语义上只对序列有效,这就要求某个对象必须是符合可迭代协议的,例如序列展开运算符 `...`、`for...of` 语句等。来看看我们的对象能否得到认可: 83 | 84 | ```javascript 85 | alert([...it]); // 1,2,3 86 | for (let i of it) { 87 | alert(i); 88 | } 89 | // 1 90 | // 2 91 | // 3 92 | ``` 93 | 94 | 成功了!总的来说,这些语法特性依赖可迭代协议。 95 | 96 | 那么,如果一个对象实现了 `@@iterator` ,却不符合可迭代协议,会怎样呢? 97 | 98 | ```javascript 99 | let fakeIt = { 100 | [Symbol.iterator] () { 101 | return "Hello"; 102 | } 103 | }; 104 | [...fakeIt]; // TypeError: Result of the Symbol.iterator method is not an object 105 | ``` 106 | 107 | 上文中说 `@@iterator` 方法是一个迭代器函数,又说它必须被实现为一个生成器函数。那么,这些名词究竟有什么关系呢?它们的逻辑关系可以描述如下: 108 | 109 | - 生成器函数是函数,它调用后返回一个对象,这个对象是生成器。 110 | - 迭代器函数是函数,它调用后返回一个对象,这个对象是迭代器。 111 | - 生成器是迭代器的一种。 112 | 113 | 这里似乎令人一头雾水,因为我们还没有了解**迭代器**的概念。 114 | 115 | 简单来说,迭代器定义一个序列,它拥有 `next` 方法,每次调用 `next` 方法,意味着从序列中获取了下一个值。它返回一个对象,包含两个属性。 116 | 117 | - `value`,它代表本次迭代所产生的值,这个值来自预定义的序列。 118 | - `done`,如果它为 `true`,代表这个序列已经终结,不再进行迭代。如果它为 `false`,迭代还未技术,这个序列还会继续产生新值。 119 | 120 | 一个迭代器对象完成迭代过程后,我们说它被*消耗*了。在它产生序列中的最后一个值之后,`done` 方法应当被设为 `true`。 121 | 122 | 数组是最常见的序列,它本身也包含一个迭代器,即 `@@iterator`方法,这个迭代器可以依次返回数组中的每个成员。显然,因为把 `@@iterator` 定义为迭代器,所以数组本身是可迭代对象。 123 | 124 | 下面实现了一个简单的迭代器函数,它构造一个可以代表无穷序列的迭代器。 125 | 126 | ```javascript 127 | const makeIterator = (start = 0, end = Infinity, step = 1) => { 128 | let nextIndex = start; 129 | let count = 0; 130 | 131 | return { 132 | next() { 133 | let result; 134 | if (nextIndex < end) { 135 | result = { value: nextIndex, done: false } 136 | nextIndex += step; 137 | count += 1; 138 | return result; 139 | } 140 | return { value: count, done: true } 141 | } 142 | }; 143 | }; 144 | ``` 145 | 146 | 测试一下: 147 | 148 | ```javascript 149 | let it = makeIterator(1, 5); // 这个迭代器代表 1 ≤ n < 5 的正整数序列 150 | it.next(); // { value: 1, done: false} 151 | it.next(); // { value: 2, done: false} 152 | it.next(); // { value: 3, done: false} 153 | it.next(); // { value: 4, done: false} 154 | it.next(); // { value: 4, done: true} 155 | ``` 156 | 157 | 158 | 159 | --- 160 | 161 | Note: 162 | 163 | 注意:可迭代协议和迭代器协议是两个容易混淆的不同概念。 164 | 165 | - 迭代器协议要求对象拥有 `next` 方法,使一个对象可以预定义一个序列,并且每次迭代时按顺序提供序列的值。这种对象叫**迭代器**。 166 | - 可迭代协议要求对象拥有 `@@iterator` 方法,这个方法应当是一个迭代器,它从外观上呈现为一个序列,实际上是还原了迭代器所定义的序列。**这个对象是可迭代对象。** 167 | 168 | 迭代器和可迭代对象的关系可以描述为发动机和汽车。可迭代对象像汽车,可以在道路上行使,但真正驱动它的是迭代器这台发动机。 169 | 170 | 可迭代协议和迭代器协议统称为迭代协议。 171 | 172 | --- 173 | 174 | 175 | 176 | 我们也可以把 `makeIterator` 作为一个另一个对象的 `@@iterator` 方法,构造一个新的可迭代对象: 177 | 178 | ```javascript 179 | let happyObject = { 180 | // 不能直接把 makeIterator 作为 @@iterator 的值,因为这样会构造无穷序列 181 | [Symbol.iterator]: () => makeIterator(1, 10) 182 | }; 183 | 184 | alert([...happyObject]); // 1,2,3,4,5,6,7,8,9 185 | ``` 186 | 187 | 我们看到,只要对象有一个符合迭代器协议的`@@iterator` 方法,它本身就符合可迭代协议,成为可迭代对象。这样就可以回答前面的问题了:生成器函数是 JavaScript 内置的一种默认迭代器函数,它可以便捷地提供可迭代对象所需要的序列。其实一个对象既可以是可迭代对象,又可以是迭代器,要做到这点十分容易。 188 | 189 | 用一个更进一步的例子来结束有关迭代协议的内容,这个例子会构造一段序列,包含 $ 1 \le n \lt 40 $ 的所有奇数。 190 | 191 | ```javascript 192 | // 建立一个迭代器函数 193 | const oggIterator = () => { 194 | let nextIndex = 1; 195 | let count = 0; 196 | 197 | return { 198 | next() { 199 | let result = { value: count, done: true }; 200 | if (nextIndex < 40) { 201 | result = { value: nextIndex, done: false } 202 | nextIndex += step; 203 | count += 2; 204 | return result; 205 | } 206 | return result; 207 | }; 208 | }; 209 | }; 210 | 211 | // 进一步构造成可迭代对象 212 | let oggObject = { 213 | [Symbol.iterator]: oggIterator 214 | }; 215 | alert([...oggIterator]); 216 | // 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39 217 | ``` 218 | 219 | 220 | 221 | --- 222 | 223 | 练习 7.4.2 224 | 225 | 1. 编写一个构造完全平方数序列的迭代器,上限自定。 226 | 2. 利用上一个练习中的迭代器,编写一个可迭代对象,将其构造为数组。 227 | 228 | --- -------------------------------------------------------------------------------- /设计原理/assets/freezeSealAndPreventExtensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/设计原理/assets/freezeSealAndPreventExtensions.png -------------------------------------------------------------------------------- /设计原理/代理对象.md: -------------------------------------------------------------------------------- 1 | ## 代理对象 2 | 3 | --- 4 | 5 | 什么是*代理对象*? 6 | 7 | -------------------------------------------------------------------------------- /设计原理/求值策略.md: -------------------------------------------------------------------------------- 1 | ## 迷雾清泉·求值策略 2 | 3 | --- 4 | 5 | ### valueOf 6 | 7 | JavaScript 通过内置的 `valueOf` 方法来将每一个对象都转换为基本值,如果一个对象自身就是基本值(如数值对象),其 `valueOf` 方法的返回值就是这个对象。如果在一个表达式里面,JavaScript 预期一个对象是基本值,就会悄悄调用这个方法,而不需要我们手动调用。每个对象都拥有 `valueOf` 方法。`Object` 类的原型拥有 `valueOf` 方法,所有对象都继承了它的 `valueOf` 方法,并根据自己的需求定义自己的 `valueOf` 以覆盖原先的。 8 | 9 | 隐式调用对象的 `valueOf` 方法以求出所需值的过程称为*求值*。只有在表达式需要一个可供计算的数值,而对象类型并不满足要求的情况下,才会自动调用 `valueOf` 方法,否则不会。对于 `Array` 等内置的类,我们可以在必要时重写它们原型上的 `valueOf` 方法,也可以直接定义一个具有 `valueOf` 成员属性的对象。 10 | 11 | ```javascript 12 | Array.prototype.valueOf = () => 100; 13 | alert([]); // 这里不需要一个数值,所以没有调用 valueOf 方法 14 | alert([] + 50); // 150 15 | 16 | alert([] + { valueOf: () => 200 }); // 300 17 | ``` 18 | 19 | 不同类型对象的 `valueOf` 方法的默认返回值: 20 | 21 | | **类型** | **返回值** | 22 | | :--------- | :------------------------------------------- | 23 | | `Array` | 返回数组对象本身。 | 24 | | `Boolean` | 布尔值本身。 | 25 | | `Date` | 从 1970 年 1 月 1 日午夜开始到现在的毫秒数。 | 26 | | `Function` | 函数本身。 | 27 | | `Number` | 数值本身。 | 28 | | `Object` | 对象本身(这是默认情况) | 29 | | `String` | 字符串值本身。 | 30 | | 其它 | Math 和 Error 对象没有 valueOf 方法。 | 31 | 32 | 假设我们有一个自己的类,需要改变它的实例的默认求值方式,可以定义这个类的专属 `valueOf` 方法,以返回我们需要的值。要注意的一点是,`valueOf` 是不应当有形参的。 33 | 34 | ```javascript 35 | class Rational { 36 | constructor(den, num) { 37 | Object.assign(this, {den, num}); 38 | } 39 | valueOf() { 40 | return [this.den, this.num]; 41 | } 42 | }; 43 | 44 | let r1 = new Rational(10, 20); 45 | alert(r1.valueOf()); // 10,20 46 | alert(r1); // 10,20 47 | ``` 48 | 49 | 重定义 `valueOf` 的一大用处就是使得我们自己的对象可以被处理为原始值并参与计算,使对象在外观上就像普通的值。我们还可以在对对象求值的时候额外执行一些操作。 50 | 51 | ```javascript 52 | class AnotherNumber { 53 | constructor(value = 0) { 54 | this.value = value; 55 | } 56 | valueOf() { 57 | return this.value; 58 | } 59 | }; 60 | 61 | let n = new AnotherNumber(100); 62 | alert(n / 20); // 5 63 | 64 | class SpecialClass { 65 | valueOf() { 66 | alert("我被求值了!"); 67 | } 68 | }; 69 | 70 | let sp = new SpecialClass(); 71 | sp.valueOf(); // "我被求值了!" 72 | sp; // 不需要数值,所以没有调用 valueOf 73 | sp && sp && sp; // 不需要数值,所以没有调用 valueOf 74 | sp + 1; // "我被求值了!" 75 | ``` 76 | 77 | 78 | 79 | 80 | 81 | ### toString 82 | 83 | JavaScript 对象中的另一个内置方法是 `toString`。当对象所处的表达式期望一个字符串参与运算,而对象本身并不是字符串时,就会隐式调用 `toString` 方法。可以理解为:`valueOf` 将对象转换为数值,而 `toString` 将对象转换为字符串形式。对于基本类型的值,`toString` 所返回的值就是这个值看起来的样子,如 `100` 转换为 `"100"`,`true` 转换为 `"true"` 等。而用对象字面量创建的对象,即“复杂对象”,情况则大不一样,它的 `toString` 方法返回一个特殊的字符串:`"[object Object]"` 。这个字符串的含义大致为“`Object` 对象”。 84 | 85 | 数组虽然也是复杂对象的一种形式,但是 `Array` 原型上的 `toString` 方法经过了重定义,可以返回这个数组自身的内容。对于数组内的每个成员,`Arrar.prototype.toString` 会进一步调用成员的 `toString` 方法,如果成员是 `null` 和 `undefined` 则会直接跳过。 86 | 87 | ```javascript 88 | let array = [1, 2, 3, 4, "Hello", true, null, undefined, "world"]; 89 | let array2 = [{ toString: () => "I am an object" }, [1, 2, 3, 4, [5, 6, 7, 8]]]; 90 | alert(array.toString()); // 1,2,3,4,Hello,true,,,world 91 | alert(array); // 1,2,3,4,Hello,true,,,world 92 | alert(array2); // I am an object,1,2,3,4,5,6,7,8 93 | ``` 94 | 95 | 函数的 `toString` 方法有些特殊,它会返回这个函数的全部定义,如果调用一个类(本质上是函数)自身而非原型的 `toString` 方法也是如此。“函数定义”所包含的字符串,是从标识这个函数的第一个字符开始,到函数体的最后一个字符。如果函数定义中包含了注释,在旧一些的浏览器中不会呈现,而在最新的浏览器中,函数定义中的注释也会被 `toString` 显示。 96 | 97 | ```javascript 98 | let f = () => 100; 99 | alert(f.toString()); 100 | alert(f); 101 | 102 | let f2 = function(a, b) { 103 | alert(a + b); 104 | }; 105 | alert(f2); // function(a, b) { 106 | // alert(a + b); 107 | // } 108 | 109 | const f3 = (a) => { 110 | let b = a + 100; 111 | return function() { 112 | return b; 113 | }; 114 | }; 115 | alert(f3); // (a) => { 116 | // let b = a + 100; 117 | // return function() { 118 | // return b; 119 | // }; 120 | // } 121 | 122 | class C { 123 | constructor() { 124 | this.value = 0; 125 | } 126 | }; 127 | alert(C); // class C { 128 | // constructor() { 129 | // this.value = 0; 130 | // } 131 | // } 132 | ``` 133 | 134 | 如果一个函数/类是原生的,那么调用它的 `toString` 方法会返回 `function 函数名/类名() { [native code] }"`。 135 | 136 | ```javascript 137 | // native code 就是“原生代码”的意思 138 | alert(alert); // "function alert() { [native code] }" 139 | alert(parseInt); // "function parseInt() { [native code] }" 140 | alert(Number); // "function Number() { [native code] }" 141 | alert(Boolean); // "function Boolean() { [native code] }" 142 | alert([].toString); // "function toString() { [native code] }" 143 | alert(Object.hasOwnProperty); // "function hasOwnProperty() { [native code] }" 144 | ``` 145 | 146 | 在 JavaScript 中,每一个类都拥有一个叫做 `[[Class]]` 的内部属性。这个内部属性意味着类的“类别”。所有内置类都具有对应的 `[[Class]]` 标识,如 `Array` 的 `[[Class]]` 是 `Array`,`Number` 的 `[[Class]]` 是 `Number` 等。`null` 和 `undefined` 也拥有以自身命名的 `[[Class]]` 标识。 147 | 148 | 而 `Object` 类以及所有我们自定义的类,其 `[[Class]]` 的值是 `Object`。访问 `[[Class]]` 的唯一办法,是用 `Object.prototype.toString.call` 调用类的实例。这样,就不是使用某个值经过重新定义的 `toString`,而是像真正的对象一样获取其 `[[Class]]` 标识。 149 | 150 | ```javascript 151 | alert(Object.prototype.toString.call(null)); // "[object Null]" 152 | alert(Object.prototype.toString.call([1, 2, 3])); // "[object Array]" 153 | alert(Object.prototype.toString.call(() => 0)); // "[object Function]" 154 | alert(Object.prototype.toString.call(new Date()); // "[object Date]" 155 | ``` 156 | 157 | 原生对象有自己的 `toString` 方法,用来标识自己。 158 | 159 | ```javascript 160 | alert(Math); // "[object Math]" 161 | alert(window); // "[object Window]" 162 | alert(document.createElement("div")); // "[object HTMLDivElement]" 163 | ``` 164 | 165 | 166 | 167 | --- 168 | 169 | Tips: 170 | 171 | 如果一个对象同时具有 `valueOf` 方法和 `toString` 方法,那么总会优先调用它的 `valueOf` 方法。 172 | 173 | --- -------------------------------------------------------------------------------- /设计原理/类和原型.md: -------------------------------------------------------------------------------- 1 | ## 类和原型 2 | 3 | ------ 4 | 5 | ### JavaScript 的类 6 | 7 | 设想一下,现在我们是一家汽车经销商,拥有随时待命的工厂。每当我们经手一辆新的汽车,我们就可以写出一个汽车对象,拥有它自己的属性。 8 | 9 | ```javascript 10 | let car = { 11 | brand: "Honda Civic", 12 | color: "blue" 13 | }; 14 | ``` 15 | 16 | 如果只有寥寥几辆的汽车需求,那么这种办法尚可接受;但是,如果汽车订单多了起来,我们恐怕不能像这样一辆辆手工生产。JavaScript 赋予了我们一种机器,可以根据设计图纸批量生产出汽车——想要多少就有多少! 17 | 18 | 我们知道,每辆汽车都属于“汽车”这类产品,同时所有的汽车都会拥有自己的 `brand` `color` 属性,我们可以制定好汽车产品的原型,每次生产时只需要说明它的品牌与颜色。现在是时候掀开这台神奇机器的面纱了! 19 | 20 | ```javascript 21 | class Car {}; 22 | ``` 23 | 24 | 嘿,等等,这是什么!`class` 是做什么的? 25 | 26 | 其实, `class` 是一个新的关键字,用于表示“一类”什么东西,用 JavaScript 的话说,是*定义了一个类*。 `Car` 就是这个类(“这类东西”)的名称,一般使用大写字母开头,这里我们定义了 `Car` 这个类以表示汽车。其后的大括号将会包含生产汽车所需要的一些工具。 27 | 28 | 有了 `Car` 类,我们就可以开动机器快速生产出一辆新的汽车。 29 | 30 | ```javascript 31 | let car = new Car(); 32 | ``` 33 | 34 | 这个写法是不是有些眼熟?我们在第三章中接触过。在第三章中,我们了解到: `new` 关键字用于*生成一个构造器的实例*,在这里 `Car` 就承担了构造器的作用,我们写下 `new Car()` ,也就相当于拥有了一辆新的汽车。 35 | 36 | 但是,我们会发现这个新的汽车并没有 `brand` 和 `color` 属性。 37 | 38 | ```javascript 39 | alert(car.brand); // undefined 40 | alert(car.color); // undefined 41 | ``` 42 | 43 | 所以,仅仅写出 `class Car` 是不够的,我们需要画好设计草图,说明这类汽车将会拥有什么属性。我们可以在 `Car` 类中写出一个名为 `constructor` 的*方法*。 44 | 45 | ```javascript 46 | class Car { 47 | constructor (brand, color) {} 48 | }; 49 | ``` 50 | 51 | 这个 `constructor` 又是做什么的呢?它就是我们将来每次生产汽车时所遵循的设计草图。它是一个特殊的方法,称为*构造函数*。就像这个名字所提示的那样,它的作用是构造一个对象。在构造一个汽车对象时我们需要做什么呢?当然是确定它的牌子与颜色。 52 | 53 | ```javascript 54 | class Car { 55 | constructor (brand, color) { 56 | this.brand = brand; 57 | this.color = color; 58 | } 59 | }; 60 | ``` 61 | 62 | 现在,我们再来生成新的汽车对象。 63 | 64 | ```javascript 65 | let car = new Car("Honda Civic", "blue"); 66 | alert(car.brand); // "Honda Civic" 67 | alert(car.color); // "blue" 68 | ``` 69 | 70 | 现在它是一辆拥有自己的属性的汽车了!我们只需要将它的品牌和颜色值当做构造函数的参数,多么容易! 71 | 72 | 现在回来看 `Car` 类的定义。它的构造函数 `constructor` 接受两个参数 `brand` 和 `color`,分别对应品牌和颜色。其后两行代码的含义是:将汽车的 `brand` 和 `color` 属性设置为参数 `brand` 和 `color` 的值。 73 | 74 | 在类的定义中,**`this` 关键字表示将会构造的对象本身,它的属性表示对象将会拥有的属性。** 75 | 76 | 也就是说,当我们写下 `this.xxx = yyy` 时,我们就说明了将来生成的对象的 `xxx` 属性的值会是 `yyy`。我们还记得第三章中引入的概念——“实例”和“实例化”——吗?现在我们可以阐述一下类所发挥的作用了。 77 | 78 | 首先,`Car` 是一个类,它拥有一个构造函数。当我们像这样:`new Car("any", "any")` 实例化一个类时,我们会接受一些参数,实际上调用了类的构造函数,并在构造函数中完成了完成了构造一个对象所需做的事情。 79 | 80 | 在 JavaScript 中,一个类是一种特殊的函数。但是它不能像通常的函数那样直接调用。 81 | 82 | ```javascript 83 | new Car("Unknown", "black"); // OK 84 | Car("Unknown", "black"); // TypeError: Class constructor Car cannot be invoked without 'new' 85 | ``` 86 | 87 | 除了 `constructor` 之外,类还可以拥有其它的方法,它们的形式像在对象字面量中一样,不过应该省略逗号。 88 | 89 | ```javascript 90 | class Car { 91 | constructor (brand, color) { 92 | this.brand = brand; 93 | this.color = color; 94 | } 95 | toString () { 96 | return `This car's brand is ${this.brand} and its color is ${this.color}.`; 97 | } 98 | } 99 | let car = new Car("Byd Auto", "silver"); 100 | alert(car.toString()); // "This car's brand is Byd Auto and its color is silver." 101 | ``` 102 | 103 | 所有的类都具有一个 `prototype` 属性,它是所有实例的*原型*,用于规定类的定义中所包含的东西。在一个类中, `this` 关键字实际上指这个原型。我们可以手动为原型的属性赋值,但是请注意:**我们在 `prototype` 属性上手动定义的方法,是无法访问 `this` 的。**也就是说,如果我们要为类添加构造函数或其他方法,必须在类的定义里面。 104 | 105 | ```javascript 106 | class Car {}; 107 | Car.prototype.model = "Unkonw"; // 默认 108 | Car.prototype.color = "black"; // 默认 109 | 110 | // 这个构造函数无法对 this 进行操作! 111 | Car.prototype.constructor = () => { 112 | this.model = "Saic Motor"; 113 | this.color = "white"; 114 | } 115 | let car = new Car() 116 | alert(car.model); // "Unknown" 117 | alert(car.color); // "black" 118 | ``` 119 | 120 | 121 | 122 | 123 | 124 | ### 面向对象 125 | 126 | 在前面章节的示例中,一个程序通常由几个函数组成,一个函数负责一项功能,几个函数组合起来完成程序的任务,最后写几个语句来调用函数。函数在对象中,就成了方法——它看起来只是把函数的位置转移到了对象中而已。 127 | 128 | 但是现在,我们可以将“要做什么事情”交给对象自己来管理,而一个完整的程序,实际上就是对不同对象的操作。我们可以使用对象和类作为程序组织的基本结构,程序所做的事情由不同的对象进行处理,而不是像先前那样写出一堆零散的函数和流程。这种程序设计的思想称为*面向对象编程*。 129 | 130 | > 面向对象程序设计是一种程序开发的抽象方针。它可能包含数据、属性、代码与方法,对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关联的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。 131 | > 132 | > ——Wikipedia 133 | 134 | Wikipedia 描述了面向对象程序设计的基本概念,更专业的叫法是“面向对象范式”。从面向对象的角度来看,应用程序是一组彼此通信的“对象”。具有一些相似特征的对象可以用相同方式构造出来,承担这一任务的就是类。对象和类一般都是基于现实世界中的事物,例如仓库中的产品或是一个自然人。对象包含数据,并根据其数据执行一些操作,而一个类则是用于构造对象的模板。对象之间用接口来互通有无,将接口与实现分离。这是一个好主意,它通常称为*封装*。 135 | 136 | 听起来是否有些熟悉——我们在第五章的开头部分就阐述过将操作的细节封装在不同函数的概念。只不过,面向对象的思考方式更进一步:它使函数抱团合体,使数据得以妥善保存与操作,使应用程序成为一个有机的整体。 137 | 138 | JavaScript 中的面向对象方式是*基于原型*的,所有的对象都由某一个类构造而来,每个类都具有一个 `prototype` 属性,它本身是一个对象的模板,当我们实例化一个对象时,就会根据类的 `prototype` 确定对象会包含什么内容,同时执行构造函数,完成实例化时需要做的一些工作,类的 `prototype` 属性就是实例对象的“原型”。在 JavaScript 中,我们可以通过包含已有的原型,来设计新的原型,这个过程称为*继承*,我们将在下一小节中了解到。 139 | 140 | 所有对象的都有一个共同的祖先,它是一个类,称为 `Object`,意思就是“对象”。它的原型包括了任何对象都应该具有的一些信息。是不是有些眼熟?我们已经在第三章接触对象时见到了它。我们还记得,当我们实例化(`new`)`Object` 时,我们便会得到一个空对象。`Object` 类和原型具有许多细节,我们将在后面的章节中详细了解。 141 | 142 | 面向对象是一种思想,它为我们指导了编写出具有专业性的代码的方式,它的基本形式是将一切数据看做一个个互相关联又彼此独立的对象,将程序的运作转化为对象之间的交流,它的重要特点是**隐藏对象的状态**,同时使我们的注意力放在如何操作对象,而非零散的“流水账式”的编程。在这个过程中,类的作用是**为我们定制我们需要的对象**。 143 | 144 | 145 | 146 | 147 | 148 | ### 继承 149 | 150 | 假设我们就像楚辞中的司命神一样掌管人类的寿夭轮回、生息繁衍,用一个类来概述世间所有人类的本质。 151 | 152 | ```javascript 153 | class Human 154 | { 155 | constructor (name, age) { 156 | this.name = name; 157 | this.age = age; 158 | } 159 | } 160 | 161 | ``` 162 | 163 | 164 | 165 | 我们知道下述事实: 166 | 167 | ``` 168 | 人类的概念包括成年人和儿童。 169 | ``` 170 | 171 | 172 | 173 | 在面向对象相关的技术中,“抽象基类”这个术语常用来描述一个 174 | 175 | 176 | 177 | 178 | 179 | ### 原型链 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /设计原理/装饰器.md: -------------------------------------------------------------------------------- 1 | ## 白马非马·装饰器 2 | 3 | --- 4 | 5 | > 这是某种令人惊骇而不知名的杰作,在不可名状的晨曦中依稀可见,宛如在欧洲文明的地平线上瞥见的亚洲文明的剪影。 6 | > 7 | > ——维克多·雨果《就英法联军远征中国致巴特勒上尉的信》 8 | 9 | JavaScript 世界新开张了一家蛋糕房。大厨拿出一块蛋糕胚,抹上糖霜,涂上果酱,放上鲜果,撒上可可粉或咖啡粉,摆上黑色和白色的巧克力,再用奶油沿着边缘雕刻几朵花,一个蛋糕就大致做好了。所有的蛋糕胚都相差无几,有了蛋糕胚,我们就能随心所欲地装饰它,赋予它特别的风味。蛋糕房里,这个裱花袋装着奶油,那个大概装着蓝莓酱或草莓酱。我们只需要按照自己的想法,借助它们来装饰蛋糕。在 JavaScript 中,我们也可以像装饰蛋糕一样装饰类或方法,*装饰器*就是我们手中的裱花袋。 10 | 11 | 装饰器是一个美妙的特性,人们说它来自另一种叫 Python 的以优雅著称的语言。装饰器在 2016 年时被加入 JavaScript,它在本质上是高阶函数的一种应用。当我们定义一个装饰器时,我们只需要写一个函数,这个函数至少要接受一个参数,这个参数代表将要被装饰的类或方法。然后就可以在需要的地方使用它,用一个 `@` 字符,其后紧接装饰器的名字,接着像通常那样写出类或方法的定义。 12 | 13 | 现在我们有一个蛋糕类,它只是一个胚,没有光鲜亮丽的色彩和令人垂涎欲滴的滋味。 14 | 15 | ```javascript 16 | class Cake { 17 | constructor() { 18 | this.flavor = "original"; 19 | } 20 | }; 21 | ``` 22 | 23 | 我们没有裱花袋,让我们来制作一个裱花袋。它装着草莓果酱。 24 | 25 | ```javascript 26 | const strawberry = (cake) => { 27 | cake.prototype.flavor = "strawberry"; 28 | } 29 | ``` 30 | 31 | 现在给蛋糕挤上草莓果酱。 32 | 33 | ```javascript 34 | @strawberry 35 | class Cake { 36 | constructor() { 37 | this.flavor = "original"; 38 | } 39 | }; 40 | ``` 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /语句/assets/1556836336765.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/语句/assets/1556836336765.png -------------------------------------------------------------------------------- /语句/assets/640.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/语句/assets/640.webp -------------------------------------------------------------------------------- /语句/for-in和for-of语句.md: -------------------------------------------------------------------------------- 1 | ## for-in 和 for-of 语句 2 | 3 | --- 4 | 5 | JavaScript 提供了 for 语句的两种变体用于更加灵活地实现遍历,不但能遍历数组的索引和值,也能遍历对象的成员,并将得到的值赋给一个变量。 6 | 7 | ### 属性遍历 8 | 9 | 我们可以使用 *`for-in` 语句*来遍历一个对象中的所有属性名称。`for-in` 语句的形式如下: 10 | 11 | ```javascript 12 | for (let 变量 in 对象) { 13 | 处理语句 14 | } 15 | ``` 16 | 17 | `for-in` 语句会依次访问对象中的每个属性的名称,并将得到的字符串存放在指定的变量中,这个过程会对每一个*可遍历*的属性都执行一次。 18 | 19 | ```javascript 20 | const person = { 21 | name: "Jason", 22 | age: 30, 23 | sex: "male", 24 | job: "teacher" 25 | }; 26 | 27 | for (let i in person) { 28 | alert(i); 29 | } 30 | // "name" 31 | // "age" 32 | // "sex" 33 | // "job" 34 | ``` 35 | 36 | 有了属性名,我们也就能同时得到它的值: 37 | 38 | ```javascript 39 | for (let i in person) { 40 | alert(person[i]); 41 | } 42 | // "Jason" 43 | // 30 44 | // "male" 45 | // "teacher" 46 | ``` 47 | 48 | 于是,我们可以简单地打印出一个对象的内容: 49 | 50 | ```javascript 51 | let s = "{\n"; 52 | for (let i in person) { 53 | s += `${i}: ${person[i]}\n`; 54 | } 55 | s += "}"; 56 | alert(s); 57 | ``` 58 | 59 | 结果如图所示: 60 | 61 | ![1556836336765](assets/1556836336765.png) 62 | 63 | 64 | 65 | `for-in` 语句提供了对对象内容进行操作的快捷方式。在这个遍历的过程中,我们可以干许多事情。比如——给每个属性都重新起一个名字,抛弃原来的: 66 | 67 | ```javascript 68 | for (let i in person) { 69 | person["属性-" + i] = person[i]; 70 | delete person[i]; 71 | } 72 | ``` 73 | 74 | 这时再用之前的方式查看对象内容,就会看到每个属性的名字都被改变了。 75 | 76 | ``` 77 | { 78 | 属性-name: Jason 79 | 属性-age: 30 80 | 属性-sex: male 81 | 属性-job: teacher 82 | } 83 | ``` 84 | 85 | 我们还可以更进一步:为每个属性都进行编号,毕竟, `for-in` 语句的本质还是循环,可以做一些适合循环做的事情。 86 | 87 | ```javascript 88 | let count = 1; 89 | for (let i in person) { 90 | person[`第${count}个属性-${i}`] = person[i]; 91 | delete person[i]; 92 | count += 1; 93 | } 94 | // { 95 | // 第1个属性-name: Jason 96 | // 第2个属性-age: 30 97 | // 第3个属性-sex: male 98 | // 第4个属性-job: teacher 99 | // } 100 | ``` 101 | 102 | 要知道 `for-in` 语句赋予了我们随意与属性和值打交道的权力——是的,我们甚至可以*交换*属性的名称与值的位置。**当然,如果原本的值就不是一个*基本类型*,我们还是不要这样做,否则会发生奇怪的事情。** 103 | 104 | ```javascript 105 | const object = { 106 | name: "Andy", 107 | checked: true, 108 | anotherObj: { 109 | a: 1, 110 | b: 2 111 | } 112 | }; 113 | 114 | for (let i in object) { 115 | let newName = object[i]; // 将原本的值存放起来 116 | if (typeof newName !== "object") { 117 | // 它不是一个对象,目测是基本类型 118 | 119 | object[newName] = i; // 值的内容来命名一个新的属性,它的值就是原本的属性名 120 | delete object[i]; // 原来的属性还在,但我们不需要它了 121 | } 122 | } 123 | // 再用之前的方式查看一下对象里的情况 124 | // { 125 | // anotherObj: [object Object] 126 | // Andy: name 127 | // true: checked 128 | // } 129 | ``` 130 | 131 | 又是 `"[object Object]"` !恐怕你已经猜测到了我们所要避免的问题所在了。这个奇怪的东西我们将会在第七章详细讨论到,现在我们只需简单了解这一情况。 132 | 133 | 使用 `for-in` 语句,我们可以自由地查看、操作一个对象的内容。它是否使我们与对象更亲近了? 134 | 135 | --- 136 | 137 | Note:我们只能遍历一个对象中的*可枚举属性*,我们将在下文了解这些概念。 138 | 139 | --- 140 | 141 | 142 | 143 | 144 | 145 | ### 可迭代对象 146 | 147 | 不单单是对象,我们也可以使用 `for-in` 语句来遍历数组,它提供了比前一节所介绍的更为简便的方法。在第三章中我们已经知道,**数组也是一种特殊的对象,它的索引都是属性,元素就是属性的值**,我们可以使用类似的方式来遍历它。 148 | 149 | ```javascript 150 | let array = ["aa", "bb", "cc", "dd", "ee", "ff"]; 151 | for (let i in array) { 152 | alert(`${i}: ${array[i]}`); 153 | } 154 | // 0: aa 155 | // 1: bb 156 | // 2: cc 157 | // 3: dd 158 | // 4: ee 159 | // 5: ff 160 | ``` 161 | 162 | 数组与我们通常所写的对象的本质区别在于,它是*可迭代的*,也就是说每个成员的排列方式都遵循固定的顺序,我们可以通过固定的方式来依次访问每个成员。还有什么东西也是这样的呢?字符串! 163 | 164 | ```javascript 165 | let s = "Hello world"; 166 | for (let i in s) { 167 | alert(`第${i}个字符是 "${s[i]}"`); 168 | } 169 | // 第0个字符是 "H" 170 | // 第1个字符是 "e" 171 | // 第2个字符是 "l" 172 | // 第3个字符是 "l" 173 | // 第4个字符是 "o" 174 | // 第5个字符是 " " 175 | // 第6个字符是 "w" 176 | // 第7个字符是 "o" 177 | // 第8个字符是 "r" 178 | // 第9个字符是 "l" 179 | // 第10个字符是 "d" 180 | ``` 181 | 182 | 字符串可以看做“字符的数组”,也就可以通过通常的方式遍历其中包含的每一个字符。 183 | 184 | 185 | 186 | --- 187 | 188 | Note: 189 | 190 | 每一个可迭代对象都包含一个**迭代器**。迭代器涉及 JavaScript 中一些相对复杂的概念,我们将在第七章中详细了解。 191 | 192 | --- 193 | 194 | 195 | 196 | for 语句的另一种变体—— *`for-of` 语句*更关注对值的操作,当我们只需要遍历一些值时,我们就可以使用它。 197 | 198 | `for-of` 语句的形式与 `for-in` 语句类似: 199 | 200 | ```javascript 201 | for (let 变量 in 对象) { 202 | 执行操作 203 | } 204 | ``` 205 | 206 | 用 `for-of` 语句来遍历数组中的每个值会格外方便。 207 | 208 | ```javascript 209 | const arr = ["aa", "bb", "cc", "dd", "ee", "ff"]; 210 | for (let i of arr) { 211 | alert(i); 212 | } 213 | 214 | // "aa" 215 | // "bb" 216 | // "cc" 217 | // "dd" 218 | // "ee" 219 | // "ff" 220 | ``` 221 | 222 | 与 `for-in` 语句的显著不同之处在于,`for-of` 语句只能对**可遍历对象**进行遍历。如果你对一个普通对象使用 `for-of` 语句,会得到一个错误。 223 | 224 | ```javascript 225 | // person 对象就是先前的那个 226 | for (let i of person) { 227 | alert(i); 228 | } // TypeError: person is not iterable 229 | ``` 230 | 231 | 但是不必就此打住:还记得第三章中见到的 `Object.keys` `Object.values` `Object.entries` 三个函数吗?它们得到的是数组!换句话说,我们可以借助于它们来遍历普通对象! 232 | 233 | ```javascript 234 | for (let [name, value] of Object.entries(person)) { 235 | alert(`${name}: ${value}`); 236 | }; 237 | // name: Jason 238 | // age: 30 239 | // sex: male 240 | // job: teacher 241 | ``` 242 | 243 | 或者使用 `Object.values` 作为跳板,直接对值进行遍历。 244 | 245 | ```javascript 246 | for (let value of Object.values(person)) { 247 | alert(value); 248 | } 249 | // "Jason" 250 | // 30 251 | // "male" 252 | // "teacher" 253 | ``` 254 | 255 | 假如我们有一个数组,里面的元素都是对象,利用前一章中了解到的解构赋值,我们可以根据对象的属性来进行处理。 256 | 257 | ```js 258 | const peoples = [ 259 | { 260 | name: 'Mike Smith', 261 | family: { 262 | mother: 'Jane Smith', 263 | father: 'Harry Smith', 264 | sister: 'Samantha Smith' 265 | }, 266 | age: 35 267 | }, 268 | { 269 | name: 'Tom Jones', 270 | family: { 271 | mother: 'Norah Jones', 272 | father: 'Richard Jones', 273 | brother: 'Howard Jones' 274 | }, 275 | age: 25 276 | } 277 | ]; 278 | 279 | for (let {name: n, family: {father: f}} of peoples) { 280 | alert('Name: ' + n + ', Father: ' + f); 281 | } 282 | // "Name: Mike Smith, Father: Harry Smith" 283 | // "Name: Tom Jones, Father: Richard Jones" 284 | ``` 285 | 286 | 287 | 288 | 289 | 290 | -------------------------------------------------------------------------------- /语句/for语句.md: -------------------------------------------------------------------------------- 1 | ## for 语句 2 | 3 | --- 4 | 5 | ### 基本概念 6 | 7 | while 和 do-while 语句可以用于实现循环结构,除此以外,JavaScript 中提供了另一种更加灵活快捷的方式来进行循环(或者叫迭代):*`for` 语句*。 8 | 9 | 一个 `for` 语句看起来像这样: 10 | 11 | ```javascript 12 | for (初始化表达式; 条件; 增量表达式) { 13 | 执行语句 14 | } 15 | ``` 16 | 17 | 等等,`for` 语句的小括号中包含了三个东西!它们都是什么? 18 | 19 | 首先是*初始化表达式*。它用于说明哪些值会被用在循环中。例如,在上一节的阶乘示例中,我们在进行计算之前要先设置变量 `i` 和 `n` 的值为 `1`,这类操作就是*初始化*。你可以将初始化的操作直接放在 `for` 语句里,称为初始化表达式。 20 | 21 | 初始化表达式的分号后面是*条件*,它表示控制循环进行的条件,与 while 语句是一致的。 22 | 23 | 而*增量表达式*用于在每次循环后,改变控制循环的变量的值,以此达到控制循环次数的作用。 24 | 25 | 事不宜迟,用一个简单的示例来看一下 `for` 语句的使用: 26 | 27 | ```javascript 28 | for (let i = 0; i < 5; i += 1) { 29 | alert(i); 30 | } 31 | // 0 32 | // 1 33 | // 2 34 | // 3 35 | // 4 36 | ``` 37 | 38 | 1. 首先设置控制循环的变量 `i` 的值为 `0`。 39 | 2. 查看 `i` 的值是否满足条件。 40 | 3. 满足,那么执行循环体的语句,显示出 `i` 的值。 41 | 4. 循环体结束,根据增量表达式,改变 `i` 的值,为下一次循环做准备。 42 | 5. 回到步骤 2。 43 | 44 | 如果 `i` 的值一开始就不满足条件,那么循环体一次也不会被执行,像 while 语句一样。 45 | 46 | 上一节中的计算 1 + 2 + 3 + ... + 100 的示例,使用 `for` 语句可以改写如下: 47 | 48 | ```javascript 49 | let n = 0; 50 | for (let i = 1; i < 100; i += 1) { 51 | n += i; 52 | } 53 | alert(n); // 5050 54 | ``` 55 | 56 | 我们使用 `i` 作为控制循环的变量,每次循环后它的值便会 +1 ,同时 `i` 也起到了从 1 增长到 100 ,用于使 `n` 进行累加的作用。 57 | 58 | 事实上,为了充分利用 `for` 语句,我们还可以更进一步: 59 | 60 | ```javascript 61 | for (let i = 1, n = 0; i < 100; i += 1, n += i) { 62 | 63 | } 64 | alert(n); // 5050 65 | ``` 66 | 67 | 我们可以在初始化表达式的位置上写几个用于进行初始化的表达式,只需使用逗号隔开。 68 | 69 | 同样,增量表达式也可以对不同的变量进行增量处理。它的求值顺序是从左到右的,也就是说,先计算了 `i += 1` ,然后再处理 `n += 1`。 70 | 71 | 如果我们的循环体没有语句,我们根本就不用写大括号,直接省略就好了!。 72 | 73 | ```javascript 74 | for (let i = 1, n = 0; i < 100; i += 1, n += i) 75 | 76 | alert(n); 77 | ``` 78 | 79 | 但是运行这段代码,就会发现,`alert` 不会在循环结束后执行,而是每进行一次循环都会显示一次当前 `n` 的值。因此,你一共要点一百次“确认”,直到循环结束。 80 | 81 | 现在,刷新页面,一切恢复正常。 82 | 83 | 为什么会这样?因为 `for` 语句和 while 语句一样,如果没有写大括号,会将循环头部(即一对小括号包裹的内容)的后面遇到的第一个语句当做是循环体,因此 `alert(n)` 被循环执行了。解决这个问题的办法是在循环头部后面写一个分号 `;` ,用一个*空语句*来代替循环体。 84 | 85 | ```javascript 86 | for (let i = 1, n = 0; i < 100; i += 1, n += i) ; 87 | // ; (更推荐写在第二行) 88 | alert(n); // 5050 89 | ``` 90 | 91 | 92 | 93 | --- 94 | 95 | 练习 4.4.1 96 | 97 | 1. 创建一个 1 ~ 100 的循环,当数字 `n` 是奇数时,打印 “ `n`是奇数”,否则打印“`n`是偶数”。 98 | 99 | 提示:使用运行器提供的 `document.write` 函数来进行“打印”操作。 100 | 101 | 2. 显示一个九九乘法表,使用 `document.writeln` 函数来打印每一行。 102 | 103 | 提示:你需要将一个 `for` 语句写在另一个内部,使用它们的控制变量来输出因数。 104 | 105 | 106 | 107 | 108 | 109 | 110 | ### 迭代算法 111 | 112 | 我们在这里第一次接触*算法*一词。Wikipedia 对此的定义如下: 113 | 114 | > 在数学和计算机科学中,**算法**是一个明确的、关于如何解决一类问题的规范。算法可以执行计算,数据处理、自动推理和其他任务。算法可以在有限的空间和时间内表达。从初始状态和初始输入开始,描述了一系列计算,当执行时,通过有限个明确定义的连续状态,最终产生“输出” ,并终止于最终结束状态。 115 | 116 | 当我们要实现某种功能时,我们将会用精确的语言,描述我们所要实施的步骤。例如在上文中对于循环过程的描述就叫算法。迭代算法则是用迭代等方式实现的算法,通俗来讲,使用循环和条件控制来进行一系列运算,以得到我们需要的结果。上文中的累加和阶乘等运算就属于迭代算法。 117 | 118 | 我们将会在本节深入了解迭代算法,一个常见的实际应用就是检测一个数是否为质数。 119 | 120 | 如果你忘记了什么叫质数,我们可以先复习一下小学数学书上对于质数的描述: 121 | 122 | > 如果一个大于 1 的整数只有 1 和它本身两个因数,那么它就是一个质数,否则就是合数。 123 | > 124 | > 2 是最小的质数,1 既不是质数也不是合数。 125 | 126 | 换句话说,一个整数如果大于 1,并且不能整除除 1 以外的所有比它小的整数,那么它就是一个质数。 127 | 128 | 我们很快就可以得到判断一个数 n 是否为质数的思路: 129 | 130 | 1. 如果这个数不大于 1 或不为整数,那么它必定不是质数。 131 | 2. 从 2 开始,列举从 2 到 n-1 的所有整数,用 n 除以列举的数。 132 | 3. 如果得到的余数为 0,说明 n 可以整除它,那么 n 不是质数。 133 | 4. 如果列举完后也没有找到一个数可以被 n 整除,那么 n 是质数。 134 | 135 | 我们可以根据这个思路尝试写出代码,列举数字的工作自然就交给 `for` 语句。 136 | 137 | ```javascript 138 | let n = 100; 139 | let isPrime = true; // 假设它是一个质数 140 | for (let i = 2; i < n; i += 1) { 141 | if (n % i === 0) { 142 | isPrime = false; 143 | } 144 | } 145 | alert(isPrime ? "质数" : "合数"); // "合数" 146 | ``` 147 | 148 | 通过列举它可能的因数,尝试进行整除,这种策略称为*试除法*。 149 | 150 | 这个代码可以正常工作,但我们很快就发现了它的问题:当我们发现 `n` 不是一个质数的时候,应该终止计算并告知结果。但是这里,即使我们发现了 `n` 是合数,`for` 语句也不会停下来,又白白将剩下的循环运行完。 151 | 152 | 因此,我们应当采取策略:当我们知道它不是一个质数的时候,我们就不再进行试除了,而是报告结果。 153 | 154 | ```javascript 155 | let n = 100; 156 | let isPrime = true; // 假设它是一个质数 157 | for (let i = 2; i < n; i += 1) { 158 | if (n % i === 0) { 159 | isPrime = false; 160 | break; 161 | } 162 | } 163 | alert(isPrime ? "质数" : "合数"); // "合数" 164 | ``` 165 | 166 | 这里再次出现了 `break;` 语句。它在这里的作用是**直接终止循环**。 167 | 168 | 169 | 170 | 我们知道,一个合数最大的因数不会超过它的平方根,例如 100 最大的因数就是 10 ,判断一个整数是否为质数,只需列举到它的平方根进行试除就足够了: 171 | 172 | ```javascript 173 | let n = 100; 174 | let isPrime = true; // 假设它是一个质数 175 | for (let i = 2, last = Math.sqrt(n); i <= last; i += 1) { 176 | if (n % i === 0) { 177 | isPrime = false; 178 | break; 179 | } 180 | } 181 | alert(isPrime ? "质数" : "合数"); // "合数" 182 | ``` 183 | 184 | 由于除了 2 以外的所有质数都是奇数,因此我们可以进行一个简单的判断: 185 | 186 | - 如果输入的数字是 2 ,那么它是一个质数。 187 | - 如果输入的数字不是 2,但能被 2 整除,那么它是一个合数。 188 | - 从 3 开始列举它的因数,每次 +2,使列举的值始终是奇数。 189 | 190 | ```javascript 191 | let n = 100; 192 | let isPrime = (n === 2) || (n % 2 !== 0); 193 | for (let i = 3, last = Math.sqrt(n); i <= last; i += 2) { 194 | if (n % i === 0) { 195 | isPrime = false; 196 | break; 197 | } 198 | } 199 | alert(isPrime ? "质数" : "合数"); // "合数" 200 | ``` 201 | 202 | 203 | 204 | 我们的程序可以用于处理用户输入并得到结果了,不过务必记得进行输入检查。 205 | 206 | ```javascript 207 | let n = parseInt(prompt("请输入一个大于 1 的正整数。")); 208 | while (true) { // 循环接受输入。 209 | if (isNaN(n) || !isFinite(n) || n <= 1) { 210 | alert("输入不符合要求,程序停止"); 211 | break; // 如果输入不符合要求,就停止循环接受输入。 212 | } 213 | 214 | let isPrime = (n === 2) || (n % 2 !== 0); 215 | for (let i = 3, last = Math.sqrt(n); i <= last; i += 2) { 216 | if (n % i === 0) { 217 | isPrime = false; 218 | break; // 这个 break 语句只退出当前所在的循环。 219 | } 220 | } 221 | alert(`${n}是一个${isPrime ? "质数" : "合数"}`); // 使用模板字符串来拼凑信息 222 | n = parseInt(prompt("请输入一个大于 1 的正整数。")); 223 | } 224 | ``` 225 | 226 | 我们的质数判断程序遵循的基本流程是“输入-处理-输出”。为了避免每次输出后都要重新运行才能开始新的流程,我们可以用一个循环将流程包进去。由于我们已经认识了 break 语句,因此可以自由决定 while 循环何时终止。循环头的条件用 `true` 来表示“条件始终成立”,表示它不再管条件判断,只需不断进行循环以重复相同的流程。这样的程序称为“Read-Eval-Print Loop”(输入-处理-输出循环),缩写为 **REPL**。 227 | 228 | 229 | 230 | --- 231 | 232 | 练习 4.4.2 233 | 234 | 1. 本节提供了一个完整的用于判断质数的 REPL 示例程序,请在此基础上对它进行修改:如果 `n` 是一个合数,那么同时显示发现的第一个因数。 235 | 236 | 2. 对本节的示例程序进行扩充,接受一个用户输入的整数 n,查找 2 ~ n 范围内的所有质数并显示。 237 | 238 | (提示:将找到的质数放在数组中) 239 | 240 | --- 241 | 242 | 243 | 244 | 245 | 246 | ### 数组遍历 247 | 248 | 假如我们有一列排列整齐的课桌,每张课桌上都写着使用它的学生的姓名,现在我们要依次浏览并记录每张课桌上的姓名,最直观的的办法显然是:从第一张课桌开始,记录课桌上的信息,然后走到下一个课桌,以此类推。 249 | 250 | 这个过程用精确的语言描述一下: 251 | 252 | 1. 走到第一张课桌的位置,记录信息 253 | 2. 走到下一张课桌的位置,如果这里确实还有课桌,就继续记录。 254 | 3. 如果没有课桌了,就停止这个过程。 255 | 4. 否则,回到步骤 2。 256 | 257 | 我们应该怎样用 JavaScript 来实现这个过程呢?相信答案已经呼之欲出了——循环!使用循环来解决这个问题。 258 | 259 | ```javascript 260 | let queue = ["Sonam", "Susanna Kliment", "Unnr Radmila", "Davide", "Rebekah "]; 261 | 262 | for (let i = 0; i < queue.length; i += 1) { 263 | alert(`第${i}个学生的姓名是:${queue[i]}`); 264 | } 265 | // Sonam 266 | // Susanna Kliment 267 | // Unnr Radmila 268 | // Davide 269 | // Rebekah 270 | ``` 271 | 272 | 我们使用 `for` 语句依次访问了数组中的每一个元素,变量 `i` 表示数组元素的*索引*,它是一个约定俗成的名称。如果这个索引值小于数组长度,说明还没有到数组尽头,那么就继续进行处理,否则就停止循环。依次访问数组每一个元素的过程称为*遍历*。 273 | 274 | -------------------------------------------------------------------------------- /语句/if语句.md: -------------------------------------------------------------------------------- 1 | ## if 语句 2 | 3 | ------ 4 | 5 | 我们已经在第三章中了解了 JavaScript 中数据的使用,掌握到的预备知识是我们具备了随心所欲操控数据的能力,在这一章中我们将学习 JavaScript 中的*语句*,以编写出“真正的”程序,并逐步将我们头脑中的逻辑,转化为程序真切的运行过程。 6 | 7 | 在前面的运行示例中,我们接触到的都是顺序结构,即程序从第一条语句,逐行执行到结尾,而在这一章中我们将了解到*分支结构*和*循环结构*。 8 | 9 | 我们首先要学习的是 *`if` 语句*。它用于实现分支结构。 10 | 11 | 想象一下,当我们要去买芹菜的时候,我们会执行这样的过程: 12 | 13 | 1. 出门,找到菜市场 14 | 2. 挑选芹菜 15 | 3. 付钱,将菜带回家。 16 | 17 | 这就是我们所说的顺序结构。但实际上,我们买菜可能会经历这样的过程: 18 | 19 | 1. 出门,找到菜市场 20 | 21 | 2. 寻找卖芹菜的摊位 22 | 23 | 如果有卖芹菜的 24 | 25 | 1. 买一斤芹菜 26 | 27 | 2. 称重,付款,回家 28 | 29 | 30 | 如果没有卖芹菜的 31 | 32 | 1. 看看有没有其他适合的蔬菜 33 | 2. 称重,付款,回家 34 | 35 | 以上过程的结构称为*分支结构*,我们可以用 `if` 语句来在程序中实现分支结构,它的*语法*是这样的: 36 | 37 | ```javascript 38 | if (条件) { 39 | 执行语句 40 | } 41 | ``` 42 | 43 | 一条 `if` 语句以关键字 `if` 开头,其后跟随一对括号,括号中是一个逻辑表达式,用于表示执行语句的条件。 44 | 45 | 例如: 46 | 47 | ```javascript 48 | let a = 4; 49 | if (a > 3) { 50 | alert("a 大于 3"); 51 | } 52 | ``` 53 | 54 | 这段代码中的 `if` 语句的条件是 `a > 3`,所执行的语句是 `alert("a 大于 3");` 。显然,`a` 大于 `3`,因此条件成立(逻辑表达式得到 `true`),那么执行大括号中的语句——显示出 `"a 大于 3"` 这行内容。 55 | 56 | `if` 语句只管理它大括号中的内容,其他部分不受条件影响。 57 | 58 | 倘若我们要考虑两种情况(如示例中的“有卖芹菜的摊位”与“没有卖芹菜的摊位”),那么我们可以使用 `if` 语句的另一种形式。 59 | 60 | ```javascript 61 | if (条件) { 62 | 语句1 63 | } else { 64 | 语句2 65 | } 66 | ``` 67 | 68 | 这种形式的作用是:如果条件成立,那么执行代码1中的内容,否则,执行代码2。 69 | 70 | `else` 关键字所*引导*的内容称为 *else 子句*,它用于说明“条件不成立”这一情况。 71 | 72 | 示例: 73 | 74 | ```javascript 75 | let a = 4; 76 | if (a > 5) { 77 | alert("a 大于 5"); 78 | } else { 79 | alert("a 小于等于 5"); 80 | } 81 | ``` 82 | 83 | 在示例中,由于 `a` 的值为 `4`,`if` 语句的判断条件不成立(得到 `false`),所以不会显示 `"a 大于 5"`,而是执行 `else` 子句中的语句,显示 `"a 小于等于 5"`。如果 `a` 的值大于 `5`,符合判断条件的话,那么就会显示 `"a 大于 5"`。 84 | 85 | 我们似乎已经能够处理条件成立和不成立两种情况了。但现实生活比这复杂的多,我们可能会遇到许多其他情况,需要对每种情况一一加以判断并分别作出相应决定。在 JavaScript 中,我们可以使用 `if` 语句的高级形式,像这样: 86 | 87 | ```javascript 88 | let score = 85; 89 | if (score >= 90) { 90 | alert("优秀"); 91 | } else if (score >= 80) { 92 | alert("良好"); 93 | } else if (score >= 60) { 94 | alert("及格"); 95 | } else { 96 | alert("不及格"); 97 | } 98 | ``` 99 | 100 | 上述代码模拟了一种常见情景:将成绩分为不同层次,并作出对应通知。 101 | 102 | 它的执行过程会先从第一个条件开始,如果条件成立,那么执行其后的语句;如果条件不成立,那么尝试判断第二个条件,若成立则执行相应语句,否则判断第三个条件……直到最后一条 `else` 子句,即当所有可能条件都不成立时,执行其中的语句。在上述代码中,分数大于等于 `80`,我们便得到了“良好”。 103 | 104 | 我们在第三章已经阐述过,JavaScript 会将一些值视为“真”,另一些视为“假”,以此进行逻辑运算,而不仅仅是局限于布尔值。同样,在 `if` 语句的条件并不要求得到一个布尔值,只要得到的值被视作“真”,就会执行相应语句。 105 | 106 | 因此,假如我们有如下代码: 107 | 108 | ```javascript 109 | // 判断一个数字是奇数还是偶数。 110 | // 如果它除以 2 的余数为 0,即能被 2 整除,那么是偶数。 111 | // 否则为奇数。 112 | let number = 100; 113 | if (number % 2 === 0) { 114 | alert("偶数"); 115 | } else { 116 | alert("奇数") 117 | } 118 | ``` 119 | 120 | 当条件的值为 0 时,它会被当做假,那么我们可以用更方便的形式书写条件: 121 | 122 | ```javascript 123 | let number = 100; 124 | if (number % 2) { // 余数不为 0,会当做“真” 125 | alert("奇数"); 126 | } else { // 余数为 0,会当做“假” 127 | alert("偶数") 128 | } 129 | ``` 130 | 131 | `if` 语句是我们接触到的第一个 JavaScript 控制语句。我们可以用它来描述真实世界中的各类选择。 132 | 133 | 一条简洁明快的 `if` 语句,将一切分成了真假两个世界,中间隔着逻辑这条天河,它向何处流动,取决于你的思考。逻辑的伟力在于泾渭分明,逻辑的生命在于它所带来的不容逾越的秩序。 134 | 135 | 136 | 137 | --- 138 | 139 | 练习 4.1.1 140 | 141 | 1. 写一个幸运转盘的程序,每次根据一个随机数字的范围来决定颁发什么奖品。 142 | 2. 写一个程序,让用户输入出生年份,判断用户的生肖属相。如果不是一个合理的年份,就显示一个错误。 143 | 144 | --- 145 | 146 | 147 | 148 | 我们可以用 `if` 语句来处理一些生活中常见的、需要进行繁琐的分类讨论的问题,例如个人所得税的计算。 149 | 150 | 假设我们的父母(成年人也可以假设为自己)是工薪阶层,每个月都可能需要根据月收入缴纳一笔个人所得税,数额会被划分为不同的层级,面临的税率和所要缴纳的相应税款也不一样。根据中国法律规定,个人所得税的起征点是 3500 元。 151 | 152 | > 在实际工作中,对于某些个人所得收入采用税后收入的概念,比如支付税后多少多少金额。这时,需要将税后的收入按一定公式换算为应税所得,然后再按照一般方法计算应交的税款。否则,将导致税款的少征。这里,在换算为应税所得过程中需要适用的税率及速算扣除数不能按照含税级距的税率表来套用,必须使用不含税级距的税率表。这就是不含税级距税率表产生的原因。这里的不含税级距指的是“税后收入”级距。 153 | 154 | 截止到 2011 年,中国的个人所得税税率如下表所示: 155 | 156 | | 级数 | 含税级距 | 不含税级距 | 税率(%) | 速算扣除数 | 157 | | ---- | ---------------------------- | ---------------------------- | ------- | ---------- | 158 | | 1 | 不超过1500元 | 不超过1455元的 | 3 | 0 | 159 | | 2 | 超过1500元至4,500元的部分 | 超过1455元至4,155元的部分 | 10 | 105 | 160 | | 3 | 超过4,500元至9,000元的部分 | 超过4,155元至7,755元的部分 | 20 | 555 | 161 | | 4 | 超过9,000元至35,000元的部分 | 超过7,755元至27,255元的部分 | 25 | 1,005 | 162 | | 5 | 超过35,000元至55,000元的部分 | 超过27,255元至41,255元的部分 | 30 | 2,755 | 163 | | 6 | 超过55,000元至80,000元的部分 | 超过31,375元至45,375元的部分 | 35 | 5,505 | 164 | | 7 | 超过80,000元的部分 | 超过57,505的部分 | 45 | 13,505 | 165 | 166 | 备注: 167 | 168 | - 本表含税级距指以每月收入额减除费用 **3500 元**后的余额或者减除附加减除费用后的余额。 169 | - 含税级距适用于由纳税人负担税款的工资、薪金所得。 170 | - 不含税级距适用于由他人(单位)代付税款的工资、薪金所得。 171 | 172 | 我们可以根据税率的级数进行分类讨论,有点像中学数学课上学过的分段函数。 173 | 174 | ```javascript 175 | let income = parseFloat(prompt("请输入原始收入")); 176 | 177 | let basic = 3500; // 个人所得税起征点 178 | let gap = income - basic; // 税前收入与起征点之差 179 | let tax = 0; // 应付税额 180 | if (gap <= 0) { 181 | tax = 0; 182 | } else if (gap > 0 && gap <= 1500) { 183 | tax = gap * 0.03; 184 | } else if (gap > 1500 && gap <= 4500) { 185 | tax = gap * 0.1 - 105; 186 | } else if (gap > 4500 && gap <= 9000) { 187 | tax = gap * 0.2 - 555; 188 | } else if (gap > 9000 && gap <= 35000) { 189 | tax = gap * 0.25 - 1005; 190 | } else if (gap > 35000 && gap <= 55000) { 191 | tax = gap * 0.3 - 2775; 192 | } else if (gap > 55000 && gap <= 80000) { 193 | tax = gap * 0.35 - 5505; 194 | } else { 195 | tax = gap * 0.45 - 13505; 196 | } 197 | 198 | alert(Math.floor(tax)); 199 | ``` 200 | 201 | 这个例子具有相当的实用性,要不要考虑将自己或家人的收入通过这个程序计算一下,看看是否与实际情况吻合呢? 202 | 203 | -------------------------------------------------------------------------------- /语句/switch语句.md: -------------------------------------------------------------------------------- 1 | ## switch 语句 2 | 3 | --- 4 | 5 | ### 初识 switch 语句 6 | 7 | 在前文中,我们已经学习并尝试了**分支结构**。我们将在几个示例中进一步了解它的使用。 8 | 9 | 设想一下,我们有一个有奖猜数活动,参与者可以随机输入一个 1 和 3 之间的整数,我们告诉参与者是否猜中,或与答案相差多少。利用 `if` 语句,我们可以这样写: 10 | 11 | ```javascript 12 | let number = +prompt("请输入 1 和 3 之间的整数"); 13 | 14 | if (number === 1) { 15 | alert("太小了!"); 16 | } else if (number === 2) { 17 | alert("刚刚好!"); 18 | } else if (number === 3) { 19 | alert("太大了!"); 20 | } else { 21 | alert("哦,数字超出范围!"); 22 | } 23 | ``` 24 | 25 | 我们共需依次比较三种情况,根据每种情况作出相应回应。而不在我们考虑范围内的“其他情况”则放入 else 子句中处理。在需考虑的情况较少时,使用 `if` 语句以此判断尚可应付需求,但当我们需要考虑到情况变得多,使用 `if` 语句就会显得力不从心。此外,如果我们要用相同的方式分别处理不同的情况,使用 `if` 语句会相当麻烦。 26 | 27 | 为此,JavaScript 中提供了另一种实现分支结构的语句:*`switch` 语句*。 28 | 29 | `switch` 语句的一般格式如下: 30 | 31 | ```javascript 32 | switch (表达式) { 33 | case 情况1: 处理语句1; break; 34 | case 情况2: 处理语句2; break; 35 | case 情况3: 处理语句3; break; 36 | //... 37 | default: 默认处理语句; 38 | } 39 | ``` 40 | 41 | `switch` 语句遵循这样的执行顺序: 42 | 43 | - 每个 `case` 关键字都用于标明一个情况。 44 | - JavaScript 依次查看每种情况,然后对表达式求值,查看表达式的值是否能够与这种情况匹配。 45 | - 如果匹配(严格相等),就执行冒号后的处理语句。如果以 `break;` 结尾,那么终止 `switch` 语句。 46 | - 否则,继续查看下一个情况,以此类推。 47 | - 当所有列举的情况都查看完之后,如果有一个 `default` 标志,就执行它的处理语句,作为默认情况。 48 | 49 | 前面的例子可以使用 `switch` 语句改写如下: 50 | 51 | ```javascript 52 | let number = +prompt("请输入 1 和 3 之间的整数"); 53 | 54 | switch (number) { 55 | case 1: 56 | alert("太小了!"); 57 | break; 58 | case 2: 59 | alert("刚刚好!"); 60 | break; 61 | case 3: 62 | alert("太大了!"); 63 | break; 64 | default: alert("哦,数字超出范围!"); 65 | } 66 | ``` 67 | 68 | 69 | 70 | --- 71 | 72 | 练习 4.2.1 73 | 74 | 1. 找到一个可以使用 switch 进行处理的生活中的例子,并编写程序实现它。 75 | 76 | --- 77 | 78 | 79 | 80 | 81 | 82 | ### 使用 break 83 | 84 | 让我们回忆一下 `if` 语句的机制: 85 | 86 | > 对每个条件进行查看,如果成立,就执行相应处理语句,然后结束 `if` 语句。 87 | 88 | 当我们使用 `switch` 语句的时候,我们并不一定希望在找到匹配情况后,仍然继续匹配其余情况。但是,`switch` 语句有个特点:**当它匹配到一种情况之后,会继续执行之后其他情况的处理语句,**甚至包括 `default` 。 89 | 90 | 为了模仿 `if` 语句“干完事就走人”,不拖泥带水,我们需要在每一个 `case` 的处理语句后添加一行 `break;` 。 91 | 92 | ```javascript 93 | let fruit = prompt("请输入一种水果的名字:"); 94 | 95 | switch (fruit) { 96 | case "橙子": 97 | alert("橙子卖 0.59 美元。"); 98 | break; 99 | case "苹果": 100 | alert("苹果卖 0.32 美元。"); 101 | break; 102 | case "香蕉": 103 | alert("香蕉卖 0.48 美元。"); 104 | break; 105 | case "车厘子": 106 | alert("车厘子卖 3 美元"); 107 | break; 108 | case "芒果": 109 | case "桑葚": 110 | alert("芒果和桑葚卖 2.79 美元。"); 111 | break; 112 | default: 113 | alert("抱歉,本店没有水果" + fruit + "。"); 114 | } 115 | 116 | ``` 117 | 118 | `switch` 语句每当遇到匹配的情况,执行相应处理语句后,就会终止。 119 | 120 | 如果 `break;` 后面还有处理语句,那么它不会被执行,因为 `break;` 已经起到了终止 `switch` 语句的作用。 121 | 122 | 我们使用 `switch` 语句时一般都会添加 `break;` ,这是一种良好习惯。没有添加 `break;` 以至于所有 `case` 都会被查看一遍的 `switch` 语句被称为 *switch 穿越*,可能会引发一些问题,我们将在下文中看到 switch 穿越所展现出的效果。。 123 | 124 | 125 | 126 | 127 | 128 | ### 关联操作 129 | 130 | 这个例子阐述了利用 `switch` 语句进行的关联操作。如前文所述,当我们输入一个数字并匹配之后,它会执行其后,一直第一个到 `break;` 之前的所有处理语句。 131 | 132 | ```js 133 | let value = 1; 134 | let output = "输出: " 135 | switch (value) { 136 | case 10: 137 | output += "所以"; 138 | case 1: 139 | output += "你的"; 140 | output += "名字"; 141 | case 2: 142 | output += "是"; 143 | case 3: 144 | output += "什么"; 145 | case 4: 146 | output += "?"; 147 | alert(output) 148 | break; 149 | case 5: 150 | output += "!"; 151 | alert(output); 152 | break; 153 | default: 154 | alert("请选择一个 1 到 6 之间的数字!"); 155 | } 156 | ``` 157 | 158 | 尝试改变 `value` 的值,相信你会对 switch 的机制有更深的体会。如果你不是特意为了制造出这种效果,请记得:务必在每种情况的相应处理语句末尾添加 `break;`。 159 | 160 | 161 | 162 | --- 163 | 164 | 练习 4.2.3 165 | 166 | 1. 使用 `switch` 语句的穿越特性,写一个程序,模拟一个会根据指令来问好的机器人。 167 | 168 | --- 169 | 170 | 171 | 172 | 173 | 174 | ### 分组 175 | 176 | 生活中有什么事情会像在 1 ~ 3 之间猜一个数字这样简单而乏味呢——让我们将目光投向更“实际”的问题。 177 | 178 | 现在我们摇身一变成了动物保护专家,向好奇的小朋友普及动物保护的知识,告诉他们哪些动物已经灭绝而湮没在历史中,哪些动物在悬崖边上苦苦挣扎,哪些动物暂时毫无危险。 179 | 180 | ```javascript 181 | let animal = prompt("请输入一种动物的名字:"); 182 | switch (animal) { 183 | case "猫": 184 | case "金鱼": 185 | case "鸵鸟": 186 | case "企鹅": 187 | case "火鸡": 188 | case "马": 189 | alert(animal + "没有危险!"); 190 | break; 191 | case "大象": 192 | case "熊猫": 193 | case "江豚": 194 | alert(animal + "处于危险之中,我们要一起保护它们。"); 195 | break; 196 | case "渡渡鸟": 197 | case "恐龙": 198 | case "象鸟": 199 | alert(animal + "已经灭绝。"); 200 | break; 201 | default: 202 | alert("我没听过这种动物的名字。"); 203 | } 204 | ``` 205 | 206 | 我们对小朋友提出的问题进行了分类,只用一行处理代码,统一回应相同性质的提问。 207 | 208 | ------ 209 | 210 | 练习 4.2.4 211 | 212 | 1. 思考生活中有哪些实际问题可以使用 `switch` 进行分组处理。 213 | 214 | ------ 215 | 216 | 217 | 218 | 219 | 220 | ### 使用动态条件 221 | 222 | 我们用一个小示例来结束本节。 223 | 224 | ```js 225 | let i = Math.floor(Math.random() * 7) 226 | switch (i) { 227 | case ((i >= 0 && i <= 5) ? i : -1): // 如果 0 ≤ i ≤ 5,那么待匹配的值为 i ,否则为 -1。 228 | alert("0 ~ 5"); 229 | break; 230 | case 6: 231 | alert("6"); 232 | break; 233 | } 234 | ``` 235 | 236 | 如上所示,你可以在 `switch` 语句的 `case` 中进行一些运算,以此呈现出“动态”的匹配效果。 237 | 238 | -------------------------------------------------------------------------------- /语句/while和do-while语句.md: -------------------------------------------------------------------------------- 1 | ## while 和 do-while 语句 2 | 3 | --- 4 | 5 | ### 循环结构 6 | 7 | 我们将开始接触 JavaScript 中的**循环结构**。顾名思义,循环结构能够在一定条件下循环执行同一段代码,这个过程称之为*迭代*。我们可以使用 *`while` 语句*来实现循环结构。 8 | 9 | `while` 语句看起来像这样: 10 | 11 | ```javascript 12 | while (条件) { 13 | 执行语句 14 | } 15 | ``` 16 | 17 | 像 if 语句的条件一样, `while` 语句的条件是一个表达式,称为*条件*,写在一对小括号中。大括号连同其中的执行语句被称为*循环体*。如果执行语句只有一行,也可以省略包裹它的大括号。 18 | 19 | - 对条件进行求值。 20 | - 如果得到的值被看做 `true`(条件成立),那么执行一次循环。否则不执行。 21 | - 循环体执行完毕后,再次对条件进行求值,如果成立则再次循环执行,否则停止。 22 | 23 | 用一个简单的示例来演示 `while` 语句的用法: 24 | 25 | ```javascript 26 | let n = 0; 27 | let x = 0; 28 | 29 | while (n < 3) { 30 | n += 1; 31 | x += n; 32 | } 33 | 34 | alert(n); // 3 35 | alert(x); // 6 36 | ``` 37 | 38 | 在每次循环中,`n` 都会自增 `1`,然后再把 `n` 加到 `x` 上。因此,在每轮循环结束后,`x` 和 `n` 的值分别是: 39 | 40 | - 第一轮后:`n` = 1,`x` = 1 41 | - 第二轮后:`n` = 2,`x` = 3 42 | - 第三轮后:`n` = 3,`x` = 6 43 | 44 | 当完成第三轮循环后,条件 `n`< 3 的值不再为真,因此循环终止。 45 | 46 | 现在我们要考虑用 `while` 语句进行一些实际应用。我们首先回忆一下高斯小时候那个著名的故事——你一定耳熟能详,对吧? 47 | 48 | > 高斯上小学时,有一天老师出了一道算术难题:计算 1+2+3+……+100 。这下可难倒了刚学数学的小朋友们,他们按照题目的要求,正把数字一个一个地相加.可这时,却传来了高斯的声音:“老师,我已经算好了!”老师很吃惊,高斯解释道:因为1+100=101,2+99=101,3+98=101,……,49+52=101,50+51=101,而像这样的等于101的组合一共有50组,所以答案很快就可以求出:101×50=5050 49 | 50 | 哦,我们不是高斯,所以我们可以使用 `while` 语句来进行循环计算,这样就解决了其他小朋友们的痛点。我们要干的事情就像任何一个不懂计算技巧的普通的小朋友那样: 51 | 52 | 1. 使用一个变量 `n`,存放初始值 `0` 。现在什么也没有,计算还没开始。 53 | 2. 使用另一个变量 i,用来计算每次应该被加上的值。`i` 每增加 `1`,`n` 就加上 `i`,直到 `i` 等于 `100`。 54 | 3. 现在 `n` 就包含了我们累加的值。 55 | 56 | 累加的过程使用 `while` 语句来处理,写成这样: 57 | 58 | ```javascript 59 | let n = 0; 60 | let i = 0; 61 | while (i < 100) { 62 | i += 1; 63 | n += i; 64 | } 65 | 66 | alert(n); // 5050 67 | ``` 68 | 69 | 上述代码演示了 `while` 语句的基本应用。使用循环结构,可以解决我们手工计算的一些常见痛点,避免带来冗余。 70 | 71 | 实际上,你也可以将这样简单有效的运算推广到更大的范围,例如从 `1` 加到 `10000`,或 `i` 增加的值换成其他。当然,计算量越大,等待计算完成的时间也就越长,如果数字大小超过了 `Number.MAX_VALUE`,就会溢出。(还记得第三章中的相关知识吗?:-D) 72 | 73 | 一个更常见的需求是*阶乘*。在数学上,`n` 的阶乘是指 1 × 2 × 3 × ... × n 这样的计算过程,其符号是 `n!` 。 74 | 75 | 我们已经有了从 1 累加到 100 的经验,而实现阶乘,看起来只是把加法运算改为乘法运算。 76 | 77 | ```javascript 78 | let n = 1; 79 | let i = 1; 80 | while (i < 5) { 81 | i += 1; 82 | n *= i; 83 | } 84 | 85 | alert(n); // 120 86 | ``` 87 | 88 | 这段代码实现了 5 的阶乘。`i` 作为*计数器*,依然是每次加上 1 ,而作为结果的 n 每次循环中乘以 i ,并作为新值。我们可以将它的步骤展开,以更清楚地观察运行过程: 89 | 90 | 1. n = 1, i = 1 91 | 2. i = 2, n = n * i = 1 * 2 = 2 92 | 3. i = 3, n = n * i = 2 * 3 = 6 93 | 4. i = 4, n = n * i = 6 * 4 = 24 94 | 5. i = 5, n = n * i = 24 * 5 = 120 95 | 96 | 我们可以从用户那里得到一个数字,并求出它的阶乘值。 97 | 98 | ```javascript 99 | let n = 1; 100 | let i = 1; 101 | let value = parseInt(prompt("请输入一个整数:")); 102 | while (i < value) { 103 | i += 1; 104 | n *= i; 105 | } 106 | 107 | if (!isFinite(n)) { 108 | alert("数字太大了!"); 109 | } else { 110 | alert(value + "的阶乘是:" + n); 111 | } 112 | ``` 113 | 114 | 我们可以输入一些数字来进行测试。如果我们输入的数字的阶乘值太大,超出了 JavaScript 的表示范围(得到 `Infinity`),那么我们会得到一个贴心的提示。或者,得到这个阶乘值(或其约数)。看起来一切正常。 115 | 116 | 但是!当我们输入负数呢?如果我们输入的内容无法解析为整数以至于得到 `NaN` 呢?我们会得出错误的结果。 117 | 118 | ```javascript 119 | value = 0; // 1,正确 120 | value = -1; // 1,错误 121 | value = -2; // 1,错误 122 | value = "Hello"; // 1,错误 123 | ``` 124 | 125 | 关于“负数是否具有阶乘”等数学概念不在这里讨论范围内,我们应当设立明确的界限,对得到的值进行检查。如果它不符合要求,就通知用户,并不进行后续计算。 126 | 127 | ```javascript 128 | let n = 1; 129 | let i = 1; 130 | let value = parseInt(prompt("请输入一个正整数:")); 131 | 132 | if (isNaN(value) || value < 0) { 133 | alert("无法进行计算!") 134 | } else { 135 | 136 | while (i < value) { 137 | i += 1; 138 | n *= i; 139 | } 140 | 141 | if (!isFinite(n)) { 142 | alert("数字太大了!"); 143 | } else { 144 | alert(value + "的阶乘是:" + n); 145 | } 146 | } 147 | ``` 148 | 149 | 这样,我们可以保证:只有当得到一个正确的值的时候,才会进行计算。 150 | 151 | 我们还可以开动脑筋,将这个程序赋予更多创意: 152 | 153 | ```javascript 154 | let n = 1; 155 | let i = 1; 156 | let value = parseInt(prompt("请输入一个正整数:")); 157 | 158 | while (!isNaN(value) && value >= 0) { 159 | while (i < value) { 160 | i += 1; 161 | n *= i; 162 | } 163 | 164 | if (!isFinite(n)) { 165 | alert("数字太大了!"); 166 | } else { 167 | alert(value + "的阶乘是:" + n); 168 | } 169 | value = parseInt(prompt("请输入一个正整数:")); 170 | } 171 | ``` 172 | 173 | 这个程序将我们已经学习的诸多概念融合在了一起,如果你一时没看明白这个程序究竟在做什么,可以多花点时间仔细看一看。这里解释一下这个程序所干的事情: 174 | 175 | 1. 得到一个用户输入的值(`value`)。 176 | 2. 如果这个值符合我们的要求,就开始执行后续过程。 177 | 3. 进行常规阶乘计算。 178 | 4. 通知用户关于阶乘计算的情况。 179 | 5. 再次要求用户输入一个值。 180 | 6. 回到步骤 2。 181 | 182 | 这个程序演示了 `while` 语句作为控制结构的一般应用。它控制了整个程序的运行流程,这类流程被称为*控制流*。我们将在本章后续了解到如何进一步完善控制流。在这个程序中,当你输入一个不符合要求的值时,控制流便会终止。当我们学习*异常处理*的概念之后,我们可以使用更加优雅的方式来处理异常和终止的情况。 183 | 184 | 185 | 186 | 187 | 188 | ### do-while 语句 189 | 190 | `while` 语句有一种变体,称为 *`do-while` 语句*。它的形式如下: 191 | 192 | ```javascript 193 | do { 194 | 执行语句 195 | } while (条件) 196 | ``` 197 | 198 | 与通常的 `while` 语句不同的是,它的循环体写在了 `do` 关键字后面,而循环条件则放在了循环体的后面。 199 | 200 | 在第一次查看条件之前,`do-while` 语句无论如何都会先执行循环体,然后再查看条件,判断是否进行下一次循环。除此以外与通常的 `while` 语句是完全等价的。 201 | 202 | ```javascript 203 | let n = 1; 204 | let i = 1; 205 | do { 206 | i += 1; 207 | n *= i; 208 | } while (n < 5) 209 | 210 | alert(n); // 120 211 | ``` 212 | 213 | `do-while` 语句适用于需要先执行一遍,再进行条件判断的情况。 -------------------------------------------------------------------------------- /语句/异常处理.md: -------------------------------------------------------------------------------- 1 | ## 异常处理 2 | 3 | ------ 4 | 5 | 无论我们多么精通编程,有时我们的程序仍会不可避免的遭遇到一些错误,可能单纯是我们的程序编写出错,或是接收到了与我们预期不符的用户输入,或者是其它什么原因。通常,一段代码会在出错的时候停止执行,如果只是一个用于练习的小程序可能及时排查出问题倒没什么,但如果实在一个监测生命健康,或是多人网络游戏中,程序一旦因为遇到异常而停止执行,会造成难以预料的不良后果。JavaScript 提供了一种*`try...catch` 语句*,它会在捕捉到异常的同时不会使代码停止执行,还能根据得到的异常信息做一些更为合理的操作。 6 | 7 | 8 | 9 | ### 语法 10 | 11 | `try...catch` 结构由两部分组成:`try` 和 `catch`: 12 | 13 | ```javascript 14 | try { 15 | // 代码... 16 | } catch (e) { 17 | // 处理异常 18 | } 19 | ``` 20 | 21 | 它按照以下步骤执行: 22 | 23 | - 首先,执行 `try` 子句中包含的代码。 24 | - 如果执行过程中没有异常,那么忽略 `catch` 子句里面的代码,try 子句执行完之后离开这个 `try...catch` 语句去做其他事情。 25 | - 如果执行过程中发生异常,控制流就转移到了 `catch` 子句的开头。变量` e`是一个包含了异常信息的对象,也可以取其它名称,但是 `e` 可以看做 `error`(错误)或`exception`(异常)的缩写。 26 | 27 | ![img](assets/640.webp) 28 | 29 | 30 | 31 | > 图片来源:https://mmbiz.qpic.cn/ 32 | 33 | 所以,发生在 `try`子句的异常不会使代码停止执行:我们可以在 `catch` 子句里处理异常。 34 | 35 | 36 | 37 | 38 | 39 | ### try...catch 语句的使用 40 | 41 | 让我们来看更多的例子。 42 | 43 | 没有异常的例子: 44 | 45 | ```javascript 46 | try { 47 | alert("开始运行 try 子句"); 48 | // ...这里没有异常 49 | alert("try 子句运行完毕"); 50 | } catch (err) { 51 | alert("没有任何异常,catch 子句被忽略了"); 52 | } 53 | alert("现在继续执行"); 54 | ``` 55 | 56 | 包含异常的例子: 57 | 58 | ```javascript 59 | try { 60 | alert("开始执行 try 子句"); 61 | lalala; // 异常,变量未定义! 62 | alert("try 子句执行完了"); // (2) 63 | } catch(err) { 64 | alert("捕捉一只异常!"); 65 | } 66 | alert("现在继续执行"); 67 | ``` 68 | 69 | 要使得 `try...catch `能工作,代码必须是可执行的,换句话说,它必须是有效的 JavaScript 代码。 70 | 71 | 如果代码包含语法错误,那么` try...catch` 不能正常工作,例如含有未闭合的大括号: 72 | 73 | ```javascript 74 | try { 75 | {{{{{{{{{{{{ 76 | } catch(e) { 77 | alert("这不是合法的代码,这段 catch 子句也不会被执行"); 78 | } 79 | ``` 80 | 81 | 浏览器读取然后执行代码,发生在读取代码阶段的异常被称为*解析时错误*(parse-time),`try...catch` 也对它们无可奈何,因为这样的代码浏览器就读不懂,也就无法理解 `try...catch` 语句。`try...catch` 只能处理有效代码之中的异常。这类异常被称为*运行时错误*(runtime errors),有时候也称为 “`exceptions`”。 82 | 83 | 当一个异常发生之后,JavaScript 生成一个包含异常细节的对象。这个对象会作为一个参数传递给 `catch`: 84 | 85 | ```javascript 86 | try { 87 | // ... 88 | } catch(e) { 89 | // “异常对象”,可以用其他参数名代替 90 | // ... 91 | } 92 | ``` 93 | 94 | 对于所有内置的异常,`catch` 子句捕捉到的相应的异常的对象都有两个属性: 95 | 96 | `name` :异常名称,对于一个未定义的变量,名称是 “ReferenceError” 97 | 98 | `message` :关于异常的文字描述。 99 | 100 | 还有很多非标准的属性在绝大多数环境中可用。其中使用最广泛并且被广泛支持的是: 101 | 102 | `stack` :当前的调用栈。它是用于调试的,一个包含引发异常的嵌套调用序列的字符串。 103 | 104 | 例如: 105 | 106 | ```javascript 107 | try { 108 | lalala; // 异常,变量未定义! 109 | } catch(err) { 110 | alert(err.name); // ReferenceError 111 | alert(err.message); // lalala 未定义 112 | alert(err.stack); // 异常捕获过程的细节 113 | alert(err); // ReferenceError: lalala 未定义 114 | } 115 | ``` 116 | 117 | 118 | 119 | 120 | 121 | ### 异常处理的应用 122 | 123 | 让我们一起探究一下真实使用场景中 `try...catch` 的使用。 124 | 125 | 在前面的章节中,我们了解过质数判断算法,并编写了一个循环接收用户输入并给出判断结果的程序。我们对用户输入进行了检查,如果符合要求,那么往后执行;否则的话,会给出一个错误信息并终止程序。由于是在一个 `while` 语句的循环体内,直接使用 `break` 语句就能达到终止程序的目的。但是`break`语句的本意是“停止循环“,而非“中止程序”,如果不是在循环内运行,就不能使用 `break` 语句,此外,在 `break` 语句之前还需要告知用户遇到的问题。 126 | 127 | 遇到不合法输入的问题本质是“处理异常”,而非“结束程序”,因此我们的程序应该拥有一个处理异常的机制,同时将“遇到异常,暂停程序”和“告知用户遇到的异常”优雅地结合在一起。前一个需求我们已经有了 `try...catch` 语句,而对于后一个,另一种语句可以做到:*`throw` 语句*。它可以*标记*一个异常信息,称为*抛出异常*。它的语法如下所示: 128 | 129 | ```javascript 130 | throw 表达式; 131 | ``` 132 | 133 | 当遇到 `throw` 语句的时候,程序暂停执行后面的内容,带着表达式的值一层一层地退出控制流,直到遇到外层的 `try` 子句。如果没有 `try` 子句包裹可能会抛出异常的语句,那么异常信息就会被直接告知浏览器,整个程序也就真正停止运行了。我们用一个示例来观察一下 `throw` 语句与 `try..catch` 语句的搭配使用。 134 | 135 | ```javascript 136 | try { 137 | alert("一二三四五,上山打老虎"); 138 | throw "老虎来了"; 139 | alert("老虎没打到,打到小松鼠"); 140 | } catch (e) { 141 | alert(e); 142 | } 143 | ``` 144 | 145 | 这段代码在输出 `"一二三四五,上山打老虎"`之后,遇到了 `throw` 语句,就暂停执行后面的内容。程序带着 `"老虎来了"` 的信息逃离现场,遇到 `try` 子句,就相当于吃了一记定心丸,带着强大的武器去捕捉老虎,便开始执行 `catch` 语句,同时捕获了`"老虎来了"`的异常信息,并输出它。如果 `throw` 语句的处于其它语句内部,也会使程序执行到这里就带着异常信息撤退,倘若遇到了 `try` 语句,就说明这个异常被捕获了,异常信息作为异常对象被传递给 `catch` 子句的括号里绑定的变量。 146 | 147 | 有了 `throw` 语句和 `try...catch` 语句搭配使用,程序便有了强大的异常处理机制,即便遇到“未知的危险”也可临危不惧,异常已经被抓在了 `catch` 子句的手心里,正常执行程序时也不会因可能遇到的异常而手忙脚乱。利用异常处理机制,我们来改写一下前面的质数判断程序。 148 | 149 | ```javascript 150 | let n = parseInt(prompt("请输入一个大于 1 的正整数。")); 151 | while (true) { // 循环接受输入。 152 | try { 153 | if (isNaN(n) || !isFinite(n) || n <= 1) { 154 | throw "输入不符合要求,程序停止"; 155 | } 156 | let isPrime = (n === 2) || (n % 2 !== 0); 157 | for (let i = 3, last = Math.sqrt(n); i <= last; i += 2) { 158 | if (n % i === 0) { 159 | isPrime = false; 160 | break; // 这个 break 语句只退出当前所在的循环。 161 | } 162 | } 163 | alert(`${n}是一个${isPrime ? "质数" : "合数"}`); // 使用模板字符串来拼凑信息 164 | n = parseInt(prompt("请输入一个大于 1 的正整数。")); 165 | } catch (e) { 166 | alert(e); 167 | } 168 | } 169 | ``` 170 | 171 | 原先的版本中,程序一旦接受到了不符合要求的用户输入,就会退出循环,也就停止了程序;而这里用异常处理机制改写之后,即使遇到异常,程序只会跳过这一轮的正常处理,直接告知用户,程序依然保持运行,同时“抛出”——“捕获”异常的语义性也远比原先的“输出信息”“跳出循环”要清晰得多。充分利用异常处理机制,我们能够写出更加优雅和*健壮*的代码。对于不可预知的异常输入而仍然能保持正常运行,并将信息及时展现给用户,这种性质被称为程序的*鲁棒性*。 172 | 173 | 174 | 175 | 176 | 177 | ### 异常对象 178 | 179 | -------------------------------------------------------------------------------- /语句/第四章练习.md: -------------------------------------------------------------------------------- 1 | ## 练习题 2 | 3 | --- 4 | 5 | 1. 编写一个程序,读取正整数 `n`,计算前 `n` 个奇数之和。例如, `n` 是 `4`,那么这个程序会计算 `1 + 3 + 5 + 7`,并显示结果 `16`。 6 | 7 | 2. 编写一个程序,不断让用户输入一个数,当输入 `0` 时停止,并显示得到的数据的最大值。 8 | 9 | 3. 编写一个程序,在练习 2 的基础上显示次大和最小值。 10 | 11 | 4. 每个大于 `1` 的正整数可以表示成几个质数的乘积,这类因式分解是唯一的,被称为**质因数**。例如,数字 `60` 可以被分解成 2 × 2 × 3 × 5,每个因数都是质数。注意:在因式分解中,同样的质数可以出现多次。 12 | 13 | 编写一个程序,要求用户输入一个整数 `n`,并对它进行质因数分解。 14 | 15 | 5. 1979 年,道格拉斯·霍夫斯塔勒教授写了《哥德尔、艾舍尔、巴赫》一书,它获得了普利策文学奖,是计算机科学的经典著作之一。书中提出了一个问题:对一个特定的正整数 n 重复执行以下规则,便可形成一系列数: 16 | 17 | - 如果 n 等于 1,那么已经到达这个序列数的终点,可以停止。 18 | - 如果 n 是偶数,将它除以 2。 19 | - 如果 n 是奇数,将它乘以 3 再加 1。 20 | 21 | 这个序列有许多名称,一般称为**冰雹序列**,因为这些值忽上忽下,如冰雹在云中的形成。 22 | 23 | 编写一个程序,让用户输入一个正整数,然后从这个数产生冰雹序列,如下所示: 24 | 25 | ![1554374449053](assets/1554374449053.png) 26 | 27 | 这个程序记录了序列的每一步所进行的操作,你可能要思考一下如何在一个对话框里放入所有信息。 28 | 29 | 冰雹序列最迷人之处是直到目前还没有人证明它最终能停止。冰雹序列的计算过程可以有很多步,但不知何故,它总能跳回到 1。 30 | 31 | 6. 德国数学家莱布尼兹(1646~1716)发现了一个事实:圆周率 π 可以用下面的公式计算: 32 | $$ 33 | \frac{\pi}{4} \cong 1 - \frac{1}{3} + \frac{1}{5} - \frac{1}{7} + \frac{1}{9} - \frac{1}{11} + ... 34 | $$ 35 | 这个等式的右边是一个无穷级数,每个分数是级数中的一项。所有奇数从 1 开始,减去三分之一,加上五分之一,以此类推按照上述公式一直做下去,所得到的值就会越来越接近于 $ \pi $/4。 36 | 37 | 编写一个程序,计算包含莱布尼兹级数的前 10 000 项的 π 的近似值。 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /软件开发/reference.md: -------------------------------------------------------------------------------- 1 | ## 参考文献 2 | 3 | --- 4 | 5 | 1. *Internationalization and localization*, https://en.wikipedia.org/wiki/Internationalization_and_localization 6 | 2. *Hanunuo Script*, https://en.wikipedia.org/wiki/Hanunuo_script 7 | 3. *IETF BCP 47*, https://tools.ietf.org/pdf/bcp47.pdf 8 | 4. *Intl.PluralRules Spec Proposal*, https://rawgit.com/caridy/intl-plural-rules-spec/master/index.html -------------------------------------------------------------------------------- /软件开发/使用JavaScript库.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/软件开发/使用JavaScript库.md -------------------------------------------------------------------------------- /软件开发/兼容性.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/软件开发/兼容性.md -------------------------------------------------------------------------------- /软件开发/国际化.md: -------------------------------------------------------------------------------- 1 | ## 孔夫子的话·国际化 2 | 3 | --- 4 | 5 | ### 国际化与本地化 6 | 7 | 在信息技术领域,*国际化*(internationalization)是指在应用程序中用来适应各种语言和区域,而无需在编程时专门处理的一种方式,它将软件与特定语言及地区、文化等摆脱直接关联。国际化提供以下机制: 8 | 9 | - 语言、地区、文化相关的内容与软件本身的逻辑不相关。 10 | - 开发软件的过程中不需要关心具体的信息呈现。 11 | - 当软件被设计为在不同的语言及地区使用时,软件本身不需要进行修改, 12 | 13 | 而*本地化*(localization)则是基于国际化的机制,将软件的交互内容,与适用的语言、文化、地区等相匹配的过程,在这个过程中决定具体给用户呈现何种信息。 14 | 15 | > 国际化和本地化之间的关系虽然微妙,但却很重要。简单来说,国际化意味着产品有适用于任何地方的“潜力”,本地化则是为了更适合于“特定”地方的使用,而另外增添的特色。对于一个软件项目来说,国际化是在设计过程中考虑、开发过程中只做一次,但本地化则要针对不同的区域各做一次。这两者之间是互补的,并且两者合起来才能让软件产品拥有良好的广泛适用性。 16 | > 17 | > ——Wikipedia 18 | 19 | 由于国际化和本地化两个英文单词太长,常被分别简称为*i18n*(代表在 i 和 n 两个首尾字母之间有 18 个字母)和*L10n*(使用大写的 L 以区分数字 1)。 20 | 21 | 22 | 23 | --- 24 | 25 | Note: 26 | 27 | 在微软及 IBM 等企业中,则会使用*全球化*(globalization)来表示此两者的合称。在英文中,也会使用 *g11n* 作为简称。 28 | 29 | --- 30 | 31 | 32 | 33 | 国际化与本地化工作的焦点包括: 34 | 35 | 1. 语言 36 | - 电子文件 37 | - 字母。目前绝大多数的应用程序都采用 Unicode 为标准来解决字符编码的问题。 38 | - 不同的数字命名系统。 39 | - 书写方向。英文、中文等是从左往右书写,而阿拉伯文、希伯来文等则是从右往左书写。另一些语言文字,如满文和蒙古文是从上往下书写,还有极少数文字(如 Hanunó'o)从下往上书写。 40 | - 相同语言在不同地区的拼法差异,如美国英语和加拿大英语使用 *localization* ,而英国英语和澳大利亚英语使用 *localisation*。 41 | - 文件处理上的差异,如某些文字存在大小写,其它则否。字母顺序。 42 | - 文字的图像表示。 43 | - 读法。 44 | - 视频的字幕。 45 | 2. 文化。 46 | - 图片和颜色:这牵涉到理解和文化适宜的议题。 47 | - 名字和称谓。 48 | - 政府给定的编码(如美国的社会安全码,英国的 National Insurance number,爱沙尼亚的 Isikukood 及其它各国的身份证号码)和护照。 49 | - 电话号码、地址和国际邮递区号。 50 | - 货币。 51 | - 标志符号。 52 | - 货币标志的位置。 53 | - 用于度量衡的单位。 54 | - 纸张大小。 55 | 3. 书写习惯。 56 | - 日期和时间的格式,包含各式日历。 57 | - 时区(以国际标准时间为准)。 58 | - 数字格式。 59 | - 小数点的类型。 60 | - 分隔点的位置。 61 | - 分隔所用的字符。 62 | - 产品和服务所面向的法规。 63 | 64 | 而有关本地化的主题则包括: 65 | 66 | - 翻译。 67 | - 针对特定语言(如东亚语言)的特别支持。 68 | - 符合当地习俗和观念的交互信息。 69 | - 符号。 70 | - 排序方法。同样写法的字母,在不同语言的字母表中可能拥有不同位置。在瑞典语中, ä 和 a 是单独的基本字母,排在 b 之前;而在普通的 Unicode 环境中,ä 的码点比 b 要高,因此排在 b 之后。 71 | - 美学、文化价值和社会环境。 72 | 73 | 74 | 75 | 76 | 77 | ### 国际化利器 Intl 78 | 79 | 国际化的意义在于本地化,本地化一般分两种情况考虑,即数据呈现格式和用户信息内容。传统的桌面应用程序根据运行环境的*区域设置*,决定使用何种方式来呈现数据,如小数点的类型、货币的单位等。 80 | 81 | 82 | > *区域设置*(locale)是计算机中一套定义用户的语言、国家和用于定义用户希望在其用户界面上看到的各种可以改变的选择的参数集合。通常一个 locale 标识符至少包括一个语言标识符和一个区域标识符。 83 | > 84 | > 在 UNIX 和 Windows 中,locale 的控制是不同的。在 UNIX 下,通常通过环境变量来控制 locale。这些环境变量包括:`LANG`, `LC_ALL`, `LC_CTYPE`, `LC_TIME`, 等等。你可以通过改变这些环境变量来控制你的程序或者命令所表现出来的 locale,前提是这些程序或者命令必须是已经被国际化的和本地化的。在 Windows 下,你可以通过改变控制面板上的“语言/区域”中的区域的值来设定 Windows 的当前用户的 locale。 85 | > 86 | > ——Wikipedia 87 | 88 | 而用户信息内容处理起来则相对棘手。一些桌面开发框架(如 C++ 的 Qt)采取的方式是,当应用程序在某个地方需要呈现一段信息,那么就在这里留一个占位记号,然后对源程序进行处理,生成对应于每种支持语言的*翻译文件*,将每条信息的含义与目标语言一一对应,再由专门的翻译人员填写每条信息应当呈现出的内容。 89 | 90 | 而在 JavaScript 中,一切都变得非常简单——浏览器封装了有关国际化与本地化的底层细节,提供了一个称为 `Intl` 的对象来完成所有事情。JavaScript 开发者无需在项目中加入许多 KB 的翻译文件。`Intl` 有各种构造函数和方法,这些构造函数和方法将 locale 作为参数并可以根据我们的需要格式化数据。 91 | 92 | `Intl` 对象包括以下这些构造函数: 93 | 94 | - `Collator` 95 | - `DateTimeFormat` 96 | - `ListFormat` 97 | - `NumberFormat` 98 | - `PluralRules` 99 | - `RelativeTimeFormat` 100 | 101 | 它们接受两个参数 `locale` 和 `options`。`locale` 必须是符合 BCP 47 语言标记规范的字符串或数组。 102 | 103 | > BCP 47 语言标记用于定义一种语言,它包含了该语言的主要信息。在正常情况下,它按顺序包含:语言代码,脚本代码和国家或地区代码,这些信息都用连字符分隔。虽然这些标签不区分大小写,但建议用标题大小写来写脚本代码,用大写来写国家和地区代码,用小写来写其他内容。 104 | > 105 | > ——Mozilla Developer Network 106 | 107 | `locale` 参数的默认值为当前运行程序的语言环境(区域设置)。`locale` 一般写成:`en`(英语),`hi`(印地语),`ta-in`(泰米尔语 - 印度)等。`options` 参数是可选的,其结构因不同的构造函数而异,主要用于提供格式化的自定义信息。 108 | 109 | 接下来,我们将详细了解如何使用 `Intl` 提供的有力工具来进行国际化和本地化处理。 110 | 111 | 112 | 113 | 114 | 115 | ### 日期和时间处理 116 | 117 | JavaScript 中的标准 `Date` 对象包含许多信息,却不一定利于人类阅读。因此,`Intl` 对象提供了 `DateTimeFormat` 构造函数,它将一个 `Date` 实例转换为不同地区的日期格式。 118 | 119 | ```javascript 120 | const date = new Date(); 121 | 122 | // 先取得具有语言信息的格式化工具,再调用 format 方法进行格式化 123 | alert(new Intl.DateTimeFormat('en-us').format(date)); // "9/7/2019" 124 | alert(new Intl.DateTimeFormat('zh-cn').format(date)); // "2019/9/7" 125 | 126 | // 使用日本日历 127 | alert(new Intl.DateTimeFormat("ja-jp-u-ca-japanese").format(date)); // "令和1/9/7" 128 | // 如果一种语言标志可能不支持,那么可以提供备选方案 129 | alert(new Intl.DateTimeFormat(['ban', 'id']).format(date)); // 7/9/2019 130 | ``` 131 | 132 | 上面的例子中,构造函数 `Intl.RelativeTimeFormat` 的第一个参数为 `"en-us"`,代表处理方式基于美国英语。它还可以接受第二个参数 `options`,用于配置对细节的要求。 133 | 134 | ```javascript 135 | const date = new Date(); 136 | let options = { 137 | // 如果省略某一项(如年份),那么格式化后的字符串不会包含年份 138 | weekday: "long", // 工作日的呈现方式(长名称) 139 | year: "numeric", // 年份的呈现方式(数字) 140 | month: "long", // 月份的呈现方式(长名称) 141 | day: "2-digit" // 天的呈现方式(2位数字) 142 | }; 143 | 144 | alert(new Intl.DateTimeFormat("zh-cn", options).format(date)); // "2019年9月07日星期六" 145 | 146 | options.timeZone = "UTC"; // 将时区设置为国际标准时间(UTC) 147 | options.timeZoneName = "short"; // 使用短时区名称 148 | alert(new Intl.DateTimeFormat("en-us", options).format(date)); // "Friday, September 06, 2019, UTC" 149 | 150 | options = { 151 | // 小时、分钟、时间的呈现方式都设置为数字 152 | // 另一个可选值是 "2-digit" 153 | hour: "numeric", 154 | minute: "numeric", 155 | second: "numeric", 156 | }; 157 | alert(new Intl.DateTimeFormat("en-au", options).format(date)); // "7:22:22 am" 158 | 159 | options = { 160 | // 省略了以上选项,默认只显示年、月、日 161 | }; 162 | alert(new Intl.DateTimeFormat("zh-cn", options).format(date)); // "2019/9/7" 163 | ``` 164 | 165 | 除了 `format` 之外,还有一个 `formatRange` 方法,它可以根据两个日期创建时间范围。如果两个日期处于同一个月,那么会组合在一起显示。 166 | 167 | ```javascript 168 | const dtf = new Intl.DateTimeFormat("zh-cn", { 169 | year: "numeric", 170 | month: "short", 171 | day: "numeric" 172 | }); 173 | const startDate = new Date("2019-7-12"); 174 | const endDate = new Date("2019-9-1"); 175 | alert(`暑假时间:${dtf.formatRange(startDate, endDate)}`); // "暑假时间:2019/7/12 – 2019/9/1" 176 | ``` 177 | 178 | `RelativeTimeFormat` 构造函数将一个数字看做相对于当前时间的差值,并转换为人性化的语言,如“3天以后”“上个月”等。让我们来看下面这个例子: 179 | 180 | ```javascript 181 | // 把相对时间转换为英语口语 182 | const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); 183 | alert(rtf.format(-1, "month")); // "last month" 184 | alert(rtf.format(0, "month")); // "this month" 185 | alert(rtf.format(1, "month")); // "next month" 186 | alert(rtf.format(-1, "week")); // "last week" 187 | alert(rtf.format(0, "week")); // "this week" 188 | alert(rtf.format(1, "week")); // "next week" 189 | alert(rtf.format(-1, "day")); // "yesterday" 190 | alert(rtf.format(0, "day")); // "today" 191 | alert(rtf.format(1, "day")); // "tomorrow" 192 | ``` 193 | 194 | 配置项 `options` 中的 `numeric` 属性可以设置为 `"always"` 或 `"auto"`,其中 `"always"` 不会将相对日期转换为口语。 195 | 196 | ```javascript 197 | // 现在 options 的 numeric 属性值为 "always" 198 | alert(rtf.format(-1, "month")); // "1 day ago" 199 | alert(rtf.format(2, "day")); // "in 2 days" 200 | ``` 201 | 202 | 我们也可以选择其它语言文字的相对日期,例如中文或泰米尔文。 203 | 204 | ```javascript 205 | const rtf2 = new Intl.RelativeTimeFormat("zh-cn", { numeric: "auto" }); 206 | alert(rtf2.format(-1, "day")); // "昨天" 207 | alert(rtf2.format(2, "day")); // "后天" 208 | alert(rtf2.format(1, "year")); // "明年" 209 | alert(rtf2.format(10, "second")); // "10秒钟后" 210 | const rtf3 = new Intl.RelativeTimeFormat("ta-in", { numeric: "auto" }); 211 | alert(rtf3.format(-1, "day")); // "நேற்று" 212 | alert(rtf3.format(2, "month")); // "2 மாதங்களில்" 213 | alert(rtf3.format(1, "year")); // "அடுத்த ஆண்டு" 214 | ``` 215 | 216 | 像昨天、今天、明天这样的短语,可以带给用户更好的使用体验。 217 | 218 | 219 | 220 | ### 文本编辑 221 | 222 | `ListFormat` 构造函数用于把字符串连接成有意义的短语。 223 | 224 | ```javascript 225 | const characters = ["George Washington", "John Adams", "Thomas Jefferson"]; 226 | const lsf = new Intl.ListFormat("en"); 227 | alert(lsf.format(characters)); // "George Washington, John Adams, and Thomas Jefferson" 228 | ``` 229 | 这样,我们就可以方便地连词成句,而不用编写专门的函数来处理。这个构造函数同样有 `options` 配置项,我们可以将它的 `type` 属性设置为 `"disjunction"`来使用 `or`,而默认值是 `"conjunction"`,这时会使用 `and`。`"unit"` 则适用于没有连接词(and, or)的情况。 230 | 231 | ```javascript 232 | const flowers = ["violet", "tulip", "rose"]; 233 | const lsf = new Intl.ListFormat("en", { type: "disjunction" }); 234 | alert(`Which flower do you like, ${lsf.format(flowers)}?`); 235 | // "Which flower do you like, violet, tulip, or rose?" 236 | ``` 237 | 238 | `ListFormat` 是一个实验性的特性,不一定能在我们的浏览器上使用,我们可以尝试一下。 239 | 240 | `PluralRules` 构造函数可以将数字格式化为有限的数词,还可以决定是基数词还是序数词。除此之外,它并没有太多用途。 241 | 242 | ```javascript 243 | // 在埃及阿拉伯语中 244 | const pr = new Intl.PluralRules("ar-eg"); 245 | alert(pr.select(0)); // "zero" 246 | alert(pr.select(1)); // "one" 247 | alert(pr.select(2)); // "two" 248 | alert(pr.select(6)); // "few" 249 | alert(pr.select(18)); // "many" 250 | 251 | // 在美国英语中 252 | const pr2 = new Intl.PluralRules("en-us"); 253 | alert(pr2.select(0)); // "other" 254 | alert(pr2.select(1)); // "one" 255 | alert(pr2.select(2)); // "other" 256 | ``` 257 | 258 | -------------------------------------------------------------------------------- /软件开发/性能.md: -------------------------------------------------------------------------------- 1 | ## 似水流年·性能 2 | 3 | --- 4 | 5 | -------------------------------------------------------------------------------- /软件开发/模块化.md: -------------------------------------------------------------------------------- 1 | ## 山中无岁月·模块化 2 | 3 | --- 4 | 5 | ### 为何使用模块 6 | 7 | > 一次又一次的事实证明,小的、组织良好的代码远比庞大的代码更容易理解、更易于维护。因此,很自然,优化程序的结构和组织的方式,就是把它们分成小的、耦合相对松散的片段,这些片段称为模板。 8 | > 9 | > ——《JavaScript Ninja》 10 | 11 | 好的作者把他们的书组织成逻辑紧密的章节,好的程序员把代码分为一些模块。章节是一些句子聚合在一起,组成一个“主题”。模块就是把一些代码聚合在一起,组成一个“功能”。模块的意图有点像类,但模块的层次比类更高,模块中代码的关系比类要松散。类可能是现实世界的事物的逻辑表示,而一个模块就是有相互关联的“一些事物”的集合,按需取用。如果说一个函数(方法)是一个零件,那么包含了一系列方法的类就是一台机器,而一个模块就是工厂里的一套车间,本身能够独立完成整个加工流程,组合而成就成了庞大的工厂。 12 | 13 | 一个好的模块应该是*独立的*,它可以被随时加入或者移除,而不会损害整个系统或应用程序。模块拥有的特点包括: 14 | 15 | - 可维护性。因为模块是独立的,一个设计良好的模块会让外面的代码对自己的依赖越少越好,这样模块的更新和改进就更加灵活,外部代码需要关注的细节也就越少,同时减少了“这些功能”对“那些功能”的关联造成的影响。 16 | - 提供命名空间。在 JavaScript 里面,如果一个标识符在最顶级的函数之外声明,它就直接变成全局可用,当应用程序越来越大,容易出现命名冲突的情况,产生难以排查的问题。使用模块化开发来封装独立的标识符,可以避免污染全局环境。 17 | - 重用代码。我们有时候会为了方便,从先前的代码中拷贝一些到新的项目,这没有问题,但是更好的方法是,通过将代码封装成可重复使用的模块,来避免机械的复制粘贴,同时保持代码的简洁和关注点的单一性。 18 | 19 | 20 | 21 | ### 模块模式 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ### AMD 和 CommonJS 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ### ECMAScript 6 的模块 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ### 在浏览器中使用现代模块 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /软件开发/测试.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/软件开发/测试.md -------------------------------------------------------------------------------- /附录/JavaScript语言参考.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/附录/JavaScript语言参考.md -------------------------------------------------------------------------------- /附录/Unicode指南.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/附录/Unicode指南.md -------------------------------------------------------------------------------- /附录/键码映射表.md: -------------------------------------------------------------------------------- 1 | ## 附录 C - 键码映射表 2 | 3 | --- 4 | 5 | 6 | 7 | ### 字母和数字键盘 8 | 9 | | 按键 | 键码 | 按键 | 键码 | 按键 | 键码 | 按键 | 键码 | 10 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 11 | | A | 65 | J | 74 | S | 83 | 1 | 49 | 12 | | B | 66 | K | 75 | T | 84 | 2 | 50 | 13 | | C | 67 | L | 76 | U | 85 | 3 | 51 | 14 | | D | 68 | M | 77 | V | 86 | 4 | 52 | 15 | | E | 69 | N | 78 | W | 87 | 5 | 53 | 16 | | F | 70 | O | 79 | X | 88 | 6 | 54 | 17 | | G | 71 | P | 80 | Y | 89 | 7 | 55 | 18 | | H | 72 | Q | 81 | Z | 90 | 8 | 56 | 19 | | I | 73 | R | 82 | 0 | 48 | 9 | 57 | 20 | 21 |    22 | 23 | 24 | 25 | ### 数字和功能键盘 26 | 27 | | 按键 | 键码 | 按键 | 键码 | 按键 | 键码 | 按键 | 键码 | 28 | | ---- | ---- | ----- | ---- | ---- | ---- | ---- | ---- | 29 | | 0 | 96 | 8 | 104 | F1 | 112 | F7 | 118 | 30 | | 1 | 97 | 9 | 105 | F2 | 113 | F8 | 119 | 31 | | 2 | 98 | * | 106 | F3 | 114 | F9 | 120 | 32 | | 3 | 99 | + | 107 | F4 | 115 | F10 | 121 | 33 | | 4 | 100 | Enter | 108 | F5 | 116 | F11 | 122 | 34 | | 5 | 101 | - | 109 | F6 | 117 | F12 | 123 | 35 | | 6 | 102 | . | 110 | | | | | 36 | | 7 | 103 | / | 111 | | | | | 37 | 38 | 39 | 40 | 41 | 42 | ### 控制键盘 43 | 44 | | 按键 | 键码 | 按键 | 键码 | 按键 | 键码 | 按键 | 键码 | 45 | | --------- | ---- | ---------- | ---- | ----------- | ---- | ---- | ---- | 46 | | BackSpace | 8 | Esc | 27 | Right Arrow | 39 | -_ | 189 | 47 | | Tab | 9 | Spacebar | 32 | Dw Arrow | 40 | .> | 190 | 48 | | Clear | 12 | Page Up | 33 | Insert | 45 | /? | 191 | 49 | | Enter | 13 | Page Down | 34 | Delete | 46 | `~ | 192 | 50 | | Shift | 16 | End | 35 | Num Lock | 144 | [{ | 219 | 51 | | Control | 17 | Home | 36 | ;: | 186 | \| | 220 | 52 | | Alt | 18 | Left Arrow | 37 | =+ | 187 | ]} | 221 | 53 | | Cape Lock | 20 | Up Arrow | 38 | ,< | 188 | '" | 222 | 54 | 55 | 56 | 57 | 58 | 59 | ### 多媒体键盘 60 | 61 | | 按键 | 键码 | 62 | | ------ | ---- | 63 | | 音量加 | 175 | 64 | | 音量减 | 174 | 65 | | 停止 | 179 | 66 | | 静音 | 173 | 67 | | 浏览器 | 172 | 68 | | 邮件 | 180 | 69 | | 搜索 | 170 | 70 | | 收藏 | 171 | -------------------------------------------------------------------------------- /魔法世界/WebRTC.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/魔法世界/WebRTC.md -------------------------------------------------------------------------------- /魔法世界/Web幻灯片.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/魔法世界/Web幻灯片.md -------------------------------------------------------------------------------- /魔法世界/再探HTML5.md: -------------------------------------------------------------------------------- 1 | ## 互通有无·再探 HTML5 2 | 3 | --- 4 | 5 | ### 多媒体 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ### 地理定位 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ### 多线程和消息传递 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ### 离线缓存 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ### 一些有用的技术 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /魔法世界/图像处理.md: -------------------------------------------------------------------------------- 1 | ## 鹰瞵鹗视·图像处理 2 | 3 | --- 4 | 5 | ### 格式与原理 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ### 裁剪与拉伸 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ### 调整颜色 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ### 简单视觉效果 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ### 元信息 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ### 复杂处理 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /魔法世界/多线程.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/魔法世界/多线程.md -------------------------------------------------------------------------------- /魔法世界/数据可视化.md: -------------------------------------------------------------------------------- 1 | ## 数据可视化 2 | 3 | --- 4 | 5 | ### D3.js 6 | 7 | 我们每个人都会对“大数据”的概念不陌生。我们生活的信息时代由海量数据支撑,有一种说法,“最近十年内互联网上产生的信息超过了过去五千年的总和”。大数据及其相关技术蓬勃发展的今天,数据处理在我们的生活和工作中的影响越来越大,也越来越复杂。每天都有惊人的数据产生,怎么提取并显示有用的信息,变得越来越重要。有用的信息以数据作为载体,而*数据可视化*,就是将数据变为人能轻松理解的信息的重要途径。 8 | 9 | 这几年来,数据可视化越来越流行,无论是与时俱进的报刊杂志,还是不断翻新的 Web 门户网站、新闻媒体,都大量使用可视化技术,使得复杂的数据和文字变得十分容易理解。“一图胜千言”的道理越来越能体现出来。在信息爆炸式增长的今天,我们的时间有限,希望一眼就能找到自己需要的信息。图片和图标不仅容易理解,而且方便记忆,能在脑海中留下深刻印象。本节讨论的主题便是在 Web 上进行可视化的方方面面。 10 | 11 | 绚丽多彩的图表,令人陶醉的可交互特性,都可以利用 JavaScript 基于 Canvas 抑或是 SVG 实现,还能设计出流畅简洁、赏心悦目的动画效果。不过,Canvas 和 SVG 本身的 API 关注的是绘制过程的细节,而我们关注的抽象的数据,为了避免在底层浪费精力,我们可以使用 D3.js。它是一个 JavaScript 可视化库。 12 | 13 | D3 的全称是 Data-Driven Documents,也就是“数据驱动的文档”。D3.js 封装了基础的 DOM 操作和图形绘制细节,为我们提供了大量开箱即用的方法。我们只需要提供我们关心的数据,D3.js 就可以据此绘制出图表,并能根据我们的喜好与需求灵活改变呈现效果。 14 | 15 | --- 16 | 17 | Story: 18 | 19 | 数据可视化(Data Visualization)起源于 18 世纪。William Playfair 出版的书籍《The Commercial and Policital Atlas》中第一次使用了*柱形图*和*折线图*。 20 | 21 | 22 | 23 | --- 24 | 25 | 26 | 27 | 28 | 29 | ### 数据集 30 | 31 | D3.js 封装了一些基础的 DOM 操作。`select` 方法返回匹配选择器的第一个元素,而 `selectAll` 方法返回匹配选择器的所有元素。它们的用法分别与 `document.querySelector` 和 `document.querySelectorAll` 相同。它们返回一个 `D3` 的实例对象,如果是已经用原生 DOM 方法获取的元素,也需要用 D3 的这两个方法进行包装,获得的对象称为*选择集*。选择集对象具有如下三个基本方法: 32 | 33 | - `empty`判断选择集是否为空。 34 | - `node` 返回第一个非空元素。如果选择集为空,返回 `null`。 35 | - `size` 返回选择集中的元素个数。 36 | 37 | 选择集对象的 `attr` 方法与 DOM 的 `setAttribute` 类似,用于设置属性,但是获取属性必须用 `property` 方法。而 `classed` 方法决定某个选择集中的元素是否开启某个类名,`style` 方法则用于设置元素的样式。其余的一些 DOM 封装方法,我们将会在后面的示例中详细了解。 38 | 39 | 40 | 41 | 42 | 43 | ### 比例尺和坐标轴 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ### 布局 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ### 交互 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ### 案例研究:思维导图 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | ### 地图 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ### 复杂的图 -------------------------------------------------------------------------------- /魔法世界/音频处理.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanpero/JavaScript-Art-Tour/cc3f2beef71edcd29077df49e6c3e813fce264c2/魔法世界/音频处理.md --------------------------------------------------------------------------------