From 22193265c18c5199ffefc3b4adeab36a3e48bc8a Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sat, 20 Oct 2018 15:28:52 +0300 Subject: [PATCH 1/6] fix --- 1-js/01-getting-started/3-devtools/chrome.png | Bin 76289 -> 42131 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/1-js/01-getting-started/3-devtools/chrome.png b/1-js/01-getting-started/3-devtools/chrome.png index fd029b747f142993b31277b5160e8b6a9d37dc2c..a506389072b1405ef1442f99a8e1b8a02150c702 100644 GIT binary patch literal 42131 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 literal 76289 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 b;v@=)bv8#Cd+jd~{^Vs;UX@6_{q+9 Date: Sat, 20 Oct 2018 17:11:22 +0300 Subject: [PATCH 2/6] epub fix --- 2-ui/1-document/11-coordinates/head.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/2-ui/1-document/11-coordinates/head.html b/2-ui/1-document/11-coordinates/head.html index efc6cd1cbe..bba7bec068 100644 --- a/2-ui/1-document/11-coordinates/head.html +++ b/2-ui/1-document/11-coordinates/head.html @@ -1,8 +1,9 @@ \ No newline at end of file + 1 t:1 From 5a2dff2c2a7e6385dd64599987b7c62e35934dc1 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 22 Oct 2018 12:01:46 +0300 Subject: [PATCH 6/6] cleanup --- .../solution.view/index.html | 2 +- .../4-behavior-tooltip/source.view/index.html | 2 +- .../3-image-gallery/solution.view/gallery.css | 6 +- .../3-image-gallery/source.view/gallery.css | 6 +- .../menu.view/menu.css | 2 +- .../solution.view/index.html | 2 +- .../source.view/index.html | 2 +- .../2-hoverintent/solution.view/style.css | 2 +- .../3-editable-div/solution.view/my.css | 2 +- .../3-editable-div/source.view/my.css | 2 +- .../01-hello-world/1-hello-alert/solution.md | 0 .../1-hello-alert/solution.view/index.html | 12 - archive/01-hello-world/1-hello-alert/task.md | 12 - archive/01-hello-world/article.md | 89 -- archive/01-hello-world/hello-world-render.png | Bin 44521 -> 0 bytes .../01-hello-world/hello-world-render@2x.png | Bin 85226 -> 0 bytes .../1-hello-alert-ext/alert.js | 1 - .../1-hello-alert-ext/index.html | 10 - .../1-hello-alert-ext/solution.md | 8 - .../1-hello-alert-ext/task.md | 9 - .../2-async-defer-first/solution.md | 5 - .../2-async-defer-first/task.md | 29 - archive/02-external-script/article.md | 251 --- archive/02-external-script/banner.js | 1 - .../06-events-and-timing-in-depth/article.md | 306 ---- archive/2-eval-calculator-errors/solution.md | 34 - archive/2-eval-calculator-errors/task.md | 14 - archive/2-events-and-timing-depth/article.md | 170 --- archive/4-random-from-array/solution.md | 12 - archive/4-random-from-array/task.md | 11 - archive/5-drag-and-drop-objects/article.md | 512 ------- archive/5-drag-and-drop-objects/between.png | Bin 2977 -> 0 bytes .../dragDemo.view/DragManager.js | 157 -- .../dragDemo.view/dragDemo.css | 15 - .../dragDemo.view/index.html | 41 - archive/5-drag-and-drop-objects/shiftx.png | Bin 1382 -> 0 bytes archive/backticks.md | 51 - archive/commend.md | 22 - archive/descriptors.md | 37 - archive/nfe.md | 140 -- archive/promise/async-then.js | 60 - archive/promise/fetch.js | 37 - archive/promise/thenable.js | 23 - archive/promise/thenable.md | 25 - archive/promise/thenable2.js | 19 - archive/promise/whereto.txt | 58 - archive/proto/user.js | 16 - .../javascript-prototype-confusion-and.html | 1353 ----------------- archive/recursion.md | 283 ---- archive/timing-decorator.md | 105 -- 50 files changed, 14 insertions(+), 3942 deletions(-) delete mode 100644 archive/01-hello-world/1-hello-alert/solution.md delete mode 100644 archive/01-hello-world/1-hello-alert/solution.view/index.html delete mode 100644 archive/01-hello-world/1-hello-alert/task.md delete mode 100644 archive/01-hello-world/article.md delete mode 100644 archive/01-hello-world/hello-world-render.png delete mode 100644 archive/01-hello-world/hello-world-render@2x.png delete mode 100644 archive/02-external-script/1-hello-alert-ext/alert.js delete mode 100644 archive/02-external-script/1-hello-alert-ext/index.html delete mode 100644 archive/02-external-script/1-hello-alert-ext/solution.md delete mode 100644 archive/02-external-script/1-hello-alert-ext/task.md delete mode 100644 archive/02-external-script/2-async-defer-first/solution.md delete mode 100644 archive/02-external-script/2-async-defer-first/task.md delete mode 100644 archive/02-external-script/article.md delete mode 100644 archive/02-external-script/banner.js delete mode 100644 archive/06-events-and-timing-in-depth/article.md delete mode 100644 archive/2-eval-calculator-errors/solution.md delete mode 100644 archive/2-eval-calculator-errors/task.md delete mode 100644 archive/2-events-and-timing-depth/article.md delete mode 100644 archive/4-random-from-array/solution.md delete mode 100644 archive/4-random-from-array/task.md delete mode 100644 archive/5-drag-and-drop-objects/article.md delete mode 100644 archive/5-drag-and-drop-objects/between.png delete mode 100644 archive/5-drag-and-drop-objects/dragDemo.view/DragManager.js delete mode 100644 archive/5-drag-and-drop-objects/dragDemo.view/dragDemo.css delete mode 100644 archive/5-drag-and-drop-objects/dragDemo.view/index.html delete mode 100644 archive/5-drag-and-drop-objects/shiftx.png delete mode 100644 archive/backticks.md delete mode 100644 archive/commend.md delete mode 100644 archive/descriptors.md delete mode 100644 archive/nfe.md delete mode 100644 archive/promise/async-then.js delete mode 100644 archive/promise/fetch.js delete mode 100644 archive/promise/thenable.js delete mode 100644 archive/promise/thenable.md delete mode 100644 archive/promise/thenable2.js delete mode 100644 archive/promise/whereto.txt delete mode 100644 archive/proto/user.js delete mode 100644 archive/proto/wrong/javascript-prototype-confusion-and.html delete mode 100644 archive/recursion.md delete mode 100644 archive/timing-decorator.md diff --git a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html index 31d0d46b12..1ef37cdc90 100644 --- a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html +++ b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html @@ -15,7 +15,7 @@ border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; - font: italic 14px/1.3 arial, sans-serif; + font: italic 14px/1.3 sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); diff --git a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html index 57a19d01a8..add3b21442 100644 --- a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html +++ b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html @@ -16,7 +16,7 @@ border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; - font: italic 14px/1.3 arial, sans-serif; + font: italic 14px/1.3 sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css index 66690e53ea..4522006aee 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css @@ -1,16 +1,16 @@ body { margin: 0; padding: 0; - font: 75%/120% Arial, Helvetica, sans-serif; + font: 75%/120% sans-serif; } h2 { - font: bold 190%/100% Arial, Helvetica, sans-serif; + font: bold 190%/100% sans-serif; margin: 0 0 .2em; } h2 em { - font: normal 80%/100% Arial, Helvetica, sans-serif; + font: normal 80%/100% sans-serif; color: #999999; } diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css index 0c5061c23d..b6e523014d 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css @@ -1,16 +1,16 @@ body { margin: 0; padding: 0; - font: 75%/120% Arial, Helvetica, sans-serif; + font: 75%/120% sans-serif; } h2 { - font: bold 190%/100% Arial, Helvetica, sans-serif; + font: bold 190%/100% sans-serif; margin: 0 0 .2em; } h2 em { - font: normal 80%/100% Arial, Helvetica, sans-serif; + font: normal 80%/100% sans-serif; color: #999999; } diff --git a/2-ui/2-events/04-default-browser-action/menu.view/menu.css b/2-ui/2-events/04-default-browser-action/menu.view/menu.css index 4c172e0515..32494ae9be 100644 --- a/2-ui/2-events/04-default-browser-action/menu.view/menu.css +++ b/2-ui/2-events/04-default-browser-action/menu.view/menu.css @@ -9,7 +9,7 @@ outline: none; text-align: center; text-decoration: none; - font: 14px/100% Arial, Helvetica, sans-serif; + font: 14px/100% sans-serif; padding: .5em 2em .55em; text-shadow: 0 1px 1px rgba(0, 0, 0, .3); border-radius: .5em; diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html index 8fbae4c790..c8e879f2ce 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html @@ -18,7 +18,7 @@ border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; - font: italic 14px/1.3 arial, sans-serif; + font: italic 14px/1.3 sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html index 8abb923d5e..2dc4394e74 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html @@ -18,7 +18,7 @@ border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; - font: italic 14px/1.3 arial, sans-serif; + font: italic 14px/1.3 sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css index f0736f3e35..fa2f09eba8 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css @@ -30,7 +30,7 @@ body { border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; - font: italic 14px/1.3 arial, sans-serif; + font: italic 14px/1.3 sans-serif; color: #333; background: #fff; z-index: 100000; diff --git a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/my.css b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/my.css index 10e9ddc205..67905e6d67 100644 --- a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/my.css +++ b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/my.css @@ -2,7 +2,7 @@ .edit { height: 150px; width: 400px; - font-family: arial; + font-family: sans-serif; font-size: 14px; display: block; } diff --git a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/my.css b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/my.css index 10e9ddc205..67905e6d67 100644 --- a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/my.css +++ b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/my.css @@ -2,7 +2,7 @@ .edit { height: 150px; width: 400px; - font-family: arial; + font-family: sans-serif; font-size: 14px; display: block; } diff --git a/archive/01-hello-world/1-hello-alert/solution.md b/archive/01-hello-world/1-hello-alert/solution.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/archive/01-hello-world/1-hello-alert/solution.view/index.html b/archive/01-hello-world/1-hello-alert/solution.view/index.html deleted file mode 100644 index 45e6744b3a..0000000000 --- a/archive/01-hello-world/1-hello-alert/solution.view/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/archive/01-hello-world/1-hello-alert/task.md b/archive/01-hello-world/1-hello-alert/task.md deleted file mode 100644 index afed6a91d3..0000000000 --- a/archive/01-hello-world/1-hello-alert/task.md +++ /dev/null @@ -1,12 +0,0 @@ -importance: 5 - ---- - -# Show an alert - -Create a page that shows a message "I'm JavaScript!". - -Do it in a sandbox, or on your hard drive, doesn't matter, just ensure that it works. - -[demo src="solution"] - diff --git a/archive/01-hello-world/article.md b/archive/01-hello-world/article.md deleted file mode 100644 index 4021e13732..0000000000 --- a/archive/01-hello-world/article.md +++ /dev/null @@ -1,89 +0,0 @@ -# Hello, world! - -大概 98% 的教程都是 JavaScript 核心,而且它们都是跨平台的。所以你可以学习如何使用 Node.Js 和其他基于 Js 的知识。 - -但是我们需要一些类似“基础环境”的东西来运行脚本,浏览器就是一个很不错的选择。 - -所以我们从在网页上添加脚本开始。对于其他环境例如 Node.JS,还有其他方式去运行它。 - -[TODO defer/async 转到第二部分?] - -## "script" 标签 - -[TODO 需要这个?并且特殊(也需要它?)] -```smart header="如果我希望进度更快点呢?" -如果你已经使用 JavaScript 开发过或者对其他语言很有经验,你可以跳过细节解释并跳到 ,在那里你可以找到重要特征的本质。 - -如果你有足够的时间希望深入学习,那么请继续看: -``` - -JavaScript 程序可以通过使用 -*/!* - -

...脚本运行后

- - - - -``` - -```online -你可以点击右上角的 "Play" 按钮运行这个示例程序。 -``` - -由 - ``` - - 这些注释会在浏览器无法解析 - - - - \ No newline at end of file diff --git a/archive/02-external-script/1-hello-alert-ext/solution.md b/archive/02-external-script/1-hello-alert-ext/solution.md deleted file mode 100644 index f42c41e6db..0000000000 --- a/archive/02-external-script/1-hello-alert-ext/solution.md +++ /dev/null @@ -1,8 +0,0 @@ -The HTML code: - -[html src="index.html"] - -For the file `alert.js` in the same folder: - -[js src="alert.js"] - diff --git a/archive/02-external-script/1-hello-alert-ext/task.md b/archive/02-external-script/1-hello-alert-ext/task.md deleted file mode 100644 index fe38de403e..0000000000 --- a/archive/02-external-script/1-hello-alert-ext/task.md +++ /dev/null @@ -1,9 +0,0 @@ -importance: 5 - ---- - -# Show an alert with an external script - -Take the solution of the previous task . Modify it by extracting the script content into an external file `alert.js`, residing in the same folder. - -Open the page, ensures that the alert works. diff --git a/archive/02-external-script/2-async-defer-first/solution.md b/archive/02-external-script/2-async-defer-first/solution.md deleted file mode 100644 index 0513e5fc05..0000000000 --- a/archive/02-external-script/2-async-defer-first/solution.md +++ /dev/null @@ -1,5 +0,0 @@ -Answers: - -1. The first is `big.js`, that's a normal sequence for external ` - -``` - -What if we add `async`? - -```html - - -``` - -What if we switch to `defer`? - -```html - - -``` - diff --git a/archive/02-external-script/article.md b/archive/02-external-script/article.md deleted file mode 100644 index 0de9bf907a..0000000000 --- a/archive/02-external-script/article.md +++ /dev/null @@ -1,251 +0,0 @@ -# 外部脚本 - -如果我们有很多 JavaScript 代码,我们可以将他们放在一个单独的文件里。 - -脚本文件可以被这样添加到 HTML 中: - -```html - -``` - - 这里的 `/path/to/script.js` 是一个脚本文件所在的绝对路径(从网站根目录) - -也可以提供一个当前页面的相对路径。例如:`src="script.js"` 意味着在当前文件夹的一个文件,名为 `"script.js"`。 - -我们也可以提供一个完整的 URL,例如: - -```html - -``` - -添加多个脚本,要使用多个标签: - -```html - - -… -``` - -```smart -作为规定,只有最简单的脚本才被直接写在 HTML 中。稍复杂的都要写在一个单独的文件中。 - -分离文件的好处是浏览器将会下载它并将其写入[缓存](https://en.wikipedia.org/wiki/Web_cache)。 - -在这之后,其他页面用到和这个相同的脚本的时候,将直接从缓存中读取而不用重新下载。所以实际上这个文件只需要被下载一次。 - -这样可以节省流量并提高页面加载速度。 -``` - -````warn header="如果 `src` 这个属性已被设置,脚本的内容将被忽略。" -一个单独的 ` -``` - -我们必须二选一:在外部 ` - -``` -```` - -## 异步脚本:defer/async - -浏览器逐渐加载并显示 HTML,当网速慢的时候可以很明显的被观察到。浏览器不会等到页面加载完毕,它显示已经被加载的部分,然后当新内容加载后再添加显示。 - -我们之前说过,当浏览器遇到一个 ` -*/!* - -

所有兔子都被计数!

- - - -``` - -这个行为叫做“同步”。通常来说这样不会产生问题,但有一种特殊的情况。 - -**如果一个脚本在外部,用户必须等到浏览器下载并执行完它。** - -所以,在这段代码中,直到 `big.js` 加载并执行,`` 的内容才得以显示。 - -```html - - -*!* - -*/!* - - - 这段文字直到浏览器执行完 big.js 才显示。 - - -``` - -问题是 — 我们真的希望用户等待直到脚本运行结束? - -大多数时间,我们不希望。 - -有时候,一个脚本可能包含着非常重要的脚本,必须在页面其他内容解析(之后执行的脚本)之前加载。但这也只是有时。 - - -通常,一个用户在脚本加载时应该可以看到页面。尤其是包含重要内容的网站(例如教程)— 即使一些接口还没被激活(脚本还没加载),用户也能阅读文字和导航。 - -在一些情况下,这些阻塞甚至是很危险的。例如,当我们想轮播中加一段脚本,或集成第三方代码。 -比如这样: - -```html run height=100 -等等,下面的文本只有在脚本执行后才会展示出来。 - - - - -

...重要信息

-``` - -页面的其余部分在加载轮播之前没有显示是错误的。如果他们的服务器过载,响应很慢怎么办?我们的用户将要等待更久,甚至离开去了其他更快的网站。 - -所以,怎么“解决”这个阻塞的问题? - -我们第一步尝试是将这些脚本放在 `` 的底部,在所有内容之后。 - -但是这种解决方法并不完美: - -1. 脚本只能在 HTML 加载完才能开始运行。如果 HTML 很大,这个延迟将会很明显。我们希望浏览器能尽早来加载脚本,还不会阻塞页面渲染。 -2. 如果在页面的底部有很多脚本,他们排起队来执行。浏览器一次只能执行一段 ` - -

...重要信息!

-``` - -现在如果我们执行它,我们将看到整个文档立即显示,并且当它加载的同时,外部脚本也在执行。 - -让我们看看更多关于 `defer` 和 `async` 的例子来更全面地了解他们的相似之处和不同。 - -这两个属性都允许浏览器不用等待脚本加载就显示页面。但... - -1. 延后的脚本保持了脚本的执行顺序,然而异步脚本没有。 - - 举个例子,在下面这段代码中(使用了 `async`)有两段脚本。先加载的将首先运行。 - - ```html - - - ``` - - 如果 `2.js` 比 `1.js` 大,就很有可能`2.js` 在 `1.js` 之前运行。这很正常,异步脚本是完全独立的。 - - 在下面这段使用了 `defer` 的代码中,强制了浏览器保持执行顺序。即使 `2.js` 现加载完毕,它也会在 `1.js` 之后执行。 - - ```html - - - ``` - - “延后”脚本的这个特性在 `2.js` 依赖 `1.js` 的结果时很重要,我们必须确定这个顺序是确定的。 - -2. 一个用了 `defer` 的脚本总是等 HTML 文档流完全加载后,`async` 脚本会在加载时立即执行。 - - 举个例子,当文档很大的时候,例如: - - ```html - - - - A long long text. Many words. - ... - ``` - - ...这里的 `async.js` 在加载时执行 — 肯能实在文本完全加载之前。 比较来说, `defer.js` 总是在等文档全部加载。 - - 所以,如果一个脚本不需要文档的剩余部分(比如web计数器),`async` 是更好的选择。另一种情况脚本可能需要整个文档才能工作,这样 `defer` 就更好。 - - -```smart header="`async` 或者 `defer`" -我们不能在一段脚本中同时使用 `defer` 和 `async`。如果这样的话,`async` 优先,`defer` 被忽略。 -``` - -```warn header="`async/defer` 属性只适用于外部脚本。" -`async/defer` 属性只有在带有 `src` 的脚本上才有效。 - -On a script without `src` like <script>...</script>, they will be ignored. -在没有 `src` 的脚本上设置 `async/defer` 属性无效,例如 <script>...</script>。 -``` - - -## 总结 - -- 外部脚本可以通过 ``插入页面。 -- 浏览器直到脚本执行完才能显示脚本以后的内容,除非脚本设置了 `async` 或 `defer` 属性。 --`async` 和 `defer` 属性允许浏览器开始加载脚同时继续渲染页面。他们只对外部脚本有效。 -- 不同点是 `defer` 保持了脚本的执行顺序,并总是在在文档完全加载之后,相反,`async` 脚本加载时就执行,没有任何条件。 - -在插入一条 ` -[/html] - -The `click` processing order: -
    -
  1. `alert('in onclick')` works.
  2. -
  3. The attribute is changed and the DOM mutation event is processed synchronously, immediately triggering `onchange`. It outputs `alert('onpropchange')`.
  4. -
  5. The rest of `onclick` executes, leading to `alert('out onclick')`.
  6. -
- - -### Nested DOM events are synchronous. - -[summary] -There are methods which trigger an immediate event, like `elem.focus()`. These events are also processed in a synchronous manner. -[/summary] - -Run the example below and click on the button. Notice that `onfocus` -doesn't wait `onclick` to complete, it works immediately. - -[html autorun height=auto] - - - - -[/html] - -In the example above, the alert order is in onclick->focus->out onclick, that clearly demonstrates the synchronous behavior. - -[smart] -The line labelled (*) is required, because `alert(message)` focuses on the message window. When it is disposed, the browser refocuses back. - -So without (*) the `focus` would be triggered one extra time after the alert. -[/smart] - -[summary] -Events are also processed immediately when triggered from JavaScript by `dispatchEvent/fireEvent`. -[/summary] - -Usually event handlers are executed one by one. So we assume that one handler finishes before the other starts. - -**Synchronous events break this one-by-one rule, that may can cause side-effects.** - -For example, the `onfocus` handler may assume that `onclick` has completed the job. - -There are two ways to fix it: -
    -
  1. Move `text.focus()` to the end of the `onclick` code.
  2. -
  3. Wrap `text.focus()` into `setTimeout(.., 0)`: -[js] -button.onclick = function() { - alert(1) - setTimeout(function() { text.focus() }, 0) - alert(2) -} -[/js] -
  4. -
- -The concrete way is chosen according to your architecture. - - -## JavaScript execution and rendering - -[summary]In most browsers, rendering and JavaScript use single event queue. It means that while JavaScript is running, no rendering occurs.[/summary] - -Check it on the demo below. **When you press `run`, the browser may halt for some time**, because it changes `div.style.backgroundColor` from #A00000 to #FFFFFF. - -In most browsers, you see nothing until the script finishes, or until the browser pauses it with a message that 'a script is running too long'. - -The exception is Opera. - -[html run] -
- - - - -[/html] - -In Opera, you may notice `div` is redrawn. Not every change causes a repaint, probably because of Opera internal scheduling. That's because event queues for rendering and JavaScript are different in this browser. - -In other browsers the repaint is postponed until the JavaScript finishes. - -Again, the implementation may be different, but generally **the nodes are marked as "dirty" (want to be recalculated and redrawn), and repaint is queued**. Or, the browser may just look for dirty nodes after every script and process them. - -[smart header="Immediate reflow"] -The browser contains many optimizations to speedup rendering and painting. Generally, it tries to postpone them until the script is finished, but some actions require nodes to be rerendered immediately. - -For example: -[js] -elem.innerHTML = 'new content' -alert(elem.offsetHeight) // <-- rerenders elem to get offsetHeight -[/js] - -In the case above, the browser has to perform relayouting to get the height. -But it doesn't have to repaint `elem` on the screen. - -Sometimes other dependant nodes may get involved into calculations. This process is called *reflow* and may consume lots of resources if script causes it often. - -Surely, there's much more to talk about rendering. It will be covered by a separate article [todo]. -[/smart] - - -## Modal and synchronous calls - -[summary]Modal and synchronous calls like `alert` pause the JavaScript thread. - -That causes related activities to freeze. -[/summary] - -The example below demonstrates it. - -
    -
  1. Press "Run". The `setInterval`-based animation will start and and `alert` button will appear.
  2. -
  3. Press the button, note that the animation stops.
  4. -
- - -[html run height=60] -
- - - -[/html] - -When you press `alert('Hello!')`, the `alert` blocks JavaScript execution and blocks the whole UI thread. That's how `alert`, `confirm` and `prompt` work. And there is only one thread. So, **`setTimeout/setInterval` can't execute while the thread is blocked.** - - -### Opera: `iframes` exception. - -[summary]Usually, `iframes` run in the same thread with the page. [/summary] - -But there is an exception called Opera. **Run the example above in Opera** and press alert in the main window. The iframe animation will continue! -That's because the example is actually running in an iframe. - -Other browsers use single thread for whole tab, so the iframe animation is paused there. - - - -## Script taking too long and heavy jobs - -JavaScript can be heavy. - -In this case, the browser may hangup for a moment or come with a warning "Script is taking too long". - -We'd want to evade that. It can be done by split the job into parts which get scheduled after each other. - -Then there is a "free time" for the browser to respond between parts. It is can render and react on other events. Both the visitor and the browser are happy. - -The background color in the example below is changed once per tick. So the browser has the time to render it, and there are no hangups. Changes are applied incrementally. - -Press the run button on the example to start. -[html run] -
- - - - - -[/html] - -The internal order: -
    -
  1. `setTimeout` appends the `func` call to the event queue.
  2. -
  3. The new call is scheduled on the next tick.
  4. -
  5. The func executes and changes the `div` which appends a repaint request to the queue.
  6. -
  7. The function finishes. The browser takes the next event from the queue which is repaint and executes it. Then it waits the next tick to execute one more `func` call (see step 2).
  8. -
  9. Repeated until `stop()`
  10. -
- -A delay may be increased from 0 to 100 ms, depending on your needs. The longer delay leads to less CPU load. - -[smart header="Evade the 'script is running for too long' warning"] -As an important side-effect, splitting the long job into parts which are executed by `setTimeout` helps to fix browser hangups and evade warnings. - -For example, modern syntax highlighters employ such technique. When a visitor opens a large text, they highlight a part of it then call something like `setTimeout(highlightNext, 50)` which highlights the next part etc. - -It would hangup otherwise, because the syntax highlighting takes time. -[/smart] - - - -## Summary - -Most browsers use single thread for UI and JavaScript, which is blocked by synchronous calls. So, JavaScript execution blocks the rendering. - -Events are processed asynchronously with the exception of DOM events. - -The `setTimeout(..,0)` trick is very useful. It allows to: -
    -
  • Let the browser render current changes.
  • -
  • Evade the "script is running too long" warning.
  • -
  • Change the execution flow.
  • -
- -Opera is special in many places when it comes to timeouts and threading. diff --git a/archive/2-eval-calculator-errors/solution.md b/archive/2-eval-calculator-errors/solution.md deleted file mode 100644 index c564d3ee19..0000000000 --- a/archive/2-eval-calculator-errors/solution.md +++ /dev/null @@ -1,34 +0,0 @@ -Вычислить любое выражение нам поможет `eval`: - -```js run -alert( eval("2+2") ); // 4 -``` - -Считываем выражение в цикле `while(true)`. Если при вычислении возникает ошибка -- ловим её в `try..catch`. - -Ошибкой считается, в том числе, получение `NaN` из `eval`, хотя при этом исключение не возникает. Можно бросить своё исключение в этом случае. - -Код решения: - -```js run demo -let expr, res; - -while (true) { - expr = prompt("Введите выражение?", '2-'); - if (expr == null) break; - - try { - res = eval(expr); - if (isNaN(res)) { - throw new Error("Результат неопределён"); - } - - break; - } catch (e) { - alert( "Ошибка: " + e.message + ", повторите ввод" ); - } -} - -alert( res ); -``` - diff --git a/archive/2-eval-calculator-errors/task.md b/archive/2-eval-calculator-errors/task.md deleted file mode 100644 index b317d52ea1..0000000000 --- a/archive/2-eval-calculator-errors/task.md +++ /dev/null @@ -1,14 +0,0 @@ -importance: 5 - ---- - -# Eval-калькулятор с ошибками [todo: NO EVAL YET] - -Напишите интерфейс, который принимает математическое выражение (в `prompt`) и результат его вычисления через `eval`. - -**При ошибке нужно выводить сообщение и просить переввести выражение**. - -Ошибкой считается не только некорректное выражение, такое как `2+`, но и выражение, возвращающее `NaN`, например `0/0`. - -[demo] - diff --git a/archive/2-events-and-timing-depth/article.md b/archive/2-events-and-timing-depth/article.md deleted file mode 100644 index 3254326fa1..0000000000 --- a/archive/2-events-and-timing-depth/article.md +++ /dev/null @@ -1,170 +0,0 @@ -# Порядок обработки событий - -События могут возникать не только по очереди, но и "пачкой" по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для `onclick` -- посетитель нажал кнопку на клавиатуре (событие `keydown`). - -Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила. - -## Главный поток - -В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM. - -Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как `alert`. - -```smart header="Дополнительные потоки тоже есть" -Есть и другие, служебные потоки, например, для сетевых коммуникаций. - -Поэтому скачивание файлов может продолжаться пока главный поток ждёт реакции на `alert`. Но управлять служебными потоками мы не можем. -``` - -```smart header="Web Workers" -Существует спецификация
Web Workers, которая позволяет запускать дополнительные JavaScript-процессы(workers). - -Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе. - -Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно. -``` - -## Очередь событий - -Произошло одновременно несколько событий или во время работы одного случилось другое -- как главному потоку обработать это? - -Если главный поток прямо сейчас занят, то он не может срочно выйти из середины одной функции и прыгнуть в другую. А потом третью. Отладка при этом могла бы превратиться в кошмар, потому что пришлось бы разбираться с совместным состоянием нескольких функций сразу. - -Поэтому используется альтернативный подход. - -**Когда происходит событие, оно попадает в очередь.** - -Внутри браузера непрерывно работает "главный внутренний цикл", который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п. - -**Иногда события добавляются в очередь сразу пачкой.** - -Например, при клике на элементе генерируется несколько событий: - -1. Сначала `mousedown` -- нажата кнопка мыши. -2. Затем `mouseup` -- кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется `click` (два события сразу). - -````online -В действии: - -```html autorun height=150 no-beautify - - - -``` -```` - -Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер обработает их строго одно за другим: `mousedown` -> `mouseup` -> `click`. - -При этом каждое событие из очереди обрабатывается полностью отдельно от других. - -## Вложенные (синхронные) события - -Обычно возникающие события "становятся в очередь". - -Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас. - -Рассмотрим в качестве примера событие `onfocus`. - -### Пример: событие onfocus - -Когда посетитель фокусируется на элементе, возникает событие `onfocus`. Обычно оно происходит, когда посетитель кликает на поле ввода, например: - -```html run height=80 autorun -

При фокусе на поле оно изменит значение.

- -``` - -Но ту же фокусировку можно вызвать и явно, вызовом метода `elem.focus()`: - -```html run - - - -``` - -В главе мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже. - -При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`. Код обработчика `onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения `onclick`. - -```html autorun height=80 no-beautify - - - - -``` - -При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`. - -```warn header="Исключение в IE" -Так ведут себя все браузеры, кроме IE. - -В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу. -``` - -## Делаем события асинхронными через setTimeout(...,0) - -А что, если мы хотим, чтобы *сначала* закончилась обработка `onclick`, а потом уже произошла обработка `onfocus` и связанные с ней действия? - -Можно добиться и этого. - -Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика `onclick`. - -Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так - -```html autorun height=80 - - - - -``` - -Такой вызов обеспечит фокусировку через минимальный "тик" таймера, по стандарту равный 4 мс. Обычно такая задержка не играет роли, а необходимую асинхронность мы получили. - -## Итого - -- JavaScript выполняется в едином потоке. Современные браузеры позволяют порождать подпроцессы Web Workers, они выполняются параллельно и могут отправлять/принимать сообщения, но не имеют доступа к DOM. -- Обычно события становятся в очередь и обрабатываются в порядке поступления, асинхронно, независимо друг от друга. -- Синхронными являются вложенные события, инициированные из кода. -- Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`. - -Отложенный вызов через `setTimeout(func, 0)` используется не только в событиях, а вообще -- всегда, когда мы хотим, чтобы некая функция `func` сработала после того, как текущий скрипт завершится. - diff --git a/archive/4-random-from-array/solution.md b/archive/4-random-from-array/solution.md deleted file mode 100644 index c07a93bc42..0000000000 --- a/archive/4-random-from-array/solution.md +++ /dev/null @@ -1,12 +0,0 @@ -We need to generate a random integer value from `0` to `arr.length-1`, and then take the element with that index. - -Here we go: - -```js run -let arr = ["Apple", "Orange", "Pear", "Lemon"]; - -let rand = Math.floor(Math.random() * arr.length); - -alert( arr[rand] ); -``` - diff --git a/archive/4-random-from-array/task.md b/archive/4-random-from-array/task.md deleted file mode 100644 index c40a10c19d..0000000000 --- a/archive/4-random-from-array/task.md +++ /dev/null @@ -1,11 +0,0 @@ -importance: 3 - ---- - -# A random array value - -Write the code to `alert` a random value from the array: - -```js -let arr = ["Apple", "Orange", "Pear", "Lemon"]; -``` diff --git a/archive/5-drag-and-drop-objects/article.md b/archive/5-drag-and-drop-objects/article.md deleted file mode 100644 index 3b8f331363..0000000000 --- a/archive/5-drag-and-drop-objects/article.md +++ /dev/null @@ -1,512 +0,0 @@ -# Мышь: Drag'n'Drop более глубоко - -В [предыдущей статье](/drag-and-drop) мы рассмотрели основы Drag'n'Drop. Здесь мы разберём дополнительные "тонкие места" и приёмы реализации, которые возникают на практике. - -Почти все javascript-библиотеки реализуют Drag'n'Drop так, как написано (хотя бывает что и менее эффективно). - -Зная, что и как, вы сможете легко написать свой код переноса или поправить, адаптировать существующую библиотеку под себя. - -Этот материал не строго обязателен для изучения, он специфичен именно для Drag'n'Drop. - -## Документ - -Как пример задачи -- возьмём документ с иконками браузера ("объекты переноса"), которые можно переносить в компьютер ("цель переноса"): - -- Элементы, которые можно переносить (иконки браузеров), помечены классом `draggable`. -- Элементы, на которые можно положить (компьютер), имеют класс `droppable`. - -```html - - - - - - -

Браузер переносить сюда:

- - -``` - -Работающий пример с переносом: - -[iframe border=1 src="dragDemo" height=280 link edit] - -Далее мы рассмотрим, как делается фреймворк для таких переносов, а в перспективе -- и для более сложных. - -Требования: - -- Поддержка большого количества элементов без "тормозов". -- Продвинутые возможности по анимации переноса. -- Удобная обработка успешного и неудачного переноса. - -## Начало переноса - -Чтобы начать перенос элемента, мы должны отловить нажатие левой кнопки мыши на нём. Для этого используем событие `mousedown`... И, конечно, делегирование. - -Переносимых элементов может быть много. В нашем документе-примере это всего лишь несколько иконок, но если мы хотим переносить элементы списка или дерева, то их может быть 100 штук и более. - -Поэтому повесим обработчик `mousedown` на контейнер, который содержит переносимые элементы, и будем определять нужный элемент поиском ближайшего `draggable` вверх по иерархии от `event.target`. - -В качестве контейнера здесь будем брать `document`, хотя это может быть и любой элемент. - -Найденный `draggable`-элемент сохраним в свойстве `dragObject.elem` и начнём двигать. - -Код обработчика `mousedown`: - -```js -let dragObject = {}; - -document.onmousedown = function(e) { - - if (e.which != 1) { // если клик правой кнопкой мыши - return; // то он не запускает перенос - } - - let elem = e.target.closest('.draggable'); - - if (!elem) return; // не нашли, клик вне draggable-объекта - - // запомнить переносимый объект - dragObject.elem = elem; - - // запомнить координаты, с которых начат перенос объекта - dragObject.downX = e.pageX; - dragObject.downY = e.pageY; -} -``` - -```warn header="Не начинаем перенос по `mousedown`" -Ранее мы по `mousedown` начинали перенос. - -Но на самом деле нажатие на элемент вовсе не означает, что его собираются куда-то двигать. Возможно, на нём просто кликают. - -Это важное различие. Снимать элемент со своего места и куда-то двигать нужно только при переносе. - -Чтобы отличить перенос от клика, в том числе -- от клика, который сопровождается нечаянным перемещением на пару пикселей (рука дрогнула), мы будем запоминать в `dragObject`, какой элемент (`elem`) и где (`downX/downY`) был зажат, а начало переноса будем инициировать из `mousemove`, если он передвинут хотя бы на несколько пикселей. -``` - -## Перенос элемента - -Первой задачей обработчика `mousemove` является инициировать начало переноса, если элемент передвинули в зажатом состоянии. - -Ну а второй задачей -- отображать его перенос при каждом передвижении мыши. - -Схематично, обработчик будет иметь такой вид: - -```js -document.onmousemove = function(e) { - if (!dragObject.elem) return; // элемент не зажат - - if (!dragObject.avatar) { // элемент нажат, но пока не начали его двигать - ...начать перенос, присвоить dragObject.avatar = переносимый элемент - } - - ...отобразить перенос элемента... -} -``` - -Здесь мы видим новое свойство `dragObject.avatar`. При начале переноса "аватар" делается из элемента и сохраняется в свойство `dragObject.avatar`. - -**"Аватар" -- это DOM-элемент, который перемещается по экрану.** - -Почему бы не перемещать по экрану сам `draggable`-элемент? Зачем, вообще, нужен аватар? - -Дело в том, что иногда сам элемент передвигать неудобно, например потому, что он слишком большой. А удобно создать некоторое визуальное представление элемента, и его уже переносить. Аватар дает такую возможность. - -А в простейшем случае аватаром можно будет сделать сам элемент, и это не повлечёт дополнительных расходов. - -### Визуальное перемещение аватара - -Для того, чтобы отобразить перенос аватара, достаточно поставить ему `position: absolute` и менять координаты `left/top`. - -Для использования абсолютных координат относительно документа, аватар должен быть прямым потомком `BODY`. - -Следующий код готовит аватар к переносу: - -```js -// в начале переноса: -if (avatar.parentNode != document.body) { - document.body.appendChild(avatar); // переместить в BODY, если надо -} -avatar.style.zIndex = 9999; // сделать, чтобы элемент был над другими -avatar.style.position = 'absolute'; -``` - -... А затем его можно двигать: - -```js -// при каждом движении мыши - -avatar.style.left = новая координата + 'px'; -avatar.style.top = новая координата + 'px'; -``` - -Как вычислять новые координаты `left/top` при переносе? - -Чтобы элемент сохранял свою позицию под курсором, необходимо при нажатии запомнить его изначальный сдвиг относительно курсора, и сохранять его при переносе. - -![](shiftx.png) - -Этот сдвиг по горизонтали обозначен `shiftX` на рисунке выше. Аналогично, есть `shiftY`. Они вычисляются как расстояние между курсором и левой/верхней границей элемента при `mousedown`. Детали вычислений описаны в главе . - -Таким образом, при `mousemove` мы будем назначать элементу координаты курсора с учетом сдвига `shiftX/shiftY`: - -```js -avatar.style.left = e.pageX - shiftX + 'px'; -avatar.style.top = e.pageY - shiftY + 'px'; -``` - -## Полный код mousemove - -Код `mousemove`, решающий задачу начала переноса и позиционирования аватара: - -```js -document.onmousemove = function(e) { - if (!dragObject.elem) return; // элемент не зажат - - if ( !dragObject.avatar ) { // если перенос не начат... - - // посчитать дистанцию, на которую переместился курсор мыши - let moveX = e.pageX - dragObject.downX; - let moveY = e.pageY - dragObject.downY; - if ( Math.abs(moveX) < 3 && Math.abs(moveY) < 3 ) { - return; // ничего не делать, мышь не передвинулась достаточно далеко - } - - *!*dragObject.avatar = createAvatar(e)*/!*; // захватить элемент - if (!dragObject.avatar) { - dragObject = {}; // аватар создать не удалось, отмена переноса - return; // возможно, нельзя захватить за эту часть элемента - } - - // аватар создан успешно - // создать вспомогательные свойства shiftX/shiftY - let coords = getCoords(dragObject.avatar); - dragObject.shiftX = dragObject.downX - coords.left; - dragObject.shiftY = dragObject.downY - coords.top; - - *!*startDrag(e)*/!*; // отобразить начало переноса - } - - // отобразить перенос объекта при каждом движении мыши - dragObject.avatar.style.left = e.pageX - dragObject.shiftX + 'px'; - dragObject.avatar.style.top = e.pageY - dragObject.shiftY + 'px'; - - return false; -} -``` - -Здесь используются две функции для начала переноса: `createAvatar(e)` и `startDrag(e)`. - -Функция `createAvatar(e)` создает аватар. В нашем случае в качестве аватара берется сам `draggable` элемент. После создания аватара в него записывается функция `avatar.rollback`, которая задает поведение при отмене переноса. - -Как правило, отмена переноса влечет за собой разрушение аватара, если это был клон, или возвращение его на прежнее место, если это сам элемент. - -В нашем случае для отмены переноса нужно запомнить старую позицию элемента и его родителя. - -```js -function createAvatar(e) { - - // запомнить старые свойства, чтобы вернуться к ним при отмене переноса - let avatar = dragObject.elem; - let old = { - parent: avatar.parentNode, - nextSibling: avatar.nextSibling, - position: avatar.position || '', - left: avatar.left || '', - top: avatar.top || '', - zIndex: avatar.zIndex || '' - }; - - // функция для отмены переноса - avatar.rollback = function() { - old.parent.insertBefore(avatar, old.nextSibling); - avatar.style.position = old.position; - avatar.style.left = old.left; - avatar.style.top = old.top; - avatar.style.zIndex = old.zIndex - }; - - return avatar; -} -``` - -Функция `startDrag(e)`, которую вызывает `mousemove`, если видит, что элемент в "зажатом" состоянии перенесли достаточно далеко, инициирует начало переноса и позиционирует аватар на странице: - -```js -function startDrag(e) { - let avatar = dragObject.avatar; - - document.body.appendChild(avatar); - avatar.style.zIndex = 9999; - avatar.style.position = 'absolute'; -} -``` - -## Окончание переноса - -Окончание переноса происходит по событию `mouseup`. - -Его обработчик можно поставить на аватаре, т.к. аватар всегда под курсором и `mouseup` происходит на нем. Но для универсальности и большей гибкости (вдруг мы захотим перемещать аватар *рядом* с курсором?) поставим его, как и остальные, на `document`. - -Задача обработчика `mouseup`: - -1. Обработать успешный перенос, если он идет (существует аватар) -2. Очистить данные `dragObject`. - -Это дает нам следующий код: - -```js -document.onmouseup = function(e) { - // (1) обработать перенос, если он идет - if (dragObject.avatar) { - *!*finishDrag(e)*/!*; - } - - // в конце mouseup перенос либо завершился, либо даже не начинался - // (2) в любом случае очистим "состояние переноса" dragObject - dragObject = {}; -} -``` - -Для завершения переноса в функции `finishDrag(e)` нам нужно понять, на каком элементе мы находимся, и если над `droppable` -- обработать перенос, а нет -- откатиться: - -```js -function finishDrag(e) { - let dropElem = *!*findDroppable(e)*/!*; - - if (dropElem) { - ... успешный перенос ... - } else { - ... отмена переноса ... - } -} -``` - -### Определяем элемент под курсором - -Чтобы понять, над каким элементом мы остановились -- используем метод [document.elementFromPoint(clientX, clientY)](https://developer.mozilla.org/en/DOM/document.elementFromPoint), который мы обсуждали в разделе [координаты](info:coordinates#elementFromPoint). Этот метод получает координаты *относительно окна* и возвращает самый глубокий элемент, который там находится. - -Функция `findDroppable(event)`, описанная ниже, использует его и находит самый глубокий элемент с атрибутом `droppable` под курсором мыши: - -```js -// возвратит ближайший droppable или null -*!* -// это предварительный вариант findDroppable, исправлен ниже! -*/!* -function findDroppable(event) { - - // взять элемент на данных координатах - let elem = document.elementFromPoint(event.clientX, event.clientY); - - // найти ближайший сверху droppable - return elem.closest('.droppable'); -} -``` - -Обратите внимание -- для `elementFromPoint` нужны координаты относительно окна `clientX/clientY`, а не `pageX/pageY`. - -Вариант выше -- предварительный. Он не будет работать. Если попробовать применить эту функцию, будет все время возвращать один и тот же элемент! А именно -- *текущий переносимый*. Почему так? - -...Дело в том, что в процессе переноса под мышкой находится именно аватар. При начале переноса ему даже `z-index` ставится большой, чтобы он был поверх всех остальных. - -**Аватар перекрывает остальные элементы. Поэтому функция `document.elementFromPoint()` увидит на текущих координатах именно его.** - -Чтобы это изменить, нужно либо поправить код переноса, чтобы аватар двигался *рядом* с курсором мыши, либо дать аватару стиль `pointer-events:none` (кроме IE10-), либо: - -1. Спрятать аватар. -2. Вызывать `elementFromPoint`. -3. Показать аватар. - -Напишем функцию `findDroppable(event)`, которая это делает: - -```js -function findDroppable(event) { - // спрячем переносимый элемент - dragObject.avatar.hidden = true; - - // получить самый вложенный элемент под курсором мыши - let elem = document.elementFromPoint(event.clientX, event.clientY); - - // показать переносимый элемент обратно - dragObject.avatar.hidden = false; - - if (elem == null) { - // такое возможно, если курсор мыши "вылетел" за границу окна - return null; - } - - return elem.closest('.droppable'); -} -``` - -## DragManager - -Из фрагментов кода, разобранных выше, можно собрать мини-фреймворк. - -Объект `DragManager` будет запоминать текущий переносимый объект и отслеживать его перенос. - -Для его создания используем не обычный синтаксис `{...}`, а вызов `new function`. Это позволит прямо при создании объявить дополнительные переменные и функции в замыкании, которыми могут пользоваться методы объекта, а также назначить обработчики: - -```js no-beautify -let DragManager = new function() { - - let dragObject = {}; - - let self = this; // для доступа к себе из обработчиков - - function onMouseDown(e) { ... } - function onMouseMove(e) { ... } - function onMouseUp(e) { ... } - - document.onmousedown = onMouseDown; - document.onmousemove = onMouseMove; - document.onmouseup = onMouseUp; - - this.onDragEnd = function(dragObject, dropElem) { }; - this.onDragCancel = function(dragObject) { }; -} -``` - -Всю работу будут выполнять обработчики `onMouse*`, которые оформлены как локальные функции. В данном случае они ставятся на `document` через `on...`, но это легко поменять на `addEventListener`. - -Код функции `onMouse*` мы подробно рассмотрели ранее, но вы сможете увидеть их в полном примере ниже. - -Внутренний объект `dragObject` будет содержать информацию об объекте переноса. - -У него будут следующие свойства, которые также разобраны выше: - -`elem` -: Текущий зажатый мышью объект, если есть (ставится в `mousedown`). - -`avatar` -: Элемент-аватар, который передвигается по странице. - -`downX/downY` -: Координаты, на которых был клик `mousedown` - -`shiftX/shiftY` -: Относительный сдвиг курсора от угла элемента, вспомогательное свойство вычисляется в начале переноса. - -Задачей `DragManager` является общее управление переносом. Что же касается действий при его окончании -- их должен назначить внешний код, который использует `DragManager`. - -Можно сделать это через вспомогательные методы `onDrag*`, которые устанавливаются внешним кодом и затем вызываются фреймворком. Разработчик, подключив `DragManager`, описывает в этих методах, что делать при завершении или отмене переноса. Конечно же, можно заменить методы `onDrag*` на генерацию "своих" событий. - -С использованием `DragManager` пример, с которого начиналась эта глава -- перенос иконок браузеров в компьютер, реализуется совсем просто: - -```js no-beautify -DragManager.onDragEnd = function(dragObject, dropElem) { - - // скрыть/удалить переносимый объект - dragObject.elem.hidden = true; - - // успешный перенос, показать улыбку классом computer-smile - dropElem.className = 'computer computer-smile'; - - // убрать улыбку через 0.2 сек - setTimeout(function() { - dropElem.classList.remove('computer-smile'); - }, 200); -}; - -DragManager.onDragCancel = function(dragObject) { - // откат переноса - dragObject.avatar.rollback(); -}; -``` - -Полный пример с кодом: - -[codetabs src="dragDemo" height=280] - -## Расширения - -Существует масса возможных применений Drag'n'Drop. Здесь мы не будем реализовывать их все, поскольку не стоит цель создать фреймворк-монстр. - -Однако, мы рассмотрим их, чтобы, при необходимости, легко было написать то, что нужно. - -### Захватывать элемент можно только за "ручку" - -Часто бывает, что перенос должен быть инициирован только при захвате за определённую зону элемента. К примеру, модальное окно можно "взять", только захватив его за заголовок. - -Для этого достаточно добавить необходимую проверку, к примеру, в функцию `createAvatar` или перед её запуском. - -Если `mousedown` был внутри элемента, помеченного, к примеру, классом `draghandle`, то начинаем перенос, иначе -- нет. - -### Проверка прав на droppable - -Бывает и так, что не на любое место в `droppable` можно положить элемент. - -Например: в админке есть дерево всех объектов сайта: статей, разделов, посетителей и т.п. - -- В этом дереве есть узлы различных типов: "статьи", "разделы" и "пользователи". -- Все узлы являются переносимыми объектами. -- Узел "статья" (draggable) можно переносить в "раздел" (droppable), а узел "пользователи" -- нельзя. Но и то и другое можно поместить в "корзину". - -Здесь решение: "можно или нет" переносить или нельзя зависит от "типа" переносимого объекта. - -Есть и более сложные варианты, когда решение зависит от конкретного места в `droppable`, над которым посетитель отпустил кнопку мыши. К примеру, переносить в верхнюю часть можно, а в нижнюю -- нет. - -Эта задача решается добавлением проверки в `findDroppable(e)`. Эта функция знает и об аватаре и о событии, включая координаты. При попытке положить в "неправильное" место функция `findDroppable(e)` должна возвращать `null`. - -Однако, на практике бывают ситуации, когда решение "прямо сейчас" принять невозможно. Например, нужно сделать запрос на сервер: "А разрешено ли текущему посетителю производить такую операцию?" - -Как при этом должен вести себя интерфейс? Можно, конечно сделать, чтобы элемент после отпускания кнопки мыши "завис" над `droppable`, ожидая ответа. Однако, такое решение неудобно в реализации и странновато выглядит для посетителя. - -Как правило, применяют "оптимистичный" алгоритм, по которому мы считаем, что перенос обычно успешен, но при необходимости можем отменить его. - -При нём посетитель кладет объект туда, куда он хочет, а затем, в коде `onDragEnd`: - -1. Визуально обрабатывается завершение переноса, как будто все ок. -2. Производится асинхронный запрос к серверу, содержащий информацию о переносе. -3. Сервер обрабатывает перенос и возвращает ответ, все ли в порядке. -4. Если нет -- выводится ошибка и возвращается `avatar.rollback()`. Аватар в этом случае должен предусматривать возможность отката после успешного завершения. - -Процесс общения с сервером сопровождается индикацией загрузки и, при необходимости, блокировкой новых операций переноса до получения подтверждения. - -### Подсветка текущего droppable - -Удобно, когда пользователь во время переноса наглядно видит, куда он сейчас положит draggable. Например, текущий droppable (или его часть) подсвечиваются. - -Для этого в `DragManager` можно добавить дополнительные методы интеграции с внешним кодом: - -- `onDragEnter` -- будет вызываться при заходе на `droppable`, из `onMouseMove`. -- `onDragMove` -- при каждом передвижении внутри `droppable`, из `onMouseMove`. -- `onDragLeave` -- при выходе с `droppable`, из `onMouseMove` и `onMouseUp`. - -Возможен более сложный вариант, когда нужно поддерживать не только перенос *в элемент*, но и перенос *между элементами*, например вставку одной статьи между двумя другими. - -Для этого код, который обрабатывает перенос, может "делить на части" droppable, к примеру, в отношении 25% - 50% - 25%, и смотреть: - -- Если перенос в верхнюю четверть, то это -- "над". -- Если перенос в середину, то это "внутрь". -- Если перенос в нижнюю четверть, то это -- "под". - -Текущий `droppable` и позиция относительно него при этом могут помечаться подсветкой и жирной чертой над/под, если требуется. - -Пример индикации из Firefox: -![](between.png) - -### Анимация отмены переноса - -Отмену переноса и возврат аватара на место можно красиво анимировать. - -Один из частых вариантов -- скольжение объекта обратно к исходному месту, откуда его взяли. Для этого достаточно поправить `avatar.rollback()`. - -## Итого - -Уточнённый алгоритм Drag'n'Drop: - -1. При `mousedown` запомнить координаты нажатия. -2. При `mousemove` инициировать перенос, как только зажатый элемент передвинули на 3 пикселя или больше. Сообщить во внешний код вызовом `onDragStart`. При этом: -
    -3. Создать аватар, если можно начать перенос элемента `draggable` с данной позиции курсора. -4. Переместить аватар по экрану, установив его новую позицию из `e.pageX/pageY` с учетом изначального сдвига элемента относительно курсора. -5. Сообщить во внешний код о текущем `droppable` под курсором и позиции над ним вызовами `onDragEnter`, `onDragMove`, `onDragLeave`. -6. При `mouseup` обработать завершение переноса. Элемент под аватаром получить по координатам, предварительно спрятав аватар. Сообщить во внешний код вызовом `onDragEnd`. - -Получившаяся реализация Drag'n'Drop проста, эффективна, изящна. - -Её очень легко поменять или адаптировать под "особые" потребности. - -ООП-вариант фреймворка находится в статье . diff --git a/archive/5-drag-and-drop-objects/between.png b/archive/5-drag-and-drop-objects/between.png deleted file mode 100644 index 4001dff0494d44058c7cecb231e0fb6030b3b171..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2977 zcmV;S3tsezP)O9 zaj34Unb^|33M6Xs^6qh-+5i9l0001si`C`0MaROgSy-V z@$vD|(b1BIs_W#`l(zZ){{FbUylR)y@9pHp#p=-G|H9Y*qs0IB_Vc#U|6+~En7aJ` z?t1O_|N8p)|Nr*<{QB?r|NsB4xzhiJdzt0`U5CHV&CHp+{7`MdLt?mn zn%s`5<$|2soW1&kqvuU)zmKWP|L}VM?tEc_(xIiyR8yRoo6P_2gVL!w|Np1ar8VNU zMF0Gvv8}B8`Stkt_W%FCj@;9(V9v<9 z^Upxp(6!~_+1i_@M{@`ou%Sr#~W!KEB#GE(P(Z>JdO{ke~?!q|e=HC3; zL!_XMl#_$Kmqp0KtNiJonwh}H&F9e9?Q?UpZ*H)Bd%1Xbwrgpv{rI`vwN%>I(aOfW zz`eN9yo&$ubgzs)ysLn)jX%rDy{3|6xuS2JqN4ow=kMH{gElYX000PFNkl*5g|my7BMD9QBlE)1$#y8aEdJ|YK#Jc6u}r+Q_!9Ng^{cF4TLWsf~@zWrx*=I!ji>%mn6c`yjWLUi>J_55<0 zA$-JTPoEd5hL{@`7WP8eOHx35y3z^pOkG~X>JEbgLmfn~@bG@&uZ(TmcI@lneS3)z z04NJeAiDRCY#rHp^r$goMvab)?A@J8h$ek&^BVc)Yj3?I4apT(#j%bZQC`AVH&o~*ya1h;|8#pj3Ds95sZ*_b*D(cy8oLp3~ z*kTN12%zE_4x&&0A??~d(mt(y`*%Xx4e8$pfsj7~MFJSII<*)Y(jcv4T7wY&)gWYO z3(ivO@#-Xh84_Ydty&%5t0ij$0wFbQLc1^;#PHD2(BR-sp`C*Hui)U}RCaa~Z$UJX z4aFG*gV^Y&swSlniGKATRRlfUe*uckO8_$sal?)*dUJVTqAP9oU;^QzkF*!TwTLr=h zBcw_o8VXV&va+TmejfeB)TE?MD1_qcl;|i+*%<+NJG8;&yFh>_6}?WW3bAQxw4klY zn~~}S@Svz8bfCHpc!29B_a{IMswZfW1!B|Y#RqwY!yOWqAdyh^uq6}<9#Dtsmi7bG zvb)^<2@nJB7c{^Ev3beV!_ix#w#4mf}!4AnA z6MpG@cb$+BA3rZ0J;ah5Ho#AQ0QrmpaF_cd_1Nj_Wri*3 zdOJH!&2#&(WBq!8VcxtgTheRNI(GW3rYdHN7zBf;t`JSVVC{^f62gnhNML>V5pPkA z8r?1^&`bve=Jk$vbDRkAG%pzf00X%_D-ZzzK=pc_-dh7qP`#cS2@M3mmY6lN)&IF95~$bnln$|NBvl9jDul4^ z|J2Q{>L+9fS)}TuY2?5j|A!nMT-9UO^MrL`jnfW|&N@!BQ7nUC5ITn9n5cyCl)nA_ zgrwzkLSUj2!enKgDraYETvpat7CjJ{von>Jm6bjy=-pBihYU^)?(CphJX36JY(T6e znjnx_JX1_jQH*h)Nr_D;=F(JvITt5RT3sh(jh-hnSyW5DcQaLKrL;PalNAa`ALR7%LY~ zCxp{;@f?~v(+NS!#dD+^22~IVVlJKog#lGjoyfU(9FnnvT;_#O>=8eKuM>usn#3SS zAV|4*@Wp1x%n@NGhQr;RCix1Ge-ls9asa|gE*_eS2U$BLPtrOB<`1>L(ek%AWEx!` zgq2)8eCE!|o&>ghg5(=_dfFRywrOK|)G={UQ5{Sy3 z9k7~s#{tb)5Fa_(o1f=IXU7p;Bx?2G&fjSowywLgxX9VL z)W|VsR}F^Zx7s5H!K#z(TIVb3-+yR^@O@O!r@rf`hM3bz&;cnRRnDRpV#bUakMnv+ zlrNnSYww1%*0<{XkOL62KE4amN3*DeSULMKNV8We&P}=G&&_UIAH=c`TJoASdD5iG zEho2pe;J1jq1}A%8+{P-m)RZ`BXwIoOTDKX$z@@n7*QO9YLM>SDnr)rc((q zbM>8&R?p;=lNOq+O#?%$>hejKprD{G;#W}6Dx!}wI>gD_5S^qFqFM~ajRwI)wf0Ls zV#i*ZArxn~pK1uy+3lwk0(Exk7C;dM=Ik^O2#Z0%aA#L>fei|QJ3FbY0Ei25DZdq$ z(wgCfJ3D2619@p#0S1l)?(9eqVoZz-fjc`Zk?=4y1n%sptWLPI!yC2-(y%>@*|2eE zN6S$a(RS3?ZKsie$rbvadF&lK=!ZCd?AUSIAu@BmJDrnBGepX%oSb#7?mM&c`|T;z zLZqHLJ!kf`v-$aZcBN7ak&(IUz%OSn7Vg=Vn*p7u1T;aU{M3B!`TUE8h3EIie3(Kh zL~425x#{^=uNKZc7gwH&KtPvJYMyt1CeaXb z|M4ry@XIa%{L}zKCLMJC+`$JY*aDGqqO|zZ?|&5+|M}~s;!LSNAz5`5LO>fdIdV>V zaDpumsXy|Il500_UMt~Oen^EO(A}neQ~-Qu4pNSsQy!dP3q(fT?t+5yKdxUdDK9A4 z-82J(02M-TWC*7|IKdVO|E9byTrr!kT;NyY{Iw06(_VI|Nk!$fIdaZ=aDq(`alH2J zUbCiTpMc|E;fI}e?6u<$*#z+bB7Z7-Y=2B_&W=Ga2nN9*7zBf05DbDrFbD=wZ6N*w XA2} - - - - - - - - - - - - -
    - - - - - -
    - -

    Браузер переносить сюда:

    - -
    -
    - - - - \ No newline at end of file diff --git a/archive/5-drag-and-drop-objects/shiftx.png b/archive/5-drag-and-drop-objects/shiftx.png deleted file mode 100644 index 52d49dd466d687e2544376a79e1aea1e538e7dc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1382 zcmZ|Pc`(}v6bJBMrH*x^rMk3@vvHKQSZqVH*4dO~LKx{{PwS3{39o0J@u8Z_DXcW3r9^WOKp|KGflP&C3o_qZ+q00u$l z0?w=R&VK2D)LqY$4g-J#icnNopn3%W72whUq*?;G00;!q($dmV%hEA0FaU$WhIxi& zY87VYYL(^|78aHiODiiYC=_aAV`FP;YiDO?Z*PC<)F}rC2S-OoCnqNt7Z*1-H+Oe; z7z_r7!+rLle0&H#sa3wdzJ7jw{{H>}0Re%5NCX0TZXYT*I2egUqEIL_8XXc6651My z!C=>r1;gM))ZLqo&E!wd#vbaZrVY;0m;f;Gotv)M1_ zUrtU=PEAcsf12hjad2K^CA7en)Bo8-^Iw%ijf>KCfheKy3aa9^k-^zh@ zQ^UZK_P3asTzf}6uJw{a8FCuo3jU!y=yk&VFA2OP9#K)h+~-X69*_9&$s!Q&zI9wB zBQt$}CXf4OAiifUv3p>DZKvunA|Ua*KfSZ}*Y@J%fzum7W+I>e8@Ih$3Cp@=u4r(f zDXEqXRGYd&V(;A;*U1nZ!1Pk?(WtYmi?N&s%Er>RhHzXug@3oU0Akg&QBrUMqF8x^~ zV&;L@*^l-fVl8M`T!)Df<>>QAP+7L5k~+Qh!uv6YTf-&n;*XJ|pRl<#RytrvK0ZQG z?~m2-JDIW4<=72Ncx_T(4Er2pYxX=r?`avMZtn!^MWoZrR)5kZ0^~z=aPou^R{rey zqvp=JYY+JzVr@F_NZ&DJ22*bVw<{yX&~9HxdX!M%@w&mY@rNu`6sp$*6bwPQYij;J z4=!Xu_!&D3z0^bQnxe}5^Q<{f?M&l|5Dx?~d*wT+q(caMXyz4#y`EOwcaouDzN?|R zX)>_w>1}wo1+jV!>=T}lVQ0b`*+2xtTMRUqoN90VcIkb`HQ{PMJ(aoD``=}-DpN9n~a z*_9Di+i{PfS_Wv{*_1+tuVj(M3;h{q{wTqkkxx%w^lu2~UrkGw5|s)PDMGW4{`S@S zZQHm^O8v><=6LA5IsC9tXe#-WgzhaPX~UF)Y5K#Yj-e4bZEU;kW8~DmHzQnEZjQ42 zSe;;H!}32@r|O6UvA>VffilUYSf%;>L}*ekMIDi-+Aphd!ot3asp!K?p}bGfP` XQCUBhw;ScV?*Gf6Ky*N-A3pzI=xL&c diff --git a/archive/backticks.md b/archive/backticks.md deleted file mode 100644 index 1f1dd5cdb7..0000000000 --- a/archive/backticks.md +++ /dev/null @@ -1,51 +0,0 @@ - -Backticks allow to specify a "template function" before the first backtick. - -The syntax is: -```js -let str = func`my string`; -``` - -The function `func` is automatically called and receives the array of string pieces split by `${...}` embeds and the list of embedded `${...}` values. - -It's better to see the example: - -```js run -function love(s, v1, v2) { - alert( s[0] ); // Hello: - alert( s[1] ); // and - alert( s[2] ); // ! - - alert( v1 ); // Ilya - alert( v2 ); // Julia - - alert( s.raw[0] ); // Hello:\n - - return v1 + ' ♥ ' + v2; -} - -let mom = "Julia"; -let dad = "Ilya"; - -// s[0] | v1 | s[1] | v2 | s[2] -let str = love`Hello:\n ${mom} and ${dad}!`; - -alert(str); // 'Julia ♥ Ilya' -``` - -The string inside love`string` is split into pieces by embeds. The result is passed to `love`: - -- The first parameter `s` stores the pieces, here `s = ["Hello:\n ", " and ", "!"]`. -- The special proeprty `s.raw` contains unparsed values, the special characters are not processes. -- Then follow the values of embeds: `v1 = mom`, `v2 = data`. - -Templating functions allow to - - - - -- The first parameter `strings` is a - -In the example above, `love` is the name for the function. It is called with an array - - diff --git a/archive/commend.md b/archive/commend.md deleted file mode 100644 index acc2c27ee0..0000000000 --- a/archive/commend.md +++ /dev/null @@ -1,22 +0,0 @@ -### Don't hesitate to comment - -Please, don't hesitate to comment. - -Comments increase the overall code footprint, but that's not a problem at all, because there are many tools which minify the code before publishing to production server. They remove comments, so they do not appear in the working scripts. - -The code clarity is what we should hunt for. And comments can really help to make it easier to understand. - -There are various types of comments, answering different questions: - -- What the code does? -- Why the code is written like that? -- Which counter-intuitive or implicit connections it has with other parts of the program? - -Further in the tutorial we'll make more notes about how to write the code better, easier to read and maintain. We'll also talk more about comments. - -```smart header="The good code is inherently readable and self-commenting" -Please note that the first type of comments ("what the code does") should be used to describe a "high-level" action, like the overall architecture, a function or a chunk of code. It's purpose is to give an overview, so a reader doesn't need to delve into the code and figure out. - -Novice programmers sometimes tend to elaborate too much. Please don't. The good code is inherently readable. No need to describe what few lines do. Unless it's something hard to grasp, and *then* it's worth to consider rewriting the code at the first place rather than commenting it. -``` - diff --git a/archive/descriptors.md b/archive/descriptors.md deleted file mode 100644 index 14f1100911..0000000000 --- a/archive/descriptors.md +++ /dev/null @@ -1,37 +0,0 @@ - - -## Property descriptors - -An object property is actually a more complex and tunable thing than just a "key-value" mapping. - -There are two kinds of properties. - -The first is *data properties*. - -They assiciate a key with the attributes: - -- **`value`** -- the value of the property. -- **`writable`** -- if `true`, can be changed, otherwise it's read-only. -- **`enumerable`** -- if `true`, then listed in loops, otherwise not listed. -- **`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not. - -All properties that we've seen yet were data properties. - -By default when a property is created, all attributes are `true`. - -We can get the the information about an existing property using [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor): - - -```js run -let user = { - name: "John" -}; - -let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); - -alert( descriptor.value ); // John -alert( descriptor.writable ); // true -alert( descriptor.enumerable ); // true -alert( descriptor.configurable ); // true -``` - diff --git a/archive/nfe.md b/archive/nfe.md deleted file mode 100644 index 7f7e2044b4..0000000000 --- a/archive/nfe.md +++ /dev/null @@ -1,140 +0,0 @@ - -## Named Function Expression - -Named Function Expression or, shortly, NFE, is a term a Function Expression that has a name. - -For instance, let's take an ordinary Function Expression: - -```js -let sayHi = function(who) { - alert(`Hello, ${who}`); -}; -``` - -...And add a name to it: - -```js -let sayHi = function *!*func*/!*(who) { - alert(`Hello, ${who}`); -}; -``` - -What's the role of that additional `"func"` name? - -First let's note, that we're still having a Function Expression. Adding the name `"func"` after `function` did not make it a Function Declaration, because it is still created as a part of an assignment expression. - -Adding such a name also did not break anything. - -The function is still available as `sayHi()`: - -```js -//+ run -let sayHi = function *!*func*/!*(who) { - alert(`Hello, ${who}`); -}; - -sayHi("John"); // Hello, John -``` - -There are two special things about the name `func`: -
      -
    1. It allows to reference the function from inside itself.
    2. -
    3. It is not visible outside of the function.
    4. -
    - -For instance, the function `sayHi` below re-calls itself with `"Guest"` if no `who` is provided: - -```js -//+ run -let sayHi = function *!*func*/!*(who) { - if (who) { - alert(`Hello, ${who}`); - } else { -*!* - func("Guest"); // use func to re-call itself -*/!* - } -}; - -sayHi(); // Hello, Guest - -// But this won't work: -func(); // Error, func is not defined (not visible outside of the function) -``` - -Later we'll see more cases when a function wants to re-call itself with modified parameters. - -So, why won't we use `sayHi` for the nested call? - -Actually, in most cases we can: - -```js -let sayHi = function(who) { - if (who) { - alert(`Hello, ${who}`); - } else { -*!* - sayHi("Guest"); -*/!* - } -}; -``` - -The problem with the latter code is that the value of `sayHi` may change. The function may go to another variable: - -```js -//+ run -let sayHi = function *!*func*/!*(who) { - if (who) { - alert(`Hello, ${who}`); - } else { -*!* - func("Guest"); // (*) -*/!* - } -}; - -let welcome = sayHi; -sayHi = null; - -welcome(); // Hello, Guest (still works under different name) -``` - -Using `func` in the line `(*)` *guarantees* that this exactly function will be called. Using `sayHi` there - - -...But the name `func` is not visible outside of the function: - -```js -//+ run -let sayHi = function *!*func*/!*(who) { - // ... -}; - -*!* -func(); // Error: func is not defined -*/!* -``` - -Named Function Expressions are used in rare cases when: -
      -
    • The function needs to reference itself from inside. For instance, call itself with another arguments.
    • -
    • The function travels between variables.
    • -
    - -The code below demonstrates both conditions: - -```js -//+ run -let sayHi = function *!*func*/!*(name) { - if (name) { - alert(`Hello, ${name}!`) - } else { - func("Guest"); - } -}; - -welcome(); // Works -``` - - diff --git a/archive/promise/async-then.js b/archive/promise/async-then.js deleted file mode 100644 index 87150473de..0000000000 --- a/archive/promise/async-then.js +++ /dev/null @@ -1,60 +0,0 @@ - -````smart header="Handlers are always asynchronous, like `setTimeout(...,0)`" -Handlers assigned by `.then/catch` are always asynchronous. But if the promise is settled they run as soon as possible, similar to `setTimeout(...,0)`. - -Here's the example to demonstrate that: - -```js run -// the promise is immediately resolved with "done!" -let promise = new Promise(resolve => resolve("done!")); - -// alert runs asynchronously, like if it were wrapped in setTimeout(..., 0); -promise.then(alert); // (2) - -alert('code end'); // (1) -``` - -We'll first see "code end" `(1)`, and then "done!" `(2)`. - -Like if we had this instead of `(2)`: -```js -promise.then(result => setTimeout(() => alert(result), 0)); -``` - -The inner mechanics is like this: -- Promise handlers form a queue. -- When the current code is complete, the queue shifts and the next handler is executed. -```` - -````smart header="Functions resolve/reject accept at most one argument" -Functions `resolve/reject` accept only one argument. - -We can call them without any arguments too, that's the same as passing `undefined`: - -```js run -let promise = new Promise(resolve => resolve()); - -promise.then(alert); // undefined -``` - -...But if we pass many arguments: `resolve(1, 2, 3)`, then all arguments after the first one are ignored. -The idea is that a promise may have only one result (or an error). Use objects and destructuring if you need to pass many values, like this: - -```js run -let promise = new Promise(function(resolve, reject) { - let name = "John"; - let age = 25; - - resolve({name, age}); // "pack" the values in an object -}); - -// destructuring -promise.then(function({name, age}) { - // here we have name and age variables as if the promise had two results - alert(`${name} ${age}`); // John 25 -}) -``` - -```` - - diff --git a/archive/promise/fetch.js b/archive/promise/fetch.js deleted file mode 100644 index b4719af836..0000000000 --- a/archive/promise/fetch.js +++ /dev/null @@ -1,37 +0,0 @@ -## Example: fetch - - -## Example: fetch - -Promises are also returned by some built-in browser methods. - -For instance, [fetch](https://fetch.spec.whatwg.org/#fetch-method) allows to load arbitrary content over the network. We can use it to send information to the server and load data from it. - -The syntax is: - -```js -let promise = fetch(url[, options]); -``` - -Here: -- `url` is the URL to load, -- `options` are various request options. - -There are many options, full information about them is in the [spec](https://fetch.spec.whatwg.org/#requestinit), here we won't need them. - -The promise returned by `fetch` resolves with the `response` object when the server responds. - -The most important properties of `response` are: -- `status` -- HTTP-code: 200 for OK, 404 for "Page not found" etc. -- `headers` -- HTTP headers. -- - -The important thing - - -```js run -fetch('/article/promise-chaining/1.html?speed=1') - .then(console.log); -``` - - diff --git a/archive/promise/thenable.js b/archive/promise/thenable.js deleted file mode 100644 index b72c0871e7..0000000000 --- a/archive/promise/thenable.js +++ /dev/null @@ -1,23 +0,0 @@ -function loadScript(src) { - return new Promise(function(resolve, reject) { - let script = document.createElement('script'); - script.src = src; - - script.onload = () => resolve(script); - script.onerror = () => reject(new Error("Script load error: " + src)); - - document.head.append(script); - }); -} -loadScript("/article/promise-chaining/one.js") - .then(script => { - return { - then(resolve, reject) { - setTimeout(() => resolve(script), 1000); - } - }; - }) - .then(function(script) { - alert(one); - }); - diff --git a/archive/promise/thenable.md b/archive/promise/thenable.md deleted file mode 100644 index 7f528f1390..0000000000 --- a/archive/promise/thenable.md +++ /dev/null @@ -1,25 +0,0 @@ -````smart header="Thenables" -To be precise, any object that has a method `.then` is treated as a promise here. So we can use custom "promise-compatible" objects in the chain. Such objects are called "thenable". - -Here's an example: - -```js run -class Thenable { - constructor(result, delay) { - this.result = result; - } - then(resolve, reject) { - setTimeout(() => resolve(this.result * 2), delay); - } -}; - -new Promise(resolve => resolve(1)) - .then(result => { - return new Thenable(result, 1000); - }) - .then(alert); // shows 2 after 1000ms -``` - -That allows to use custom implementations of promises from 3rd-party libraries along with native promises. -```` - diff --git a/archive/promise/thenable2.js b/archive/promise/thenable2.js deleted file mode 100644 index 3b72aaf3e7..0000000000 --- a/archive/promise/thenable2.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -new Promise() - .then(result => { - return { - then(resolve, reject) { - setTimeout(() => resolve("two"), 1000); - } - }; - }) - .then(result => { - console.log("HERE", result); - return result; - }) - .then(console.log) - .catch(err => console.error("ERROR", err)); - -// https://tc39.github.io/ecma262/#sec-promise-resolve-functions - diff --git a/archive/promise/whereto.txt b/archive/promise/whereto.txt deleted file mode 100644 index 2100b9b3b8..0000000000 --- a/archive/promise/whereto.txt +++ /dev/null @@ -1,58 +0,0 @@ - -## Extending promises, thenables - -Promises are very simple by design. One of the thing they miss is the ability to cancel the process. - -For instance, `loadScript(src)` in previous examples returns a promise that allows to track success/failure of the loading. But can we abort it? No. - -We can inherit from `Promise` to introduce such functionality, like this: - - -```js run -function loadScript(src) { - let script = document.createElement('script'); - alert(1); - script.src = src; - - let promise = new Promise(function(resolve, reject) { - script.onload = () => { alert('onload'); resolve(script); } -*!* - script.onerror = err => reject(new Error("Script load error: " + src)); // (*) -*/!* - }); - - alert(2); - - document.head.append(script); - alert(3); - promise.abort = () => { - script.src = 'javascript:"" - '; - }; - return promise; -} - -let promise = loadScript("/article/promise-chaining/one.js?speed=0"); -promise.then(alert); -promise.abort(); -alert(4); -``` - - - - - - - -## Inheriting from promise, thenables, promise api, async/await - --------- - - -An object that has a method called `.then` is called a "thenable". - -Instead of checking if something is `instanceof Promise`, we should usually check it for being thenable, and if it is, then treat it as a promise ("duck typing"). - -JavaScript specification also checks the value returned by a handler for being a thenable, not exactly a promise, when it decides whether to pass it along the chain or wait for the result. So in the examples above we could use custom thenables instead of `Promise` instances. - -For instance, native promises give no way to "abort" the execution. The `loadScript` above cannot "cancel" script loading, just because there's no `.abort` method on promises, we can only listen for the state change using `.then/catch`. diff --git a/archive/proto/user.js b/archive/proto/user.js deleted file mode 100644 index e79d0b422a..0000000000 --- a/archive/proto/user.js +++ /dev/null @@ -1,16 +0,0 @@ -```js run -function User(name, birthday) { - let age = calcAge(); - - function calcAge() { - new Date().getFullYear() - birthday.getFullYear(); - } - - this.sayHi = function() { - alert(name + ', age:' + age); - }; -} - -let user = new User("John", new Date(2000,0,1)); -user.sayHi(); // John -``` diff --git a/archive/proto/wrong/javascript-prototype-confusion-and.html b/archive/proto/wrong/javascript-prototype-confusion-and.html deleted file mode 100644 index abc8f71a16..0000000000 --- a/archive/proto/wrong/javascript-prototype-confusion-and.html +++ /dev/null @@ -1,1353 +0,0 @@ - - - - - - - - - - - - - - - - - - - -Nowhere Near Ithaca: Javascript "Prototype" Confusion, and the Dubiously Useful "instanceof" - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    - - -
    - -
    -
    - -

    -Javascript "Prototype" Confusion, and the Dubiously Useful "instanceof" -

    -
    -
    -
    -
    - - - - - - - - - -
    - - - -

    Recently, I became aware that I was more confused than I realized regarding javascript's use of the word "prototype", as the word is used in two different ways. -

    -

    This draft note is the result of pondering on this a bit more. This pondering also resulted in some clarification (for me) of how the instanceof operator seems to work, and how its utility is doubtful. -

    - - - - - - -
    -Every object has a prototype, accessed via its "__proto__" property -
    - -

    Javascript objects are just bags of named things: properties. Properties can be other objects, functions (which are also objects), or primitives like numbers and strings.

    - -

    Every javascript object has a prototype, which can be accessed directly in most modern browsers via the named property __proto__.

    - -

    -When a property named foo is requested from an object obj via obj.foo, first the set of properties for object itself is searched for the property. If it is not found, then obj.__proto__ is searched for a property named foo. If not found there, then it searches obj.__proto__.__proto__, continuing up the "prototype chain" until it either finds the property or obj.__proto__.__proto__....__proto__ is null, and undefined is returned. -Note that the javascript engine is supposed to prevent cyclic loops in the prototype chain. -

    - - -

    Again: the name of the property for the prototype of an object is not "prototype": its name is "__proto__".

    - -

    That is one part of the potential confusion.

    - - - -
    -Every javascript function f has a property named "prototype" - which is not its prototype -
    - - -

    This is another part of the potential confusion.

    - -

    -f.prototypeis not the function's - prototype. -

    - -

    -As for any object, f.__proto__ is the function's prototype. -

    - -
    -What is the purpose of f.prototype? -
    - -

    Any function f can be used to generate new objects via newObject = new f(args). -When used this way, f is sometimes called the "constructor" for newObject. - -

    - -

    When an object is created from a function f via newObject = new f(args), the new object's prototype __proto__ -property is set to the property named "prototype" of f: -newObject.__proto__ = f.prototype .

    - -

    That is what f.prototype is used for.

    - -

    Note that there are other ways to create an object with a specified prototype, and in fact these other methods are recommended by some folks, avoiding the "new" keyword altogether.

    - -
    -Aside: The "constructor" Property -
    - -

    While every function can be used as a constructor, there is also a property named "constructor" for some objects.

    - -

    -In particular, for any function f, f.prototype.constructor = f. -

    - -

    Since objects created from f via new f(args) have their "__proto__" property initialized to f.prototype, this means that for any object newObject created via newObject = new f(args) for some function f, newObject.constructor will refer to the function used to create the object. Initially, at least, as you can change the constructor later. -

    - -

    Other than a record of the object's ancestry that might be useful in a handful of cases (that I've never encountered), I don't know how the knowledge of the constructor would be generally useful.

    - - -
    - What this has to do with how javascript's instanceof works -
    - -

    Javascript has an instanceof operator that returns true or false, and can be called like so: -

    obj instanceof f
    -
    -

    You can only use a function on the right side: instanceof will throw an error if f is not a function.

    - -

    The Mozilla Developer network summarizes this operator quite succinctly (I have used "function" instead of "constructor", since every function can be a constructor): - -

    -The instanceof operator tests whether an object has in its prototype chain the prototype property of a given function. -
    - -

    - - - - - - - - -

    This is illustrated in the figure below.

    - - - - - -
    - -
    -(obj instanceof f -) is true -if f.prototype is equal to -
    -any __proto__ in -the prototype chain of obj -
    - -
    Do People Use instanceof?
    - -

    Answer: I think few do.

    - - - -

    When queried on twitter, both Eric Elliot and Kyle Simpson (two javascript folks I think a lot of) tweeted back that they never did. When I asked on hacker news, one commenter said it was a code smell, while another said it was useful when checking if an object is an array (it should be noted that jquery checks the result of a toString call to determine if something is an array).

    - -

    There are also problems with trying to use instanceof across iframes.

    - -

    @kangax discourages its use (2009). He also recommends the same method jquery uses to check if something is an array, btw.

    - -

    Crockford has remarked (in 2003) about the useless instanceof operator (link via kangax's article).

    - -

    So, I don't foresee using it much myself, but it is personally satisfying to come to a better understanding of it and the surrounding javascript constructs.

    - - - - - - -
    -And be Warned... -
    - - -

    Compared to "standard" OOP that can result in rigid, iron-like structures that complicate change and reuse, javascript with its prototype-based reuse pattern is as firm as a jello: you can change things bit more willy-nilly, if you so desire.

    - -

    In particular, you can muck with the prototype and constructor properties after-the-fact, and this would likely invalidate the notes above in whatever curious ways you (or the code you are using) has mucked with things.

    - - - -
    -
    -
    - -
    - -
    -
    - -
    - -
    -
    - -Newer Post - - -Older Post - -Home -
    -
    -
    - -
    - -
    -

    Popular Posts

    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/archive/recursion.md b/archive/recursion.md deleted file mode 100644 index 4b142bd486..0000000000 --- a/archive/recursion.md +++ /dev/null @@ -1,283 +0,0 @@ -# Recursion, stack - -It's ok for a function to rely on other functions for sub-tasks. - -We've already been using it when `welcome()` relied on `alert()` call to show the message. - -In particular, a function can make a sub-call *itself*. That's called *a recursion*. - -We'll fiddle around that advanced case to better learn how functions work. - -[smart header="A topic you already know?"] -Recursion is a general programming term. If you are already familiar with it, then you may list the page to the next chapter. Please read on if you are new to functions or just prefer not to skip parts. -[/smart] - -## Divide and conquer - -Divide and conquer (or "divide and rule") is an ancient strategy attributed to Philip II of Macedon and mentioned by the Roman emperor Julius Caesar, the French emperor Napolen and many other leaders. - -They used it to rule, but the similar thing is often done in programming. If we have a complex task, that we're not sure how to solve as a whole, then maybe we can split it and try to solve the smaller parts. - -And if smaller parts are still too big, then let's cut them and try to solve even smaller parts. - -Repeat the cutting until the parts are so small that it's obvious how to deal with them. - -Sounds good? Now let's explore a case of this approach. - -## Eating an elephant - -Let's say we have a task "to eat an elephant". But how? - -The well-known humorous solution is: "piece by piece". We cut a piece, eat it and repeat the process. Sooner or later, the elephant is eaten. - -The algorithm can be reflected in the code: - -``` -function eat(elephant) { - cut a piece from the elephant - eat it - if (something left) { -*!* - eat(what's left of the elephant) // (*) -*/!* - } -} -``` - -The recursion occurs at line `(*)`: function `eat` after making things simpler passes the remaining data again to itself. - -Of course that's only one variant. For example, we could cut the elephant in 2 halves, then cut the halves in half again etc, until the parts are small enough to eat. Then put them into plates and pass to the guests. - -The core idea is the same: splitting the problem into multiple subproblems and doing the same with them if they're still big. - -## A power of a number - - - -## Степень pow(x, n) через рекурсию - -В качестве первого примера использования рекурсивных вызовов -- рассмотрим задачу возведения числа `x` в натуральную степень `n`. - -Её можно представить как совокупность более простого действия и более простой задачи того же типа вот так: - -```js -pow(x, n) = x * pow(x, n - 1) -``` - -То есть, xn = x * xn-1. - -Например, вычислим `pow(2, 4)`, последовательно переходя к более простой задаче: - -
      -
    1. `pow(2, 4) = 2 * pow(2, 3)`
    2. -
    3. `pow(2, 3) = 2 * pow(2, 2)`
    4. -
    5. `pow(2, 2) = 2 * pow(2, 1)`
    6. -
    7. `pow(2, 1) = 2`
    8. -
    - -На шаге 1 нам нужно вычислить `pow(2,3)`, поэтому мы делаем шаг 2, дальше нам нужно `pow(2,2)`, мы делаем шаг 3, затем шаг 4, и на нём уже можно остановиться, ведь очевидно, что результат возведения числа в степень 1 -- равен самому числу. - -Далее, имея результат на шаге 4, он подставляется обратно в шаг 3, затем имеем `pow(2,2)` -- подставляем в шаг 2 и на шаге 1 уже получаем результат. - -Этот алгоритм на JavaScript: - -```js -//+ run -function pow(x, n) { - if (n != 1) { // пока n != 1, сводить вычисление pow(x,n) к pow(x,n-1) - return x * pow(x, n - 1); - } else { - return x; - } -} - -alert( pow(2, 3) ); // 8 -``` - -Говорят, что "функция `pow` *рекурсивно вызывает сама себя*" до `n == 1`. - -Значение, на котором рекурсия заканчивается называют *базисом рекурсии*. В примере выше базисом является `1`. - -Общее количество вложенных вызовов называют *глубиной рекурсии*. В случае со степенью, всего будет `n` вызовов. - -Максимальная глубина рекурсии в браузерах ограничена, точно можно рассчитывать на `10000` вложенных вызовов, но некоторые интерпретаторы допускают и больше. - -Итак, рекурсию используют, когда вычисление функции можно свести к её более простому вызову, а его -- ещё к более простому, и так далее, пока значение не станет очевидно. - -## Контекст выполнения, стек - -Теперь мы посмотрим, как работают рекурсивные вызовы. Для этого мы рассмотрим, как вообще работают функции, что происходит при вызове. - -**У каждого вызова функции есть свой "контекст выполнения" (execution context).** - -Контекст выполнения -- это служебная информация, которая соответствует текущему запуску функции. Она включает в себя локальные переменные функции и конкретное место в коде, на котором находится интерпретатор. - -Например, для вызова `pow(2, 3)` из примера выше будет создан контекст выполнения, который будет хранить переменные `x = 2, n = 3`. Мы схематично обозначим его так: - -
      -
    • Контекст: { x: 2, n: 3, строка 1 }
    • -
    - -Далее функция `pow` начинает выполняться. Вычисляется выражение `n != 1` -- оно равно `true`, ведь в текущем контексте `n=3`. Поэтому задействуется первая ветвь `if` : - -```js -function pow(x, n) { - if (n != 1) { // пока n != 1 сводить вычисление pow(x,n) к pow(x,n-1) -*!* - return x * pow(x, n - 1); -*/!* - } else { - return x; - } -} -``` - -Чтобы вычислить выражение `x * pow(x, n-1)`, требуется произвести запуск `pow` с новыми аргументами. - -**При любом вложенном вызове JavaScript запоминает текущий контекст выполнения в специальной внутренней структуре данных -- "стеке контекстов".** - -Затем интерпретатор приступает к выполнению вложенного вызова. - -В данном случае вызывается та же `pow`, однако это абсолютно неважно. Для любых функций процесс одинаков. - -Для нового вызова создаётся свой контекст выполнения, и управление переходит в него, а когда он завершён -- старый контекст достаётся из стека и выполнение внешней функции возобновляется. - -Разберём происходящее с контекстами более подробно, начиная с вызова `(*)`: - -```js -//+ run -function pow(x, n) { - if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1) - return x * pow(x, n - 1); - } else { - return x; - } -} - -*!* -alert( pow(2, 3) ); // (*) -*/!* -``` - -
    -
    `pow(2, 3)`
    -
    Запускается функция `pow`, с аргументами `x=2`, `n=3`. Эти переменные хранятся в контексте выполнения, схематично изображённом ниже: - -
      -
    • Контекст: { x: 2, n: 3, строка 1 }
    • -
    -Выполнение в этом контексте продолжается, пока не встретит вложенный вызов в строке 3. -
    -
    `pow(2, 2)`
    -
    В строке `3` происходит вложенный вызов `pow` с аргументами `x=2`, `n=2`. Текущий контекст сохраняется в стеке, а для вложеннного вызова создаётся новый контекст (выделен жирным ниже): - -
      -
    • Контекст: { x: 2, n: 3, строка 3 }
    • -
    • Контекст: { x: 2, n: 2, строка 1 }
    • -
    -Обратим внимание, что контекст включает в себя не только переменные, но и место в коде, так что когда вложенный вызов завершится -- можно будет легко вернуться назад. - -Слово "строка" здесь условно, на самом деле, конечно, запомнено более точное место в цепочке команд. -
    -
    `pow(2, 1)`
    -
    Опять вложенный вызов в строке `3`, на этот раз -- с аргументами `x=2`, `n=1`. Создаётся новый текущий контекст, предыдущий добавляется в стек: -
      -
    • Контекст: { x: 2, n: 3, строка 3 }
    • -
    • Контекст: { x: 2, n: 2, строка 3 }
    • -
    • Контекст: { x: 2, n: 1, строка 1 }
    • -
    -На текущий момент в стеке уже два старых контекста. -
    -
    Выход из `pow(2, 1)`.
    -
    При выполнении `pow(2, 1)`, в отличие от предыдущих запусков, выражение `n != 1` будет равно `false`, поэтому сработает вторая ветка `if..else`: - -```js -function pow(x, n) { - if (n != 1) { - return x * pow(x, n - 1); - } else { -*!* - return x; // первая степень числа равна самому числу -*/!* - } -} -``` - -Здесь вложенных вызовов нет, так что функция заканчивает свою работу, возвращая `2`. Текущий контекст больше не нужен и удаляется из памяти, из стека восстанавливается предыдущий: - -
      -
    • Контекст: { x: 2, n: 3, строка 3 }
    • -
    • Контекст: { x: 2, n: 2, строка 3 }
    • -
    -Возобновляется обработка внешнего вызова `pow(2, 2)`. -
    -
    Выход из `pow(2, 2)`.
    -
    ...И теперь уже `pow(2, 2)` может закончить свою работу, вернув `4`. Восстанавливается контекст предыдущего вызова: -
      -
    • Контекст: { x: 2, n: 3, строка 3 }
    • -
    -Возобновляется обработка внешнего вызова `pow(2, 3)`. -
    -
    Выход из `pow(2, 3)`.
    -
    Самый внешний вызов заканчивает свою работу, его результат: `pow(2, 3) = 8`.
    -
    - -Глубина рекурсии в данном случае составила: **3**. - -Как видно из иллюстраций выше, глубина рекурсии равна максимальному числу контекстов, одновременно хранимых в стеке. - -Обратим внимание на требования к памяти. Рекурсия приводит к хранению всех данных для неоконченных внешних вызовов в стеке, в данном случае это приводит к тому, что возведение в степень `n` хранит в памяти `n` различных контекстов. - -Реализация возведения в степень через цикл гораздо более экономна: - -```js -function pow(x, n) { - let result = x; - for let i = 1; i < n; i++) { - result *= x; - } - return result; -} -``` - -У такой функции `pow` будет один контекст, в котором будут последовательно меняться значения `i` и `result`. - -**Любая рекурсия может быть переделана в цикл. Как правило, вариант с циклом будет эффективнее.** - -Но переделка рекурсии в цикл может быть нетривиальной, особенно когда в функции, в зависимости от условий, используются различные рекурсивные подвызовы, когда ветвление более сложное. - -## Итого - -Рекурсия -- это когда функция вызывает сама себя, как правило, с другими аргументами. - -Существуют много областей применения рекурсивных вызовов. Здесь мы посмотрели на один из них -- решение задачи путём сведения её к более простой (с меньшими аргументами), но также рекурсия используется для работы с "естественно рекурсивными" структурами данных, такими как HTML-документы, для "глубокого" копирования сложных объектов. - -Есть и другие применения, с которыми мы встретимся по мере изучения JavaScript. - -Здесь мы постарались рассмотреть происходящее достаточно подробно, однако, если пожелаете, допустимо временно забежать вперёд и открыть главу [](/debugging-chrome), с тем чтобы при помощи отладчика построчно пробежаться по коду и посмотреть стек на каждом шаге. Отладчик даёт к нему доступ. - - - -[head] - -[/head] - diff --git a/archive/timing-decorator.md b/archive/timing-decorator.md deleted file mode 100644 index b412f5c416..0000000000 --- a/archive/timing-decorator.md +++ /dev/null @@ -1,105 +0,0 @@ - - -## Instrumentation: timing decorator - -Let's say we have a function and want to measure time it takes to run. - -Of course, we could modify it. Add something like `timerStart()` to the start and `timerEnd()` to all exit points. Then later we may want to log which arguments it receives and results. That requires additional code as well. - -That is called [instrumentation](https://en.wikipedia.org/wiki/Instrumentation) of the code -- adding stuff to measure times, log what's happening and do other watching/measuring without interfering with the main functionality. - -Putting instrumentation inside the function manually is not pretty at all. It takes space, shadows the core functionality and makes it harder to debug. - -There are tools that analyze javascript code and add instrumentation calls to it automatically. But here we'll take a "median" approach. We will "wrap" instrumentation over function without touching its code. - -What we going to do is a special function `timingDecorator(func)`, that takes a function `func` and returns a "wrapper" around it, that transfers all calls to `func` and measures time they take. - -For simplicity let's assume that `func` has only one argument. - -The code with `timingDecorator` and example function: - - - - -```js run -function fibo(n) { // a function to measure, here we count fibonacci number - return (n > 2) ? fibo(n - 1) + fibo(n - 2) : 1; -} - -let timers = {}; // timers to store data - -function timingDecorator(func) { - return function(x) { - let start = performance.now(); - - let result = func(x); - - let time = performance.now() - start; - - if (!timers[func.name]) timers[func.name] = 0; - timers[func.name] += time; - - return result; - }; -} - -// decorate it -fibo = timingDecorator(fibo); - -// run -alert( fibo(10) ); // 55 -alert( fibo(20) ); // 6765 -alert( fibo(30) ); // 832040 - -alert( `Total time: ${timers.fibo.toFixed(3)}ms` ); // total count of fibo calls -``` - -При помощи декоратора `timingDecorator` мы сможем взять произвольную функцию и одним движением руки прикрутить к ней измеритель времени. - -Его реализация: - -```js run -let timers = {}; - -// прибавит время выполнения f к таймеру timers[timer] -function timingDecorator(f, timer) { - return function() { - let start = performance.now(); - - let result = f.apply(this, arguments); // (*) - - if (!timers[timer]) timers[timer] = 0; - timers[timer] += performance.now() - start; - - return result; - } -} - -// функция может быть произвольной, например такой: -let fibonacci = function f(n) { - return (n > 2) ? f(n - 1) + f(n - 2) : 1; -} - -*!* -// использование: завернём fibonacci в декоратор -fibonacci = timingDecorator(fibonacci, "fibo"); -*/!* - -// неоднократные вызовы... -alert( fibonacci(10) ); // 55 -alert( fibonacci(20) ); // 6765 -// ... - -*!* -// в любой момент можно получить общее количество времени на вызовы -alert( timers.fibo + 'мс' ); -*/!* -``` - -Обратим внимание на строку `(*)` внутри декоратора, которая и осуществляет передачу вызова: - -```js -let result = f.apply(this, arguments); // (*) -``` - -Этот приём называется "форвардинг вызова" (от англ. forwarding): текущий контекст и аргументы через `apply` передаются в функцию `f`, так что изнутри `f` всё выглядит так, как была вызвана она напрямую, а не декоратор.