From e1948130f62d42b031d27f57ef7baa4763787979 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Wed, 14 Jan 2015 10:23:45 +0300 Subject: [PATCH] renovations --- .../10-arguments-pseudoarray/article.md | 24 +- 1-js/4-data-structures/11-datetime/article.md | 75 ++-- .../12-typeof-duck-typing/article.md | 26 +- .../11-array-unique/solution.md | 2 +- .../9-array-iteration/article.md | 70 ++-- .../9-array-iteration/reduce.svg | 64 ++++ .../1-global-object/article.md | 31 +- .../2-closures/article.md | 229 ++++++------- .../3-scope-new-function/article.md | 57 ++-- .../4-closures-module/article.md | 304 ----------------- .../1-closure-sum/solution.md | 0 .../1-closure-sum/task.md | 0 .../2-stringbuffer/_js.view/solution.js | 0 .../2-stringbuffer/_js.view/test.js | 0 .../2-stringbuffer/solution.md | 0 .../2-stringbuffer/task.md | 0 .../_js.view/solution.js | 0 .../_js.view/test.js | 0 .../3-stringbuffer-with-clear/solution.md | 0 .../3-stringbuffer-with-clear/task.md | 0 .../4-sort-by-field/solution.md | 0 .../4-sort-by-field/task.md | 0 .../_js.view/solution.js | 0 .../_js.view/source.js | 0 .../_js.view/test.js | 0 .../5-filter-through-function/solution.md | 0 .../5-filter-through-function/task.md | 0 .../6-make-army/_js.view/solution.js | 0 .../6-make-army/_js.view/source.js | 0 .../6-make-army/_js.view/test.js | 0 .../6-make-army/solution.md | 0 .../6-make-army/task.md | 0 .../article.md | 4 +- .../5-closures-module/article.md | 316 +++++++++++++++++ .../hello-conflict.view/hello.js} | 0 .../hello-conflict.view}/index.html | 2 +- .../hello-module.view/hello.js} | 0 .../hello-module.view}/index.html | 2 +- .../6-memory-management/article.md | 323 +++++------------- .../family-ext-nofatherlink-nohusband.png | Bin 38813 -> 0 bytes .../6-memory-management/family-ext-nolink.png | Bin 60039 -> 0 bytes .../6-memory-management/family-ext.png | Bin 12302 -> 0 bytes .../6-memory-management/family-no-family.svg | 59 ++++ .../family-no-father-2.svg | 37 ++ .../6-memory-management/family-no-father.svg | 51 +++ .../family-nofatherlink-junk-cleanup.png | Bin 9393 -> 0 bytes .../family-nofatherlink-junk.png | Bin 36889 -> 0 bytes .../family-nofatherlink.png | Bin 9604 -> 0 bytes .../6-memory-management/family.png | Bin 10526 -> 0 bytes .../6-memory-management/family.svg | 59 ++++ 1-js/5-functions-closures/7-with/article.md | 21 +- .../7-with/with_obj_size.png | Bin 9594 -> 0 bytes .../1-object-methods/article.md | 54 +-- .../1-array-equals-string/solution.md | 0 .../1-array-equals-string/task.md | 0 .../2-tostring-valueof/solution.md | 0 .../2-tostring-valueof/task.md | 0 .../3-compare-empty-arrays/solution.md | 0 .../3-compare-empty-arrays/task.md | 0 .../solution.md | 0 .../task.md | 0 .../5-sum-many-brackets/solution.md | 0 .../5-sum-many-brackets/task.md | 0 .../2-object-conversion}/article.md | 17 +- .../1-two-functions-one-object/solution.md | 0 .../1-two-functions-one-object/task.md | 0 .../_js.view/solution.js | 0 .../2-calculator-constructor/_js.view/test.js | 0 .../2-calculator-constructor/solution.md | 0 .../2-calculator-constructor/task.md | 0 .../3-accumulator/_js.view/solution.js | 0 .../3-accumulator/_js.view/test.js | 0 .../3-accumulator/solution.md | 0 .../3-accumulator/task.md | 0 .../_js.view/solution.js | 0 .../4-calculator-extendable/_js.view/test.js | 0 .../4-calculator-extendable/solution.md | 0 .../4-calculator-extendable/task.md | 0 .../article.md | 24 +- .../1-replace-property-getter/solution.md | 49 +++ .../1-replace-property-getter/task.md | 32 ++ .../4-descriptors-getters-setters/article.md | 79 ++--- .../1-objects-counter/_js.view/solution.js | 0 .../1-objects-counter/_js.view/test.js | 0 .../1-objects-counter/solution.md | 0 .../1-objects-counter/task.md | 0 .../article.md | 2 +- .../1-rewrite-sum-arguments/solution.md | 0 .../1-rewrite-sum-arguments/task.md | 0 .../_js.view/solution.js | 0 .../_js.view/test.js | 0 .../solution.md | 0 .../task.md | 0 .../{4-call-apply => 6-call-apply}/article.md | 62 ++-- .../1-cross-browser-bind/solution.md | 0 .../1-cross-browser-bind/task.md | 0 .../2-write-to-object-after-bind/solution.md | 0 .../2-write-to-object-after-bind/task.md | 0 .../3-second-bind/solution.md | 0 .../{5-bind => 7-bind}/3-second-bind/task.md | 0 .../solution.md | 0 .../4-function-property-after-bind/task.md | 0 .../5-question-use-bind/solution.md | 0 .../5-question-use-bind/task.md | 0 .../6-ask-currying/solution.md | 0 .../{5-bind => 7-bind}/6-ask-currying/task.md | 0 .../{5-bind => 7-bind}/article.md | 37 +- .../1-logging-decorator/_js.view/solution.js | 0 .../1-logging-decorator/_js.view/test.js | 0 .../1-logging-decorator/solution.md | 0 .../1-logging-decorator/task.md | 0 .../_js.view/solution.js | 0 .../_js.view/test.js | 0 .../2-logging-decorator-arguments/solution.md | 0 .../2-logging-decorator-arguments/task.md | 0 .../3-caching-decorator/_js.view/solution.js | 0 .../3-caching-decorator/_js.view/test.js | 0 .../3-caching-decorator/solution.md | 0 .../3-caching-decorator/task.md | 0 .../{6-decorators => 8-decorators}/article.md | 51 ++- 1-js/6-objects-more/index.md | 2 +- .../article.md | 4 +- .../1-serialize-object/solution.md | 0 .../1-serialize-object/task.md | 0 .../2-serialize-object-circular/solution.md | 0 .../2-serialize-object-circular/task.md | 0 1-js/7-js-misc/{3-json => 2-json}/article.md | 16 +- .../1-output-numbers-100ms/solution.md | 0 .../1-output-numbers-100ms/task.md | 0 .../solution.md | 0 .../2-output-numbers-100ms-settimeout/task.md | 0 .../3-highlight-tactics/solution.md | 0 .../3-highlight-tactics/task.md | 0 .../4-settimeout-result/solution.md | 0 .../4-settimeout-result/task.md | 0 .../5-setinterval-result/solution.md | 0 .../5-setinterval-result/task.md | 0 .../6-who-runs-faster/solution.md | 0 .../6-who-runs-faster/task.md | 0 .../7-delay/_js.view/solution.js | 0 .../7-delay/_js.view/test.js | 0 .../7-delay/solution.md | 0 .../7-delay/task.md | 0 .../8-debounce/_js.view/solution.js | 0 .../8-debounce/_js.view/test.js | 0 .../8-debounce/solution.md | 0 .../8-debounce/task.md | 0 .../9-throttle/_js.view/solution.js | 0 .../9-throttle/_js.view/test.js | 0 .../9-throttle/solution.md | 0 .../9-throttle/task.md | 0 .../article.md | 20 +- .../setInterval-anim.view/index.html | 0 .../setinterval-interval.svg | 51 +++ .../settimeout-interval.svg | 62 ++++ .../1-eval-calculator/solution.md | 0 .../1-eval-calculator/task.md | 0 1-js/7-js-misc/{5-eval => 4-eval}/article.md | 26 +- .../4-setTimeout-setInterval/interval1.png | Bin 4636 -> 0 bytes .../4-setTimeout-setInterval/timeout.png | Bin 2404 -> 0 bytes .../1-finally-ili-prosto-kod/solution.md | 0 .../1-finally-ili-prosto-kod/task.md | 0 .../2-eval-calculator-errors/solution.md | 0 .../2-eval-calculator-errors/task.md | 0 .../{6-exception => 5-exception}/article.md | 192 +++++++---- 1-js/8-oop/1-about-oop/article.md | 13 +- .../2-internal-external-interface/article.md | 48 +-- 1-js/8-oop/3-getters-setters/article.md | 11 +- .../1-replace-property-getter/solution.md | 27 -- .../1-replace-property-getter/task.md | 22 -- 170 files changed, 1496 insertions(+), 1161 deletions(-) create mode 100644 1-js/4-data-structures/9-array-iteration/reduce.svg delete mode 100644 1-js/5-functions-closures/4-closures-module/article.md rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/1-closure-sum/solution.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/1-closure-sum/task.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/2-stringbuffer/_js.view/solution.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/2-stringbuffer/_js.view/test.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/2-stringbuffer/solution.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/2-stringbuffer/task.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/3-stringbuffer-with-clear/_js.view/solution.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/3-stringbuffer-with-clear/_js.view/test.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/3-stringbuffer-with-clear/solution.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/3-stringbuffer-with-clear/task.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/4-sort-by-field/solution.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/4-sort-by-field/task.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/5-filter-through-function/_js.view/solution.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/5-filter-through-function/_js.view/source.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/5-filter-through-function/_js.view/test.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/5-filter-through-function/solution.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/5-filter-through-function/task.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/6-make-army/_js.view/solution.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/6-make-army/_js.view/source.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/6-make-army/_js.view/test.js (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/6-make-army/solution.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/6-make-army/task.md (100%) rename 1-js/5-functions-closures/{5-closures-usage => 4-closures-usage}/article.md (97%) create mode 100644 1-js/5-functions-closures/5-closures-module/article.md rename 1-js/5-functions-closures/{4-closures-module/highlight-conflict.view/highlight.js => 5-closures-module/hello-conflict.view/hello.js} (100%) rename 1-js/5-functions-closures/{4-closures-module/highlight-conflict.view => 5-closures-module/hello-conflict.view}/index.html (86%) rename 1-js/5-functions-closures/{4-closures-module/highlight-module.view/highlight.js => 5-closures-module/hello-module.view/hello.js} (100%) rename 1-js/5-functions-closures/{4-closures-module/highlight-module.view => 5-closures-module/hello-module.view}/index.html (86%) delete mode 100755 1-js/5-functions-closures/6-memory-management/family-ext-nofatherlink-nohusband.png delete mode 100755 1-js/5-functions-closures/6-memory-management/family-ext-nolink.png delete mode 100755 1-js/5-functions-closures/6-memory-management/family-ext.png create mode 100644 1-js/5-functions-closures/6-memory-management/family-no-family.svg create mode 100644 1-js/5-functions-closures/6-memory-management/family-no-father-2.svg create mode 100644 1-js/5-functions-closures/6-memory-management/family-no-father.svg delete mode 100755 1-js/5-functions-closures/6-memory-management/family-nofatherlink-junk-cleanup.png delete mode 100755 1-js/5-functions-closures/6-memory-management/family-nofatherlink-junk.png delete mode 100755 1-js/5-functions-closures/6-memory-management/family-nofatherlink.png delete mode 100755 1-js/5-functions-closures/6-memory-management/family.png create mode 100644 1-js/5-functions-closures/6-memory-management/family.svg delete mode 100755 1-js/5-functions-closures/7-with/with_obj_size.png rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/1-array-equals-string/solution.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/1-array-equals-string/task.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/2-tostring-valueof/solution.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/2-tostring-valueof/task.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/3-compare-empty-arrays/solution.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/3-compare-empty-arrays/task.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/4-object-types-conversion-questions/solution.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/4-object-types-conversion-questions/task.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/5-sum-many-brackets/solution.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/5-sum-many-brackets/task.md (100%) rename 1-js/{7-js-misc/1-object-conversion => 6-objects-more/2-object-conversion}/article.md (87%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/1-two-functions-one-object/solution.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/1-two-functions-one-object/task.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/2-calculator-constructor/_js.view/solution.js (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/2-calculator-constructor/_js.view/test.js (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/2-calculator-constructor/solution.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/2-calculator-constructor/task.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/3-accumulator/_js.view/solution.js (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/3-accumulator/_js.view/test.js (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/3-accumulator/solution.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/3-accumulator/task.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/4-calculator-extendable/_js.view/solution.js (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/4-calculator-extendable/_js.view/test.js (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/4-calculator-extendable/solution.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/4-calculator-extendable/task.md (100%) rename 1-js/6-objects-more/{2-constructor-new => 3-constructor-new}/article.md (80%) create mode 100644 1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/solution.md create mode 100644 1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/task.md rename 1-js/{8-oop => 6-objects-more}/4-descriptors-getters-setters/article.md (71%) rename 1-js/6-objects-more/{3-static-properties-and-methods => 5-static-properties-and-methods}/1-objects-counter/_js.view/solution.js (100%) rename 1-js/6-objects-more/{3-static-properties-and-methods => 5-static-properties-and-methods}/1-objects-counter/_js.view/test.js (100%) rename 1-js/6-objects-more/{3-static-properties-and-methods => 5-static-properties-and-methods}/1-objects-counter/solution.md (100%) rename 1-js/6-objects-more/{3-static-properties-and-methods => 5-static-properties-and-methods}/1-objects-counter/task.md (100%) rename 1-js/6-objects-more/{3-static-properties-and-methods => 5-static-properties-and-methods}/article.md (97%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/1-rewrite-sum-arguments/solution.md (100%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/1-rewrite-sum-arguments/task.md (100%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/2-apply-function-skip-first-argument/_js.view/solution.js (100%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/2-apply-function-skip-first-argument/_js.view/test.js (100%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/2-apply-function-skip-first-argument/solution.md (100%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/2-apply-function-skip-first-argument/task.md (100%) rename 1-js/6-objects-more/{4-call-apply => 6-call-apply}/article.md (82%) rename 1-js/6-objects-more/{5-bind => 7-bind}/1-cross-browser-bind/solution.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/1-cross-browser-bind/task.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/2-write-to-object-after-bind/solution.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/2-write-to-object-after-bind/task.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/3-second-bind/solution.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/3-second-bind/task.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/4-function-property-after-bind/solution.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/4-function-property-after-bind/task.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/5-question-use-bind/solution.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/5-question-use-bind/task.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/6-ask-currying/solution.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/6-ask-currying/task.md (100%) rename 1-js/6-objects-more/{5-bind => 7-bind}/article.md (77%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/1-logging-decorator/_js.view/solution.js (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/1-logging-decorator/_js.view/test.js (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/1-logging-decorator/solution.md (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/1-logging-decorator/task.md (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/2-logging-decorator-arguments/_js.view/solution.js (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/2-logging-decorator-arguments/_js.view/test.js (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/2-logging-decorator-arguments/solution.md (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/2-logging-decorator-arguments/task.md (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/3-caching-decorator/_js.view/solution.js (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/3-caching-decorator/_js.view/test.js (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/3-caching-decorator/solution.md (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/3-caching-decorator/task.md (100%) rename 1-js/6-objects-more/{6-decorators => 8-decorators}/article.md (76%) rename 1-js/7-js-misc/{2-class-property => 1-class-property}/article.md (91%) rename 1-js/7-js-misc/{3-json => 2-json}/1-serialize-object/solution.md (100%) rename 1-js/7-js-misc/{3-json => 2-json}/1-serialize-object/task.md (100%) rename 1-js/7-js-misc/{3-json => 2-json}/2-serialize-object-circular/solution.md (100%) rename 1-js/7-js-misc/{3-json => 2-json}/2-serialize-object-circular/task.md (100%) rename 1-js/7-js-misc/{3-json => 2-json}/article.md (93%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/1-output-numbers-100ms/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/1-output-numbers-100ms/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/2-output-numbers-100ms-settimeout/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/2-output-numbers-100ms-settimeout/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/3-highlight-tactics/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/3-highlight-tactics/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/4-settimeout-result/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/4-settimeout-result/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/5-setinterval-result/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/5-setinterval-result/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/6-who-runs-faster/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/6-who-runs-faster/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/7-delay/_js.view/solution.js (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/7-delay/_js.view/test.js (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/7-delay/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/7-delay/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/8-debounce/_js.view/solution.js (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/8-debounce/_js.view/test.js (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/8-debounce/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/8-debounce/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/9-throttle/_js.view/solution.js (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/9-throttle/_js.view/test.js (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/9-throttle/solution.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/9-throttle/task.md (100%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/article.md (94%) rename 1-js/7-js-misc/{4-setTimeout-setInterval => 3-setTimeout-setInterval}/setInterval-anim.view/index.html (100%) create mode 100644 1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg create mode 100644 1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg rename 1-js/7-js-misc/{5-eval => 4-eval}/1-eval-calculator/solution.md (100%) rename 1-js/7-js-misc/{5-eval => 4-eval}/1-eval-calculator/task.md (100%) rename 1-js/7-js-misc/{5-eval => 4-eval}/article.md (81%) delete mode 100755 1-js/7-js-misc/4-setTimeout-setInterval/interval1.png delete mode 100755 1-js/7-js-misc/4-setTimeout-setInterval/timeout.png rename 1-js/7-js-misc/{6-exception => 5-exception}/1-finally-ili-prosto-kod/solution.md (100%) rename 1-js/7-js-misc/{6-exception => 5-exception}/1-finally-ili-prosto-kod/task.md (100%) rename 1-js/7-js-misc/{6-exception => 5-exception}/2-eval-calculator-errors/solution.md (100%) rename 1-js/7-js-misc/{6-exception => 5-exception}/2-eval-calculator-errors/task.md (100%) rename 1-js/7-js-misc/{6-exception => 5-exception}/article.md (71%) delete mode 100644 1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/solution.md delete mode 100644 1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/task.md diff --git a/1-js/4-data-structures/10-arguments-pseudoarray/article.md b/1-js/4-data-structures/10-arguments-pseudoarray/article.md index 47d13f8e..485ae3d8 100644 --- a/1-js/4-data-structures/10-arguments-pseudoarray/article.md +++ b/1-js/4-data-structures/10-arguments-pseudoarray/article.md @@ -37,7 +37,9 @@ log(a,b,c); // вызовется вторая функция Это называется "полиморфизмом функций" или "перегрузкой функций". В JavaScript ничего подобного нет. -**Может быть только одна функция с именем `log`, которая вызывается с любыми аргументами.** А уже внутри она может посмотреть, с чем вызвана и по-разному отработать. +**Может быть только одна функция с именем `log`, которая вызывается с любыми аргументами.** + +А уже внутри она может посмотреть, с чем вызвана и по-разному отработать. В примере выше второе объявление `log` просто переопределит первое. [/smart] @@ -142,9 +144,7 @@ for(var i=0; i
`new Date(datestring)`
-
Если единственный аргумент - строка, используется вызов `Date.parse` для ее разбора.
+
Если единственный аргумент - строка, используется вызов `Date.parse` (см. далее) для чтения даты из неё.
`new Date(year, month, date, hours, minutes, seconds, ms)`
Дату можно создать, используя компоненты в местной временной зоне. Для этого формата обязательны только первые два аргумента. Отсутствующие параметры, начиная с `hours` считаются равными нулю, а `date` -- единице. -**Заметим, что год `year` должен быть из 4 цифр, а отсчет месяцев `month` начинается с нуля 0.** Например: +Заметим: +
    +
  • Год `year` должен быть из 4 цифр.
  • +
  • Отсчет месяцев `month` начинается с нуля 0.
  • +
+ +Например: ```js -new Date(2011, 0, 1) // 1 января 2011, 00:00:00 в местной временной зоне -new Date(2011, 0) // то же самое, date по умолчанию равно 1 -new Date(2011, 0, 1, 0, 0, 0, 0); // то же самое +new Date(2011, 0, 1, 0, 0, 0, 0); // // 1 января 2011, 00:00:00 +new Date(2011, 0, 1); // то же самое, часы/секунды по умолчанию равны 0 ``` Дата задана с точностью до миллисекунд: ```js //+ run -var d = new Date(2011, 0, 1, 2, 3, 4, 567); -alert(d); // 1.01.2011, 02:03:04.567 +var date = new Date(2011, 0, 1, 2, 3, 4, 567); +alert(date); // 1.01.2011, 02:03:04.567 ```
@@ -67,7 +72,7 @@ alert(d); // 1.01.2011, 02:03:04.567
Получить соответствующие компоненты.
-[warn header="Устаревший `getYear()`"] +[warn header="Не `getYear()`, а `getFullYear()`"] Некоторые браузеры реализуют нестандартный метод `getYear()`. Где-то он возвращает только две цифры из года, где-то четыре. Так или иначе, этот метод отсутствует в стандарте JavaScript. Не используйте его. Для получения года есть `getFullYear()`. [/warn] @@ -85,17 +90,22 @@ alert(d); // 1.01.2011, 02:03:04.567 ```js //+ run +// текущая дата var date = new Date(); -alert( date.getHours() ); // час в вашей зоне для даты date -alert( date.getUTCHours() ); // час в зоне GMT+0 для даты date +// час в текущей временной зоне +alert( date.getHours() ); + +// сколько сейчас времени в Лондоне? +// час в зоне GMT+0 +alert( date.getUTCHours() ); ``` Кроме описанных выше, существуют два специальных метода без UTC-варианта:
`getTime()`
-
Возвращает число миллисекунд, прошедших с 01.01.1970 00:00:00 UTC. Это то же число, которое используется в конструкторе `new Date(milliseconds)`.
+
Возвращает число миллисекунд, прошедших с 1 января 1970 года GMT+0, то есть того же вида, который используется в конструкторе `new Date(milliseconds)`.
`getTimezoneOffset()`
Возвращает разницу между местным и UTC-временем, в минутах. @@ -296,7 +306,9 @@ alert('Время walkLength: ' +timeLength + 'мс'); ``` [smart header="Более точное время с `performance.now()`"] -В современных браузерах (кроме IE9-) вызов [performance.now()](https://developer.mozilla.org/en-US/docs/Web/API/performance.now) возвращает количество миллисекунд, прошедшее с начала загрузки страницы, а если точнее -- с момента выгрузки предыдущей страницы из памяти. +В современных браузерах (кроме IE9-) вызов [performance.now()](https://developer.mozilla.org/en-US/docs/Web/API/performance.now) возвращает количество миллисекунд, прошедшее с начала загрузки страницы. Причём именно с самого начала, до того, как загрузился HTML-файл, если точнее -- с момента выгрузки предыдущей страницы из памяти. + +Так что это время включает в себя всё, включая начальное обращение к серверу. Его можно посмотреть в любом месте страницы, даже в ``, чтобы узнать, сколько времени потребовалось браузеру, чтобы до него добраться, включая загрузку HTML. @@ -348,17 +360,17 @@ console.timeEnd("All Benchmarks");
  • Автоматически выносят инвариант, то есть постоянное в цикле значение типа `arr.length`, за пределы цикла.
  • Стараются понять, значения какого типа хранит данная переменная или массив, какую структуру имеет объект и, исходя из этого, оптимизировать внутренние алгоритмы.
  • Выполняют простейшие операции, например сложение явно заданных чисел и строк, на этапе компиляции.
  • -
  • В теории, могут выкинуть код, который ни на что не влияет, например присваивание к неиспользуемой локальной переменной, хотя делают это редко.
  • +
  • Могут обнаружить, что некий код, например присваивание к неиспользуемой локальной переменной, ни на что не влияет и вообще исключить его из выполнения, хотя делают это редко.
  • -Они могут влиять на результаты тестов. +Эти оптимизации могут влиять на результаты тестов, поэтому измерять скорость базовых операций JavaScript ("проводить миробенчмаркинг") до того, как вы изучите внутренности JavaScript-интерпретаторов и поймёте, что они реально делают на таком коде, не рекомендуется. [/warn] -## Форматирование +## Форматирование и вывод дат Во всех браузерах, кроме IE10-, поддерживается новый стандарт [Ecma 402](http://www.ecma-international.org/publications/standards/Ecma-402.htm), который добавляет специальные методы для форматирования дат. -Это делается взыовом `date.toLocaleString(локаль, опции)`, у которого много настроек. Он позволяет указать, какие параметры даты нужно вывести, и ряд настроек вывода, после чего интерпретатор сам сформирует строку. +Это делается вызовом `date.toLocaleString(локаль, опции)`, в котором можно задать много настроек. Он позволяет указать, какие параметры даты нужно вывести, и ряд настроек вывода, после чего интерпретатор сам сформирует строку. Пример с почти всеми параметрами даты и русским, затем английским (США) форматированием: @@ -389,7 +401,7 @@ alert( date.toLocaleString("en-US", options) ); // Wednesday, December 31, 2014
    `toString()`, `toDateString()`, `toTimeString()`
    -
    Возвращают стандартное строчное представление, не указанное в стандарте, а зависящее от браузера. Единственное требование - читаемость человеком. Метод `toString` возвращает дату целиком, `toDateString()` и `toTimeString()` - только дату и время соответственно. +
    Возвращают стандартное строчное представление, не заданное жёстко в стандарте, а зависящее от браузера. Единственное требование к нему -- читаемость человеком. Метод `toString` возвращает дату целиком, `toDateString()` и `toTimeString()` -- только дату и время соответственно. ```js //+ run @@ -413,7 +425,7 @@ alert( d.toISOString() ); // вывод, похожий на '2011-01-26T13:51:5
    -**Если хочется иметь большую гибкость и кросс-браузерность, то также можно воспользоваться специальной библиотекой, например [Moment.JS](http://momentjs.com/) или написать свою функцию.** +Если хочется иметь большую гибкость и кросс-браузерность, то также можно воспользоваться специальной библиотекой, например [Moment.JS](http://momentjs.com/) или написать свою функцию форматирования. @@ -421,31 +433,29 @@ alert( d.toISOString() ); // вывод, похожий на '2011-01-26T13:51:5 Все современные браузеры, включая IE9+, понимают даты в упрощённом формате ISO 8601 Extended. -Этот формат выглядит так: `YYYY-MM-DDTHH:mm:ss.sssZ`. Для разделения даты и времени в нем используется символ `'T'`. Часть `'Z'` обозначает (необязательную) временную зону -- она может отсутствовать, тогда зона UTC, либо может быть символ `z` -- тоже UTC, или зона в формате `+-hh:mm`. +Этот формат выглядит так: `YYYY-MM-DDTHH:mm:ss.sssZ`, где: -Также возможны упрощенные варианты, к примеру: +
      +
    • `YYYY-MM-DD` -- дата в формате год-месяц-день.
    • +
    • Обычный символ `T` используется как разделитель.
    • +
    • `HH:mm:ss.sss` -- время: часы-минуты-секунды-миллисекунды.
    • +
    • Часть `'Z'` обозначает временную зону -- в формате `+-hh:mm`, либо символ `Z`, обозначающий UTC. По стандарту её можно не указывать, тогда UTC, но в Safari с этим ошибка, так что лучше указывать всегда.
    • +
    -```js -YYYY -YYYY-MM -YYYY-MM-DD -``` +Также возможны укороченные варианты, например `YYYY-MM-DD` или `YYYY-MM` или даже только `YYYY`. Метод `Date.parse(str)` разбирает строку `str` в таком формате и возвращает соответствующее ей количество миллисекунд. Если это невозможно, `Date.parse` возвращает `NaN`. -На момент написания некоторые браузеры (Safari) воспринимали формат без `'Z'` как дату в локальной таймзоне (по стандарту UTC), поэтому пример ниже в них работает некорректно: +Например: ```js //+ run -var msNoZone = Date.parse('2012-01-26T13:51:50.417'); // без зоны, значит UTC +var msUTC = Date.parse('2012-01-26T13:51:50.417Z'); // зона UTC -alert(msNoZone); // 1327571510417 (число миллисекунд) - -var msZ = Date.parse('2012-01-26T13:51:50.417z'); // зона z означает UTC -alert(msZ == msNoZone); // true, если браузер правильный +alert(msUTC); // 1327571510417 (число миллисекунд) ``` -С таймзоной `-07:00 GMT` в конце все современные браузеры работают правильно: +С таймзоной `-07:00 GMT`: ```js //+ run @@ -454,6 +464,7 @@ var ms = Date.parse('2012-01-26T13:51:50.417-07:00'); alert(ms); // 1327611110417 (число миллисекунд) ``` + [smart header="Формат дат для IE8-"] До появления спецификации EcmaScript 5 формат не был стандартизован, и браузеры, включая IE8-, имели свои собственные форматы дат. Частично, эти форматы пересекаются. diff --git a/1-js/4-data-structures/12-typeof-duck-typing/article.md b/1-js/4-data-structures/12-typeof-duck-typing/article.md index cfcfe2d4..85b633a3 100644 --- a/1-js/4-data-structures/12-typeof-duck-typing/article.md +++ b/1-js/4-data-structures/12-typeof-duck-typing/article.md @@ -19,11 +19,11 @@
    Строки, такие как `"Мяу"` или пустая строка `""`.
    -Все остальные значения являются **объектами**, включая функции и массивы. +Все остальные значения, включая даты и массивы, являются объектами. ## Оператор typeof [#type-typeof] -Оператор `typeof` возвращает тип аргумента. У него есть два синтаксиса: +Оператор `typeof` возвращает тип аргумента. У него есть два синтаксиса: со скобками и без:
    1. Синтаксис оператора: `typeof x`.
    2. Синтаксис функции: `typeof(x)`.
    3. @@ -91,14 +91,14 @@ alert( typeof new Date ); // 'object' Смысл утиной типизации -- в проверке необходимых методов и свойств. -Например, у нас функция работает с массивами. Мы можем проверить, что объект -- массив, уточнив наличие метода `splice`: +Например, у нас функция работает с массивами. Мы можем проверить, что объект -- массив, уточнив наличие метода `splice`, который, как известно, есть у всех массивов: ```js //+ run -var something = [1,2,3]; +var something = [1, 2, 3]; if (something.splice) { - alert('Массив!'); + alert('Это утка! То есть, массив!'); } ``` @@ -121,16 +121,14 @@ if (x.getTime) { ## Полиморфизм -Используем проверку типов для того, чтобы создать полиморфную функцию `sayHi(who)`, которая говорит "Привет" своему аргументу. - -При этом, если передали массив, она должна вызвать себя для каждого подэлемента. +Пример полимофрной функции -- `sayHi(who)`, которая будет говорить "Привет" своему аргументу, причём если передан массив -- то "Привет" каждому: ```js //+ run function sayHi(who) { - if (who.splice) { // проверка на массив (или что-то похожее) - for(var i=0; iДля функций он возвращает `function`, по стандарту функция не считается базовым типом, но на практике это удобно и полезно.
    -Там, где нужно различать объекты, обычно используется утиная типизация, то есть мы смотрим, есть ли в объекте нужный метод, желательно -- тот, который мы собираемся исползовать, но это не обязательно. +Там, где нужно различать объекты, обычно используется утиная типизация, то есть мы смотрим, есть ли в объекте нужный метод, желательно -- тот, который мы собираемся использовать. diff --git a/1-js/4-data-structures/8-array-methods/11-array-unique/solution.md b/1-js/4-data-structures/8-array-methods/11-array-unique/solution.md index 7240bc6c..d367656a 100644 --- a/1-js/4-data-structures/8-array-methods/11-array-unique/solution.md +++ b/1-js/4-data-structures/8-array-methods/11-array-unique/solution.md @@ -41,7 +41,7 @@ alert( unique(strings) ); // кришна, харе, 8-()
  • Для второго элемента -- это обойдётся в `1` операцию доступа к элементам `result`.
  • Для третьего элемента -- это обойдётся в `2` операции доступа к элементам `result`.
  • ...Для n-го элемента -- это обойдётся в `n-1` операций доступа к элементам `result`.
  • - + Всего 0 + 1 + 2 + ... + n-1 = (n-1)*n/2 = n2/2 - n/2 (как сумма арифметической прогрессии), то есть количество операций растёт примерно как квадрат от `n`. diff --git a/1-js/4-data-structures/9-array-iteration/article.md b/1-js/4-data-structures/9-array-iteration/article.md index 90fe2ce4..bf02ed2b 100644 --- a/1-js/4-data-structures/9-array-iteration/article.md +++ b/1-js/4-data-structures/9-array-iteration/article.md @@ -8,9 +8,9 @@ Метод ["arr.forEach(callback[, thisArg])"](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach) используется для перебора массива. -Он позволяет перебрать массив при помощи функции `callback`, что зачастую гораздо элегантнее, нежели цикл `for`. +Он позволяет для каждого элемента массива вызывает функцию `callback`. -Функция `callback` вызывается для каждого элемента с тремя параметрами `callback(item, i, arr)`: +Этой функции он передаёт три параметра `callback(item, i, arr)`: -Иными словами, вызов `return` с объектом вернёт объект, а с чем угодно, кроме объекта -- прекратит выполнение функции и возвратит `this`. +Иными словами, вызов `return` с объектом вернёт объект, а с чем угодно, кроме объекта -- возвратит, как обычно, `this`. Например, возврат объекта: @@ -97,9 +100,9 @@ function BigAnimal() { alert( new BigAnimal().name ); // Мышь, получили this (а Годзилла пропал) ``` -Эта особенность работы `new` прописана в стандарте, знать о ней полезно, но используется она весьма редко. +Эта особенность работы `new` прописана в стандарте, но используется она весьма редко. -[smart] +[smart header="Можно без скобок"] Кстати, при вызове `new` без аргументов скобки можно не ставить: ```js @@ -108,11 +111,14 @@ var animal = new BigAnimal; // <-- без скобок var animal = new BigAnimal(); ``` +Не сказать, что выбрасывание скобок -- "хороший стиль", но такой синтаксис допустим стандартом. [/smart] ## Создание методов в конструкторе -Использование функций для создания объекта дает большую гибкость. Можно передавать конструктору параметры, определяющие как его создавать. +Использование функций для создания объекта дает большую гибкость. Можно передавать конструктору параметры, определяющие как его создавать, и он будет "клепать" объекты заданным образом. + +Добавим в создаваемый объект ещё и метод. Например, `new User(name)` создает объект с заданным значением свойства `name` и методом `sayHi`: diff --git a/1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/solution.md b/1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/solution.md new file mode 100644 index 00000000..1719a50b --- /dev/null +++ b/1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/solution.md @@ -0,0 +1,49 @@ + +```js +//+ run + +function User(fullName) { + this.fullName = fullName; + + Object.defineProperties(this, { + + firstName: { + + get: function() { + return this.fullName.split(' ')[0]; + }, + + set: function(newFirstName) { + this.fullName = newFirstName + ' ' + this.lastName; + } + + }, + + lastName: { + + get: function() { + return this.fullName.split(' ')[1]; + }, + + set: function(newLastName) { + this.fullName = this.firstName + ' ' + newLastName; + } + + } + + + }); +} + +var vasya = new User("Василий Попкин"); + +// чтение firstName/lastName +alert(vasya.firstName); // Василий +alert(vasya.lastName); // Попкин + +// запись в lastName +vasya.lastName = 'Сидоров'; + +alert(vasya.fullName); // Василий Сидоров +``` + diff --git a/1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/task.md b/1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/task.md new file mode 100644 index 00000000..7f4950aa --- /dev/null +++ b/1-js/6-objects-more/4-descriptors-getters-setters/1-replace-property-getter/task.md @@ -0,0 +1,32 @@ +# Добавить get/set-свойства + +[importance 5] + +Вам попал в руки код объекта `User`, который хранит имя и фамилию в свойстве `this.fullName`: + +```js +function User(fullName) { + this.fullName = fullName; +} + +var vasya = new User("Василий Попкин"); +``` + +Имя и фамилия всегда разделяются пробелом. + +Сделайте, чтобы были доступны свойства `firstName` и `lastName`, причём не только на чтение, но и на запись, вот так: + +```js +var vasya = new User("Василий Попкин"); + +// чтение firstName/lastName +alert(vasya.firstName); // Василий +alert(vasya.lastName); // Попкин + +// запись в lastName +vasya.lastName = 'Сидоров'; + +alert(vasya.fullName); // Василий Сидоров +``` + +Важно: не рекомендуется дублировать одни и те же данные в различных свойствах. Поэтому в этой задаче `fullName` должно остаться свойством, а `firstName/lastName` -- реализованы через `get/set`. \ No newline at end of file diff --git a/1-js/8-oop/4-descriptors-getters-setters/article.md b/1-js/6-objects-more/4-descriptors-getters-setters/article.md similarity index 71% rename from 1-js/8-oop/4-descriptors-getters-setters/article.md rename to 1-js/6-objects-more/4-descriptors-getters-setters/article.md index bc7f5eb2..5cd7c99f 100644 --- a/1-js/8-oop/4-descriptors-getters-setters/article.md +++ b/1-js/6-objects-more/4-descriptors-getters-setters/article.md @@ -2,9 +2,7 @@ В этой главе мы рассмотрим возможности, которые позволяют очень гибко и мощно управлять всеми свойствами объекта, включая их аспекты -- изменяемость, видимость в цикле `for..in` и даже "невидимые" геттеры-сеттеры. -Они поддерживаются всеми современными браузерами, но не IE8-. Точнее говоря, они поддерживаются даже в IE8, но не для всех объектов, а только для DOM-объектов (они используются при работе со страницей, это сейчас вне нашего рассмотрения). - -Большая часть этих методов, в частности, работа с дескрипторами, не задействуется в других главах учебника для обеспечения совместимости с IE8-, но во вспомогательных скриптах -- библиотеках для тестирования, сборки, а также для сервера Node.JS они используются достаточно активно. +Они поддерживаются всеми современными браузерами, но не IE8-. Точнее говоря, они поддерживаются даже в IE8, но не для всех объектов, а только для DOM-объектов (используются при работе со страницей, это сейчас вне нашего рассмотрения). [cut] ## Дескрипторы в примерах @@ -28,28 +26,20 @@ Object.defineProperty(obj, prop, descriptor)
    `descriptor`
    Дескриптор -- объект, который описывает поведение свойства. В нём могут быть следующие поля: -
    -
    `value`
    -
    Значение свойства, по умолчанию `undefined`
    -
    `writable`
    -
    Значение свойства можно менять, если `true`. По умолчанию `false`.
    -
    `configurable`
    -
    Если `true`, то свойство можно удалять, а также менять его в дальнейшем при помощи `defineProperty`. По умолчанию `false`.
    -
    `enumerable`
    -
    Если `true`, то свойство будет участвовать в переборе `for..in`. По умолчанию `false`.
    -
    `get`
    -
    Функция, которая возвращает значение свойства. По умолчанию `undefined`.
    -
    `set`
    -
    Функция, которая записывает значение свойства. По умолчанию `undefined`.
    -
    -
    - +
      +
    • `value` -- значение свойства, по умолчанию `undefined`
    • +
    • `writable` -- значение свойства можно менять, если `true`. По умолчанию `false`.
    • +
    • `configurable` -- если `true`, то свойство можно удалять, а также менять его в дальнейшем при помощи новых вызовов `defineProperty`. По умолчанию `false`.
    • +
    • `enumerable` -- если `true`, то свойство будет участвовать в переборе `for..in`. По умолчанию `false`.
    • +
    • `get` -- функция, которая возвращает значение свойства. По умолчанию `undefined`.
    • +
    • `set` -- функция, которая записывает значение свойства. По умолчанию `undefined`.
    • +
    Чтобы избежать конфликта, запрещено одновременно указывать значение `value` и функции `get/set`. Либо значение, либо функции для его чтения-записи, одно из двух. Также запрещено и не имеет смысла указывать `writable` при наличии `get/set`-функций. Далее мы подробно разберём эти свойства на примерах. -### Пример: обычное свойство +## Обычное свойство Обычное свойство добавить очень просто. @@ -65,7 +55,7 @@ user.name = "Вася"; Object.defineProperty(user, "name", { value: "Вася" }); ``` -### Пример: свойство-константа +## Свойство-константа Для того, чтобы сделать свойство неизменяемым, добавим ему флаги `writable` и `configurable`: @@ -91,11 +81,9 @@ user.name = "Петя"; */!* ``` -**Заметим, что ошибки при попытке изменения такого свойства произойдут только при `use strict`.** +Заметим, что без `use strict` операция записи "молча" не сработает, а при `use strict` дополнительно генерируется ошибка. -Без `use strict` операция записи "молча" не сработает. - -### Пример: свойство, скрытое для for..in +## Свойство, скрытое для for..in Встроенный метод `toString`, как и большинство встроенных методов, не участвует в цикле `for..in`. Это удобно, так как обычно такое свойство является "служебным". @@ -113,7 +101,9 @@ for(var key in user) alert(key); // name, toString */!* ``` -`Object.defineProperty` может помочь исключить `toString` из списка итерации. Достаточно поставить ему флаг `enumerable: false`: +Мы бы хотели, чтобы поведение нашего метода `toString` было таким же, как и стандартного. + +`Object.defineProperty` может исключить `toString` из списка итерации, поставив ему флаг `enumerable: false`. По стандарту, у встроенного `toString` этот флаг уже стоит. ```js //+ run @@ -123,6 +113,7 @@ var user = { }; *!* +// помечаем toString как не подлежащий перебору в for..in Object.defineProperty(user, "toString", {enumerable: false}); for(var key in user) alert(key); // name @@ -131,7 +122,7 @@ for(var key in user) alert(key); // name Обратим внимание, вызов `defineProperty` не перезаписал свойство, а просто модифицировал настройки у существующего `toString`. -### Пример: свойство как функция-геттер +## Свойство-функция Дескриптор позволяет задать свойство, которое на самом деле работает как функция. Для этого в нём нужно указать эту функцию в `get`. @@ -157,11 +148,7 @@ alert(user.fullName); // Вася Петров */!* ``` -**Обратим внимание, снаружи это обычное свойство `user.fullName`.** - -Лишь в описании указывается, что на самом деле его значение возвращается функцией. - -### Пример: свойство геттер-сеттер +Обратим внимание, снаружи `fullName` -- это обычное свойство `user.fullName`. Но дескриптор указывает, что на самом деле его значение возвращается функцией. Также можно указать функцию, которая используется для записи значения, при помощи дескриптора `set`. @@ -196,9 +183,9 @@ alert(user.firstName); // Петя alert(user.surname); // Иванов ``` -## Геттеры и сеттеры в литералах +## Указание get/set в литералах -Если мы создаём объект при помощи синтаксиса `{ ... }`, то задать геттеры/сеттеры можно прямо в его определении. +Если мы создаём объект при помощи синтаксиса `{ ... }`, то задать свойства-функции можно прямо в его определении. Для этого используется особый синтаксис: `get свойство` или `set свойство`. @@ -234,13 +221,13 @@ alert(user.surname); // Иванов (поставил сеттер) */!* ``` -## Да здравствуют геттеры и сеттеры! +## Да здравствуют get/set! -Казалось бы, зачем нам назначать геттеры и сеттеры через всякие хитрые вызовы? Можно же сделать функции `getFullName`, `setFullName`... +Казалось бы, зачем нам назначать get/set для свойства через всякие хитрые вызовы, когда можно сделать просто функции с самого начала? Например, `getFullName`, `setFullName`... -**Основной бонус -- возможность получить контроль над свойством в любой момент!** +Конечно, в ряде случаев свойства выглядят короче, такое решение просто может быть красивым. Но основной бонус -- это гибкость, возможность получить контроль над свойством в любой момент! -В начале разработки мы можем использовать обычные свойства, например у `User` будет имя `name` и возраст `age`: +Например, в начале разработки мы используем обычные свойства, например у `User` будет имя `name` и возраст `age`: ```js function User(name, age) { @@ -253,11 +240,11 @@ var pete = new User("Петя", 25); alert(pete.age); // 25 ``` -**С обычными свойствами в коде меньше букв, они удобны.** +С обычными свойствами в коде меньше букв, они удобны, причины использовать функции пока нет. -...Но рано или поздно может наступить расплата! +...Но рано или поздно может произойти что-то, что потребует более сложной логики. -Например, когда написано много кода, который использует эти свойства, формат данных изменился и теперь вместо возраста `age` хранится дата рождения `birthday`: +Например, формат данных изменился и теперь вместо возраста `age` хранится дата рождения `birthday`: ```js function User(name, birthday) { @@ -272,9 +259,7 @@ var pete = new User("Петя", new Date(1987, 6, 1)); Можно, конечно, найти все места и поправить их, но это долго, а иногда и невозможно, скажем, если вы взаимодействуете со сторонней библиотекой, код в которой -- чужой и влезать в него нежелательно. -Геттеры позволяют обойти проблему легко и непринуждённо. - -Просто добавляем геттер `age`: +Добавление `get`-функции `age` позволяет обойти проблему легко и непринуждённо: ```js //+ run @@ -297,7 +282,7 @@ var pete = new User("Петя", new Date(1987, 6, 1)); alert(pete.age); // получает возраст из даты рождения ``` -**Таким образом, `defineProperty` позволяет нам использовать обычные свойства и, при необходимости, в любой момент заменить их на функции, сохраняя совместимость внешнего интерфейса.** +Таким образом, `defineProperty` позволяет нам использовать обычные свойства и, при необходимости, в любой момент заменить их на функции, сохраняя полную совместимость. ## Другие методы работы со свойствами @@ -338,7 +323,9 @@ alert( user.fullName ); // Петя Иванов
    [Object.keys(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys), [Object.getOwnPropertyNames(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames)
    Возвращают массив -- список свойств объекта. -При этом `Object.keys` возвращает только `enumerable`-свойства, а `Object.getOwnPropertyNames` -- все: +`Object.keys` возвращает только `enumerable`-свойства. + +`Object.getOwnPropertyNames` -- возвращает все: ```js //+ run diff --git a/1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/_js.view/solution.js b/1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/_js.view/solution.js similarity index 100% rename from 1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/_js.view/solution.js rename to 1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/_js.view/solution.js diff --git a/1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/_js.view/test.js b/1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/_js.view/test.js similarity index 100% rename from 1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/_js.view/test.js rename to 1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/_js.view/test.js diff --git a/1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/solution.md b/1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/solution.md similarity index 100% rename from 1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/solution.md rename to 1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/solution.md diff --git a/1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/task.md b/1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/task.md similarity index 100% rename from 1-js/6-objects-more/3-static-properties-and-methods/1-objects-counter/task.md rename to 1-js/6-objects-more/5-static-properties-and-methods/1-objects-counter/task.md diff --git a/1-js/6-objects-more/3-static-properties-and-methods/article.md b/1-js/6-objects-more/5-static-properties-and-methods/article.md similarity index 97% rename from 1-js/6-objects-more/3-static-properties-and-methods/article.md rename to 1-js/6-objects-more/5-static-properties-and-methods/article.md index decd48c6..2ee1e348 100644 --- a/1-js/6-objects-more/3-static-properties-and-methods/article.md +++ b/1-js/6-objects-more/5-static-properties-and-methods/article.md @@ -50,7 +50,7 @@ Article.showCount(); // (2) Здесь `Article.count` -- статическое свойство, а `Article.showCount` -- статический метод. -**Обратите внимание на контекст `this`. Несмотря на то, что переменная и метод -- статические, он всё ещё полезен. В строке `(1)` он равен `Article`!** +Обратим внимание на использование `this` в примере выше. Несмотря на то, что переменная и метод -- статические, он всё ещё полезен. В строке `(1)` он равен `Article`. ## Пример: сравнение объектов diff --git a/1-js/6-objects-more/4-call-apply/1-rewrite-sum-arguments/solution.md b/1-js/6-objects-more/6-call-apply/1-rewrite-sum-arguments/solution.md similarity index 100% rename from 1-js/6-objects-more/4-call-apply/1-rewrite-sum-arguments/solution.md rename to 1-js/6-objects-more/6-call-apply/1-rewrite-sum-arguments/solution.md diff --git a/1-js/6-objects-more/4-call-apply/1-rewrite-sum-arguments/task.md b/1-js/6-objects-more/6-call-apply/1-rewrite-sum-arguments/task.md similarity index 100% rename from 1-js/6-objects-more/4-call-apply/1-rewrite-sum-arguments/task.md rename to 1-js/6-objects-more/6-call-apply/1-rewrite-sum-arguments/task.md diff --git a/1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/_js.view/solution.js b/1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/_js.view/solution.js similarity index 100% rename from 1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/_js.view/solution.js rename to 1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/_js.view/solution.js diff --git a/1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/_js.view/test.js b/1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/_js.view/test.js similarity index 100% rename from 1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/_js.view/test.js rename to 1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/_js.view/test.js diff --git a/1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/solution.md b/1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/solution.md similarity index 100% rename from 1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/solution.md rename to 1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/solution.md diff --git a/1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/task.md b/1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/task.md similarity index 100% rename from 1-js/6-objects-more/4-call-apply/2-apply-function-skip-first-argument/task.md rename to 1-js/6-objects-more/6-call-apply/2-apply-function-skip-first-argument/task.md diff --git a/1-js/6-objects-more/4-call-apply/article.md b/1-js/6-objects-more/6-call-apply/article.md similarity index 82% rename from 1-js/6-objects-more/4-call-apply/article.md rename to 1-js/6-objects-more/6-call-apply/article.md index 8308ec8d..2af573cb 100644 --- a/1-js/6-objects-more/4-call-apply/article.md +++ b/1-js/6-objects-more/6-call-apply/article.md @@ -1,6 +1,6 @@ # Явное указание this: "call", "apply" -Итак, мы знаем, что в `this` -- это текущий объект при вызове "через точку" и новый объект при конструировании через `new`. +Итак, мы знаем, что `this` -- это текущий объект при вызове "через точку" и новый объект при конструировании через `new`. В этой главе наша цель получить окончательное и полное понимание `this` в JavaScript. Для этого не хватает всего одного элемента: способа явно указать `this` при помощи методов `call` и `apply`. @@ -26,7 +26,7 @@ function showFullName() { } ``` -**Обратите внимание, JavaScript позволяет использовать `this` везде. Любая функция может в своём коде упомянуть `this`, каким будет это значение -- выяснится в момент запуска.** +Пока объекта нет, но это нормально, ведь JavaScript позволяет использовать `this` везде. Любая функция может в своём коде упомянуть `this`, каким будет это значение -- выяснится в момент запуска. Вызов `showFullName.call(user)` запустит функцию, установив `this = user`, вот так: @@ -138,13 +138,13 @@ alert( obj.join(';') ); // "A;Б;В" [/smart] -**...Однако, копирование метода из одного объекта в другой не всегда приемлемо!** +...Однако, копирование метода из одного объекта в другой не всегда приемлемо! Представим на минуту, что вместо `arguments` у нас -- произвольный объект. У него тоже есть числовые индексы, `length` и мы хотим вызвать в его контексте метод `[].join`. То есть, ситуация похожа на `arguments`, но (!) вполне возможно, что у объекта есть *свой* метод `join`. Поэтому копировать `[].join`, как сделано выше, нельзя: если он перезапишет собственный `join` объекта, то будет страшный бардак и путаница. -**Безопасно вызвать метод нам поможет `call`:** +Безопасно вызвать метод нам поможет `call`: ```js //+ run @@ -208,7 +208,16 @@ showFullName.call(user, 'firstName', 'surname'); showFullName.apply(user, ['firstName', 'surname']); ``` -Преимущество `apply` перед `call` отчётливо видно в следующем примере, когда мы формируем массив аргументов динамически: +Преимущество `apply` перед `call` отчётливо видно, когда мы формируем массив аргументов динамически. + +Например, в JavaScript есть встроенная функция `Math.max(a, b, c...)`, которая возвращает максимальное значение из аргументов: + +```js +//+ run +alert( Math.max(1, 5, 2) ); // 5 +``` + +При помощи `apply` мы могли бы найти максимум в произвольном массиве, вот так: ```js //+ run @@ -221,57 +230,46 @@ arr.push(2); alert( Math.max.apply(null, arr) ); // 5 ``` -Обратим внимание, в примере выше вызывается метод `Math.max`. Его стандартное применение -- это выбор максимального аргумента из переданных, которых может быть сколько угодно: - -```js -//+ run -alert( Math.max(1, 5, 2) ); // 5 -alert( Math.max(1, 5, 2, 8) ); // 8 -``` - В примере выше мы передали аргументы через массив -- второй параметр `apply`... Но вы, наверное, заметили небольшую странность? В качестве контекста `this` был передан `null`. Строго говоря, полным эквивалентом вызову `Math.max(1,2,3)` был бы вызов `Math.max.apply(Math, [1,2,3])`. В обоих этих вызовах контекстом будет объект `Math`. -Но в данном случае в качестве контекста можно передавать что угодно, поскольку в своей внутренней реализации метод `Math.max`не использует `this`. Действительно, зачем `this`, если нужно всего лишь выбрать максимальный из аргументов? - -**Вот так, при помощи `apply` мы получили короткий и элегантный способ вычислить максимальное значение в массиве!** +Но в данном случае в качестве контекста можно передавать что угодно, поскольку в своей внутренней реализации метод `Math.max` не использует `this`. Действительно, зачем `this`, если нужно всего лишь выбрать максимальный из аргументов? Вот так, при помощи `apply` мы получили короткий и элегантный способ вычислить максимальное значение в массиве! [smart header="Вызов `call/apply` с `null` или `undefined`"] -В старом стандарте при указании первого аргумента `null` или `undefined` в `call/apply`, функция получала `this = window`, например: - -```js -//+ run -function f() { - alert(this); -} - -f.call(null); // window -``` - -Это поведение исправлено в современном стандарте ([15.3](http://es5.github.com/x15.3.html#x15.3.4.3)). - -Если функция работает в строгом режиме, то `this` передаётся "как есть": +В современном стандарте `call/apply` передают `this` "как есть". А в старом, без `use strict`, при указании первого аргумента `null` или `undefined` в `call/apply`, функция получает `this = window`, например: +Современный стандарт: ```js //+ run function f() { "use strict"; *!* - alert(this); // null, а не window + alert(this); // null */!* } f.call(null); ``` +Без `use strict`: + +```js +//+ run +function f() { + alert(this); // window +} + +f.call(null); +``` + [/smart] ## Итого про this - Значение `this` устанавливается в зависимости от того, как вызвана функция: +
    При вызове функции как метода
    diff --git a/1-js/6-objects-more/5-bind/1-cross-browser-bind/solution.md b/1-js/6-objects-more/7-bind/1-cross-browser-bind/solution.md similarity index 100% rename from 1-js/6-objects-more/5-bind/1-cross-browser-bind/solution.md rename to 1-js/6-objects-more/7-bind/1-cross-browser-bind/solution.md diff --git a/1-js/6-objects-more/5-bind/1-cross-browser-bind/task.md b/1-js/6-objects-more/7-bind/1-cross-browser-bind/task.md similarity index 100% rename from 1-js/6-objects-more/5-bind/1-cross-browser-bind/task.md rename to 1-js/6-objects-more/7-bind/1-cross-browser-bind/task.md diff --git a/1-js/6-objects-more/5-bind/2-write-to-object-after-bind/solution.md b/1-js/6-objects-more/7-bind/2-write-to-object-after-bind/solution.md similarity index 100% rename from 1-js/6-objects-more/5-bind/2-write-to-object-after-bind/solution.md rename to 1-js/6-objects-more/7-bind/2-write-to-object-after-bind/solution.md diff --git a/1-js/6-objects-more/5-bind/2-write-to-object-after-bind/task.md b/1-js/6-objects-more/7-bind/2-write-to-object-after-bind/task.md similarity index 100% rename from 1-js/6-objects-more/5-bind/2-write-to-object-after-bind/task.md rename to 1-js/6-objects-more/7-bind/2-write-to-object-after-bind/task.md diff --git a/1-js/6-objects-more/5-bind/3-second-bind/solution.md b/1-js/6-objects-more/7-bind/3-second-bind/solution.md similarity index 100% rename from 1-js/6-objects-more/5-bind/3-second-bind/solution.md rename to 1-js/6-objects-more/7-bind/3-second-bind/solution.md diff --git a/1-js/6-objects-more/5-bind/3-second-bind/task.md b/1-js/6-objects-more/7-bind/3-second-bind/task.md similarity index 100% rename from 1-js/6-objects-more/5-bind/3-second-bind/task.md rename to 1-js/6-objects-more/7-bind/3-second-bind/task.md diff --git a/1-js/6-objects-more/5-bind/4-function-property-after-bind/solution.md b/1-js/6-objects-more/7-bind/4-function-property-after-bind/solution.md similarity index 100% rename from 1-js/6-objects-more/5-bind/4-function-property-after-bind/solution.md rename to 1-js/6-objects-more/7-bind/4-function-property-after-bind/solution.md diff --git a/1-js/6-objects-more/5-bind/4-function-property-after-bind/task.md b/1-js/6-objects-more/7-bind/4-function-property-after-bind/task.md similarity index 100% rename from 1-js/6-objects-more/5-bind/4-function-property-after-bind/task.md rename to 1-js/6-objects-more/7-bind/4-function-property-after-bind/task.md diff --git a/1-js/6-objects-more/5-bind/5-question-use-bind/solution.md b/1-js/6-objects-more/7-bind/5-question-use-bind/solution.md similarity index 100% rename from 1-js/6-objects-more/5-bind/5-question-use-bind/solution.md rename to 1-js/6-objects-more/7-bind/5-question-use-bind/solution.md diff --git a/1-js/6-objects-more/5-bind/5-question-use-bind/task.md b/1-js/6-objects-more/7-bind/5-question-use-bind/task.md similarity index 100% rename from 1-js/6-objects-more/5-bind/5-question-use-bind/task.md rename to 1-js/6-objects-more/7-bind/5-question-use-bind/task.md diff --git a/1-js/6-objects-more/5-bind/6-ask-currying/solution.md b/1-js/6-objects-more/7-bind/6-ask-currying/solution.md similarity index 100% rename from 1-js/6-objects-more/5-bind/6-ask-currying/solution.md rename to 1-js/6-objects-more/7-bind/6-ask-currying/solution.md diff --git a/1-js/6-objects-more/5-bind/6-ask-currying/task.md b/1-js/6-objects-more/7-bind/6-ask-currying/task.md similarity index 100% rename from 1-js/6-objects-more/5-bind/6-ask-currying/task.md rename to 1-js/6-objects-more/7-bind/6-ask-currying/task.md diff --git a/1-js/6-objects-more/5-bind/article.md b/1-js/6-objects-more/7-bind/article.md similarity index 77% rename from 1-js/6-objects-more/5-bind/article.md rename to 1-js/6-objects-more/7-bind/article.md index 6864e875..c21511fa 100644 --- a/1-js/6-objects-more/5-bind/article.md +++ b/1-js/6-objects-more/7-bind/article.md @@ -39,7 +39,7 @@ setTimeout( user.sayHi, 1000); // undefined (не Вася!) */!* ``` -**При запуске кода выше через секунду выводится вовсе не `"Вася"`, а `undefined`!** +При запуске кода выше через секунду выводится вовсе не `"Вася"`, а `undefined`! Это произошло потому, что в примере выше `setTimeout` получил функцию `user.sayHi`, но не её контекст. То есть, последняя строчка аналогична двум таким: @@ -48,7 +48,8 @@ var f = user.sayHi; setTimeout(f, 1000); // контекст user потеряли ``` -**Ситуация довольно типична -- мы хотим передать метод объекта куда-то в другое место кода, откуда он потом может быть вызван. Как бы прикрепить к нему контекст, желательно, с минимумом плясок с бубном и при этом надёжно?** + +Ситуация довольно типична -- мы хотим передать метод объекта куда-то в другое место кода, откуда он потом может быть вызван. Как бы прикрепить к нему контекст, желательно, с минимумом плясок с бубном и при этом надёжно? Есть несколько способов решения, среди которых мы, в зависимости от ситуации, можем выбирать. @@ -86,24 +87,23 @@ setTimeout(function() { ```js function bind(func, context) { - return function() { + return function() { // (*) return func.apply(context, arguments); }; } ``` -Параметры: -
    -
    `func`
    -
    Произвольная функция
    -
    `context`
    -
    Произвольный объект
    +Результатом вызова `bind(func, context)`, как видно из кода, является анонимная функция функция `(*)`, вот она отдельно: -Результатом вызова `bind(func, context)` будет, как видно из кода, функция-обёртка, которая передаёт все вызовы `func`, указывая при этом правильный контекст `context`. +```js +function() { // (*) + return func.apply(context, arguments); +}; +``` -Чтобы понять, как она работает, нужно вспомнить тему "замыкания" -- здесь `bind` возвращает анонимную функцию, которая при вызове получает контекст `context` из внешней области видимости и передаёт вызов в `func` вместе с этим контекстом `context` и аргументами `arguments`. +Если её вызвать с какими-то аргументами, то она сама ничего не делает, а "передаёт вызов" в `func`. Здесь используется `apply`, чтобы вызвать `func` с теми же аргументами, которые получила эта анонимная функция и с контекстом `context`, который берётся из замыкания (был задан при вызове `bind`). -**В результате вызова `bind` мы получаем как бы "ту же функцию, но с фиксированным контекстом".** +Иными словами, в результате вызова `bind` мы получаем "функцию-обёртку", которая прозрачно передаёт вызов в `func`, с фиксированным контекстом `context`. Пример с `bind`: @@ -151,7 +151,7 @@ var wrapper = func.bind(context[, arg1, arg2...])
    Если указаны аргументы `arg1, arg2...` -- они будут прибавлены к каждому вызову новой функции, причем встанут *перед* теми, которые указаны при вызове.
    -Результат вызова: `func.bind(context)` аналогичен вызову `bind(func, context)`, описанному выше. То есть, `wrapper` -- это обёртка, фиксирующая контекст и передающая вызовы в `func`. Также можно указать аргументы, но это делается редко, мы поговорим о них позже. +Результат вызова `func.bind(context)` аналогичен вызову `bind(func, context)`, описанному выше. То есть, `wrapper` -- это обёртка, фиксирующая контекст и передающая вызовы в `func`. Также можно указать аргументы, тогда и они будут фиксированы, а новые будут уже за ними, но об этом чуть позже. Пример со встроенным методом `bind`: @@ -234,13 +234,12 @@ alert( triple(4) ); // = mul(3, 4) = 12 alert( triple(5) ); // = mul(3, 5) = 15 ``` -**При помощи `bind` мы можем получить из функции её "частный вариант" как самостоятельную функцию и дальше передать в `setTimeout` или сделать с ней что-то ещё.** +При помощи `bind` мы можем получить из функции её "частный вариант" как самостоятельную функцию и дальше передать в `setTimeout` или сделать с ней что-то ещё. -## Задачи +## Функция дла задач - -Рассмотрим для дальнейших задач "функцию для вопросов" `ask`: +В задачах этого раздела предполагается, что объявлена следующая "функция вопросов" `ask`: ```js function ask(question, answer, ok, fail) { @@ -250,9 +249,9 @@ function ask(question, answer, ok, fail) { } ``` -Пока в этой функции ничего особого нет. Её назначение -- задать вопрос `question` и, если ответ совпадёт с `answer`, то запустить функцию `ok()`, а иначе -- функцию `fail()`. +Её назначение -- задать вопрос `question` и, если ответ совпадёт с `answer`, то запустить функцию `ok()`, а иначе -- функцию `fail()`. -Однако, тем не менее, эта функция взята из реального проекта. Просто обычно она сложнее, вместо `alert/prompt` -- вывод красивого JavaScript-диалога с рамочками, кнопочками и так далее, но это нам сейчас не нужно. +В реальном проекте она будет сложнее, вместо `alert/prompt` -- вывод красивого JavaScript-диалога с рамочками, кнопочками и так далее, но это нам сейчас не нужно. Пример использования: diff --git a/1-js/6-objects-more/6-decorators/1-logging-decorator/_js.view/solution.js b/1-js/6-objects-more/8-decorators/1-logging-decorator/_js.view/solution.js similarity index 100% rename from 1-js/6-objects-more/6-decorators/1-logging-decorator/_js.view/solution.js rename to 1-js/6-objects-more/8-decorators/1-logging-decorator/_js.view/solution.js diff --git a/1-js/6-objects-more/6-decorators/1-logging-decorator/_js.view/test.js b/1-js/6-objects-more/8-decorators/1-logging-decorator/_js.view/test.js similarity index 100% rename from 1-js/6-objects-more/6-decorators/1-logging-decorator/_js.view/test.js rename to 1-js/6-objects-more/8-decorators/1-logging-decorator/_js.view/test.js diff --git a/1-js/6-objects-more/6-decorators/1-logging-decorator/solution.md b/1-js/6-objects-more/8-decorators/1-logging-decorator/solution.md similarity index 100% rename from 1-js/6-objects-more/6-decorators/1-logging-decorator/solution.md rename to 1-js/6-objects-more/8-decorators/1-logging-decorator/solution.md diff --git a/1-js/6-objects-more/6-decorators/1-logging-decorator/task.md b/1-js/6-objects-more/8-decorators/1-logging-decorator/task.md similarity index 100% rename from 1-js/6-objects-more/6-decorators/1-logging-decorator/task.md rename to 1-js/6-objects-more/8-decorators/1-logging-decorator/task.md diff --git a/1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/_js.view/solution.js b/1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/_js.view/solution.js similarity index 100% rename from 1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/_js.view/solution.js rename to 1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/_js.view/solution.js diff --git a/1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/_js.view/test.js b/1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/_js.view/test.js similarity index 100% rename from 1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/_js.view/test.js rename to 1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/_js.view/test.js diff --git a/1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/solution.md b/1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/solution.md similarity index 100% rename from 1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/solution.md rename to 1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/solution.md diff --git a/1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/task.md b/1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/task.md similarity index 100% rename from 1-js/6-objects-more/6-decorators/2-logging-decorator-arguments/task.md rename to 1-js/6-objects-more/8-decorators/2-logging-decorator-arguments/task.md diff --git a/1-js/6-objects-more/6-decorators/3-caching-decorator/_js.view/solution.js b/1-js/6-objects-more/8-decorators/3-caching-decorator/_js.view/solution.js similarity index 100% rename from 1-js/6-objects-more/6-decorators/3-caching-decorator/_js.view/solution.js rename to 1-js/6-objects-more/8-decorators/3-caching-decorator/_js.view/solution.js diff --git a/1-js/6-objects-more/6-decorators/3-caching-decorator/_js.view/test.js b/1-js/6-objects-more/8-decorators/3-caching-decorator/_js.view/test.js similarity index 100% rename from 1-js/6-objects-more/6-decorators/3-caching-decorator/_js.view/test.js rename to 1-js/6-objects-more/8-decorators/3-caching-decorator/_js.view/test.js diff --git a/1-js/6-objects-more/6-decorators/3-caching-decorator/solution.md b/1-js/6-objects-more/8-decorators/3-caching-decorator/solution.md similarity index 100% rename from 1-js/6-objects-more/6-decorators/3-caching-decorator/solution.md rename to 1-js/6-objects-more/8-decorators/3-caching-decorator/solution.md diff --git a/1-js/6-objects-more/6-decorators/3-caching-decorator/task.md b/1-js/6-objects-more/8-decorators/3-caching-decorator/task.md similarity index 100% rename from 1-js/6-objects-more/6-decorators/3-caching-decorator/task.md rename to 1-js/6-objects-more/8-decorators/3-caching-decorator/task.md diff --git a/1-js/6-objects-more/6-decorators/article.md b/1-js/6-objects-more/8-decorators/article.md similarity index 76% rename from 1-js/6-objects-more/6-decorators/article.md rename to 1-js/6-objects-more/8-decorators/article.md index 746ac446..7b038f03 100644 --- a/1-js/6-objects-more/6-decorators/article.md +++ b/1-js/6-objects-more/8-decorators/article.md @@ -3,15 +3,14 @@ JavaScript предоставляет удивительно гибкие возможности по работе с функциями: их можно передавать, в них можно записывать данные как в объекты, у них есть свои встроенные методы... Конечно, этим нужно уметь пользоваться. В этой главе, чтобы более глубоко понимать работу с функциями, мы рассмотрим создание функций-обёрток или, иначе говоря, "декораторов". + [cut] -## Примеры декораторов +[Декоратор](http://en.wikipedia.org/wiki/Decorator_pattern) -- приём программирования, который позволяет взять существующую функцию и изменить/расширить ее поведение. -
    Декоратор -- приём программирования, который позволяет взять существующую функцию и изменить/расширить ее поведение. +*Декоратор* получает функцию и возвращает обертку, которая делает что-то своё "вокруг" вызова основной функции. -***Декоратор* получает функцию и возвращает обертку, которая делает что-то своё "вокруг" вызова основной функции.** - -### bind -- привязка контекста +## bind -- привязка контекста Один простой декоратор вы уже видели ранее -- это функция [bind](/bind): @@ -25,11 +24,32 @@ function bind(func, context) { Вызов `bind(func, context)` возвращает обёртку, которая ставит `this` и передаёт основную работу функции `func`. -### Декоратор -- измеритель времени +## Декоратор-таймер -Посмотрим немного более сложный декоратор, замеряющий время выполнения функции: +Создадим более сложный декоратор, замеряющий время выполнения функции. -**При помощи декоратора `timingDecorator` мы можем взять произвольную функцию и одним движением руки прикрутить к ней измеритель времени:** +Он будет называться `timingDecorator` и получать функцию вместе с "названием таймера", а возвращать -- функцию-обёртку, которая измеряет время и прибавляет его в специальный объект `timer` по свойству-названию. + +Использование: +```js +function f(x) { } // любая функция + +var timers = {}; // объект для таймеров + +// отдекорировали +f = timingDecorator(f, "myFunc"); + +// запускаем +f(1); +f(2); +f(3); // функция работает как раньше, но время подсчитывается + +alert(timers.myFunc); // общее время выполнения всех вызовов f +``` + +При помощи декоратора `timingDecorator` мы сможем взять произвольную функцию и одним движением руки прикрутить к ней измеритель времени. + +Его реализация: ```js //+ run @@ -40,7 +60,7 @@ function timingDecorator(f, timer) { return function() { var start = performance.now(); - var result = f.apply(this, arguments); + var result = f.apply(this, arguments); // (*) if (!timers[timer]) timers[timer] = 0; timers[timer] += performance.now() - start; @@ -70,11 +90,15 @@ alert( timers.fibo + 'мс' ); */!* ``` -Обратим внимание на ключевую строку декоратора`var result = f.apply(this, arguments)`. +Обратим внимание на строку `(*)` внутри декоратора, которая и осуществляет передачу вызова: -**Этот приём называется "форвардинг вызова" (от англ. forwarding): текущий контекст и аргументы через `apply` передаются в функцию, так что изнутри `f` всё выглядит так, как будто это была вызвана она, а не декоратор.** +```js +var result = f.apply(this, arguments); // (*) +``` -### Декоратор для проверки типа +Этот приём называется "форвардинг вызова" (от англ. forwarding): текущий контекст и аргументы через `apply` передаются в функцию `f`, так что изнутри `f` всё выглядит так, как была вызвана она напрямую, а не декоратор. + +## Декоратор для проверки типа В JavaScript, как правило, пренебрегают проверками типа. В функцию, которая должна получать число, может быть передана строка, булево значение или даже объект. @@ -85,6 +109,7 @@ function sum(a, b) { return a + b; } +// передадим в функцию для сложения чисел нечисловые значения alert( sum(true, { name: "Вася", age: 35 }) ); // true[Object object] ``` @@ -142,7 +167,7 @@ sum(1, ["array", "in", "sum?!?"]); // некорректный аргумент **Один раз пишем декоратор и дальше просто применяем этот функционал везде, где нужно.** -### Декоратор проверки доступа +## Декоратор проверки доступа И наконец посмотрим ещё один, последний пример. diff --git a/1-js/6-objects-more/index.md b/1-js/6-objects-more/index.md index e021da19..94f1be86 100644 --- a/1-js/6-objects-more/index.md +++ b/1-js/6-objects-more/index.md @@ -1,3 +1,3 @@ # Методы объектов и контекст вызова -Начинаем изучать объектно-ориентированную разработку -- как работают объекты и функции, что такое контекст вызова и почему его значение нельзя предсказать. Как, всё же, гарантировать правильный контекст и многие другие, не самые простые, темы. \ No newline at end of file +Начинаем изучать объектно-ориентированную разработку -- как работают объекты и функции, что такое контекст вызова и способы его передачи. \ No newline at end of file diff --git a/1-js/7-js-misc/2-class-property/article.md b/1-js/7-js-misc/1-class-property/article.md similarity index 91% rename from 1-js/7-js-misc/2-class-property/article.md rename to 1-js/7-js-misc/1-class-property/article.md index fca95e60..53a4b3b5 100644 --- a/1-js/7-js-misc/2-class-property/article.md +++ b/1-js/7-js-misc/1-class-property/article.md @@ -2,7 +2,7 @@ Для встроенных объектов есть одна "секретная" возможность узнать их тип, которая связана с методом `toString`. -**Во всех встроенных объектах есть специальное свойство `[[Class]]`, в котором хранится информация о его типе или конструкторе.** +Во всех встроенных объектах есть специальное свойство `[[Class]]`, в котором хранится информация о его типе или конструкторе. Оно взято в квадратные скобки, так как это свойство -- внутреннее. Явно получить его нельзя, но можно прочитать его "в обход", воспользовавшись методом `toString` из `Object`. @@ -100,4 +100,4 @@ alert( {}.toString.call("строка") ); // [object String]
  • Для доступа к `[[Class]]` используется `{}.toString.call(obj).slice(8, -1)`.
  • -Обычно в JavaScript используется утиная типизация. Свойство `[[Class]]` -- самое надёжное средство проверки типа встроенных объектов, но обычно утиной типизации вполне хватает. \ No newline at end of file +Обычно в JavaScript используется "утиная" типизация. Свойство `[[Class]]` -- самое надёжное средство проверки типа встроенных объектов, но обычно утиной типизации вполне хватает. \ No newline at end of file diff --git a/1-js/7-js-misc/3-json/1-serialize-object/solution.md b/1-js/7-js-misc/2-json/1-serialize-object/solution.md similarity index 100% rename from 1-js/7-js-misc/3-json/1-serialize-object/solution.md rename to 1-js/7-js-misc/2-json/1-serialize-object/solution.md diff --git a/1-js/7-js-misc/3-json/1-serialize-object/task.md b/1-js/7-js-misc/2-json/1-serialize-object/task.md similarity index 100% rename from 1-js/7-js-misc/3-json/1-serialize-object/task.md rename to 1-js/7-js-misc/2-json/1-serialize-object/task.md diff --git a/1-js/7-js-misc/3-json/2-serialize-object-circular/solution.md b/1-js/7-js-misc/2-json/2-serialize-object-circular/solution.md similarity index 100% rename from 1-js/7-js-misc/3-json/2-serialize-object-circular/solution.md rename to 1-js/7-js-misc/2-json/2-serialize-object-circular/solution.md diff --git a/1-js/7-js-misc/3-json/2-serialize-object-circular/task.md b/1-js/7-js-misc/2-json/2-serialize-object-circular/task.md similarity index 100% rename from 1-js/7-js-misc/3-json/2-serialize-object-circular/task.md rename to 1-js/7-js-misc/2-json/2-serialize-object-circular/task.md diff --git a/1-js/7-js-misc/3-json/article.md b/1-js/7-js-misc/2-json/article.md similarity index 93% rename from 1-js/7-js-misc/3-json/article.md rename to 1-js/7-js-misc/2-json/article.md index 6fe91086..d52a8f1e 100644 --- a/1-js/7-js-misc/3-json/article.md +++ b/1-js/7-js-misc/2-json/article.md @@ -32,8 +32,6 @@
  • `JSON.stringify` -- превращает объекты в строку в формате JSON, используется, когда нужно из JavaScript передать данные по сети.
  • -Далее мы разберём их подробнее. - ## Метод JSON.parse Вызов `JSON.parse(str)` превратит строку с данными в формате JSON в JavaScript-объект/массив/значение. @@ -63,7 +61,7 @@ alert( user.friends[1] ); // 1 Данные могут быть сколь угодно сложными, объекты и массивы могут включать в себя другие объекты и массивы. Главное чтобы они соответствовали формату. [warn header="JSON-объекты ≠ JavaScript-объекты"] -**Объекты в формате JSON похожи на обычные JavaScript-объекты, но отличаются от них более строгими требованиями к строкам -- они должны быть именно в двойных кавычках.** +Объекты в формате JSON похожи на обычные JavaScript-объекты, но отличаются от них более строгими требованиями к строкам -- они должны быть именно в двойных кавычках. В частности, первые два свойства объекта ниже -- некорректны: @@ -77,6 +75,8 @@ alert( user.friends[1] ); // 1 ``` Кроме того, в формате JSON не поддерживаются комментарии. Он предназначен только для передачи данных. + +Есть нестандартное расширение формата JSON, которое называется [JSON5](http://json5.org/) и как раз разрешает ключи без кавычек, комментарии и т.п, как в обычном JavaScript. На данном этапе, это отдельная библиотека. [/warn] ## Умный разбор: JSON.parse(str, reviver) @@ -111,7 +111,7 @@ alert( event.date.getDate() ); // ошибка! Дело в том, что значением `event.date` является строка, а отнюдь не объект `Date`. Откуда методу `JSON.parse` знать, что нужно превратить строку именно в дату? -**Для интеллектуального восстановления из строки у `JSON.parse(str, reviver)` есть второй параметр `reviver`, который является функцией `function (key, value)`.** +**Для интеллектуального восстановления из строки у `JSON.parse(str, reviver)` есть второй параметр `reviver`, который является функцией `function(key, value)`.** Если она указана, то в процессе чтения объекта из строки `JSON.parse` передаёт ей по очереди все создаваемые пары ключ-значение и может возвратить либо преобразованное значение, либо `undefined`, если его нужно пропустить. @@ -155,9 +155,7 @@ alert( schedule.events[1].date.getDate() ); // сработает! ## Сериализация, метод JSON.stringify -Метод `JSON.stringify(value, replacer, space)` преобразует (*"сериализует"*) значение в JSON-строку. - -Он поддерживается во всех браузерах, включая IE8+. Для более IE7- рекомендуется библиотека [JSON-js](https://github.com/douglascrockford/JSON-js), которая добавляет аналогичную функциональность. +Метод `JSON.stringify(value, replacer, space)` преобразует ("сериализует") значение в JSON-строку. Пример использования: @@ -292,9 +290,9 @@ alert(str); // {"name":"Вася","age":25} В примере выше функция пропустит свойство с названием `window`. Для остальных она просто возвращает значение, передавая его стандартному алгоритму. А могла бы и как-то обработать. -**Функция `replacer` работает рекурсивно.** - +[smart header="Функция `replacer` работает рекурсивно"] То есть, если объект содержит вложенные объекты, массивы и т.п., то все они пройдут через `replacer`. +[/smart] ### Красивое форматирование diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/1-output-numbers-100ms/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/1-output-numbers-100ms/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/1-output-numbers-100ms/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/1-output-numbers-100ms/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/1-output-numbers-100ms/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/1-output-numbers-100ms/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/1-output-numbers-100ms/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/1-output-numbers-100ms/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/2-output-numbers-100ms-settimeout/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/2-output-numbers-100ms-settimeout/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/2-output-numbers-100ms-settimeout/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/2-output-numbers-100ms-settimeout/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/2-output-numbers-100ms-settimeout/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/2-output-numbers-100ms-settimeout/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/2-output-numbers-100ms-settimeout/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/2-output-numbers-100ms-settimeout/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/3-highlight-tactics/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/3-highlight-tactics/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/3-highlight-tactics/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/3-highlight-tactics/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/3-highlight-tactics/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/3-highlight-tactics/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/3-highlight-tactics/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/3-highlight-tactics/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/4-settimeout-result/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/4-settimeout-result/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/4-settimeout-result/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/4-settimeout-result/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/4-settimeout-result/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/4-settimeout-result/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/4-settimeout-result/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/4-settimeout-result/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/5-setinterval-result/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/5-setinterval-result/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/5-setinterval-result/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/5-setinterval-result/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/5-setinterval-result/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/5-setinterval-result/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/5-setinterval-result/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/5-setinterval-result/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/6-who-runs-faster/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/6-who-runs-faster/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/6-who-runs-faster/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/6-who-runs-faster/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/6-who-runs-faster/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/6-who-runs-faster/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/6-who-runs-faster/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/6-who-runs-faster/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/7-delay/_js.view/solution.js b/1-js/7-js-misc/3-setTimeout-setInterval/7-delay/_js.view/solution.js similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/7-delay/_js.view/solution.js rename to 1-js/7-js-misc/3-setTimeout-setInterval/7-delay/_js.view/solution.js diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/7-delay/_js.view/test.js b/1-js/7-js-misc/3-setTimeout-setInterval/7-delay/_js.view/test.js similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/7-delay/_js.view/test.js rename to 1-js/7-js-misc/3-setTimeout-setInterval/7-delay/_js.view/test.js diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/7-delay/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/7-delay/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/7-delay/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/7-delay/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/7-delay/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/7-delay/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/7-delay/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/7-delay/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/_js.view/solution.js b/1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/_js.view/solution.js similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/_js.view/solution.js rename to 1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/_js.view/solution.js diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/_js.view/test.js b/1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/_js.view/test.js similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/_js.view/test.js rename to 1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/_js.view/test.js diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/8-debounce/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/8-debounce/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/_js.view/solution.js b/1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/_js.view/solution.js similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/_js.view/solution.js rename to 1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/_js.view/solution.js diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/_js.view/test.js b/1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/_js.view/test.js similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/_js.view/test.js rename to 1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/_js.view/test.js diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/solution.md b/1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/solution.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/solution.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/solution.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/task.md b/1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/task.md similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/9-throttle/task.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/9-throttle/task.md diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/article.md b/1-js/7-js-misc/3-setTimeout-setInterval/article.md similarity index 94% rename from 1-js/7-js-misc/4-setTimeout-setInterval/article.md rename to 1-js/7-js-misc/3-setTimeout-setInterval/article.md index bec1b1a0..5ac95ffe 100644 --- a/1-js/7-js-misc/4-setTimeout-setInterval/article.md +++ b/1-js/7-js-misc/3-setTimeout-setInterval/article.md @@ -53,7 +53,7 @@ setTimeout(func, 1000, "Привет", "Вася"); // Привет, Вася */!* ``` -**Если первый аргумент является строкой, то интерпретатор создаёт анонимную функцию из этой строки.** +Если первый аргумент является строкой, то интерпретатор создаёт анонимную функцию из этой строки. То есть такая запись тоже сработает: @@ -62,7 +62,7 @@ setTimeout(func, 1000, "Привет", "Вася"); // Привет, Вася setTimeout("alert('Привет')", 1000); ``` -**Однако, использование строк не рекомендуется, так как они могут вызвать проблемы при минимизации кода, и, вообще, сама возможность использовать строку сохраняется лишь для совместимости.** +Однако, использование строк не рекомендуется, так как они могут вызвать проблемы при минимизации кода, и, вообще, сама возможность использовать строку сохраняется лишь для совместимости. Вместо них используйте анонимные функции, вот так: @@ -184,7 +184,7 @@ setTimeout(function run() { При `setInterval` внутренний таймер будет срабатывать чётко каждые `100` мс и вызывать `func(i)`: - + Вы обратили внимание?... @@ -202,7 +202,7 @@ setTimeout(function run() { А так будет выглядить картинка с рекурсивным `setTimeout`: - + **При рекурсивном `setTimeout` задержка всегда фиксирована и равна 100мс.** @@ -223,7 +223,7 @@ setTimeout(function() {}, 100);
  • Для `setInterval` -- ссылка исчезнет при очистке таймера.
  • -**Так как функция также тянет за собой всё замыкание, то ставшие неактуальными, но не отменённые `setInterval` могут приводить к излишним тратам памяти.** +Так как функция также тянет за собой всё замыкание, то ставшие неактуальными, но не отменённые `setInterval` могут приводить к излишним тратам памяти. [/smart] @@ -259,8 +259,10 @@ setTimeout(function() {}, 100); **Вывод: на частоту 4мс стоит ориентироваться, но не стоит рассчитывать.** +[online] Посмотрим снижение частоты в действии на небольшом примере. + При клике на кнопку ниже запускается `setInterval(..., 90)`, который выводит список интервалов времени между 25 последними срабатываниями таймера. Запустите его. Перейдите на другую вкладку и вернитесь.
    @@ -287,7 +289,7 @@ function timerIntervalLog() { Если ваш браузер увеличивает таймаут при фоновом выполнении вкладки, то вы увидите увеличенные интервалы, помеченные красным. Кроме того, вы заметите, что таймер не является идеально точным ;) - +[/online] ## Разбивка долгих скриптов @@ -314,9 +316,3 @@ function timerIntervalLog() { -## Полезные декораторы - -Эти декораторы часто востребованы в реальных проектах. Постарайтесь сделать их без подсказок. - - - diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/setInterval-anim.view/index.html b/1-js/7-js-misc/3-setTimeout-setInterval/setInterval-anim.view/index.html similarity index 100% rename from 1-js/7-js-misc/4-setTimeout-setInterval/setInterval-anim.view/index.html rename to 1-js/7-js-misc/3-setTimeout-setInterval/setInterval-anim.view/index.html diff --git a/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg b/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg new file mode 100644 index 00000000..5c53444b --- /dev/null +++ b/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg @@ -0,0 +1,51 @@ + + + + setinterval-interval + Created with Sketch. + + + + + + + func(1) + + + + + + + + + + func(2) + + + + + + + + + + func(3) + + + + + + + + 100 + + + 200 + + + 300 + + + + + \ No newline at end of file diff --git a/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg b/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg new file mode 100644 index 00000000..1c9d78cc --- /dev/null +++ b/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg @@ -0,0 +1,62 @@ + + + + settimeout-interval + Created with Sketch. + + + + + + + func(1) + + + + + + + + + + func(2) + + + + + + + + + + func(3) + + + + + + + + 100 + + + 100 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/1-js/7-js-misc/5-eval/1-eval-calculator/solution.md b/1-js/7-js-misc/4-eval/1-eval-calculator/solution.md similarity index 100% rename from 1-js/7-js-misc/5-eval/1-eval-calculator/solution.md rename to 1-js/7-js-misc/4-eval/1-eval-calculator/solution.md diff --git a/1-js/7-js-misc/5-eval/1-eval-calculator/task.md b/1-js/7-js-misc/4-eval/1-eval-calculator/task.md similarity index 100% rename from 1-js/7-js-misc/5-eval/1-eval-calculator/task.md rename to 1-js/7-js-misc/4-eval/1-eval-calculator/task.md diff --git a/1-js/7-js-misc/5-eval/article.md b/1-js/7-js-misc/4-eval/article.md similarity index 81% rename from 1-js/7-js-misc/5-eval/article.md rename to 1-js/7-js-misc/4-eval/article.md index d2c961fa..26f6ee40 100644 --- a/1-js/7-js-misc/5-eval/article.md +++ b/1-js/7-js-misc/4-eval/article.md @@ -65,15 +65,15 @@ alert(a); // ошибка, переменная не определена Иными словами, в новом стандарте `eval` имеет свою область видимости, а к внешним переменным обращается через замыкание, аналогично тому, как работают обычные функции. [/smart] -## Грамотное использование eval +## Неграмотное использование eval Начнём с того, что `eval` применяется очень редко. Действительно редко. Есть даже такое выражение "eval is evil" (eval -- зло). -Причина проста: когда-то JavaScript был гораздо более слабым языком, чем сейчас и некоторые вещи без `eval` было сделать невозможно. Но те времена давно прошли. И теперь найти тот случай, когда действительно надо выполнить код из строки -- это надо постараться. +Причина проста: когда-то JavaScript был гораздо более слабым языком, чем сейчас, и некоторые вещи без `eval` было сделать невозможно. Но те времена давно прошли. И теперь найти тот случай, когда действительно надо выполнить код из строки -- это надо постараться. Но если вы действительно знаете, что это именно тот случай и вам необходим `eval` -- есть ряд вещей, которые нужно иметь в виду. -**Доступ к локальным переменным -- самое страшное зло `eval`.** +Доступ к локальным переменным -- худшее, что можно сделать при `eval`. Дело в том, что локальные переменные могут быть легко переименованы: @@ -86,9 +86,7 @@ function sayHi() { Переменная `phrase` может быть переименована в `hello`, и если строка `str` обращается к ней -- будет ошибка. -**Современные средства сжатия JavaScript переименовывают локальные переменные автоматически.** - -Это считается безопасным, так как локальная переменная видна лишь внутри функции и если в ней везде поменять `phrase` на `p`, то никто этого не заметит. +Современные средства сжатия JavaScript переименовывают локальные переменные автоматически. Это считается безопасным, так как локальная переменная видна лишь внутри функции и если в ней везде поменять `phrase` на `p`, то никто этого не заметит. До сжатия: @@ -116,17 +114,19 @@ function sayHi() { } ``` -**Теперь очевидно, что если где-то в функции есть `eval`, то любое его взаимодействие с локальными переменными будет нарушено с непредсказуемыми побочными эффектами.** +Итак, если где-то в функции есть `eval`, то его взаимодействие с локальными переменными будет нарушено с непредсказуемыми побочными эффектами. Некоторые инструменты сжатия предупреждают, когда видят `eval` или стараются вообще не сжимать такой код вместе с его внешними функциями, но всё это борьба с последствиями кривого кода. -### Запуск скрипта в глобальной области +Как правило, `eval` не нужен, именно поэтому говорят, "eval is evil". + +## Запуск скрипта в глобальной области Ок, взаимодействовать с локальными переменными нельзя. -Но допустим мы загрузили с сервера или вручную сгенерировали скрипт, который нужно выполнить в глобальной области, вне любых функций. +Но допустим мы загрузили с сервера или вручную сгенерировали скрипт, который нужно выполнить. Желательно, в глобальной области, вне любых функций, чтобы он уж точно к локальным переменным отношения не имел. -**Есть два трюка для выполнения кода в глобальной области.** +Здесь `eval` может пригодиться. Есть два трюка для выполнения кода в глобальной области:
    1. Везде, кроме IE8-, достаточно вызвать `eval` не напрямую, а через `window.eval`. @@ -171,11 +171,11 @@ var a = 1; })(); ``` -### Взаимодействие с внешним кодом, new Function +## Внешние данные через new Function -Бывает, что в код, выполняемый при помощи `eval`, всё же нужно передать какие-то значения. +Итак, у нас есть код, который, всё же, нужно выполнить динамически, через `eval`, но не просто скрипт -- а ему нужно передать какие-то значения. - Считать их из локальных переменных нельзя: как мы видели, это подвержено ошибкам при переименовании переменных и сразу ломается при сжатии JavaScript. +Как мы говорили ранее, считать их из локальных переменных нельзя: это подвержено ошибкам при переименовании переменных и сразу ломается при сжатии JavaScript. Да и вообще, неочевидно и криво. **К счастью, существует отличная альтернатива `eval`, которая позволяет корректно взаимодействовать c внешним кодом: `new Function`.** diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/interval1.png b/1-js/7-js-misc/4-setTimeout-setInterval/interval1.png deleted file mode 100755 index 2c60a630a25be05a8bd412f24c6b9cce98560ace..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4636 zcmX|@byU>N_s2gxptPV83ZfvjG>Cw7EZymXAV@BPq$ns4r4j;ybh(I1m*mpjAl=;{ zjfBL;{Py|&@tZR<_nv$1xij~5-sjAG)Yny`reLCgAc$H+UD*(VhzbZkl8l(Je~d+O z5DdAyy4gzzqP+RvB7#!C+=3tqz2|VazP_WIm)lE6H+NPIIGokp)6L=e3wsFinL!yL zjg7V#WX~2(l(fPEB(3Pl_CP^Vz_wO$Z0jh*uG8E8?;Y%fj-3cP_m`33yWdRO2od>=Qj$TT4aHAkiy$2rEEw+l_(IE%w+^+kU-D+Y;9H{eF5khcfiId6o@)c@g;)H zY_(IRR+1Y)d4=E6x>JdWb4_`6uCNF_hN|{Uzhonl7IhlY5DFPvQ7dD{5$VVq` z{f~93f?(^Ll-m!29?~ow|AL^57ygG|;D5FuRgfg~f5XbKSu~GTMC zuq>nZ+cj)zOnK>64Q_ZuDW^srKghcOQ7M+EgL4Gftt|3mh~*Z=pb{rJBHtK++^4q0 zf3>)2O}&~|&LHTh&aGt(Mn)Xy_1(t`+ z296VwEh6uZf8TC>fB5!r;BvPY3IrUaj}wWyQah~QxD z38OQ(uN5PvE@BvN%xa>}awO~dk7THYYHf^kH}v2gHl$unBtEFjKz59(5?+%l!Sq4( zJLQm_mq=$88Q+0m!i5t;3g=l@l;O!C|H$K82aMyhPN24^;c;v>2>WHf$ z`xbv*+Cz0F_PCxIJLfV|$#{NNzV6^P)LPq`=o<4H+mSg1%0|H>^W10!X4vbbd3&99 z{o(pm`{W0TCO=XQ=X1XqUl)shqEz$^ZTQn5H23DiSE-Wy?-?|-vWmVZj50YgI-WiM z`P-}Z`h3D&-J~JJNZ*#)@1)=FlW?muVy!`C^g?vhrW0OAP=sZg!zhYGrQKQv7 z^0CAMlYEr^juEpFGEX`;G#{DkW-OoSZ-)DL)vQgsO%I-FkWp&2THnb^X?wfeho8-v zx*+6LkXSocY5(-UuX*c|aV&KaviW)6B;lf(qKI-^DkbyCM8sO$!nIx|erHN`GXZVPTTWc0ul z)TYt2(R>a?4ug)Y)M>F`zH=7yw>o9-CfA((8m$#jT$xXG{xbBRHHQ7Ab(%RD6UH!lMzxPk1h^5JjOg`95+f18GTdaEa z#ODb=Iu~7SuTi%4B-?BsUGEzFMA9tF)a1$2a@8VK5$8l*QC-gUoEqIY-GcN9WLwD} zbPqbO>UPQc)YaKyvu!70&I^vyn=_U9<0v{5xABeLe@`{S^Pk6Q2g1e4?h)0jKw zyBpRty-q!zF25Z)MWvOeweIE4{@fSef4(0&_x0DquRQ5yqA!Qqy5UO(>%;PtUlW9# z7YqIM;CeyG%1w45TcH?4(PT+gmUsS{^#P;YUpddOt8%L{5(^I&%&dM%G*@+3_35>_ zrCS9r!UNcW9e4|dp`1WYA|&z0AjKf_e=z57|7B~6l9!sb|64Y_rx=-0qe!n9{jTWU zdP@w9N3{7VE3}=G#E)NpKEBp5P6BsYC_sFx^M-hl;ekxeSqoAWqbT0A zva}-a3k{18hfTpPc<<^Ssq^Z*jopo@7kk6eA)3X%Uh{!F?{mF2wf20PR60ZIYgU&B zvQiBq?_*;=dno0z>BOOt+sF-v*L{Wd!DaXk*N7^iy9uN{&gGvAub2r?pp~+J8$W?f z@{*Pz?GX-5{U3WJ6ARM$`uBd7_s)Kn_;TU+u^fxe3eiFcu$XQ%6go+k$#}e( zrC6oE!WZ)RYN1SZIGWdGT(4jckyPy8!1Y$gImoi5_gzaRk@F6v3B`J_-_G3R-cvnO zA+yCw>&p-nvmE`$@M5}I#w7ZHDcGFPI&-L7Z&S)#d_`VCdyUZF&uGG^r71WE5EA~$Rs>}){^}}ZC-lO3)KbmB!>+N%T2KuGtjWdl;j4zGF_ebU{ zttZc2hxeI%>;FF79iKI?Kf}rOG|rk3ndpo=jN4;0Fy&|R#UJyx&J!^%Pm6S2JodVX zM~HEx?AOqfHDlu}BeKSZ(Gz?-YS+5%HR4eR-U8I()Fz+4bgrli;$C2>QSm{no%mo+{V0C@0Z;0#P-5ltlI27nlBG!%-(1o8}~e) z*?Kn`hddgLL?xj7`~@%Op3Qzo`)=9}EIQG+9n{i#Esv;xh)wBw=>dzQYOkZR+4Y-< zYDDT!DJz$4uWcE8+^Akx-Iu!5;u;I*n*Fy8mA(#VTg>Sa1M?>hZ~icViScwv+4+X! zw{B-0XQzt{^ZuUPxPiX9X>acRqI$i0s}qGi#cB9_=(xN&es1buF2JeTJL7nGPVMY! z@cTi0%xN?Y{7Q66bYBQ{$WHnrIZ4SCc~9IHHU~avH^`OFi2si7D`sR6@;@KnnH(5S z9{!eeloZ$G@^2PyyLspK&)Lq_&by60KB6L)Cyz0EE#7n9EC)XpY4q0qWetD<$BWWa zqoEv3-0iqHKth~+jI|XNm?iu-S#8w~bs*?1CjeuWhUX2m^uw0s@3NIB2P;s33?0f)o@KKqC)sC<8R{f)Z|QPZ*&zsN-Fl z>;w6nBZc1Q=jWKdc+g;YK2iY6WkIbWXnG3ToIvMGFcbz3nzztlnC;2Gaoix04U@zM zia9|kFQ`_;bw`30OVEzQuQY>J?|DC-%iZOTs7LGXxK7e|0GbDsbAbvzP^So*4MCS3 z81&zcemK=pj>BR$=SF4*TK?8*f+jvt1p`&wpqdXf$bcS8Y#x00s|yy31t(LWQDmq# z6Wb99>R_Or7u1XHrE;CeijHIjjJ8)`Cx7737Pxw6T+1t5Me?Tp0rC4xQ!+*lYGMd4=9xWz`$D*zgKL4`h8r~s=?U@8|g_ys%s z4f87r^D7bmX9cWDgZWRGo+vO~27dQ|-BEC|f*H-+9B9HW)MCpmK|Sl~);#`n=koFr z?B<@8NL|*wIxO(`GgSfb7;Il6ZlMl0UV#6qJ)C3*W@P?kzy>Sg!KM#beRQ17Jy;rb zad82#7neVE!B0hO?F(GvD_mP3cBui^pNJby$BpNKjxew_vM@1-+nrbn=LI<;U?K%% zapFtW@wHa?Mkjom7rrY9j3nR(9KeVv=+y@;+@OsQv~YkP1+bC~s&v-sV{w@C(;q6J z1P*@bfevoa$pw1F!Qux{uQ^!tWqGU{kHc;>r)*at!LT&w<_3L2V8IQvX{~*N0qo($ zVhorP2K{_s-W>F4ZN_kqG!+7H0j4d$Bp;a50wZeM@m!$91e}h8Q3MW;e|~dF(M=NL=tyN%L*kwJh}r2A5$9-O`TAM`P{f2n}YpPr|Oeh zt}x8@e82*RWu_AB9t*`2S!qzO-$V0QSrtn?di$I7-fP@R1D7HEBDCoF?1sP-ba=X) z-A2i>ZB|Gb&206bn_Rl7U!1oq>P0QnIK|=vrW}B1m21N#HIulLMR{V0E>gads8{~Q zE5m-UkGVq0C?Myz{C!UUFF8Vxv&r*x=9F0?!0!-q&l5f!x;NO>V}whRgLi7DSoI#2 zjp^g9`%(pbqY#$;DdOJgvlsYSCPJ|bN;-by>h2vkI)TUa${Km!ux*dG`>PiGbG%Z@ z)_W1TrW#JURMx&#M@@J2Dz`-fa_A^@w*MFx+&$*tR5L*b7h%*i zq{?(QdTa9vXCh_RlyTdbS2svas58*D6tZs@n&e&MNT|${jrYQ0gkiT8%xXsJW9Aa_ zrTR>EsnaD?7Luy--of@CeP0@BWkHAKHcC$E=88t~joicf-_vLKN)5 z2ln*!b^^2v0u=(Zf9Q&rdZ$^GRcfw;SJYIT=THYLeba~0QT)H@KzQs_D%+heP-MED zwdS@cOrV`U@g2k&;SY)o>D9~P^XIFoAM3a&?(iRuQ)C{5!t#?;mCP$0ZH9P9cgpt_ z7}UpK^AwwuOVjT6LV}UKQwaepcHUWE50`ptu5-`YkRxK!zbuD$cqzaB!jV#UUpm^M zI$W9FLUJL&t?B#0&oy_>LkG(`tI{0?ip^J)9U-R*i}Efriu5!22{&Uuk0Vih9?lE3 pei1?2dO|)BYA{P7tQda}aw1#Su!&{B65dEiLq%7)Sn1ij{{uKmb{7Bu diff --git a/1-js/7-js-misc/4-setTimeout-setInterval/timeout.png b/1-js/7-js-misc/4-setTimeout-setInterval/timeout.png deleted file mode 100755 index 2ab19038bf1a85c1fe6be2618a6212e5f7263935..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2404 zcmY*bdpwi-AAhV>d*;%tMJE=I=8{`Q$yCgcTNp{{b^AMoow%m8Loi-c* z?g!XBCquq4WnU7Bw2UW{Zm*1p@IU<5BUL!h@dOp8QCGexX$Usc~I5lvBScs=9hv7j=_RM}Mstt`FW8%fG^>Lizy}`LrIrKWGy4FLxu1|RH(+3w#&jttY0l-~*l^jvd$`n+2 zoQo~nNw}kAcJHufVEr{GkwUkd{~5K+5B(}WF7hV%ii~(C;jHuRSxLB`yT*+SZTbn4b^mTbkx!cNPuf%bvK|R62L;n=-2H zc~a-`-QZJ*H+%>%Ei!}Jb>t5Gwf}u=wTk(Rr-!huFYf5)=alBs%6!U_ zedZfTqTMtH;aR`V2m&FIDhJ+d^#dE9wsFM^0U@p1H1E$XkH9%T44il#EHq#Qme!XG z>%Dia)OJw*oG;!dso=GaW{{%wuc+s>rMW0%8$PUxY_WUh7`3m`dE~u+TF}u}4nh7y z`N^b?vxDO9YiH#Z5O$2t&S7et7W1;FIxcA46nmKA{ww&R#@rL7Is1kzh%gNKa1$-ZsP-sHG{|DP1PgO6N|dCiU}TPacf zU*dqT*V&PS5n%-fKRwT@=r|+1>pfD7ZFba1PXW-;=92Lk5l{EBI`#{5D>|w%2P;|@ z2{Ui(s1_#)?CVz2-i_1RLQfP%BO@cz8XettRoo@J`p5rZS?ius$|RLZ1qh1?(H^=x ze;11)qoO;jj;XhWR1k=Q{ARTEij=6*iAb-rR%6ISP5OLOEUZ9_c>;GCBaxNc9PQrY zolWCCdg>f%-+E2V^n_x@(J; zSQ66?&oCZM$fIG(u=+uDC=`+a21*J5Fzi22q$=H7iiyea78%J2*=`BGQHz?^su9>wTVNOQ6z`dr>qc3)(x`Ukd9ruFvWq(J>xTu-scXPC85)kNVM$3MqKWB6 z97~9+&8;|;KEy9h#J4d?Fj^N2s+=|YNYO8ec*I6HK)PYi59fLOEq6*r2Ww3B)!d` zWNpj~gZDEIME?T>cMyVbRBDP^434XBtWsV-*rcr2Gt_f$N^M*OM1%)*kKeCt#v}=j zD|uR-?s&d?en=l`DCdYy9QjhaJV|iQM|^#SHdpjbAA(~_V|yD$`&YvPKeqQ>7OEUc zdpu?!x=<|FL<+*#)BWWS%ZHViOo?8gjOEPD12Rzai)?UnX<-{Sg5^O`;Lk6V)cYvk|6EzEhT{!m19PK zdl&Y*mTC{L`zyxmR66yEn|5p^M+E+mbL#M{iuF%0jMI0HVJt7`o#GnE> z%Ot4{EP@~vkdSZ?1`G-OdJ8781&GICwkQAxxHr}V>JR{+-(2GC;5w~EE;Ak6;O^0| zL0wOeLUq8|K7uIJ^-MOO;GvP8^`xabSF_!S-Kce?XUGM`W_mYFx->je0gy8BM4$)- z=)iwTXU3{O1r*+|=S{@{l#SN2+-lY{F#ZNIy<+FW)Rn>-ZB_LIV(@^1m~l}U@na86 zy_9(SM~WUio)~iz1t6I}pZ^nGoFjg9-WE@+$p#6~pe}X7O9lf2XaE6{E)oKeBD=@K zGi%R5?5;WSPJ!YEe=5dL@SsvJ_04HB8;>;paJYW#knIJAqFV~~fj29;iK*W@V$c0% zsxelK(l0q4l*?1e%N|tUl&rLcxZ4gwn+QZB;ZH>|f84_jH27N-3T#~Oot)@gQrJ51 zVJ={|aU4Y5?3Ah4h}dYI$P2q&*5db>LksunsqQye2T427m=e79ywGqjbQSb<>n%ki zBJxi0Np|x)9<6SJk@PE34?Mmf8%qmYb1p$AHF~3WM(f|>m%Ve-|I|;kA+EuXxg&Ij z-tG38zxN`j;ws*B=LTD9p#N$kh(^BGb|K~Qjj9(EwHyYx@mB+6ezD_!Ce67GDyca+ z$MVUO{5VYF%0pz&uk+Y5rz~cRQNcI&cocfa@@f5-#5(XDd2^FpNv~(BHdL!ExFSpz zTjOQVhQ{`nq=&GGXcLkm>9Zi=}+h7OL6tV>Mk|g!<}* ll@z>U$
    -**Таким образом, при ошибке в `try` скрипт не падает, а продолжает выполнение, и мы получаем возможность обработать ошибку внутри `catch`.** +**Таким образом, при ошибке в `try` скрипт не "падает", и мы получаем возможность обработать ошибку внутри `catch`.** Посмотрим это на примерах. @@ -87,14 +89,33 @@ alert("Потом код продолжит выполнение..."); [warn header="`try..catch` подразумевает, что код синтаксически верен"] -Если грубо нарушена структура кода, например не закрыта фигурная скобка или где-то стоит лишняя запятая, то никакой `try..catch` здесь не поможет. Такие ошибки называются *синтаксическими*, интерпретатор не может понять такой код и запустить его. +Если грубо нарушена структура кода, например не закрыта фигурная скобка или где-то стоит лишняя запятая, то никакой `try..catch` здесь не поможет. Такие ошибки называются *синтаксическими*, интерпретатор не может понять такой код. Здесь же мы рассматриваем ошибки *семантические*, то есть происходящие в корректном коде, в процессе выполнения. [/warn] +[warn header="`try..catch` работает только в синхронном коде"] +Ошибку, которая произойдёт в коде, запланированном "на будущее", например, в `setTimeout`, `try..catch` не поймает: -### Объект ошибки +```js +//+ run +try { + setTimeout(function() { + throw new Error(); // вылетит в консоль + }, 1000); +} catch(e) { + alert("не сработает"); +} +``` + +На момент запуска функции, назначенной через `setTimeout`, этот код уже завершится, интерпретатор выйдет из блока `try..catch`. + +Чтобы поймать ошибку внутри функции из `setTimeout`, и `try..catch` должен быть в той же функции. +[/warn] + + +## Объект ошибки В примере выше мы видим объект ошибки. У него есть три основных свойства:
    @@ -195,7 +216,9 @@ try { **В качестве конструктора ошибок можно использовать встроенный конструктор: `new Error(message)` или любой другой.** -В данном случае мы используем конструктор `new SyntaxError(message)`, он создаст ошибку того же типа, что и `JSON.parse`. +В JavaScript встроен ряд конструкторов для стандартных ошибок: `SyntaxError`, `ReferenceError`, `RangeError` и некоторые другие. Можно использовать и их, но только чтобы не было путаницы. + +В данном случае мы используем конструктор `new SyntaxError(message)`. Он создаёт ошибку того же типа, что и `JSON.parse`. ```js //+ run @@ -226,15 +249,15 @@ try { Конечно, может! Код -- это вообще мешок с ошибками, бывает даже так что библиотеку выкладывают в открытый доступ, она там 10 лет лежит, её смотрят миллионы людей и на 11й год находятся опаснейшие ошибки. Такова жизнь, таковы люди. -**Блок `catch` в нашем примере предназначен для обработки ошибок, возникающих при некорректных данных.** +Блок `catch` в нашем примере предназначен для обработки ошибок, возникающих при некорректных данных. Если же в него попала какая-то другая ошибка, то вывод сообщения о "некорректных данных" будет дезинформацией посетителя. -Если же в него попала какая-то другая ошибка, то вывод сообщения о "некорректных данных" будет дезинформацией посетителя. Ошибку, о которой `catch` не знает, он должен пропустить. +**Ошибку, о которой `catch` не знает, он не должен обрабатывать.** -**Такая техника называется *"проброс исключения"*: в `catch(e)` мы анализируем объект ошибки, и если он нам не подходит, то делаем `throw e`.** +Такая техника называется *"проброс исключения"*: в `catch(e)` мы анализируем объект ошибки, и если он нам не подходит, то делаем `throw e`. При этом ошибка "выпадает" из `try..catch` наружу. Далее она может быть поймана либо внешним блоком `try..catch` (если есть), либо "повалит" скрипт. -Например: +В примере ниже `catch` обрабатывает только ошибки `SyntaxError`, а остальные -- выбрасывает дальше: ```js //+ run @@ -267,11 +290,9 @@ try { } ``` -**Ошибка, которая возникла внутри `catch`, "выпадает" наружу.** +Заметим, что ошибка, которая возникла внутри блока `catch`, "выпадает" наружу, как если бы была в обычном коде. -Возможно, что этот `try..catch` вызывается внутри другого, более общего `try.catch`, и он как раз умеет обрабатывать выпавшую ошибку. - -Тогда получится так: +В следующем примере такие ошибки обрабатываются ещё одним, "более внешним" `try..catch`: ```js //+ run @@ -305,44 +326,60 @@ try { В примере выше `try..catch` внутри `readData` умеет обрабатывать только `SyntaxError`, а внешний -- все ошибки. -Если же внешнего `try..catch` нет, то ошибка "вываливается" в консоль, и скрипт умирает. +Без внешнего проброшенная ошибка "вывалилась" бы в консоль, с остановкой скрипта. -### Последняя надежда: window.onerror +## Оборачивание исключений -Допустим, ошибка произошла вне блока `try..catch` или выпала из `try..catch` наружу, во внешний код. Скрипт упал. +И, для полноты картины -- последняя, самая продвинутая техника по работе с ошибками. Она, впрочем, является стандартной практикой во многих объектно-ориентированных языках. -Можно ли как-то узнать о том, что произошло? Да, конечно. +Цель функции `readData` в примере выше -- прочитать данные. При чтении могут возникать разные ошибки, не только `SyntaxError`, но и, возможно, к примеру, `URIError` (неправильное применение функций работы с URI), да и другие. -**В браузере существует специальное свойство `window.onerror`, если в него записать функцию, то она выполнится и получит в аргументах сообщение ошибки, текущий URL и номер строки, откуда "выпала" ошибка.** +Код, который вызвал `readData`, хотел бы иметь информацию об ошибке, но проверять и перехватывать все типы ошибок ему недосуг. Тогда мы делаем одну ошибку, которая относится именно к общей операции, например `ReadError` и будем генерировать её. А "исходную" ошибку -- присваивать в свойство, на случай, если она, всё же, понадобится. -Необходимо лишь позаботиться, чтобы функция была назначена заранее. - -Например: - -```html - - +function readData() { + var data = '{ bad data }'; + + try { + // ... + JSON.parse(data); + // ... + } catch(e) { + // ... + if (e.name == 'URIError') { + throw new ReadError("Ошибка в URI", e); + } else if (e.name == 'SyntaxError') { +*!* + throw new ReadError("Синтаксическая ошибка в данных", e); +*/!* + } else { + throw e; // пробрасываем + } + } +} + + +try { + readData(); +} catch(e) { + if (e.name == 'ReadError') { + alert(e.message); + alert(e.cause); // оригинальная ошибка-причина + } else { + throw e; + } +} ``` -Как правило, роль `window.onerror` заключается в том, чтобы не оживить скрипт -- скорее всего, это уже невозможно, а в том, чтобы отослать сообщение об ошибке на сервер, где разработчики о ней узнают. - -Существуют даже специальные веб-сервисы, которые предоставляют скрипты для отлова и аналитики таких ошибок, например: [](https://errorception.com/) или [](http://www.muscula.com/). - - - ## Секция finally Конструкция `try..catch` может содержать ещё один блок: `finally`. @@ -464,10 +501,49 @@ alert( func() ); // сначала finally, потом 1 [/smart] +## Последняя надежда: window.onerror + +Допустим, ошибка произошла вне блока `try..catch` или выпала из `try..catch` наружу, во внешний код. Скрипт упал. + +Можно ли как-то узнать о том, что произошло? Да, конечно. + +В браузере существует специальное свойство `window.onerror`, если в него записать функцию, то она выполнится и получит в аргументах сообщение ошибки, текущий URL и номер строки, откуда "выпала" ошибка. + +Необходимо лишь позаботиться, чтобы функция была назначена заранее. + +Например: + +```html + + +``` + +Как правило, роль `window.onerror` заключается в том, чтобы не оживить скрипт -- скорее всего, это уже невозможно, а в том, чтобы отослать сообщение об ошибке на сервер, где разработчики о ней узнают. + +Существуют даже специальные веб-сервисы, которые предоставляют скрипты для отлова и аналитики таких ошибок, например: [](https://errorception.com/) или [](http://www.muscula.com/). + ## Итого -Конструкция `try..catch` позволяет обработать произвольные ошибки в блоке кода. +Обработка ошибок -- большая и важная тема. + +В JavaScript для этого предусмотрены: + +
      +
    • Конструкция `try..catch..finally` -- она позволяет обработать произвольные ошибки в блоке кода. Это удобно в тех случаях, когда проще сделать действие и потом разбираться с результатом, чем долго и нудно проверять, не упадёт ли чего. @@ -485,28 +561,18 @@ alert( func() ); // сначала finally, потом 1 } ``` -Возможны также варианты `try..catch` или `try..finally`. - -Вместе с `try..catch` используется оператор `throw err`, который генерирует ошибку `err`, причём в качестве `err` рекомендуется генерировать объекты встроенного типа, например [Error](http://javascript.ru/Error) или совместимые с ним. - -[warn header="`try..catch` работает только в синхронном коде"] -Ошибку, которая произойдёт в будущем, например, в `setTimeout`, `try..catch` не поймает: - -```js -//+ run -try { - setTimeout(function() { - throw new Error(); // вылетит в консоль - }, 1000); -} catch(e) { - alert("не сработает"); -} -// на момент срабатывания setTimeout этот код уже завершится -``` - -[/warn] +Возможны также варианты `try..catch` или `try..finally`.
    • +
    • Оператор `throw err` генерирует свою ошибку, в качестве `err` рекомендуется использовать объекты, совместимые с встроенным типом [Error](http://javascript.ru/Error), содержащие свойства `message` и `name`.
    • +
    +Кроме того, мы рассмотрели некоторые важные приёмы: +
      +
    • Проброс исключения -- `catch(err)` должен обрабатывать только те ошибки, которые мы рассчитываем в нём увидеть, остальные -- пробрасывать дальше через `throw err`. +Определить, нужная ли это ошибка, можно, например, по свойству `name`.
    • +
    • Оборачивание исключений -- функция, в процессе работы которой возможны различные виды ошибок, может "обернуть их" в одну общую ошибку, специфичную для её задачи, и уже её пробросить дальше. Чтобы, при необходимости, можно было подробно определить, что произошло, исходную ошибку обычно присваивают в свойство этой, общей. Обычно это нужно для логирования.
    • +
    • В `window.onerror` можно присвоить функцию, которая выполнится при любой "выпавшей" из скрипта ошибке. Как правило, это используют в информационных целях, например отправляют информацию об ошибке на специальный сервис.
    • +
    diff --git a/1-js/8-oop/1-about-oop/article.md b/1-js/8-oop/1-about-oop/article.md index c6842bbc..e03121f4 100644 --- a/1-js/8-oop/1-about-oop/article.md +++ b/1-js/8-oop/1-about-oop/article.md @@ -4,16 +4,17 @@ Гораздо позже появилось [объектно-ориентированное программирование](http://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5) (ООП), которое позволяет группировать функции и данные в единой сущности -- "объекте". -Например, "пользователь", "меню", "компонент интерфейса"... -**Чтобы ООП-подход "работал", объект должен представлять собой законченную, интуитивно понятную сущность.** +При объектно-ориентированной разработке мы описываем происходящее на уровне объектов, которые создаются, меняют свои свойства, взаимодействуют друг с другом и (в случае браузера) со страницей, в общем, живут. + +Например, "пользователь", "меню", "компонент интерфейса"... При объектно-ориентированном подходе каждый объект должен представлять собой интуитивно понятную сущность, у которой есть методы и данные. [warn header="ООП -- это не просто объекты"] В JavaScript объекты часто используются просто как коллекции. Например, встроенный объект [Math](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Math) содержит функции (`Math.sin`, `Math.pow`, ...) и данные (константа `Math.PI`). -При таком использовании объектов мы не можем сказать, что "применён объектно-ориентированный подход". В частности, никакую "единую сущность" `Math` из себя не представляет. +При таком использовании объектов мы не можем сказать, что "применён объектно-ориентированный подход". В частности, никакую "единую сущность" `Math` из себя не представляет, это просто коллекция независимых функций с общим префиксом `Math`. [/warn] @@ -35,9 +36,9 @@ vasya.sayHi(); // пользователь умеет говор Здесь мы видим ярко выраженную сущность -- `User` (посетитель). -**При объектно-ориентированной разработке мы описываем происходящее на уровне объектов, которые создаются, меняют свои свойства, взаимодействуют друг с другом и (в случае браузера) со страницей, в общем, живут.** +ООП -- это наука о том, как делать правильную архитектуру. У неё есть свои принципы, например [SOLID](https://ru.wikipedia.org/wiki/SOLID_%28%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29). -ООП -- это наука о том, как делать правильную архитектуру. У неё есть свои принципы, по ним пишут книги, к примеру: +По приёмам объектно-ориентированной разработки пишут книги, к примеру: -Далее мы поговорим подробно как про ООП, так и об основных принципах, которых нужно придерживаться. \ No newline at end of file +Здесь мы не имеем возможности углубиться в теорию ООП, но основных "китов", на которых оно стоит, потрогаем за усы вдумчиво и конкретно. diff --git a/1-js/8-oop/2-internal-external-interface/article.md b/1-js/8-oop/2-internal-external-interface/article.md index 533545c2..291e205d 100644 --- a/1-js/8-oop/2-internal-external-interface/article.md +++ b/1-js/8-oop/2-internal-external-interface/article.md @@ -32,7 +32,7 @@ ## Внутренний и внешний интерфейс -В программировании есть чёткое разграничение методов и свойств объекта на две группы: +В программировании мы будем разделять методы и свойства объекта на две группы:
    • *Внутренний интерфейс* -- это свойства и методы, доступ к которым может быть осуществлен только из других методов объекта, их также называют "приватными" (есть и другие термины, встретим их далее).
    • @@ -46,27 +46,13 @@ Но снаружи кофеварка закрыта специальным кожухом, чтобы никто к ним не подобрался. Детали скрыты и недоступны. Виден лишь внешний интерфейс. -**Все, что нужно для пользования объектом -- это внешний интерфейс.** - -О внутреннем пользователю вообще знать не обязательно. - -[smart header="Между приватным и публичным"] -Приватные свойства полностью закрыты для доступа снаружи, а публичные -- наоборот, полностью открыты. Это крайности, между которыми бывают промежуточные варианты. - -
        -
      • В языке С++ можно открыть доступ к приватным переменным одного класса -- другому, объявив его "дружественным".
      • -
      • В языке Java можно объявлять переменные, которые доступны всем классам внутри "пакета".
      • -
      • Между объектами можно организовать "наследование" и сделать свойства открытыми только для "наследников", такой вариант доступа называют "защищённым".
      • -
      - -В этом учебнике будем изучать наследование и защищённые свойства, но позже, а пока сосредоточимся на приватном и публичном доступе... И, конечно, использовать мы будем JavaScript :) -[/smart] +Получив объект, всё, что нужно для пользования им -- это знать внешний интерфейс. О внутреннем же знать вообще не обязательно. Это были общие слова по теории программирования. Далее мы реализуем кофеварку на JavaScript с приватными и публичными свойствами. В кофеварке много деталей, мы конечно, не будем моделировать каждый винтик, а сосредоточимся на основных приёмах разработки. -### Шаг 1: публичное и приватное свойство +## Шаг 1: публичное и приватное свойство Конструктор кофеварок будет называться `CoffeeMachine`. @@ -85,28 +71,28 @@ var coffeeMachine = new CoffeeMachine(100); coffeeMachine.waterAmount = 200; ``` -**Локальные переменные, включая параметры конструктора, являются приватными свойствами.** +**Локальные переменные, включая параметры конструктора, можно считать приватными свойствами.** В примере выше это `power` -- мощность кофеварки, которая указывается при создании и далее будет использована для расчёта времени кипячения. К локальным переменным конструктора нельзя обратиться снаружи, но они доступны внутри самого конструктора. -**Свойства, записанные в `this`, являются публичными.** +**Свойства, записанные в `this`, можно считать публичными.** Здесь свойство `waterAmount` записано в объект, а значит -- доступно для модификации снаружи. Можно доливать и выливать воду в любом количестве. [smart header="Вопрос терминологии"] -Может возникнуть вопрос -- почему я назвал `power` "приватным свойством", ведь это локальная *переменная*, а никакое не *свойство* объекта? +Далее мы будем называть `power` как "локальной переменной", так и "приватным свойством" объекта. -Здесь небольшой конфликт терминологий. +Это, смотря, с какой стороны посмотреть. Термины "приватное свойство/метод", "публичное свойство/метод" относятся к общей теории ООП. А их конкретная реализация в языке программирования может быть различной. -Здесь ООП-принцип "приватного свойства" реализован через локальные переменные, поэтому и "локальная переменная" и "приватное свойство" -- правильные термины, в зависимости от того, с какой точки зрения посмотреть -- кода или архитектуры ООП. +Здесь ООП-принцип "приватного свойства" реализован через локальные переменные, поэтому и "локальная переменная" и "приватное свойство" -- правильные термины, в зависимости от того, с какой точки зрения взглянуть -- кода или архитектуры ООП. [/smart] -### Шаг 2: публичный и приватный методы +## Шаг 2: публичный и приватный методы Добавим публичный метод `run`, запускающий кофеварку, а также вспомогательные внутренние методы `getBoilTime` и `onReady`: @@ -141,11 +127,11 @@ coffeeMachine.waterAmount = 200; coffeeMachine.run(); ``` -**Приватные методы, такие как `onReady`, `getBoilTime` объявляются как вложенные функции.** +Приватные методы, такие как `onReady`, `getBoilTime` могут быть объявлены как вложенные функции. В результате естественным образом получается, что доступ к ним (через замыкание) имеют только другие функции, объявленные в том же конструкторе. -### Шаг 3: константа +## Шаг 3: константа Для расчёта времени на кипячение воды используется формула `c*m*ΔT / power`, где:
        @@ -171,7 +157,7 @@ function CoffeeMachine(power) { // расчёт времени для кипячения function getBoilTime() { - return this.waterAmount * WATER_HEAT_CAPACITY * 80 / power; + return this.waterAmount * WATER_HEAT_CAPACITY * 80 / power; // ошибка! } */!* @@ -181,7 +167,7 @@ function CoffeeMachine(power) { } this.run = function() { - setTimeout(onReady, getBoilTime()); + setTimeout(onReady, getBoilTime()); }; } @@ -194,9 +180,9 @@ coffeeMachine.run(); Удельная теплоёмкость `WATER_HEAT_CAPACITY` выделена большими буквами, так как это константа. -**Внимание, при запуске кода выше в методе `getBoilTime` будет ошибка. Как вы думаете, почему?** +Внимание, при запуске кода выше в методе `getBoilTime` будет ошибка. Как вы думаете, почему? -### Шаг 4: доступ к объекту из внутреннего метода +## Шаг 4: доступ к объекту из внутреннего метода Внутренний метод вызывается так: `getBoilTime()`. А чему при этом равен `this`?... Как вы наверняка помните, в современном стандарте он будет `undefined` (в старом -- `window`), из-за этого при чтении `this.waterAmount` возникнет ошибка! @@ -311,11 +297,11 @@ coffeeMachine.run(); Теперь `getBoilTime` получает `self` из замыкания. -**Конечно, чтобы это работало, мы не должны изменять `self`, а все приватные методы, которые хотят привязаться к текущему объекту, должны использовать внутри себя `self` вместо `this`.** +**Конечно, чтобы это работало, мы не должны изменять `self`, а все приватные методы, которые хотят иметь доступ к текущему объекту, должны использовать внутри себя `self` вместо `this`.** Вместо `self` можно использовать любое другое имя переменной, например `var me = this`. -## Что нам даст разделение доступов? +## Итого Итак, мы сделали кофеварку с публичными и приватными методами и заставили их корректно работать. diff --git a/1-js/8-oop/3-getters-setters/article.md b/1-js/8-oop/3-getters-setters/article.md index a6888832..c961d184 100644 --- a/1-js/8-oop/3-getters-setters/article.md +++ b/1-js/8-oop/3-getters-setters/article.md @@ -153,5 +153,14 @@ alert( coffeeMachine.waterAmount() ); // 450 Единый геттер-сеттер используется реже, чем две отдельные функции, но в некоторых JavaScript-библиотеках, например [jQuery](http://jquery.com) и [D3](http://d3js.org) подобный подход принят на уровне концепта. -## Задачи +## Итого + +
          +
        • Для большего контроля над присвоением и чтением значения, вместо свойства делают "функцию-геттер" и "функцию-сеттер", геттер возвращает значение, сеттер -- устанавливает.
        • +
        • Если свойство предназначено только для чтения, то может быть только геттер, только для записи -- только сеттер.
        • +
        • В качестве альтернативы паре геттер/сеттер применяют единую функцию, которая без аргументов ведёт себя как геттер, а с аргументом -- как сеттер.
        • +
        + +Также можно организовать геттеры/сеттеры для свойства, не меняя структуры кода, через [дескрипторы свойств](/descriptors-getters-setters). + diff --git a/1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/solution.md b/1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/solution.md deleted file mode 100644 index efe1006b..00000000 --- a/1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/solution.md +++ /dev/null @@ -1,27 +0,0 @@ - - -```js -//+ run -function CoffeeMachine(power, capacity) { - var waterAmount = 0; - - Object.defineProperty(this, "waterAmount", { - - get: function() { - return waterAmount; - }, - - set: function(amount) { - if (amount > capacity) { - throw new Error("Нельзя залить больше, чем " + capacity); - } - - waterAmount = amount; - } - }); -} - -var coffeeMachine = new CoffeeMachine(1000, 300); -coffeeMachine.waterAmount = 500; -``` - diff --git a/1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/task.md b/1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/task.md deleted file mode 100644 index 73d213ef..00000000 --- a/1-js/8-oop/4-descriptors-getters-setters/1-replace-property-getter/task.md +++ /dev/null @@ -1,22 +0,0 @@ -# Заменить свойство на встроенные геттеры/сеттеры - -[importance 5] - -Вам попал в руки код кофеварки, который использует свойство `this.waterAmount` для хранения количества воды: - -```js -function CoffeeMachine(power, capacity) { - // количество воды в кофеварке - this.waterAmount = 0; -} - -// создать кофеварку -var coffeeMachine = new CoffeeMachine(1000, 300); - -// залить воды -coffeeMachine.waterAmount = 500; -``` - -Задача -- сделать так, чтобы при присвоении `coffeeMachine.waterAmount = 500` выдавалась ошибка, если значение больше `capacity` (в примере выше `300`). - -Для этого реализуйте `waterAmount` через геттер и сеттер, который будет проверять корректность установки. Используйте для этого `Object.defineProperty`. \ No newline at end of file