├── .gitattributes ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── 1-js ├── 01-getting-started │ ├── 1-intro │ │ ├── article.md │ │ └── limitations.svg │ ├── 2-manuals-specifications │ │ └── article.md │ ├── 3-code-editors │ │ └── article.md │ ├── 4-devtools │ │ ├── article.md │ │ ├── bug.html │ │ ├── chrome.png │ │ ├── chrome@2x.png │ │ ├── safari.png │ │ └── safari@2x.png │ └── index.md ├── 02-first-steps │ ├── 01-hello-world │ │ ├── 1-hello-alert │ │ │ ├── index.html │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 2-hello-alert-ext │ │ │ ├── alert.js │ │ │ ├── index.html │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 02-structure │ │ └── article.md │ ├── 03-strict-mode │ │ └── article.md │ ├── 04-variables │ │ ├── 1-hello-variables │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-declare-variables │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-uppercast-constant │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── variable-change.svg │ │ └── variable.svg │ ├── 05-types │ │ ├── 1-string-quotes │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 06-alert-prompt-confirm │ │ ├── 1-simple-page │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 07-type-conversions │ │ └── article.md │ ├── 08-operators │ │ ├── 1-increment-order │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-assignment-result │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-primitive-conversions-questions │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-fix-prompt │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 09-comparison │ │ ├── 1-comparison-questions │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 10-ifelse │ │ ├── 1-if-zero-string │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-check-standard │ │ │ ├── ifelse_task2.svg │ │ │ ├── ifelse_task2 │ │ │ │ └── index.html │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-sign │ │ │ ├── if_sign │ │ │ │ └── index.html │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-rewrite-if-question │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-rewrite-if-else-question │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 11-logical-operators │ │ ├── 1-alert-null-2-undefined │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-alert-or │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-alert-1-null-2 │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-alert-and │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-alert-and-or │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-check-if-in-range │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 7-check-if-out-range │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-if-question │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 9-check-login │ │ │ ├── ifelse_task.svg │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 12-nullish-coalescing-operator │ │ └── article.md │ ├── 13-while-for │ │ ├── 1-loop-last-value │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-which-value-while │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-which-value-for │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-for-even │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-replace-for-while │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-repeat-until-correct │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 7-list-primes │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 14-switch │ │ ├── 1-rewrite-switch-if-else │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-rewrite-if-switch │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 15-function-basics │ │ ├── 1-if-else-required │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-rewrite-function-question-or │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-min │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-pow │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 16-function-expressions │ │ └── article.md │ ├── 17-arrow-functions-basics │ │ ├── 1-rewrite-arrow │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 18-javascript-specials │ │ └── article.md │ └── index.md ├── 03-code-quality │ ├── 01-debugging-chrome │ │ ├── article.md │ │ ├── chrome-open-sources.svg │ │ ├── chrome-sources-breakpoint.svg │ │ ├── chrome-sources-console.svg │ │ ├── chrome-sources-debugger-pause.svg │ │ ├── chrome-sources-debugger-trace-1.svg │ │ ├── chrome-tabs.svg │ │ ├── debugging.view │ │ │ ├── hello.js │ │ │ └── index.html │ │ ├── head.html │ │ └── largeIcons.svg │ ├── 02-coding-style │ │ ├── 1-style-errors │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── code-style.svg │ ├── 03-comments │ │ └── article.md │ ├── 04-ninja-code │ │ └── article.md │ ├── 05-testing-mocha │ │ ├── 3-pow-test-wrong │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── beforeafter.view │ │ │ ├── index.html │ │ │ └── test.js │ │ ├── index.html │ │ ├── pow-1.view │ │ │ ├── index.html │ │ │ └── test.js │ │ ├── pow-2.view │ │ │ ├── index.html │ │ │ └── test.js │ │ ├── pow-3.view │ │ │ ├── index.html │ │ │ └── test.js │ │ ├── pow-4.view │ │ │ ├── index.html │ │ │ └── test.js │ │ ├── pow-full.view │ │ │ ├── index.html │ │ │ └── test.js │ │ ├── pow-min.view │ │ │ ├── index.html │ │ │ └── test.js │ │ └── pow-nan.view │ │ │ ├── index.html │ │ │ └── test.js │ ├── 06-polyfills │ │ └── article.md │ └── index.md ├── 04-object-basics │ ├── 01-object │ │ ├── 2-hello-object │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-is-empty │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-sum-object │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-multiply-numeric │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── object-user-delete.svg │ │ ├── object-user-empty.svg │ │ ├── object-user-isadmin.svg │ │ ├── object-user-props.svg │ │ ├── object-user.svg │ │ └── object.svg │ ├── 02-object-copy │ │ ├── article.md │ │ ├── variable-contains-reference.svg │ │ ├── variable-copy-reference.svg │ │ └── variable-copy-value.svg │ ├── 03-garbage-collection │ │ ├── article.md │ │ ├── family-delete-refs.svg │ │ ├── family-no-family.svg │ │ ├── family-no-father-2.svg │ │ ├── family-no-father.svg │ │ ├── family.svg │ │ ├── garbage-collection-1.svg │ │ ├── garbage-collection-2.svg │ │ ├── garbage-collection-3.svg │ │ ├── garbage-collection-4.svg │ │ ├── garbage-collection-5.svg │ │ ├── memory-user-john-admin.svg │ │ ├── memory-user-john-lost.svg │ │ └── memory-user-john.svg │ ├── 04-object-methods │ │ ├── 4-object-property-this │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 7-calculator │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-chain-calls │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 06-constructor-new │ │ ├── 1-two-functions-one-object │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-calculator-constructor │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-accumulator │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 07-optional-chaining │ │ └── article.md │ ├── 08-symbol │ │ └── article.md │ ├── 09-object-toprimitive │ │ └── article.md │ └── index.md ├── 05-data-types │ ├── 01-primitives-methods │ │ ├── 1-string-new-property │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 02-number │ │ ├── 1-sum-interface │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-why-rounded-down │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-repeat-until-number │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-endless-loop-error │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-random-min-max │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 9-random-int-min-max │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 03-string │ │ ├── 1-ucfirst │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-check-spam │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-truncate │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-extract-currency │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 04-array │ │ ├── 1-item-value │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 10-maximal-subarray │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-create-array │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-call-array-this │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-array-input-sum │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── array-pop.svg │ │ ├── array-shift.svg │ │ ├── array-speed.svg │ │ ├── article.md │ │ ├── queue.svg │ │ └── stack.svg │ ├── 05-array-methods │ │ ├── 1-camelcase │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 10-average-age │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 11-array-unique │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 12-reduce-object │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-filter-range │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-filter-range-in-place │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-sort-back │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-copy-sort-array │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-array-get-names │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-calculator-extendable │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 7-map-objects │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-sort-objects │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 9-shuffle │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── reduce.svg │ ├── 06-iterable │ │ └── article.md │ ├── 07-map-set │ │ ├── 01-array-unique-map │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-filter-anagrams │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-iterable-keys │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 08-weakmap-weakset │ │ ├── 01-recipients-read │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-recipients-when-read │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 09-keys-values-entries │ │ ├── 01-sum-salaries │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-count-properties │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 10-destructuring-assignment │ │ ├── 1-destruct-user │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-max-salary │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── destructuring-complex.svg │ ├── 11-date │ │ ├── 1-new-date │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-get-week-day │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-weekday │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-get-date-ago │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-last-day-of-month │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-get-seconds-today │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 7-get-seconds-to-tomorrow │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-format-date-relative │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 12-json │ │ ├── 1-serialize-object │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-serialize-event-circular │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── json-meetup.svg │ └── index.md ├── 06-advanced-functions │ ├── 01-recursion │ │ ├── 01-sum-to │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-factorial │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-fibonacci-numbers │ │ │ ├── fibonacci-recursion-tree.svg │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 04-output-single-linked-list │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 05-output-single-linked-list-reverse │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── head.html │ │ ├── linked-list-0.svg │ │ ├── linked-list-remove-1.svg │ │ ├── linked-list-split.svg │ │ ├── linked-list.svg │ │ ├── recursion-pow.svg │ │ └── recursive-salaries.svg │ ├── 02-rest-parameters-spread │ │ └── article.md │ ├── 03-closure │ │ ├── 1-closure-latest-changes │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 10-make-army │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── lexenv-makearmy-empty.svg │ │ │ ├── lexenv-makearmy-for-fixed.svg │ │ │ ├── lexenv-makearmy-while-fixed.svg │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-closure-variable-access │ │ │ ├── lexenv-nested-work.svg │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-counter-independent │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-counter-object-independent │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-function-in-if │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-closure-sum │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 7-let-scope │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 8-filter-through-function │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 9-sort-by-field │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── closure-function-declaration.svg │ │ ├── closure-makecounter-environment.svg │ │ ├── closure-makecounter-nested-call-2.svg │ │ ├── closure-makecounter-nested-call.svg │ │ ├── closure-makecounter.svg │ │ ├── closure-variable-phrase.svg │ │ ├── lexenv-if.svg │ │ ├── lexenv-nested-makecounter-1.svg │ │ ├── lexenv-nested-makecounter-2.svg │ │ ├── lexenv-nested-makecounter-3.svg │ │ ├── lexenv-nested-makecounter-4.svg │ │ ├── lexenv-nested-makecounter-5.svg │ │ ├── lexenv-nested-makecounter-6.svg │ │ ├── lexical-environment-global-2.svg │ │ ├── lexical-environment-global-3.svg │ │ ├── lexical-environment-global.svg │ │ ├── lexical-environment-simple-lookup.svg │ │ ├── lexical-environment-simple.svg │ │ ├── lexical-search-order.svg │ │ └── variable-scope-lookup.svg │ ├── 04-var │ │ └── article.md │ ├── 05-global-object │ │ └── article.md │ ├── 06-function-object │ │ ├── 2-counter-inc-dec │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-sum-many-brackets │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 07-new-function │ │ └── article.md │ ├── 08-settimeout-setinterval │ │ ├── 1-output-numbers-100ms │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-settimeout-result │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── setinterval-interval.svg │ │ └── settimeout-interval.svg │ ├── 09-call-apply-decorators │ │ ├── 01-spy-decorator │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ ├── source.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-delay │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-debounce │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── debounce.svg │ │ │ ├── debounce.view │ │ │ │ └── index.html │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 04-throttle │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── decorator-makecaching-wrapper.svg │ ├── 10-bind │ │ ├── 2-write-to-object-after-bind │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-second-bind │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-function-property-after-bind │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-question-use-bind │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-ask-partial │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── head.html │ ├── 12-arrow-functions │ │ └── article.md │ └── index.md ├── 07-object-properties │ ├── 01-property-descriptors │ │ └── article.md │ ├── 02-property-accessors │ │ └── article.md │ └── index.md ├── 08-prototypes │ ├── 01-prototype-inheritance │ │ ├── 1-property-after-delete │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-search-algorithm │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-proto-and-this │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-hamster-proto │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── object-prototype-empty.svg │ │ ├── proto-animal-rabbit-chain.svg │ │ ├── proto-animal-rabbit-walk-2.svg │ │ ├── proto-animal-rabbit-walk-3.svg │ │ ├── proto-animal-rabbit-walk.svg │ │ ├── proto-animal-rabbit.svg │ │ ├── proto-user-admin.svg │ │ └── rabbit-animal-object.svg │ ├── 02-function-prototype │ │ ├── 1-changing-prototype │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-new-object-same-constructor │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── function-prototype-constructor.svg │ │ ├── proto-constructor-animal-rabbit.svg │ │ └── rabbit-prototype-constructor.svg │ ├── 03-native-prototypes │ │ ├── 1-defer-to-prototype │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-defer-to-prototype-extended │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── console_dir_array.png │ │ ├── function-prototype-constructor.svg │ │ ├── native-prototypes-array-tostring.svg │ │ ├── native-prototypes-classes.svg │ │ ├── object-prototype-1.svg │ │ ├── object-prototype-null.svg │ │ ├── object-prototype.svg │ │ └── rabbit-prototype-constructor.svg │ ├── 04-prototype-methods │ │ ├── 2-dictionary-tostring │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-compare-calls │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── object-prototype-2.svg │ │ └── object-prototype-null.svg │ └── index.md ├── 09-classes │ ├── 01-class │ │ ├── 1-rewrite-to-class │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── source.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── class-user.svg │ ├── 02-class-inheritance │ │ ├── 1-class-constructor-error │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-clock-class-extended │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── clock.js │ │ │ │ ├── extended-clock.js │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ ├── clock.js │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── animal-rabbit-extends.svg │ │ ├── article.md │ │ ├── class-inheritance-array-object.svg │ │ ├── class-inheritance-rabbit-animal-2.svg │ │ ├── class-inheritance-rabbit-animal.svg │ │ ├── rabbit-animal-independent-animal.svg │ │ ├── rabbit-animal-independent-rabbit.svg │ │ ├── super-homeobject-wrong.svg │ │ └── this-super-loop.svg │ ├── 03-static-properties-methods │ │ ├── 3-class-extend-object │ │ │ ├── rabbit-extends-object.svg │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── animal-rabbit-static.svg │ │ └── article.md │ ├── 04-private-protected-properties-methods │ │ ├── article.md │ │ ├── coffee-inside.jpg │ │ └── coffee.jpg │ ├── 05-extend-natives │ │ ├── article.md │ │ └── object-date-inheritance.svg │ ├── 06-instanceof │ │ ├── 1-strange-instanceof │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── instanceof.svg │ ├── 07-mixins │ │ ├── article.md │ │ ├── head.html │ │ └── mixin-inheritance.svg │ └── index.md ├── 10-error-handling │ ├── 1-try-catch │ │ ├── 1-finally-or-code-after │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── try-catch-flow.svg │ ├── 2-custom-errors │ │ ├── 1-format-error │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ └── index.md ├── 11-async │ ├── 01-callbacks │ │ ├── article.md │ │ ├── callback-hell.svg │ │ └── one.js │ ├── 02-promise-basics │ │ ├── 01-re-resolve │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-delay-promise │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-animate-circle-promise │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ ├── head.html │ │ ├── promise-reject-1.svg │ │ ├── promise-resolve-1.svg │ │ └── promise-resolve-reject.svg │ ├── 03-promise-chaining │ │ ├── 01-then-vs-catch │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── getMessage.js │ │ ├── head.html │ │ ├── one.js │ │ ├── promise-handler-variants.svg │ │ ├── promise-then-chain.svg │ │ ├── promise-then-many.svg │ │ ├── three.js │ │ ├── two.js │ │ └── user.json │ ├── 04-promise-error-handling │ │ ├── 01-error-async │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── getMessage.js │ │ ├── head.html │ │ ├── one.js │ │ ├── promise-then-chain.svg │ │ ├── promise-then-many.svg │ │ ├── three.js │ │ ├── two.js │ │ └── user.json │ ├── 05-promise-api │ │ ├── article.md │ │ ├── head.html │ │ ├── iliakan.json │ │ ├── one.js │ │ └── two.js │ ├── 06-promisify │ │ └── article.md │ ├── 07-microtask-queue │ │ ├── article.md │ │ └── promiseQueue.svg │ ├── 08-async-await │ │ ├── 01-rewrite-async │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-rewrite-async-2 │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-async-from-regular │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── head.html │ └── index.md ├── 12-generators-iterators │ ├── 1-generators │ │ ├── 01-pseudo-random-generator │ │ │ ├── _js.view │ │ │ │ ├── solution.js │ │ │ │ └── test.js │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── genYield2-2.svg │ │ ├── genYield2.svg │ │ ├── generateSequence-1.svg │ │ ├── generateSequence-2.svg │ │ ├── generateSequence-3.svg │ │ └── generateSequence-4.svg │ ├── 2-async-iterators-generators │ │ ├── article.md │ │ └── head.html │ └── index.md ├── 13-modules │ ├── 01-modules-intro │ │ ├── article.md │ │ ├── say.view │ │ │ ├── index.html │ │ │ └── say.js │ │ ├── scopes-working.view │ │ │ ├── hello.js │ │ │ ├── index.html │ │ │ └── user.js │ │ └── scopes.view │ │ │ ├── hello.js │ │ │ ├── index.html │ │ │ └── user.js │ ├── 02-import-export │ │ └── article.md │ ├── 03-modules-dynamic-imports │ │ ├── article.md │ │ └── say.view │ │ │ ├── index.html │ │ │ └── say.js │ └── index.md ├── 99-js-misc │ ├── 01-proxy │ │ ├── 01-error-nonexisting │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 02-array-negative │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-observable │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── proxy-inherit-admin.svg │ │ ├── proxy-inherit.svg │ │ └── proxy.svg │ ├── 02-eval │ │ ├── 1-eval-calculator │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 03-currying-partials │ │ └── article.md │ ├── 04-reference-type │ │ ├── 2-check-syntax │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-why-this │ │ │ ├── solution.md │ │ │ └── task.md │ │ └── article.md │ ├── 05-bigint │ │ └── article.md │ ├── 06-unicode │ │ └── article.md │ └── index.md └── index.md ├── 2-ui ├── 1-document │ ├── 01-browser-environment │ │ ├── article.md │ │ └── windowObjects.svg │ ├── 02-dom-nodes │ │ ├── article.md │ │ ├── domconsole0.svg │ │ ├── domconsole1.svg │ │ ├── elk.html │ │ ├── elk.svg │ │ ├── head.html │ │ ├── inspect.svg │ │ └── toolbarButtonGlyphs.svg │ ├── 03-dom-navigation │ │ ├── 1-dom-children │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 3-navigation-links-which-null │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-select-diagonal-cells │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ ├── dom-links-elements.svg │ │ ├── dom-links.svg │ │ └── head.html │ ├── 04-searching-elements-dom │ │ ├── 1-find-elements │ │ │ ├── solution.md │ │ │ ├── table.html │ │ │ └── task.md │ │ └── article.md │ ├── 05-basic-dom-node-properties │ │ ├── 2-lastchild-nodetype-inline │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-tree-info │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 3-tag-in-comment │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-where-document-in-hierarchy │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── dom-class-hierarchy.svg │ ├── 06-dom-attributes-and-properties │ │ ├── 1-get-user-attribute │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-yellow-links │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ └── article.md │ ├── 07-modifying-document │ │ ├── 1-createtextnode-vs-innerhtml │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 10-clock-setinterval │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 11-append-to-list │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 12-sort-table │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 4-clear-elem │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 5-why-aaa │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 6-create-list │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 7-create-object-tree │ │ │ ├── build-tree-dom.view │ │ │ │ └── index.html │ │ │ ├── innerhtml.view │ │ │ │ └── index.html │ │ │ ├── solution.md │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 8-tree-count │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 9-calendar-table │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ ├── before-prepend-append-after.svg │ │ └── insert-adjacent.svg │ ├── 08-styles-and-classes │ │ ├── 2-create-notification │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ └── task.md │ │ └── article.md │ ├── 09-size-and-scroll │ │ ├── 1-get-scroll-height-bottom │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-scrollbar-width │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 4-put-ball-in-center │ │ │ ├── ball-half │ │ │ │ └── index.html │ │ │ ├── field.svg │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 6-width-vs-clientwidth │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── cssWidthScroll.view │ │ │ └── index.html │ │ ├── metric-all.svg │ │ ├── metric-client-left-top-rtl.svg │ │ ├── metric-client-left-top.svg │ │ ├── metric-client-width-height.svg │ │ ├── metric-client-width-nopadding.svg │ │ ├── metric-css.svg │ │ ├── metric-offset-parent.svg │ │ ├── metric-offset-width-height.svg │ │ ├── metric-scroll-top.svg │ │ ├── metric-scroll-width-height.svg │ │ └── metric.view │ │ │ └── index.html │ ├── 10-size-and-scroll-window │ │ ├── article.md │ │ └── document-client-width-height.svg │ ├── 11-coordinates │ │ ├── 1-find-point-coordinates │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 2-position-at │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 3-position-at-absolute │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 4-position-inside-absolute │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.css │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ ├── coordinates-negative.svg │ │ ├── coordinates.svg │ │ ├── document-and-window-coordinates-scrolled.svg │ │ └── head.html │ └── index.md ├── 2-events │ ├── 01-introduction-browser-events │ │ ├── 01-hide-other │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 02-hide-self-onclick │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 03-which-handlers-run │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 04-move-ball-field │ │ │ ├── move-ball-coords.svg │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 05-sliding-menu │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 06-hide-message │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── messages.css │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── messages.css │ │ │ └── task.md │ │ ├── 07-carousel │ │ │ ├── carousel1.svg │ │ │ ├── carousel2.svg │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── style.css │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── style.css │ │ │ └── task.md │ │ ├── article.md │ │ └── head.html │ ├── 02-bubbling-and-capturing │ │ ├── article.md │ │ ├── both.view │ │ │ ├── example.css │ │ │ ├── index.html │ │ │ └── script.js │ │ ├── bubble-target.view │ │ │ ├── example.css │ │ │ ├── index.html │ │ │ └── script.js │ │ ├── capture.view │ │ │ ├── example.css │ │ │ ├── index.html │ │ │ └── script.js │ │ ├── event-order-bubbling.svg │ │ └── eventflow.svg │ ├── 03-event-delegation │ │ ├── 1-hide-message-delegate │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── messages.css │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── messages.css │ │ │ └── task.md │ │ ├── 2-sliding-tree │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 3-sortable-table │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 4-behavior-tooltip │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ ├── bagua-bubble.svg │ │ └── bagua.view │ │ │ ├── bagua.css │ │ │ └── index.html │ ├── 04-default-browser-action │ │ ├── 1-why-return-false-fails │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── 2-catch-link-navigation │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 3-image-gallery │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── gallery.css │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ ├── gallery.css │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ └── menu.view │ │ │ ├── index.html │ │ │ ├── menu.css │ │ │ └── menu.js │ ├── 05-dispatch-events │ │ └── article.md │ └── index.md ├── 3-event-details │ ├── 1-mouse-events-basics │ │ ├── 01-selectable-list │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ └── head.html │ ├── 3-mousemove-mouseover-mouseout-mouseenter-mouseleave │ │ ├── 1-behavior-nested-tooltip │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 2-hoverintent │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── hoverIntent.js │ │ │ │ ├── index.html │ │ │ │ ├── style.css │ │ │ │ └── test.js │ │ │ ├── source.view │ │ │ │ ├── hoverIntent.js │ │ │ │ ├── index.html │ │ │ │ ├── style.css │ │ │ │ └── test.js │ │ │ └── task.md │ │ ├── article.md │ │ ├── mouseenter-mouseleave-delegation-2.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ ├── mouseenter-mouseleave-delegation.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ ├── mouseleave-table.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ ├── mouseleave.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ ├── mouseover-bubble-nested.svg │ │ ├── mouseover-mouseout-from-outside.svg │ │ ├── mouseover-mouseout-over-elems.svg │ │ ├── mouseover-mouseout.svg │ │ ├── mouseover-to-child.svg │ │ ├── mouseoverout-child.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ ├── mouseoverout-fast.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ └── mouseoverout.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ ├── 4-mouse-drag-and-drop │ │ ├── 1-slider │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── style.css │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── style.css │ │ │ └── task.md │ │ ├── 2-drag-heroes │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── field.svg │ │ │ │ ├── index.html │ │ │ │ ├── soccer.css │ │ │ │ └── soccer.js │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ ├── soccer.css │ │ │ │ └── soccer.js │ │ │ └── task.md │ │ ├── article.md │ │ ├── ball.view │ │ │ └── index.html │ │ ├── ball2.view │ │ │ └── index.html │ │ ├── ball3.view │ │ │ └── index.html │ │ ├── ball4.view │ │ │ ├── index.html │ │ │ └── style.css │ │ └── ball_shift.svg │ ├── 6-pointer-events │ │ ├── article.md │ │ ├── ball-2.view │ │ │ └── index.html │ │ ├── ball.view │ │ │ └── index.html │ │ ├── multitouch.view │ │ │ └── index.html │ │ ├── slider-html.view │ │ │ ├── index.html │ │ │ └── style.css │ │ └── slider.view │ │ │ ├── index.html │ │ │ └── style.css │ ├── 7-keyboard-events │ │ ├── 2-check-sync-keydown │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ ├── german-layout.svg │ │ ├── keyboard-dump.view │ │ │ ├── index.html │ │ │ ├── script.js │ │ │ └── style.css │ │ └── us-layout.svg │ ├── 8-onscroll │ │ ├── 1-endless-page │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 2-updown-button │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── 3-load-visible-img │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── placeholder.svg │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── placeholder.svg │ │ │ └── task.md │ │ └── article.md │ └── index.md ├── 4-forms-controls │ ├── 1-form-elements │ │ ├── 1-add-select-option │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ └── form-navigation.svg │ ├── 2-focus-blur │ │ ├── 3-editable-div │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── my.css │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── my.css │ │ │ └── task.md │ │ ├── 4-edit-td-click │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── bagua.css │ │ │ │ ├── index.html │ │ │ │ ├── my.css │ │ │ │ └── script.js │ │ │ ├── source.view │ │ │ │ ├── bagua.css │ │ │ │ ├── index.html │ │ │ │ ├── my.css │ │ │ │ └── script.js │ │ │ └── task.md │ │ ├── 5-keyboard-mouse │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ └── article.md │ ├── 3-events-change-input │ │ ├── 1-deposit-calculator │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ └── article.md │ ├── 4-forms-submit │ │ ├── 1-modal-dialog │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ ├── index.html │ │ │ │ └── style.css │ │ │ ├── source.view │ │ │ │ ├── index.html │ │ │ │ └── style.css │ │ │ └── task.md │ │ └── article.md │ └── index.md ├── 5-loading │ ├── 01-onload-ondomcontentloaded │ │ ├── article.md │ │ ├── readystate.view │ │ │ ├── iframe.html │ │ │ └── index.html │ │ └── window-onbeforeunload.view │ │ │ └── index.html │ ├── 02-script-async-defer │ │ ├── article.md │ │ ├── long.js │ │ └── small.js │ ├── 03-onload-onerror │ │ ├── 1-load-img-callback │ │ │ ├── solution.md │ │ │ ├── solution.view │ │ │ │ └── index.html │ │ │ ├── source.view │ │ │ │ └── index.html │ │ │ └── task.md │ │ ├── article.md │ │ └── crossorigin.view │ │ │ └── error.js │ └── index.md ├── 99-ui-misc │ ├── 01-mutation-observer │ │ └── article.md │ ├── 02-selection-range │ │ ├── article.md │ │ ├── range-example-p-0-1.svg │ │ ├── range-example-p-1-3.svg │ │ ├── range-example-p-2-b-3-range.svg │ │ ├── range-example-p-2-b-3.svg │ │ ├── range-hello-1.svg │ │ ├── selection-direction-backward.svg │ │ ├── selection-direction-forward.svg │ │ └── selection-firefox.svg │ ├── 03-event-loop │ │ ├── 2-micro-macro-queue │ │ │ ├── solution.md │ │ │ └── task.md │ │ ├── article.md │ │ ├── eventLoop-full.svg │ │ └── eventLoop.svg │ └── index.md └── index.md ├── 3-frames-and-windows ├── 01-popup-windows │ └── article.md ├── 03-cross-window-communication │ ├── article.md │ ├── postmessage.view │ │ ├── iframe.html │ │ └── index.html │ └── sandbox.view │ │ ├── index.html │ │ └── sandboxed.html ├── 06-clickjacking │ ├── article.md │ ├── clickjacking-visible.view │ │ ├── facebook.html │ │ └── index.html │ ├── clickjacking.view │ │ ├── facebook.html │ │ └── index.html │ ├── protector.view │ │ ├── iframe.html │ │ └── index.html │ └── top-location.view │ │ ├── iframe.html │ │ └── index.html └── index.md ├── 4-binary ├── 01-arraybuffer-binary-arrays │ ├── 01-concat │ │ ├── _js.view │ │ │ ├── solution.js │ │ │ ├── source.js │ │ │ └── test.js │ │ ├── solution.md │ │ └── task.md │ ├── 8bit-integer-256.svg │ ├── 8bit-integer-257.svg │ ├── arraybuffer-view-buffersource.svg │ ├── arraybuffer-views.svg │ └── article.md ├── 02-text-decoder │ └── article.md ├── 03-blob │ ├── article.md │ └── blob.svg ├── 04-file │ └── article.md └── index.md ├── 5-network ├── 01-fetch │ ├── 01-fetch-users │ │ ├── _js.view │ │ │ ├── solution.js │ │ │ ├── source.js │ │ │ └── test.js │ │ ├── solution.md │ │ └── task.md │ ├── article.md │ ├── logo-fetch.svg │ └── post.view │ │ └── server.js ├── 02-formdata │ ├── article.md │ └── post.view │ │ └── server.js ├── 03-fetch-progress │ ├── article.md │ ├── logo-fetch.svg │ └── progress.view │ │ ├── index.html │ │ └── long.txt ├── 04-fetch-abort │ ├── article.md │ └── demo.view │ │ └── server.js ├── 05-fetch-crossorigin │ ├── 1-do-we-need-origin │ │ ├── solution.md │ │ └── task.md │ ├── article.md │ ├── cors-gmail-messages.svg │ ├── xhr-another-domain.svg │ └── xhr-preflight.svg ├── 06-fetch-api │ ├── article.md │ ├── logo-fetch.svg │ └── post.view │ │ ├── index.html │ │ └── server.js ├── 07-url │ ├── article.md │ └── url-object.svg ├── 08-xmlhttprequest │ ├── article.md │ ├── example.view │ │ ├── index.html │ │ └── server.js │ ├── hello.txt │ ├── phones-async.view │ │ ├── index.html │ │ ├── phones.json │ │ └── server.js │ ├── phones.json │ ├── phones.view │ │ ├── index.html │ │ ├── phones.json │ │ └── server.js │ └── post.view │ │ ├── index.html │ │ └── server.js ├── 09-resume-upload │ ├── article.md │ └── upload-resume.view │ │ ├── index.html │ │ ├── server.js │ │ └── uploader.js ├── 10-long-polling │ ├── article.md │ ├── long-polling.svg │ └── longpoll.view │ │ ├── browser.js │ │ ├── index.html │ │ └── server.js ├── 11-websocket │ ├── article.md │ ├── chat.view │ │ ├── index.html │ │ └── server.js │ ├── demo.view │ │ └── server.js │ └── websocket-handshake.svg ├── 12-server-sent-events │ ├── article.md │ └── eventsource.view │ │ ├── index.html │ │ └── server.js └── index.md ├── 6-data-storage ├── 01-cookie │ ├── article.md │ ├── cookie-third-party-2.svg │ ├── cookie-third-party-3.svg │ ├── cookie-third-party.svg │ ├── cookie-xsrf.svg │ └── cookie.js ├── 02-localstorage │ ├── 1-form-autosave │ │ ├── solution.md │ │ ├── solution.view │ │ │ └── index.html │ │ ├── source.view │ │ │ └── index.html │ │ └── task.md │ ├── article.md │ └── sessionstorage.view │ │ ├── iframe.html │ │ └── index.html ├── 03-indexeddb │ ├── article.md │ ├── books.view │ │ └── index.html │ ├── indexeddb-cursor.svg │ ├── indexeddb-index.svg │ └── indexeddb-structure.svg └── index.md ├── 7-animation ├── 1-bezier-curve │ ├── article.md │ ├── bezier-car.svg │ ├── bezier-letter.svg │ ├── bezier-vase.svg │ ├── bezier2.svg │ ├── bezier3-draw1.svg │ ├── bezier3-draw2.svg │ ├── bezier3-e.svg │ ├── bezier3.svg │ ├── bezier4-e.svg │ ├── bezier4.svg │ ├── demo.svg │ ├── pause.png │ └── play.png ├── 2-css-animations │ ├── 1-animate-logo-css │ │ ├── solution.md │ │ ├── solution.view │ │ │ └── index.html │ │ ├── source.view │ │ │ └── index.html │ │ └── task.md │ ├── 2-animate-logo-bezier-css │ │ ├── bezier-up.svg │ │ ├── solution.md │ │ ├── solution.view │ │ │ └── index.html │ │ └── task.md │ ├── 3-animate-circle │ │ ├── solution.md │ │ ├── solution.view │ │ │ └── index.html │ │ ├── source.view │ │ │ └── index.html │ │ └── task.md │ ├── 4-animate-circle-callback │ │ ├── solution.md │ │ ├── solution.view │ │ │ └── index.html │ │ └── task.md │ ├── article.md │ ├── bezier-linear.svg │ ├── bezier-train-over.svg │ ├── boat.view │ │ ├── index.html │ │ └── style.css │ ├── digits-negative-delay.view │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ ├── digits.view │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ ├── ease-in-out.svg │ ├── ease-in.svg │ ├── ease-out.svg │ ├── ease.svg │ ├── step-end.view │ │ ├── index.html │ │ └── style.css │ ├── step-list.view │ │ ├── index.html │ │ └── style.css │ ├── step.view │ │ ├── index.html │ │ └── style.css │ ├── train-curve.svg │ ├── train-linear.view │ │ ├── index.html │ │ └── style.css │ ├── train-over.view │ │ ├── index.html │ │ └── style.css │ └── train.view │ │ ├── index.html │ │ └── style.css ├── 3-js-animation │ ├── 1-animate-ball │ │ ├── solution.md │ │ ├── solution.view │ │ │ ├── index.html │ │ │ └── style.css │ │ ├── source.view │ │ │ ├── index.html │ │ │ └── style.css │ │ └── task.md │ ├── 2-animate-ball-hops │ │ ├── solution.md │ │ ├── solution.view │ │ │ ├── index.html │ │ │ └── style.css │ │ └── task.md │ ├── article.md │ ├── back.svg │ ├── back.view │ │ ├── index.html │ │ └── style.css │ ├── bezier-linear.svg │ ├── bounce-easeinout.view │ │ ├── index.html │ │ └── style.css │ ├── bounce-easeout.view │ │ ├── index.html │ │ └── style.css │ ├── bounce-inout.svg │ ├── bounce.view │ │ ├── index.html │ │ └── style.css │ ├── circ-ease.svg │ ├── circ.svg │ ├── circ.view │ │ ├── index.html │ │ └── style.css │ ├── elastic.svg │ ├── elastic.view │ │ ├── index.html │ │ └── style.css │ ├── linear.svg │ ├── move-raf.view │ │ └── index.html │ ├── move.view │ │ └── index.html │ ├── quad.svg │ ├── quad.view │ │ ├── index.html │ │ └── style.css │ ├── quint.svg │ ├── quint.view │ │ ├── index.html │ │ └── style.css │ ├── text.view │ │ ├── index.html │ │ └── style.css │ └── width.view │ │ ├── animate.js │ │ └── index.html └── index.md ├── 8-web-components ├── 1-webcomponents-intro │ ├── article.md │ ├── satellite-expanded.jpg │ ├── satellite-expanded@2x.jpg │ ├── satellite.jpg │ ├── satellite@2x.jpg │ └── web-components-twitter.svg ├── 2-custom-elements │ ├── 1-live-timer │ │ ├── solution.md │ │ ├── solution.view │ │ │ ├── index.html │ │ │ ├── live-timer.js │ │ │ └── time-formatted.js │ │ ├── source.view │ │ │ ├── index.html │ │ │ ├── live-timer.js │ │ │ └── time-formatted.js │ │ └── task.md │ ├── article.md │ └── head.html ├── 3-shadow-dom │ ├── article.md │ ├── shadow-dom-range.png │ ├── shadow-dom-range@2x.png │ ├── shadow-dom-say-hello.png │ └── shadow-dom-say-hello@2x.png ├── 4-template-element │ └── article.md ├── 5-slots-composition │ ├── article.md │ ├── menu.view │ │ └── index.html │ └── shadow-dom-user-card.svg ├── 6-shadow-dom-style │ └── article.md ├── 7-shadow-dom-events │ └── article.md └── index.md ├── 9-regular-expressions ├── 01-regexp-introduction │ └── article.md ├── 02-regexp-character-classes │ ├── article.md │ └── love-html5-classes.svg ├── 03-regexp-unicode │ └── article.md ├── 04-regexp-anchors │ ├── 1-start-end │ │ ├── solution.md │ │ └── task.md │ └── article.md ├── 05-regexp-multiline-mode │ └── article.md ├── 06-regexp-boundary │ ├── 1-find-time-hh-mm │ │ ├── solution.md │ │ └── task.md │ ├── article.md │ └── hello-java-boundaries.svg ├── 07-regexp-escaping │ └── article.md ├── 08-regexp-character-sets-and-ranges │ ├── 1-find-range-1 │ │ ├── solution.md │ │ └── task.md │ ├── 2-find-time-2-formats │ │ ├── solution.md │ │ └── task.md │ └── article.md ├── 09-regexp-quantifiers │ ├── 1-find-text-manydots │ │ ├── solution.md │ │ └── task.md │ ├── 2-find-html-colors-6hex │ │ ├── solution.md │ │ └── task.md │ └── article.md ├── 10-regexp-greedy-and-lazy │ ├── 1-lazy-greedy │ │ ├── solution.md │ │ └── task.md │ ├── 3-find-html-comments │ │ ├── solution.md │ │ └── task.md │ ├── 4-find-html-tags-greedy-lazy │ │ ├── solution.md │ │ └── task.md │ ├── article.md │ ├── witch_greedy1.svg │ ├── witch_greedy2.svg │ ├── witch_greedy3.svg │ ├── witch_greedy4.svg │ ├── witch_greedy5.svg │ ├── witch_greedy6.svg │ ├── witch_lazy3.svg │ ├── witch_lazy4.svg │ ├── witch_lazy5.svg │ └── witch_lazy6.svg ├── 11-regexp-groups │ ├── 01-test-mac │ │ ├── solution.md │ │ └── task.md │ ├── 02-find-webcolor-3-or-6 │ │ ├── solution.md │ │ └── task.md │ ├── 03-find-decimal-numbers │ │ ├── solution.md │ │ └── task.md │ ├── 04-parse-expression │ │ ├── solution.md │ │ └── task.md │ ├── article.md │ ├── regexp-nested-groups-matches.svg │ └── regexp-nested-groups-pattern.svg ├── 12-regexp-backreferences │ └── article.md ├── 13-regexp-alternation │ ├── 01-find-programming-language │ │ ├── solution.md │ │ └── task.md │ ├── 02-find-matching-bbtags │ │ ├── solution.md │ │ └── task.md │ ├── 03-match-quoted-string │ │ ├── solution.md │ │ └── task.md │ ├── 04-match-exact-tag │ │ ├── solution.md │ │ └── task.md │ └── article.md ├── 14-regexp-lookahead-lookbehind │ ├── 1-find-non-negative-integers │ │ ├── solution.md │ │ └── task.md │ ├── 2-insert-after-head │ │ ├── solution.md │ │ └── task.md │ └── article.md ├── 15-regexp-catastrophic-backtracking │ └── article.md ├── 16-regexp-sticky │ └── article.md ├── 17-regexp-methods │ └── article.md └── index.md ├── AUTHORING.md ├── LICENSE.md ├── README.md ├── changes.sketch ├── css.md ├── figures.sketch ├── script └── clean-unused-png.php ├── svgs.zip └── todo.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.svg binary 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.diff 2 | *.err 3 | *.orig 4 | *.log 5 | *.rej 6 | *.swo 7 | *.swp 8 | *.vi 9 | *~ 10 | *.sass-cache 11 | 12 | # OS or Editor folders 13 | .DS_Store 14 | .idea 15 | .cache 16 | .project 17 | .settings 18 | .tmproj 19 | .nvmrc 20 | sftp-config.json 21 | Thumbs.db 22 | 23 | 24 | /svgs -------------------------------------------------------------------------------- /1-js/01-getting-started/4-devtools/bug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 本页面的脚本中有一个错误。 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /1-js/01-getting-started/4-devtools/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/01-getting-started/4-devtools/chrome.png -------------------------------------------------------------------------------- /1-js/01-getting-started/4-devtools/chrome@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/01-getting-started/4-devtools/chrome@2x.png -------------------------------------------------------------------------------- /1-js/01-getting-started/4-devtools/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/01-getting-started/4-devtools/safari.png -------------------------------------------------------------------------------- /1-js/01-getting-started/4-devtools/safari@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/01-getting-started/4-devtools/safari@2x.png -------------------------------------------------------------------------------- /1-js/01-getting-started/index.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | 介绍 JavaScript 语言及其开发环境。 4 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/1-hello-alert/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md: -------------------------------------------------------------------------------- 1 | 2 | [html src="index.html"] 3 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/1-hello-alert/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 显示一个提示语 6 | 7 | 创建一个页面,然后显示一个消息 “I'm JavaScript!”。 8 | 9 | 在沙箱中或者在你的硬盘上做这件事都无所谓,只要确保它能运行起来。 10 | 11 | [demo src="solution"] 12 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js: -------------------------------------------------------------------------------- 1 | alert("I'm JavaScript!"); -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/2-hello-alert-ext/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md: -------------------------------------------------------------------------------- 1 | HTML 代码: 2 | 3 | [html src="index.html"] 4 | 5 | 同一个文件夹中的 `alert.js` 文件: 6 | 7 | [js src="alert.js"] 8 | 9 | -------------------------------------------------------------------------------- /1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 使用外部的脚本显示一个提示语 6 | 7 | 打开前一个任务 的答案。将脚本的内容提取到一个外部的 `alert.js` 文件中,放置在相同的文件夹中。 8 | 9 | 打开页面,确保它能够工作。 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/04-variables/1-hello-variables/solution.md: -------------------------------------------------------------------------------- 1 | 下面的代码,每一行都对应着任务列表中的对应项。 2 | 3 | ```js run 4 | let admin, name; // 一次声明两个变量。 5 | 6 | name = "John"; 7 | 8 | admin = name; 9 | 10 | alert( admin ); // "John" 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/04-variables/1-hello-variables/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 使用变量 6 | 7 | 1. 声明两个变量:`admin` 和 `name`。 8 | 2. 将值 `"John"` 赋给 `name`。 9 | 3. 从 `name` 变量中拷贝其值给 `admin`。 10 | 4. 使用 `alert` 显示 `admin` 的值(必须输出 "John")。 11 | -------------------------------------------------------------------------------- /1-js/02-first-steps/04-variables/2-declare-variables/solution.md: -------------------------------------------------------------------------------- 1 | ## 代表我们星球的变量 2 | 3 | 这很简单: 4 | 5 | ```js 6 | let ourPlanetName = "Earth"; 7 | ``` 8 | 9 | 注意,我们也可以用一个更简短的名字 `planet`,但这样可能并不能表达清楚它指的是哪个星球。再啰嗦一点也挺好的。至少让这个变量别太长就行。 10 | 11 | ## 网站当前访问者的名字 12 | 13 | ```js 14 | let currentUserName = "John"; 15 | ``` 16 | 17 | 同样,如果我们的确知道用户就是当前的用户的话,我们可以使用更短的变量名 `userName`。 18 | 19 | 现代编辑器的自动补全可以让长变量名变得容易编写。不要浪费这个特性。一个名字中包含三个词挺好的。 20 | 21 | 如果你的编辑器没有合适的自动补全功能,换 [一个新的吧](/code-editors)。 22 | -------------------------------------------------------------------------------- /1-js/02-first-steps/04-variables/2-declare-variables/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 给出正确的名字 6 | 7 | 1. 使用我们的星球的名字创建一个变量。你会怎么命名这个变量? 8 | 2. 创建一个变量来存储当前浏览者的名字。你会怎么命名这个变量? 9 | -------------------------------------------------------------------------------- /1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md: -------------------------------------------------------------------------------- 1 | 我们通常用大写字母表示“硬编码(hard-coded)”的常量。或者,换句话说就是,当值在执行之前或在被写入代码的时候,我们就知道值是什么了。 2 | 3 | 在这个代码中 `birthday` 确实是这样的。因此我们可以使用大写。 4 | 5 | 在对照组中,`age` 是在程序运行时计算出的。今天我们有一个年龄,一年以后我们就会有另一个。它在某种意义上不会随着代码的执行而改变。但与 `birthday` 相比,它还是有一定的可变性:它是计算出来的,因此我们应该使用小写。 6 | -------------------------------------------------------------------------------- /1-js/02-first-steps/05-types/1-string-quotes/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 反引号将包装在 `${...}` 中的表达式嵌入到了字符串。 3 | 4 | ```js run 5 | let name = "Ilya"; 6 | 7 | // 表达式为数字 1 8 | alert( `hello ${1}` ); // hello 1 9 | 10 | // 表达式是一个字符串 "name" 11 | alert( `hello ${"name"}` ); // hello name 12 | 13 | // 表达式是一个变量,嵌入进去了。 14 | alert( `hello ${name}` ); // hello Ilya 15 | ``` 16 | -------------------------------------------------------------------------------- /1-js/02-first-steps/05-types/1-string-quotes/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 字符串的反引号 6 | 7 | 下面的脚本会输出什么? 8 | 9 | ```js 10 | let name = "Ilya"; 11 | 12 | alert( `hello ${1}` ); // ? 13 | 14 | alert( `hello ${"name"}` ); // ? 15 | 16 | alert( `hello ${name}` ); // ? 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md: -------------------------------------------------------------------------------- 1 | JavaScript 代码: 2 | 3 | ```js demo run 4 | let name = prompt("What is your name?", ""); 5 | alert(name); 6 | ``` 7 | 8 | 整个页面的代码: 9 | 10 | ```html 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 创建一个简单的页面 6 | 7 | 创建一个要求用户输入 `name`,并通过浏览器窗口对键入的内容进行输出的 web 页面。 8 | 9 | [demo] 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/08-operators/1-increment-order/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 答案如下: 3 | 4 | - `a = 2` 5 | - `b = 2` 6 | - `c = 2` 7 | - `d = 1` 8 | 9 | ```js run no-beautify 10 | let a = 1, b = 1; 11 | 12 | alert( ++a ); // 2,前置运算符返回最新值 13 | alert( b++ ); // 1,后置运算符返回旧值 14 | 15 | alert( a ); // 2,自增一次 16 | alert( b ); // 2,自增一次 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/08-operators/1-increment-order/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 后置运算符和前置运算符 6 | 7 | 以下代码中变量 `a`、`b`、`c`、`d` 的最终值分别是多少? 8 | 9 | ```js 10 | let a = 1, b = 1; 11 | 12 | let c = ++a; // ? 13 | let d = b++; // ? 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/02-first-steps/08-operators/2-assignment-result/solution.md: -------------------------------------------------------------------------------- 1 | 答案如下: 2 | 3 | - `a = 4`(乘以 2) 4 | - `x = 5`(相当于计算 1 + 4) 5 | -------------------------------------------------------------------------------- /1-js/02-first-steps/08-operators/2-assignment-result/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 赋值结果 6 | 7 | 下面这段代码运行完成后,代码中的 `a` 和 `x` 的值是多少? 8 | 9 | ```js 10 | let a = 2; 11 | 12 | let x = 1 + (a *= 2); 13 | ``` 14 | -------------------------------------------------------------------------------- /1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 类型转换 6 | 7 | 下面这些表达式的结果是什么? 8 | 9 | ```js no-beautify 10 | "" + 1 + 0 11 | "" - 1 + 0 12 | true + false 13 | 6 / "3" 14 | "2" * "3" 15 | 4 + 5 + "px" 16 | "$" + 4 + 5 17 | "4" - 2 18 | "4px" - 2 19 | " -9 " + 5 20 | " -9 " - 5 21 | null + 1 22 | undefined + 1 23 | " \t \n" - 2 24 | ``` 25 | 26 | 好好思考一下,把它们写下来然后和答案比较一下。 27 | -------------------------------------------------------------------------------- /1-js/02-first-steps/08-operators/4-fix-prompt/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 修正加法 6 | 7 | 这里有一段代码,要求用户输入两个数字并显示它们的总和。 8 | 9 | 它的运行结果不正确。下面例子中的输出是 `12`(对于默认的 prompt 的值)。 10 | 11 | 为什么会这样?修正它。结果应该是 `3`。 12 | 13 | ```js run 14 | let a = prompt("First number?", 1); 15 | let b = prompt("Second number?", 2); 16 | 17 | alert(a + b); // 12 18 | ``` 19 | -------------------------------------------------------------------------------- /1-js/02-first-steps/09-comparison/1-comparison-questions/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 值的比较 6 | 7 | 以下表达式的执行结果是? 8 | 9 | ```js no-beautify 10 | 5 > 4 11 | "apple" > "pineapple" 12 | "2" > "12" 13 | undefined == null 14 | undefined === null 15 | null == "\n0\n" 16 | null === +"\n0\n" 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md: -------------------------------------------------------------------------------- 1 | **是的,它会** 2 | 3 | 任何非空字符串(`"0"` 不是空字符串)的逻辑值都是 `true`。 4 | 5 | 我们可以执行下面的代码来进行验证: 6 | 7 | ```js run 8 | if ("0") { 9 | alert( 'Hello' ); 10 | } 11 | ``` 12 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # if(值为 0 的字符串) 6 | 7 | `alert` 弹窗会出来吗? 8 | 9 | ```js 10 | if ("0") { 11 | alert( 'Hello' ); 12 | } 13 | ``` 14 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/2-check-standard/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [html run src="ifelse_task2/index.html"] 4 | 5 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/2-check-standard/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # JavaScript 的名字 6 | 7 | 使用 `if..else` 结构,实现提问 "What's the “official” name of JavaScript?" 的代码。 8 | 9 | 如果访问者输入了 "ECMAScript",输出就提示 "Right!",否则 —— 输出:"You don't know? “ECMAScript”!" 10 | 11 | ![](ifelse_task2.svg) 12 | 13 | [demo src="ifelse_task2"] 14 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/3-sign/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run 4 | let value = prompt('Type a number', 0); 5 | 6 | if (value > 0) { 7 | alert( 1 ); 8 | } else if (value < 0) { 9 | alert( -1 ); 10 | } else { 11 | alert( 0 ); 12 | } 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/3-sign/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 显示符号 6 | 7 | 使用 `if..else` 语句,编写代码实现通过 `prompt` 获取一个数字并用 `alert` 显示结果: 8 | 9 | - 如果这个数字大于 0,就显示 `1`, 10 | - 如果这个数字小于 0,就显示 `-1`, 11 | - 如果这个数字等于 0,就显示 `0`。 12 | 13 | 在这个任务中,我们假设输入永远是一个数字。 14 | 15 | [demo src="if_sign"] 16 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/5-rewrite-if-question/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js 4 | let result = (a + b < 4) ? 'Below' : 'Over'; 5 | ``` 6 | 7 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/5-rewrite-if-question/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 使用 '?' 重写 'if' 语句 6 | 7 | 使用条件运算符 `'?'` 重写下面的 `if` 语句: 8 | 9 | ```js 10 | let result; 11 | 12 | if (a + b < 4) { 13 | result = 'Below'; 14 | } else { 15 | result = 'Over'; 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/6-rewrite-if-else-question/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js 4 | let message = (login == 'Employee') ? 'Hello' : 5 | (login == 'Director') ? 'Greetings' : 6 | (login == '') ? 'No login' : 7 | ''; 8 | ``` 9 | 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/10-ifelse/6-rewrite-if-else-question/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 使用 '?' 重写 'if..else' 语句 6 | 7 | 使用多个三元运算符 `'?'` 重写下面的 `if..else` 语句。 8 | 9 | 为了增强代码可读性,建议将代码分成多行。 10 | 11 | ```js 12 | let message; 13 | 14 | if (login == 'Employee') { 15 | message = 'Hello'; 16 | } else if (login == 'Director') { 17 | message = 'Greetings'; 18 | } else if (login == '') { 19 | message = 'No login'; 20 | } else { 21 | message = ''; 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/solution.md: -------------------------------------------------------------------------------- 1 | 结果是 `2`,这是第一个真值。 2 | 3 | ```js run 4 | alert( null || 2 || undefined ); 5 | ``` 6 | 7 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 或运算的结果是什么? 6 | 7 | 如下代码将会输出什么? 8 | 9 | ```js 10 | alert( null || 2 || undefined ); 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md: -------------------------------------------------------------------------------- 1 | 答案:首先是 `1`,然后是 `2`。 2 | 3 | ```js run 4 | alert( alert(1) || 2 || alert(3) ); 5 | ``` 6 | 7 | 对 `alert` 的调用没有返回值。或者说返回的是 `undefined`。 8 | 9 | 1. 第一个或运算 `||` 对它的左值 `alert(1)` 进行了计算。这就显示了第一条信息 `1`。 10 | 2. 函数 `alert` 返回了 `undefined`,所以或运算继续检查第二个操作数以寻找真值。 11 | 3. 第二个操作数 `2` 是真值,所以执行就中断了。`2` 被返回,并且被外层的 alert 显示。 12 | 13 | 这里不会显示 `3`,因为运算没有抵达 `alert(3)`。 14 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/2-alert-or/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 或运算和 alert 的结果是什么? 6 | 7 | 下面的代码将会输出什么? 8 | 9 | ```js 10 | alert( alert(1) || 2 || alert(3) ); 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`null`,因为它是列表中第一个假值。 2 | 3 | ```js run 4 | alert(1 && null && 2); 5 | ``` 6 | 7 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 与操作的结果是什么? 6 | 7 | 下面这段代码将会显示什么? 8 | 9 | ```js 10 | alert( 1 && null && 2 ); 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/4-alert-and/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`1`,然后 `undefined`。 2 | 3 | ```js run 4 | alert( alert(1) && alert(2) ); 5 | ``` 6 | 7 | 调用 `alert` 返回了 `undefined`(它只展示消息,所以没有有意义的返回值)。 8 | 9 | 因此,`&&` 计算了它左边的操作数(显示 `1`),然后立即停止了,因为 `undefined` 是一个假值。`&&` 就是寻找假值然后返回它,所以运算结束。 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/4-alert-and/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 与运算连接的 alert 的结果是什么? 6 | 7 | 这段代码将会显示什么? 8 | 9 | ```js 10 | alert( alert(1) && alert(2) ); 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`3`。 2 | 3 | ```js run 4 | alert( null || 2 && 3 || 4 ); 5 | ``` 6 | 7 | 与运算 `&&` 的优先级比 `||` 高,所以它第一个被执行。 8 | 9 | 结果是 `2 && 3 = 3`,所以表达式变成了: 10 | 11 | ``` 12 | null || 3 || 4 13 | ``` 14 | 15 | 现在的结果就是第一个真值:`3`。 16 | 17 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 或运算、与运算、或运算串联的结果 6 | 7 | 结果将会是什么? 8 | 9 | ```js 10 | alert( null || 2 && 3 || 4 ); 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/6-check-if-in-range/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js 4 | if (age >= 14 && age <= 90) 5 | ``` 6 | 7 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/6-check-if-in-range/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 检查值是否位于范围区间内 6 | 7 | 写一个 `if` 条件句来检查 `age` 是否位于 `14` 到 `90` 的闭区间。 8 | 9 | “闭区间”意味着,`age` 的值可以取 `14` 或 `90`。 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/7-check-if-out-range/solution.md: -------------------------------------------------------------------------------- 1 | 第一个表达式: 2 | 3 | ```js 4 | if (!(age >= 14 && age <= 90)) 5 | ``` 6 | 7 | 第二个表达式: 8 | 9 | ```js 10 | if (age < 14 || age > 90) 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/7-check-if-out-range/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 检查值是否位于范围之外 6 | 7 | 写一个 `if` 条件句,检查 `age` 是否不位于 `14` 到 `90` 的闭区间。 8 | 9 | 创建两个表达式:第一个用非运算 `!`,第二个不用。 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/8-if-question/solution.md: -------------------------------------------------------------------------------- 1 | 答案:第一个和第三个将会被执行。 2 | 3 | 详解: 4 | 5 | ```js run 6 | // 执行。 7 | // -1 || 0 的结果为 -1,真值 8 | if (-1 || 0) alert( 'first' ); 9 | 10 | // 不执行。 11 | // -1 && 0 = 0,假值 12 | if (-1 && 0) alert( 'second' ); 13 | 14 | // 执行 15 | // && 运算的优先级比 || 高 16 | // 所以 -1 && 1 先执行,给出如下运算链: 17 | // null || -1 && 1 -> null || 1 -> 1 18 | if (null || -1 && 1) alert( 'third' ); 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /1-js/02-first-steps/11-logical-operators/8-if-question/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 一个关于 "if" 的问题 6 | 7 | 下面哪一个 `alert` 将会被执行? 8 | 9 | `if(...)` 语句内表达式的结果是什么? 10 | 11 | ```js 12 | if (-1 || 0) alert( 'first' ); 13 | if (-1 && 0) alert( 'second' ); 14 | if (null || -1 && 1) alert( 'third' ); 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md: -------------------------------------------------------------------------------- 1 | 答案是:`1`。 2 | 3 | ```js run 4 | let i = 3; 5 | 6 | while (i) { 7 | alert( i-- ); 8 | } 9 | ``` 10 | 11 | 每次循环迭代都将 `i` 减 `1`。当检查到 `i = 0` 时,`while(i)` 循环停止。 12 | 13 | 因此,此循环执行的步骤如下(“循环展开”): 14 | 15 | ```js 16 | let i = 3; 17 | 18 | alert(i--); // 显示 3,i 减至 2 19 | 20 | alert(i--) // 显示 2,i 减至 1 21 | 22 | alert(i--) // 显示 1,i 减至 0 23 | 24 | // 完成,while(i) 检查循环条件并停止循环 25 | ``` 26 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/1-loop-last-value/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 最后一次循环的值 6 | 7 | 此代码最后一次 alert 值是多少?为什么? 8 | 9 | ```js 10 | let i = 3; 11 | 12 | while (i) { 13 | alert( i-- ); 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/2-which-value-while/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # while 循环显示哪些值? 6 | 7 | 对于每次循环,写出你认为会显示的值,然后与答案进行比较。 8 | 9 | 以下两个循环的 `alert` 值是否相同? 10 | 11 | 1. 前缀形式 `++i`: 12 | 13 | ```js 14 | let i = 0; 15 | while (++i < 5) alert( i ); 16 | ``` 17 | 2. 后缀形式 `i++` 18 | 19 | ```js 20 | let i = 0; 21 | while (i++ < 5) alert( i ); 22 | ``` 23 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/3-which-value-for/solution.md: -------------------------------------------------------------------------------- 1 | **答案:在这两种情况下都是从 `0` 到 `4`。** 2 | 3 | ```js run 4 | for (let i = 0; i < 5; ++i) alert( i ); 5 | 6 | for (let i = 0; i < 5; i++) alert( i ); 7 | ``` 8 | 9 | 这可以很容易地从 `for` 算法中推导出: 10 | 11 | 1. 在一切开始之前执行 `i = 0`。 12 | 2. 检查 `i < 5` 条件 13 | 3. 如果 `true` —— 执行循环体并 `alert(i)`,然后进行 `i++` 14 | 15 | 递增 `i++` 与检查条件(2)分开。这只是另一种写法。 16 | 17 | 在这没使用返回的递增值,因此 `i++` 和 `++i`之间没有区别。 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/3-which-value-for/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # "for" 循环显示哪些值? 6 | 7 | 对于每次循环,写下它将显示的值。然后与答案进行比较。 8 | 9 | 两次循环 `alert` 值是否相同? 10 | 11 | 1. 后缀形式: 12 | 13 | ```js 14 | for (let i = 0; i < 5; i++) alert( i ); 15 | ``` 16 | 2. 前缀形式: 17 | 18 | ```js 19 | for (let i = 0; i < 5; ++i) alert( i ); 20 | ``` 21 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/4-for-even/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run demo 4 | for (let i = 2; i <= 10; i++) { 5 | if (i % 2 == 0) { 6 | alert( i ); 7 | } 8 | } 9 | ``` 10 | 11 | 我们使用 "modulo" 运算符 `%` 来获取余数,并检查奇偶性。 12 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/4-for-even/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 使用 for 循环输出偶数 6 | 7 | 使用 `for` 循环输出从 `2` 到 `10` 的偶数。 8 | 9 | [demo] 10 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run 4 | let i = 0; 5 | while (i < 3) { 6 | alert( `number ${i}!` ); 7 | i++; 8 | } 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/5-replace-for-while/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 用 "while" 替换 "for" 6 | 7 | 重写代码,在保证不改变其行为的情况下,将 `for` 循环更改为 `while`(输出应保持不变)。 8 | 9 | ```js run 10 | for (let i = 0; i < 3; i++) { 11 | alert( `number ${i}!` ); 12 | } 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run demo 3 | let num; 4 | 5 | do { 6 | num = prompt("Enter a number greater than 100?", 0); 7 | } while (num <= 100 && num); 8 | ``` 9 | 10 | 两个检查都为真时,继续执行 `do..while` 循环: 11 | 12 | 1. 检查 `num <= 100` —— 即输入值仍然不大于 `100`。 13 | 2. 当 `num` 为 `null` 或空字符串时,`&& num` 的结果为 false。那么 `while` 循环也会停止。 14 | 15 | P.S. 如果 `num` 为 `null`,那么 `num <= 100` 为 `true`。因此用户单击取消,如果没有第二次检查,循环就不会停止。两次检查都是必须的。 16 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | --- 3 | 4 | # 重复输入,直到正确为止 5 | 6 | 编写一个提示用户输入大于 `100` 的数字的循环。如果用户输入其他数值 —— 请他重新输入。 7 | 8 | 循环一直在请求一个数字,直到用户输入了一个大于 `100` 的数字、取消输入或输入了一个空行为止。 9 | 10 | 在这我们假设用户只会输入数字。在本题目中,不需要对非数值输入进行特殊处理。 11 | 12 | [demo] 13 | -------------------------------------------------------------------------------- /1-js/02-first-steps/13-while-for/7-list-primes/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 输出素数(prime) 6 | 7 | 大于 `1` 且不能被除了 `1` 和它本身以外的任何数整除的整数叫做[素数](https://en.wikipedia.org/wiki/Prime_number)。 8 | 9 | 换句话说,`n > 1` 且不能被 `1` 和 `n` 以外的任何数整除的整数,被称为素数。 10 | 11 | 例如,`5` 是素数,因为它不能被 `2`、`3` 和 `4` 整除,会产生余数。 12 | 13 | **写一个可以输出 `2` 到 `n` 之间的所有素数的代码。** 14 | 15 | 当 `n = 10`,结果输出 `2、3、5、7`。 16 | 17 | P.S. 代码应适用于任何 `n`,而不是对任何固定值进行硬性调整。 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 将 "if" 结构重写为 "switch" 结构 6 | 7 | 用 `switch` 重写以下代码: 8 | 9 | ```js run 10 | let a = +prompt('a?', ''); 11 | 12 | if (a == 0) { 13 | alert( 0 ); 14 | } 15 | if (a == 1) { 16 | alert( 1 ); 17 | } 18 | 19 | if (a == 2 || a == 3) { 20 | alert( '2,3' ); 21 | } 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md: -------------------------------------------------------------------------------- 1 | 没有区别。 2 | 3 | 在这两种情况下,`return confirm('父母允许吗?')` 均只会在 `if` 条件为假时执行。 4 | -------------------------------------------------------------------------------- /1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md: -------------------------------------------------------------------------------- 1 | 使用问号运算符 `'?'`: 2 | 3 | ```js 4 | function checkAge(age) { 5 | return (age > 18) ? true : confirm('Did parents allow you?'); 6 | } 7 | ``` 8 | 9 | 使用或运算符 `||`(最短的变体): 10 | 11 | ```js 12 | function checkAge(age) { 13 | return (age > 18) || confirm('Did parents allow you?'); 14 | } 15 | ``` 16 | 17 | 请注意此处不需要 `age > 18` 左右的括号。写上括号是为了提高可读性。 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/15-function-basics/3-min/solution.md: -------------------------------------------------------------------------------- 1 | 使用 `if` 的解决方案: 2 | 3 | ```js 4 | function min(a, b) { 5 | if (a < b) { 6 | return a; 7 | } else { 8 | return b; 9 | } 10 | } 11 | ``` 12 | 13 | 使用问号运算符 `'?'` 的解决方案: 14 | 15 | ```js 16 | function min(a, b) { 17 | return a < b ? a : b; 18 | } 19 | ``` 20 | 21 | P.S. 在 `a == b` 的情况下,返回什么都无关紧要。 22 | -------------------------------------------------------------------------------- /1-js/02-first-steps/15-function-basics/3-min/task.md: -------------------------------------------------------------------------------- 1 | importance: 1 2 | 3 | --- 4 | 5 | # 函数 min(a, b) 6 | 7 | 写一个返回数字 `a` 和 `b` 中较小的那个数字的函数 `min(a,b)`。 8 | 9 | 例如: 10 | 11 | ```js 12 | min(2, 5) == 2 13 | min(3, -1) == -1 14 | min(1, 1) == 1 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /1-js/02-first-steps/15-function-basics/4-pow/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run demo 3 | function pow(x, n) { 4 | let result = x; 5 | 6 | for (let i = 1; i < n; i++) { 7 | result *= x; 8 | } 9 | 10 | return result; 11 | } 12 | 13 | let x = prompt("x?", ''); 14 | let n = prompt("n?", ''); 15 | 16 | if (n < 1) { 17 | alert(`Power ${n} is not supported, use a positive integer`); 18 | } else { 19 | alert( pow(x, n) ); 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /1-js/02-first-steps/15-function-basics/4-pow/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 函数 pow(x,n) 6 | 7 | 写一个函数 `pow(x,n)`,返回 `x` 的 `n` 次方。换句话说,将 `x` 与自身相乘 `n` 次,返回最终结果。 8 | 9 | ```js 10 | pow(3, 2) = 3 * 3 = 9 11 | pow(3, 3) = 3 * 3 * 3 = 27 12 | pow(1, 100) = 1 * 1 * ...*1 = 1 13 | ``` 14 | 15 | 创建一个 web 页面,提示输入 `x` 和 `n`,然后返回 `pow(x,n)` 的运算结果。 16 | 17 | [demo] 18 | 19 | P.S. 在这个任务中,函数应该只支持自然数 `n`:从 `1` 开始的整数。 20 | -------------------------------------------------------------------------------- /1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run 3 | function ask(question, yes, no) { 4 | if (confirm(question)) yes(); 5 | else no(); 6 | } 7 | 8 | ask( 9 | "Do you agree?", 10 | *!* 11 | () => alert("You agreed."), 12 | () => alert("You canceled the execution.") 13 | */!* 14 | ); 15 | ``` 16 | 17 | 是不是看起来精简多了? 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md: -------------------------------------------------------------------------------- 1 | 2 | # 用箭头函数重写 3 | 4 | 用箭头函数重写下面的函数表达式: 5 | 6 | ```js run 7 | function ask(question, yes, no) { 8 | if (confirm(question)) yes(); 9 | else no(); 10 | } 11 | 12 | ask( 13 | "Do you agree?", 14 | function() { alert("You agreed."); }, 15 | function() { alert("You canceled the execution."); } 16 | ); 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/02-first-steps/index.md: -------------------------------------------------------------------------------- 1 | # JavaScript 基础知识 2 | 3 | 让我们来一起学习 JavaScript 脚本构建的基础知识。 4 | -------------------------------------------------------------------------------- /1-js/03-code-quality/01-debugging-chrome/debugging.view/hello.js: -------------------------------------------------------------------------------- 1 | function hello(name) { 2 | let phrase = `Hello, ${name}!`; 3 | 4 | say(phrase); 5 | } 6 | 7 | function say(phrase) { 8 | alert(`** ${phrase} **`); 9 | } 10 | -------------------------------------------------------------------------------- /1-js/03-code-quality/01-debugging-chrome/debugging.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | An example for debugging. 8 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /1-js/03-code-quality/01-debugging-chrome/head.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /1-js/03-code-quality/05-testing-mocha/3-pow-test-wrong/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 测试代码中有什么错误? 6 | 7 | 下面这个 `pow` 的测试代码有什么错误? 8 | 9 | ```js 10 | it("Raises x to the power n", function() { 11 | let x = 5; 12 | 13 | let result = x; 14 | assert.equal(pow(x, 1), result); 15 | 16 | result *= x; 17 | assert.equal(pow(x, 2), result); 18 | 19 | result *= x; 20 | assert.equal(pow(x, 3), result); 21 | }); 22 | ``` 23 | 24 | 附:从语法上来说这些测试代码是正确且通过的。 25 | -------------------------------------------------------------------------------- /1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js: -------------------------------------------------------------------------------- 1 | describe("pow", function() { 2 | 3 | it("raises to n-th power", function() { 4 | assert.equal(pow(2, 3), 8); 5 | }); 6 | 7 | }); 8 | -------------------------------------------------------------------------------- /1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js: -------------------------------------------------------------------------------- 1 | describe("pow", function() { 2 | 3 | it("2 raised to power 3 is 8", function() { 4 | assert.equal(pow(2, 3), 8); 5 | }); 6 | 7 | it("3 raised to power 4 is 81", function() { 8 | assert.equal(pow(3, 4), 81); 9 | }); 10 | 11 | }); 12 | -------------------------------------------------------------------------------- /1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js: -------------------------------------------------------------------------------- 1 | describe("pow", function() { 2 | 3 | function makeTest(x) { 4 | let expected = x * x * x; 5 | it(`${x} in the power 3 is ${expected}`, function() { 6 | assert.equal(pow(x, 3), expected); 7 | }); 8 | } 9 | 10 | for (let x = 1; x <= 5; x++) { 11 | makeTest(x); 12 | } 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js: -------------------------------------------------------------------------------- 1 | describe("pow", function() { 2 | 3 | it("raises to n-th power", function() { 4 | assert.equal(pow(2, 3), 8); 5 | }); 6 | 7 | }); 8 | -------------------------------------------------------------------------------- /1-js/03-code-quality/index.md: -------------------------------------------------------------------------------- 1 | # 代码质量 2 | 3 | 本章介绍了我们将在开发中进一步使用的编码实践。 4 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/2-hello-object/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js 4 | let user = {}; 5 | user.name = "John"; 6 | user.surname = "Smith"; 7 | user.name = "Pete"; 8 | delete user.name; 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/2-hello-object/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 你好,对象 6 | 7 | 按下面的要求写代码,一条对应一行代码: 8 | 9 | 1. 创建一个空的对象 `user`。 10 | 2. 为这个对象增加一个属性,键是 `name`,值是 `John`。 11 | 3. 再增加一个属性,键是 `surname`,值是 `Smith`。 12 | 4. 把键为 `name` 的属性的值改成 `Pete`。 13 | 5. 删除这个对象中键为 `name` 的属性。 14 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function isEmpty(obj) { 2 | for (let key in obj) { 3 | // 如果进到循环里面,说明有属性。 4 | return false; 5 | } 6 | return true; 7 | } -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("isEmpty", function() { 2 | it("returns true for an empty object", function() { 3 | assert.isTrue(isEmpty({})); 4 | }); 5 | 6 | it("returns false if a property exists", function() { 7 | assert.isFalse(isEmpty({ 8 | anything: false 9 | })); 10 | }); 11 | }); -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/3-is-empty/solution.md: -------------------------------------------------------------------------------- 1 | 只需要遍历这个对象,如果对象存在任何属性则 `return false`。 2 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/3-is-empty/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 检查空对象 6 | 7 | 写一个 `isEmpty(obj)` 函数,当对象没有属性的时候返回 `true`,否则返回 `false`。 8 | 9 | 应该像这样: 10 | 11 | ```js 12 | let schedule = {}; 13 | 14 | alert( isEmpty(schedule) ); // true 15 | 16 | schedule["8:30"] = "get up"; 17 | 18 | alert( isEmpty(schedule) ); // false 19 | ``` 20 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/5-sum-object/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run 3 | let salaries = { 4 | John: 100, 5 | Ann: 160, 6 | Pete: 130 7 | }; 8 | 9 | let sum = 0; 10 | for (let key in salaries) { 11 | sum += salaries[key]; 12 | } 13 | 14 | alert(sum); // 390 15 | ``` 16 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/5-sum-object/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 对象属性求和 6 | 7 | 我们有一个保存着团队成员工资的对象: 8 | 9 | ```js 10 | let salaries = { 11 | John: 100, 12 | Ann: 160, 13 | Pete: 130 14 | } 15 | ``` 16 | 17 | 写一段代码求出我们的工资总和,将计算结果保存到变量 `sum`。从所给的信息来看,结果应该是 `390`。 18 | 19 | 如果 `salaries` 是一个空对象,那结果就为 `0`。 20 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function multiplyNumeric(obj) { 2 | for (let key in obj) { 3 | if (typeof obj[key] == 'number') { 4 | obj[key] *= 2; 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js: -------------------------------------------------------------------------------- 1 | let menu = { 2 | width: 200, 3 | height: 300, 4 | title: "My menu" 5 | }; 6 | 7 | 8 | function multiplyNumeric(obj) { 9 | 10 | /* 你的代码 */ 11 | 12 | } 13 | 14 | multiplyNumeric(menu); 15 | 16 | alert( "menu width=" + menu.width + " height=" + menu.height + " title=" + menu.title ); 17 | 18 | -------------------------------------------------------------------------------- /1-js/04-object-basics/01-object/8-multiply-numeric/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/04-object-basics/01-object/8-multiply-numeric/solution.md -------------------------------------------------------------------------------- /1-js/04-object-basics/04-object-methods/4-object-property-this/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 在对象字面量中使用 "this" 6 | 7 | 这里 `makeUser` 函数返回了一个对象。 8 | 9 | 访问 `ref` 的结果是什么?为什么? 10 | 11 | ```js 12 | function makeUser() { 13 | return { 14 | name: "John", 15 | ref: this 16 | }; 17 | } 18 | 19 | let user = makeUser(); 20 | 21 | alert( user.ref.name ); // 结果是什么? 22 | ``` 23 | -------------------------------------------------------------------------------- /1-js/04-object-basics/04-object-methods/7-calculator/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | let calculator = { 2 | sum() { 3 | return this.a + this.b; 4 | }, 5 | 6 | mul() { 7 | return this.a * this.b; 8 | }, 9 | 10 | read() { 11 | this.a = +prompt('a?', 0); 12 | this.b = +prompt('b?', 0); 13 | } 14 | }; -------------------------------------------------------------------------------- /1-js/04-object-basics/04-object-methods/7-calculator/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run demo solution 3 | let calculator = { 4 | sum() { 5 | return this.a + this.b; 6 | }, 7 | 8 | mul() { 9 | return this.a * this.b; 10 | }, 11 | 12 | read() { 13 | this.a = +prompt('a?', 0); 14 | this.b = +prompt('b?', 0); 15 | } 16 | }; 17 | 18 | calculator.read(); 19 | alert( calculator.sum() ); 20 | alert( calculator.mul() ); 21 | ``` 22 | -------------------------------------------------------------------------------- /1-js/04-object-basics/04-object-methods/7-calculator/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 创建一个计算器 6 | 7 | 创建一个有三个方法的 `calculator` 对象: 8 | 9 | - `read()` 提示输入两个值,并将其保存为对象属性,属性名分别为 `a` 和 `b`。 10 | - `sum()` 返回保存的值的和。 11 | - `mul()` 将保存的值相乘并返回计算结果。 12 | 13 | ```js 14 | let calculator = { 15 | // ……你的代码…… 16 | }; 17 | 18 | calculator.read(); 19 | alert( calculator.sum() ); 20 | alert( calculator.mul() ); 21 | ``` 22 | 23 | [demo] 24 | -------------------------------------------------------------------------------- /1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | 2 | let ladder = { 3 | step: 0, 4 | up: function() { 5 | this.step++; 6 | return this; 7 | }, 8 | down: function() { 9 | this.step--; 10 | return this; 11 | }, 12 | showStep: function() { 13 | alert(this.step); 14 | return this; 15 | } 16 | }; -------------------------------------------------------------------------------- /1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md: -------------------------------------------------------------------------------- 1 | 是的,这是可以的。 2 | 3 | 如果一个函数返回一个对象,那么 `new` 返回那个对象而不是 `this`。 4 | 5 | 所以它们可以,例如,返回相同的外部定义的对象 `obj`: 6 | 7 | ```js run no-beautify 8 | let obj = {}; 9 | 10 | function A() { return obj; } 11 | function B() { return obj; } 12 | 13 | alert( new A() == new B() ); // true 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 两个函数 —— 一个对象 6 | 7 | 是否可以创建像 `new A() == new B()` 这样的函数 `A` 和 `B`? 8 | 9 | ```js no-beautify 10 | function A() { ... } 11 | function B() { ... } 12 | 13 | let a = new A; 14 | let b = new B; 15 | 16 | alert( a == b ); // true 17 | ``` 18 | 19 | 如果可以,请提供一个它们的代码示例。 20 | -------------------------------------------------------------------------------- /1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function Calculator() { 2 | 3 | this.read = function() { 4 | this.a = +prompt('a?', 0); 5 | this.b = +prompt('b?', 0); 6 | }; 7 | 8 | this.sum = function() { 9 | return this.a + this.b; 10 | }; 11 | 12 | this.mul = function() { 13 | return this.a * this.b; 14 | }; 15 | } -------------------------------------------------------------------------------- /1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 创建 new Calculator 6 | 7 | 创建一个构造函数 `Calculator`,它创建的对象中有三个方法: 8 | 9 | - `read()` 使用 `prompt` 请求两个值并把它们记录在对象的属性中。 10 | - `sum()` 返回这些属性的总和。 11 | - `mul()` 返回这些属性的乘积。 12 | 13 | 例如: 14 | 15 | ```js 16 | let calculator = new Calculator(); 17 | calculator.read(); 18 | 19 | alert( "Sum=" + calculator.sum() ); 20 | alert( "Mul=" + calculator.mul() ); 21 | ``` 22 | 23 | [demo] 24 | -------------------------------------------------------------------------------- /1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function Accumulator(startingValue) { 2 | this.value = startingValue; 3 | 4 | this.read = function() { 5 | this.value += +prompt('How much to add?', 0); 6 | }; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run demo 4 | function Accumulator(startingValue) { 5 | this.value = startingValue; 6 | 7 | this.read = function() { 8 | this.value += +prompt('How much to add?', 0); 9 | }; 10 | 11 | } 12 | 13 | let accumulator = new Accumulator(1); 14 | accumulator.read(); 15 | accumulator.read(); 16 | alert(accumulator.value); 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/04-object-basics/index.md: -------------------------------------------------------------------------------- 1 | # Object(对象):基础知识 2 | -------------------------------------------------------------------------------- /1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 我能为字符串添加一个属性吗? 6 | 7 | 8 | 思考下面的代码: 9 | 10 | ```js 11 | let str = "Hello"; 12 | 13 | str.test = 5; 14 | 15 | alert(str.test); 16 | ``` 17 | 18 | 你怎么想的呢,它会工作吗?会得到什么样的结果? 19 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/1-sum-interface/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run demo 4 | let a = +prompt("The first number?", ""); 5 | let b = +prompt("The second number?", ""); 6 | 7 | alert( a + b ); 8 | ``` 9 | 10 | 注意在 `prompt` 前面的一元加号 `+`。它将立即把值转换成数字。 11 | 12 | 否则,`a` 和 `b` 将会是字符串,它们的总和将是它们的连接,即:`"1" + "2" = "12"`。 13 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/1-sum-interface/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 来自访问者的数字的总和 6 | 7 | 创建一个脚本,提示访问者输入两个数字,然后显示它们的总和。 8 | 9 | [demo] 10 | 11 | P.S. 有一个类型陷阱。 12 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/2-why-rounded-down/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 为什么 6.35.toFixed(1) == 6.3? 6 | 7 | 根据文档,`Math.round` 和 `toFixed` 都将数字舍入到最接近的数字:`0..4` 会被舍去,而 `5..9` 会进一位。 8 | 9 | 例如: 10 | 11 | ```js run 12 | alert( 1.35.toFixed(1) ); // 1.4 13 | ``` 14 | 15 | 在下面这个类似的示例中,为什么 `6.35` 被舍入为 `6.3` 而不是 `6.4`? 16 | 17 | ```js run 18 | alert( 6.35.toFixed(1) ); // 6.3 19 | ``` 20 | 21 | 如何以正确的方式来对 `6.35` 进行舍入? 22 | 23 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/3-repeat-until-number/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | 2 | function readNumber() { 3 | let num; 4 | 5 | do { 6 | num = prompt("Enter a number please?", 0); 7 | } while ( !isFinite(num) ); 8 | 9 | if (num === null || num === '') return null; 10 | 11 | return +num; 12 | } -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/3-repeat-until-number/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 重复,直到输入的是一个数字 6 | 7 | 创建一个函数 `readNumber`,它提示输入一个数字,直到访问者输入一个有效的数字为止。 8 | 9 | 结果值必须以数字形式返回。 10 | 11 | 访问者也可以通过输入空行或点击“取消”来停止该过程。在这种情况下,函数应该返回 `null`。 12 | 13 | [demo] 14 | 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/4-endless-loop-error/solution.md: -------------------------------------------------------------------------------- 1 | 那是因为 `i` 永远不会等于 `10`。 2 | 3 | 运行下面这段代码来查看 `i` 的 **实际** 值: 4 | 5 | ```js run 6 | let i = 0; 7 | while (i < 11) { 8 | i += 0.2; 9 | if (i > 9.8 && i < 10.2) alert( i ); 10 | } 11 | ``` 12 | 13 | 它们中没有一个恰好是 `10`。 14 | 15 | 之所以发生这种情况,是因为对 `0.2` 这样的小数时进行加法运算时出现了精度损失。 16 | 17 | 结论:在处理小数时避免相等性检查。 18 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/4-endless-loop-error/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 一个偶发的无限循环 6 | 7 | 这是一个无限循环。它永远不会结束。为什么? 8 | 9 | ```js 10 | let i = 0; 11 | while (i != 10) { 12 | i += 0.2; 13 | } 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/8-random-min-max/solution.md: -------------------------------------------------------------------------------- 1 | 我们需要将区间 0..1 中的所有值“映射”为范围在 `min` 到 `max` 中的值。 2 | 3 | 这可以分两个阶段完成: 4 | 5 | 1. 如果我们将 0..1 的随机数乘以 `max-min`,则随机数的范围将从 0..1 增加到 `0..max-min`。 6 | 2. 现在,如果我们将随机数与 `min` 相加,则随机数的范围将为 `min` 到 `max`。 7 | 8 | 函数实现: 9 | 10 | ```js run 11 | function random(min, max) { 12 | return min + Math.random() * (max - min); 13 | } 14 | 15 | alert( random(1, 5) ); 16 | alert( random(1, 5) ); 17 | alert( random(1, 5) ); 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/8-random-min-max/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 从 min 到 max 的随机数 6 | 7 | 内建函数 `Math.random()` 会创建一个在 `0` 到 `1` 之间(不包括 `1`)的随机数。 8 | 9 | 编写一个 `random(min, max)` 函数,用以生成一个在 `min` 到 `max` 之间的随机浮点数(不包括 `max`))。 10 | 11 | 运行示例: 12 | 13 | ```js 14 | alert( random(1, 5) ); // 1.2345623452 15 | alert( random(1, 5) ); // 3.7894332423 16 | alert( random(1, 5) ); // 4.3435234525 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/05-data-types/02-number/9-random-int-min-max/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 从 min 到 max 的随机整数 6 | 7 | 创建一个函数 `randomInteger(min, max)`,该函数会生成一个范围在 `min` 到 `max` 中的随机整数,包括 `min` 和 `max`。 8 | 9 | 在 `min..max` 范围中的所有数字的出现概率必须相同。 10 | 11 | 12 | 运行示例: 13 | 14 | ```js 15 | alert( randomInteger(1, 5) ); // 1 16 | alert( randomInteger(1, 5) ); // 3 17 | alert( randomInteger(1, 5) ); // 5 18 | ``` 19 | 20 | 你可以使用 [上一个任务](info:task/random-min-max) 的解决方案作为基础。 21 | -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function ucFirst(str) { 2 | if (!str) return str; 3 | 4 | return str[0].toUpperCase() + str.slice(1); 5 | } -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/1-ucfirst/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("ucFirst", function() { 2 | it('Uppercases the first symbol', function() { 3 | assert.strictEqual(ucFirst("john"), "John"); 4 | }); 5 | 6 | it("Doesn't die on an empty string", function() { 7 | assert.strictEqual(ucFirst(""), ""); 8 | }); 9 | }); -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/1-ucfirst/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 首字母大写 6 | 7 | 写一个函数 `ucFirst(str)`,并返回首字母大写的字符串 `str`,例如: 8 | 9 | ```js 10 | ucFirst("john") == "John"; 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/2-check-spam/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function checkSpam(str) { 2 | let lowerStr = str.toLowerCase(); 3 | 4 | return lowerStr.includes('viagra') || lowerStr.includes('xxx'); 5 | } -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/2-check-spam/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("checkSpam", function() { 2 | it('finds spam in "buy ViAgRA now"', function() { 3 | assert.isTrue(checkSpam('buy ViAgRA now')); 4 | }); 5 | 6 | it('finds spam in "free xxxxx"', function() { 7 | assert.isTrue(checkSpam('free xxxxx')); 8 | }); 9 | 10 | it('no spam in "innocent rabbit"', function() { 11 | assert.isFalse(checkSpam('innocent rabbit')); 12 | }); 13 | }); -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/2-check-spam/solution.md: -------------------------------------------------------------------------------- 1 | 为了使搜索不区分大小写,我们将字符串改为小写,然后搜索: 2 | 3 | ```js run demo 4 | function checkSpam(str) { 5 | let lowerStr = str.toLowerCase(); 6 | 7 | return lowerStr.includes('viagra') || lowerStr.includes('xxx'); 8 | } 9 | 10 | alert( checkSpam('buy ViAgRA now') ); 11 | alert( checkSpam('free xxxxx') ); 12 | alert( checkSpam("innocent rabbit") ); 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/2-check-spam/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 检查 spam 6 | 7 | 写一个函数 `checkSpam(str)`,如果 `str` 包含 `viagra` 或 `XXX` 就返回 `true`,否则返回 `false`。 8 | 9 | 函数必须不区分大小写: 10 | 11 | ```js 12 | checkSpam('buy ViAgRA now') == true 13 | checkSpam('free xxxxx') == true 14 | checkSpam("innocent rabbit") == false 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/3-truncate/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function truncate(str, maxlength) { 2 | return (str.length > maxlength) ? 3 | str.slice(0, maxlength - 1) + '…' : str; 4 | } -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/3-truncate/solution.md: -------------------------------------------------------------------------------- 1 | 最大长度必须是 `maxlength`,因此为了给省略号留空间我们需要缩短它。 2 | 3 | 请注意,省略号实际上有一个单独的 Unicode 字符,而不是三个点。 4 | 5 | ```js run demo 6 | function truncate(str, maxlength) { 7 | return (str.length > maxlength) ? 8 | str.slice(0, maxlength - 1) + '…' : str; 9 | } 10 | ``` 11 | -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/3-truncate/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 截断文本 6 | 7 | 创建函数 `truncate(str, maxlength)` 来检查 `str` 的长度,如果超过 `maxlength` —— 应使用 `"…"` 来代替 `str` 的结尾部分,长度仍然等于 `maxlength`。 8 | 9 | 函数的结果应该是截断后的文本(如果需要的话)。 10 | 11 | 例如: 12 | 13 | ```js 14 | truncate("What I'd like to tell on this topic is:", 20) = "What I'd like to te…" 15 | 16 | truncate("Hi everyone!", 20) = "Hi everyone!" 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/4-extract-currency/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function extractCurrencyValue(str) { 2 | return +str.slice(1); 3 | } -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/4-extract-currency/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("extractCurrencyValue", function() { 2 | 3 | it("for the string $120 returns the number 120", function() { 4 | assert.strictEqual(extractCurrencyValue('$120'), 120); 5 | }); 6 | 7 | 8 | }); -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/4-extract-currency/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/05-data-types/03-string/4-extract-currency/solution.md -------------------------------------------------------------------------------- /1-js/05-data-types/03-string/4-extract-currency/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 提取货币 6 | 7 | 我们有以 `"$120"` 这样的格式表示的花销。意味着:先是美元符号,然后才是数值。 8 | 9 | 创建函数 `extractCurrencyValue(str)` 从字符串中提取数值并返回。 10 | 11 | 例如: 12 | 13 | ```js 14 | alert( extractCurrencyValue('$120') === 120 ); // true 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/1-item-value/solution.md: -------------------------------------------------------------------------------- 1 | 结果是 `4`: 2 | 3 | 4 | ```js run 5 | let fruits = ["Apples", "Pear", "Orange"]; 6 | 7 | let shoppingCart = fruits; 8 | 9 | shoppingCart.push("Banana"); 10 | 11 | *!* 12 | alert( fruits.length ); // 4 13 | */!* 14 | ``` 15 | 16 | 这是因为数组是对象。所以 `shoppingCart` 和 `fruits` 是同一数组的引用。 17 | 18 | -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/1-item-value/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 数组被拷贝了吗? 6 | 7 | 下面的代码将会显示什么? 8 | 9 | ```js 10 | let fruits = ["Apples", "Pear", "Orange"]; 11 | 12 | // 在“副本”里 push 了一个新的值 13 | let shoppingCart = fruits; 14 | shoppingCart.push("Banana"); 15 | 16 | // fruits 里面是什么? 17 | alert( fruits.length ); // ? 18 | ``` 19 | -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/10-maximal-subarray/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function getMaxSubSum(arr) { 2 | let maxSum = 0; 3 | let partialSum = 0; 4 | 5 | for (let item of arr) { 6 | partialSum += item; 7 | maxSum = Math.max(maxSum, partialSum); 8 | if (partialSum < 0) partialSum = 0; 9 | } 10 | return maxSum; 11 | } -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/2-create-array/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run 4 | let styles = ["Jazz", "Blues"]; 5 | styles.push("Rock-n-Roll"); 6 | styles[Math.floor((styles.length - 1) / 2)] = "Classics"; 7 | alert( styles.shift() ); 8 | styles.unshift("Rap", "Reggae"); 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/3-call-array-this/solution.md: -------------------------------------------------------------------------------- 1 | `arr[2]()` 调用从句法来看可以类比于 `obj[method]()`,与 `obj` 对应的是 `arr`,与 `method` 对应的是 `2`。 2 | 3 | 所以调用 `arr[2]` 函数也就是调用对象函数。自然地,它接收 `this` 引用的对象 `arr` 然后输出该数组: 4 | 5 | ```js run 6 | let arr = ["a", "b"]; 7 | 8 | arr.push(function() { 9 | alert( this ); 10 | }) 11 | 12 | arr[2](); // a,b,function(){...} 13 | ``` 14 | 15 | 该数组有 3 项:最开始有两个,后来添加进来一个函数。 16 | -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/3-call-array-this/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 在数组上下文调用 6 | 7 | 结果是什么?为什么? 8 | 9 | ```js 10 | let arr = ["a", "b"]; 11 | 12 | arr.push(function() { 13 | alert( this ); 14 | }); 15 | 16 | arr[2](); // ? 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/05-data-types/04-array/5-array-input-sum/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 输入数字求和 6 | 7 | 写出函数 `sumInput()`,要求如下: 8 | 9 | - 使用 `prompt` 向用户索要值,并存在数组中。 10 | - 当用户输入了非数字、空字符串或者点击“取消”按钮的时候,问询结束。 11 | - 计算并返回数组所有项之和。 12 | 13 | P.S. `0` 是有效的数字,不要因为是 0 就停止问询。 14 | 15 | [demo] 16 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/1-camelcase/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/05-data-types/05-array-methods/1-camelcase/solution.md -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/10-average-age/solution.md: -------------------------------------------------------------------------------- 1 | ```js run 2 | function getAverageAge(users) { 3 | return users.reduce((prev, user) => prev + user.age, 0) / users.length; 4 | } 5 | 6 | let john = { name: "John", age: 25 }; 7 | let pete = { name: "Pete", age: 30 }; 8 | let mary = { name: "Mary", age: 29 }; 9 | 10 | let arr = [ john, pete, mary ]; 11 | 12 | alert( getAverageAge(arr) ); // 28 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/11-array-unique/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function unique(arr) { 2 | let result = []; 3 | 4 | for (let str of arr) { 5 | if (!result.includes(str)) { 6 | result.push(str); 7 | } 8 | } 9 | 10 | return result; 11 | } 12 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/11-array-unique/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 数组去重 6 | 7 | `arr` 是一个数组。 8 | 9 | 创建一个函数 `unique(arr)`,返回去除重复元素后的数组 `arr`。 10 | 11 | 例如: 12 | 13 | ```js 14 | function unique(arr) { 15 | /* your code */ 16 | } 17 | 18 | let strings = ["Hare", "Krishna", "Hare", "Krishna", 19 | "Krishna", "Krishna", "Hare", "Hare", ":-O" 20 | ]; 21 | 22 | alert( unique(strings) ); // Hare, Krishna, :-O 23 | ``` 24 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function groupById(array) { 2 | return array.reduce((obj, value) => { 3 | obj[value.id] = value; 4 | return obj; 5 | }, {}) 6 | } 7 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/12-reduce-object/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/05-data-types/05-array-methods/12-reduce-object/solution.md -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | 2 | function filterRange(arr, a, b) { 3 | // added brackets around the expression for better readability 4 | return arr.filter(item => (a <= item && item <= b)); 5 | } -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/2-filter-range/solution.md: -------------------------------------------------------------------------------- 1 | ```js run demo 2 | function filterRange(arr, a, b) { 3 | // 在表达式周围添加了括号,以提高可读性 4 | return arr.filter(item => (a <= item && item <= b)); 5 | } 6 | 7 | let arr = [5, 3, 8, 1]; 8 | 9 | let filtered = filterRange(arr, 1, 4); 10 | 11 | alert( filtered ); // 3,1(匹配的值) 12 | 13 | alert( arr ); // 5,3,8,1(未经改动的数组中的值) 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/2-filter-range/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 过滤范围 6 | 7 | 写一个函数 `filterRange(arr, a, b)`,该函数获取一个数组 `arr`,在其中查找数值大于或等于 `a`,且小于或等于 `b` 的元素,并将结果以数组的形式返回。 8 | 9 | 该函数不应该修改原数组。它应该返回新的数组。 10 | 11 | 例如: 12 | 13 | ```js 14 | let arr = [5, 3, 8, 1]; 15 | 16 | let filtered = filterRange(arr, 1, 4); 17 | 18 | alert( filtered ); // 3,1(匹配值) 19 | 20 | alert( arr ); // 5,3,8,1(未修改) 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | 2 | function filterRangeInPlace(arr, a, b) { 3 | 4 | for (let i = 0; i < arr.length; i++) { 5 | let val = arr[i]; 6 | 7 | // remove if outside of the interval 8 | if (val < a || val > b) { 9 | arr.splice(i, 1); 10 | i--; 11 | } 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("filterRangeInPlace", function() { 2 | 3 | it("returns the filtered values", function() { 4 | 5 | let arr = [5, 3, 8, 1]; 6 | 7 | filterRangeInPlace(arr, 2, 5); 8 | 9 | assert.deepEqual(arr, [5, 3]); 10 | }); 11 | 12 | it("doesn't return anything", function() { 13 | assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4)); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md: -------------------------------------------------------------------------------- 1 | ```js run demo 2 | function filterRangeInPlace(arr, a, b) { 3 | 4 | for (let i = 0; i < arr.length; i++) { 5 | let val = arr[i]; 6 | 7 | // 如果超出范围,则删除 8 | if (val < a || val > b) { 9 | arr.splice(i, 1); 10 | i--; 11 | } 12 | } 13 | 14 | } 15 | 16 | let arr = [5, 3, 8, 1]; 17 | 18 | filterRangeInPlace(arr, 1, 4); // 删除 1 到 4 范围之外的值 19 | 20 | alert( arr ); // [3, 1] 21 | ``` 22 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 原位(in place)过滤范围 6 | 7 | 写一个函数 `filterRangeInPlace(arr, a, b)`,该函数获取一个数组 `arr`,并删除其中介于 `a` 和 `b` 区间以外的所有值。检查:`a ≤ arr[i] ≤ b`。 8 | 9 | 该函数应该只修改数组。它不应该返回任何东西。 10 | 11 | 例如: 12 | ```js 13 | let arr = [5, 3, 8, 1]; 14 | 15 | filterRangeInPlace(arr, 1, 4); // 删除了范围在 1 到 4 之外的所有值 16 | 17 | alert( arr ); // [3, 1] 18 | ``` 19 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/4-sort-back/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run 4 | let arr = [5, 2, 1, -10, 8]; 5 | 6 | arr.sort((a, b) => b - a); 7 | 8 | alert( arr ); 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/4-sort-back/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 降序排列 6 | 7 | ```js 8 | let arr = [5, 2, 1, -10, 8]; 9 | 10 | // ……你的代码以降序对其进行排序 11 | 12 | alert( arr ); // 8, 5, 2, 1, -10 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md: -------------------------------------------------------------------------------- 1 | 我们可以使用 `slice()` 来创建一个副本并对其进行排序: 2 | 3 | ```js run 4 | function copySorted(arr) { 5 | return arr.slice().sort(); 6 | } 7 | 8 | let arr = ["HTML", "JavaScript", "CSS"]; 9 | 10 | *!* 11 | let sorted = copySorted(arr); 12 | */!* 13 | 14 | alert( sorted ); 15 | alert( arr ); 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 复制和排序数组 6 | 7 | 我们有一个字符串数组 `arr`。我们希望有一个排序过的副本,但保持 `arr` 不变。 8 | 9 | 创建一个函数 `copySorted(arr)` 返回这样一个副本。 10 | 11 | ```js 12 | let arr = ["HTML", "JavaScript", "CSS"]; 13 | 14 | let sorted = copySorted(arr); 15 | 16 | alert( sorted ); // CSS, HTML, JavaScript 17 | alert( arr ); // HTML, JavaScript, CSS (no changes) 18 | ``` 19 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/6-array-get-names/solution.md: -------------------------------------------------------------------------------- 1 | ```js run 2 | 3 | let john = { name: "John", age: 25 }; 4 | let pete = { name: "Pete", age: 30 }; 5 | let mary = { name: "Mary", age: 28 }; 6 | 7 | let users = [ john, pete, mary ]; 8 | 9 | let names = users.map(item => item.name); 10 | 11 | alert( names ); // John, Pete, Mary 12 | ``` 13 | 14 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md: -------------------------------------------------------------------------------- 1 | 2 | - 请注意方法的存储方式。它们只是被添加到 `this.methods` 属性中。 3 | - 所有检测和数字转换都通过 `calculate` 方法完成。将来可能会扩展它以支持更复杂的表达式。 4 | -------------------------------------------------------------------------------- /1-js/05-data-types/05-array-methods/9-shuffle/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 随机排列数组 6 | 7 | 编写函数 `shuffle(array)` 来随机排列数组的元素。 8 | 9 | 多次运行 `shuffle` 可能导致元素顺序的不同。例如: 10 | 11 | ```js 12 | let arr = [1, 2, 3]; 13 | 14 | shuffle(arr); 15 | // arr = [3, 2, 1] 16 | 17 | shuffle(arr); 18 | // arr = [2, 1, 3] 19 | 20 | shuffle(arr); 21 | // arr = [3, 1, 2] 22 | // ... 23 | ``` 24 | 25 | 所有元素顺序应该具有相等的概率。例如,可以将 `[1,2,3]` 重新排序为 `[1,2,3]` 或 `[1,3,2]` 或 `[3,1,2]` 等,每种情况的概率相等。 26 | -------------------------------------------------------------------------------- /1-js/05-data-types/07-map-set/01-array-unique-map/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function unique(arr) { 2 | return Array.from(new Set(arr)); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/05-data-types/07-map-set/01-array-unique-map/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/05-data-types/07-map-set/01-array-unique-map/solution.md -------------------------------------------------------------------------------- /1-js/05-data-types/07-map-set/02-filter-anagrams/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | 2 | function aclean(arr) { 3 | let map = new Map(); 4 | 5 | for(let word of arr) { 6 | let sorted = word.toLowerCase().split("").sort().join(""); 7 | map.set(sorted, word); 8 | } 9 | 10 | return Array.from(map.values()); 11 | } -------------------------------------------------------------------------------- /1-js/05-data-types/07-map-set/03-iterable-keys/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 这是因为 `map.keys()` 返回的是可迭代对象而非数组。 3 | 4 | 我们可以使用方法 `Array.from` 来将它转换为数组: 5 | 6 | 7 | ```js run 8 | let map = new Map(); 9 | 10 | map.set("name", "John"); 11 | 12 | *!* 13 | let keys = Array.from(map.keys()); 14 | */!* 15 | 16 | keys.push("more"); 17 | 18 | alert(keys); // name, more 19 | ``` 20 | -------------------------------------------------------------------------------- /1-js/05-data-types/07-map-set/03-iterable-keys/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 迭代键 6 | 7 | 我们期望使用 `map.keys()` 得到一个数组,然后使用例如 `.push` 等特定的方法对其进行处理。 8 | 9 | 但是运行不了: 10 | 11 | ```js run 12 | let map = new Map(); 13 | 14 | map.set("name", "John"); 15 | 16 | let keys = map.keys(); 17 | 18 | *!* 19 | // Error: keys.push is not a function 20 | keys.push("more"); 21 | */!* 22 | ``` 23 | 24 | 为什么?我们应该如何修改代码让 `keys.push` 工作? 25 | -------------------------------------------------------------------------------- /1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 我们可以使用 `WeakMap` 保存日期: 3 | 4 | ```js 5 | let messages = [ 6 | {text: "Hello", from: "John"}, 7 | {text: "How goes?", from: "John"}, 8 | {text: "See you soon", from: "Alice"} 9 | ]; 10 | 11 | let readMap = new WeakMap(); 12 | 13 | readMap.set(messages[0], new Date(2017, 1, 1)); 14 | // 我们稍后将学习 Date 对象 15 | ``` 16 | -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function sumSalaries(salaries) { 2 | 3 | let sum = 0; 4 | for (let salary of Object.values(salaries)) { 5 | sum += salary; 6 | } 7 | 8 | return sum; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("sumSalaries", function() { 2 | it("returns sum of salaries", function() { 3 | let salaries = { 4 | "John": 100, 5 | "Pete": 300, 6 | "Mary": 250 7 | }; 8 | 9 | assert.equal( sumSalaries(salaries), 650 ); 10 | }); 11 | 12 | it("returns 0 for the empty object", function() { 13 | assert.strictEqual( sumSalaries({}), 0); 14 | }); 15 | }); -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 属性求和 6 | 7 | 有一个带有任意数量薪水的 `salaries` 对象。 8 | 9 | 编写函数 `sumSalaries(salaries)`,该函数使用 `Object.values` 和 `for..of` 循环返回所有薪水的总和。 10 | 11 | 如果 `salaries` 是空对象,那么结果必须是 `0`。 12 | 13 | 举个例子: 14 | 15 | ```js 16 | let salaries = { 17 | "John": 100, 18 | "Pete": 300, 19 | "Mary": 250 20 | }; 21 | 22 | alert( sumSalaries(salaries) ); // 650 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function count(obj) { 2 | return Object.keys(obj).length; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("count", function() { 2 | it("counts the number of properties", function() { 3 | assert.equal( count({a: 1, b: 2}), 2 ); 4 | }); 5 | 6 | it("returns 0 for an empty object", function() { 7 | assert.equal( count({}), 0 ); 8 | }); 9 | 10 | it("ignores symbolic properties", function() { 11 | assert.equal( count({ [Symbol('id')]: 1 }), 0 ); 12 | }); 13 | }); -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/02-count-properties/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/05-data-types/09-keys-values-entries/02-count-properties/solution.md -------------------------------------------------------------------------------- /1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 计算属性数量 6 | 7 | 写一个函数 `count(obj)`,该函数返回对象中的属性的数量: 8 | 9 | ```js 10 | let user = { 11 | name: 'John', 12 | age: 30 13 | }; 14 | 15 | alert( count(user) ); // 2 16 | ``` 17 | 18 | 试着使代码尽可能简短。 19 | 20 | P.S. 忽略 Symbol 类型属性,只计算“常规”属性。 21 | 22 | -------------------------------------------------------------------------------- /1-js/05-data-types/10-destructuring-assignment/1-destruct-user/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run 3 | let user = { 4 | name: "John", 5 | years: 30 6 | }; 7 | 8 | let {name, years: age, isAdmin = false} = user; 9 | 10 | alert( name ); // John 11 | alert( age ); // 30 12 | alert( isAdmin ); // false 13 | ``` 14 | -------------------------------------------------------------------------------- /1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function topSalary(salaries) { 2 | 3 | let maxSalary = 0; 4 | let maxName = null; 5 | 6 | for(let [name, salary] of Object.entries(salaries)) { 7 | if (maxSalary < salary) { 8 | maxSalary = salary; 9 | maxName = name; 10 | } 11 | } 12 | 13 | return maxName; 14 | } 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js: -------------------------------------------------------------------------------- 1 | describe("topSalary", function() { 2 | it("returns top-paid person", function() { 3 | let salaries = { 4 | "John": 100, 5 | "Pete": 300, 6 | "Mary": 250 7 | }; 8 | 9 | assert.equal( topSalary(salaries), "Pete" ); 10 | }); 11 | 12 | it("returns null for the empty object", function() { 13 | assert.isNull( topSalary({}) ); 14 | }); 15 | }); -------------------------------------------------------------------------------- /1-js/05-data-types/10-destructuring-assignment/6-max-salary/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/05-data-types/10-destructuring-assignment/6-max-salary/solution.md -------------------------------------------------------------------------------- /1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 最高薪资 6 | 7 | 这儿有一个 `salaries` 对象: 8 | 9 | ```js 10 | let salaries = { 11 | "John": 100, 12 | "Pete": 300, 13 | "Mary": 250 14 | }; 15 | ``` 16 | 17 | 新建一个函数 `topSalary(salaries)`,返回收入最高的人的姓名。 18 | 19 | - 如果 `salaries` 是空的,函数应该返回 `null`。 20 | - 如果有多个收入最高的人,返回其中任意一个即可。 21 | 22 | P.S. 使用 `Object.entries` 和解构语法来遍历键/值对。 23 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/1-new-date/solution.md: -------------------------------------------------------------------------------- 1 | `new Date` 构造函数默认使用本地时区。所以唯一需要牢记的就是月份从 0 开始计数。 2 | 3 | 所以二月对应的数值是 1。 4 | 5 | 这是一个以数字作为日期参数的示例: 6 | 7 | ```js run 8 | // new Date(year, month, date, hour, minute, second, millisecond) 9 | let d1 = new Date(2012, 1, 20, 3, 12); 10 | alert( d1 ); 11 | ``` 12 | 我们还可以从字符串创建日期,像这样: 13 | 14 | ```js run 15 | // new Date(datestring) 16 | let d2 = new Date("2012-02-20T03:12"); 17 | alert( d2 ); 18 | ``` 19 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/1-new-date/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 创建日期 6 | 7 | 创建一个 `Date` 对象,日期是:Feb 20, 2012, 3:12am。时区是当地时区。 8 | 9 | 使用 `alert` 显示结果。 10 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/2-get-week-day/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function getWeekDay(date) { 2 | let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; 3 | 4 | return days[date.getDay()]; 5 | } 6 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/2-get-week-day/solution.md: -------------------------------------------------------------------------------- 1 | `date.getDay()` 方法返回从星期日开始的星期数。 2 | 3 | 我们创建一个关于星期的数组,这样我们就可以通过编号获取正确的日期名称: 4 | 5 | ```js run demo 6 | function getWeekDay(date) { 7 | let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; 8 | 9 | return days[date.getDay()]; 10 | } 11 | 12 | let date = new Date(2014, 0, 3); // 3 Jan 2014 13 | alert( getWeekDay(date) ); // FR 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/2-get-week-day/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 显示星期数 6 | 7 | 编写一个函数 `getWeekDay(date)` 以短格式来显示一个日期的星期数:'MO','TU','WE','TH','FR','SA','SU'。 8 | 9 | 例如: 10 | 11 | ```js no-beautify 12 | let date = new Date(2012, 0, 3); // 3 Jan 2012 13 | alert( getWeekDay(date) );       // 应该输出 "TU" 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/3-weekday/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function getLocalDay(date) { 2 | 3 | let day = date.getDay(); 4 | 5 | if (day == 0) { // weekday 0 (sunday) is 7 in european 6 | day = 7; 7 | } 8 | 9 | return day; 10 | } 11 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/3-weekday/solution.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/3-weekday/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 欧洲的星期表示方法 6 | 7 | 欧洲国家的星期计算是从星期一(数字 1)开始的,然后是星期二(数字 2),直到星期日(数字 7)。编写一个函数 `getLocalDay(date)`,并返回日期的欧洲式星期数。 8 | 9 | ```js no-beautify 10 | let date = new Date(2012, 0, 3); // 3 Jan 2012 11 | alert( getLocalDay(date) ); // 星期二,应该显示 2 12 | ``` 13 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/4-get-date-ago/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function getDateAgo(date, days) { 2 | let dateCopy = new Date(date); 3 | 4 | dateCopy.setDate(date.getDate() - days); 5 | return dateCopy.getDate(); 6 | } 7 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/5-last-day-of-month/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function getLastDayOfMonth(year, month) { 2 | let date = new Date(year, month + 1, 0); 3 | return date.getDate(); 4 | } 5 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/5-last-day-of-month/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 某月的最后一天? 6 | 7 | 写一个函数 `getLastDayOfMonth(year, month)` 返回 month 月的最后一天。有时候是 30,有时是 31,甚至在二月的时候会是 28/29。 8 | 9 | 参数: 10 | 11 | - `year` —— 四位数的年份,比如 2012。 12 | - `month` —— 月份,从 0 到 11。 13 | 14 | 举个例子,`getLastDayOfMonth(2012, 1) = 29`(闰年,二月) 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/6-get-seconds-today/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 今天过去了多少秒? 6 | 7 | 写一个函数 `getSecondsToday()`,返回今天已经过去了多少秒? 8 | 9 | 例如:如果现在是 `10:00 am`,并且没有夏令时转换,那么: 10 | 11 | ```js 12 | getSecondsToday() == 36000 // (3600 * 10) 13 | ``` 14 | 15 | 该函数应该在任意一天都能正确运行。那意味着,它不应具有“今天”的硬编码值。 16 | -------------------------------------------------------------------------------- /1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 距离明天还有多少秒? 6 | 7 | 写一个函数 `getSecondsToTomorrow()`,返回距离明天的秒数。 8 | 9 | 例如,现在是 `23:00`,那么: 10 | 11 | ```js 12 | getSecondsToTomorrow() == 3600 13 | ``` 14 | 15 | P.S. 该函数应该在任意一天都能正确运行。那意味着,它不应具有“今天”的硬编码值。 16 | -------------------------------------------------------------------------------- /1-js/05-data-types/12-json/1-serialize-object/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js 4 | let user = { 5 | name: "John Smith", 6 | age: 35 7 | }; 8 | 9 | *!* 10 | let user2 = JSON.parse(JSON.stringify(user)); 11 | */!* 12 | ``` 13 | 14 | -------------------------------------------------------------------------------- /1-js/05-data-types/12-json/1-serialize-object/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 将对象转换为 JSON,然后再转换回来 6 | 7 | 将 `user` 转换为 JSON,然后将其转换回到另一个变量。 8 | 9 | ```js 10 | let user = { 11 | name: "John Smith", 12 | age: 35 13 | }; 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/05-data-types/index.md: -------------------------------------------------------------------------------- 1 | # 数据类型 2 | 3 | 更多的数据结构和更深入的类型研究。 4 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 反向输出单链表 6 | 7 | 反向输出前一个任务 中的单链表。 8 | 9 | 使用两种解法:循环和递归。 10 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md: -------------------------------------------------------------------------------- 1 | 答案:**Pete**。 2 | 3 | 函数将从内到外依次在对应的词法环境中寻找目标变量,它使用最新的值。 4 | 5 | 旧变量值不会保存在任何地方。当一个函数想要一个变量时,它会从自己的词法环境或外部词法环境中获取当前值。 6 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 函数会选择最新的内容吗? 6 | 7 | 函数 sayHi 使用外部变量。当函数运行时,将使用哪个值? 8 | 9 | ```js 10 | let name = "John"; 11 | 12 | function sayHi() { 13 | alert("Hi, " + name); 14 | } 15 | 16 | name = "Pete"; 17 | 18 | sayHi(); // 会显示什么:"John" 还是 "Pete"? 19 | ``` 20 | 21 | 这种情况在浏览器和服务器端开发中都很常见。一个函数可能被计划在创建之后一段时间后才执行,例如在用户行为或网络请求之后。 22 | 23 | 因此,问题是:它会接收最新的修改吗? 24 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function makeArmy() { 2 | 3 | let shooters = []; 4 | 5 | for(let i = 0; i < 10; i++) { 6 | let shooter = function() { // shooter function 7 | alert( i ); // should show its number 8 | }; 9 | shooters.push(shooter); 10 | } 11 | 12 | return shooters; 13 | } 14 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md: -------------------------------------------------------------------------------- 1 | 答案:**Pete**. 2 | 3 | 下方代码中的函数 `work()` 在其被创建的位置通过外部词法环境引用获取 `name`: 4 | 5 | ![](lexenv-nested-work.svg) 6 | 7 | 所以这里的结果是 `"Pete"`。 8 | 9 | 但如果在 `makeWorker()` 中没有 `let name`,那么将继续向外搜索并最终找到全局变量,正如我们可以从上图中看到的那样。在这种情况下,结果将是 `"John"`。 10 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md: -------------------------------------------------------------------------------- 1 | 答案是:**0,1。** 2 | 3 | 函数 `counter` 和 `counter2` 是通过 `makeCounter` 的不同调用创建的。 4 | 5 | 因此,它们具有独立的外部词法环境,每一个都有自己的 `count`。 6 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md: -------------------------------------------------------------------------------- 1 | 答案:**error**。 2 | 3 | 函数 `sayHi` 是在 `if` 内声明的,所以它只存在于 `if` 中。外部是没有 `sayHi` 的。 4 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/5-function-in-if/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # if 内的函数 6 | 7 | 看看下面这个代码。最后一行代码的执行结果是什么? 8 | 9 | ```js run 10 | let phrase = "Hello"; 11 | 12 | if (true) { 13 | let user = "John"; 14 | 15 | function sayHi() { 16 | alert(`${phrase}, ${user}`); 17 | } 18 | } 19 | 20 | *!* 21 | sayHi(); 22 | */!* 23 | ``` 24 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md: -------------------------------------------------------------------------------- 1 | 为了使第二个括号有效,第一个(括号)必须返回一个函数。 2 | 3 | 就像这样: 4 | 5 | ```js run 6 | function sum(a) { 7 | 8 | return function(b) { 9 | return a + b; // 从外部词法环境获得 "a" 10 | }; 11 | 12 | } 13 | 14 | alert( sum(1)(2) ); // 3 15 | alert( sum(5)(-1) ); // 4 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/6-closure-sum/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 闭包 sum 6 | 7 | 编写一个像 `sum(a)(b) = a+b` 这样工作的 `sum` 函数。 8 | 9 | 是的,就是这种通过双括号的方式(并不是错误)。 10 | 11 | 举个例子: 12 | 13 | ```js 14 | sum(1)(2) = 3 15 | sum(5)(-1) = 4 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/7-let-scope/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 变量可见吗? 6 | 7 | 下面这段代码的结果会是什么? 8 | 9 | ```js 10 | let x = 1; 11 | 12 | function func() { 13 | console.log(x); // ? 14 | 15 | let x = 2; 16 | } 17 | 18 | func(); 19 | ``` 20 | 21 | P.S. 这个任务有一个陷阱。解决方案并不明显。 22 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | 2 | function inArray(arr) { 3 | return x => arr.includes(x); 4 | } 5 | 6 | function inBetween(a, b) { 7 | return x => (x >= a && x <= b); 8 | } -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js: -------------------------------------------------------------------------------- 1 | 2 | let arr = [1, 2, 3, 4, 5, 6, 7]; 3 | 4 | function inBetween(a, b) { 5 | // ...your code... 6 | } 7 | 8 | function inArray(arr) { 9 | // ...your code... 10 | } 11 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function byField(fieldName){ 2 | return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1; 3 | } 4 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js: -------------------------------------------------------------------------------- 1 | function byField(fieldName){ 2 | 3 | // Your code goes here. 4 | 5 | } 6 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function makeCounter() { 2 | let count = 0; 3 | 4 | function counter() { 5 | return count++; 6 | } 7 | 8 | counter.set = value => count = value; 9 | 10 | counter.decrease = () => count--; 11 | 12 | return counter; 13 | } 14 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/source.js: -------------------------------------------------------------------------------- 1 | function makeCounter() { 2 | let count = 0; 3 | 4 | // ... your code ... 5 | } 6 | 7 | let counter = makeCounter(); 8 | 9 | alert( counter() ); // 0 10 | alert( counter() ); // 1 11 | 12 | counter.set(10); // set the new count 13 | 14 | alert( counter() ); // 10 15 | 16 | counter.decrease(); // decrease the count by 1 17 | 18 | alert( counter() ); // 10 (instead of 11) 19 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 该解决方案在局部变量中使用 `count`,而进行加法操作的方法是直接写在 `counter` 中的。它们共享同一个外部词法环境,并且可以访问当前的 `count`。 3 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 为 counter 添加 set 和 decrease 方法 6 | 7 | 修改 `makeCounter()` 代码,使得 counter 可以进行减一和设置值的操作: 8 | 9 | - `counter()` 应该返回下一个数字(与之前的逻辑相同)。 10 | - `counter.set(value)` 应该将 `count` 设置为 `value`。 11 | - `counter.decrease()` 应该把 `count` 减 1。 12 | 13 | 查看沙箱中的代码获取完整使用示例。 14 | 15 | P.S. 你可以使用闭包或者函数属性来保持当前的计数,或者两种都写。 16 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function sum(a) { 2 | 3 | let currentSum = a; 4 | 5 | function f(b) { 6 | currentSum += b; 7 | return f; 8 | } 9 | 10 | f.toString = function() { 11 | return currentSum; 12 | }; 13 | 14 | return f; 15 | } 16 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/source.js: -------------------------------------------------------------------------------- 1 | function sum(a){ 2 | // Your code goes here. 3 | 4 | } 5 | 6 | /* 7 | sum(1)(2) == 3; // 1 + 2 8 | sum(1)(2)(3) == 6; // 1 + 2 + 3 9 | sum(5)(-1)(2) == 6 10 | sum(6)(-1)(-2)(-3) == 0 11 | sum(0)(1)(2)(3)(4)(5) == 15 12 | */ 13 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 任意数量的括号求和 6 | 7 | 写一个函数 `sum`,它有这样的功能: 8 | 9 | ```js 10 | sum(1)(2) == 3; // 1 + 2 11 | sum(1)(2)(3) == 6; // 1 + 2 + 3 12 | sum(5)(-1)(2) == 6 13 | sum(6)(-1)(-2)(-3) == 0 14 | sum(0)(1)(2)(3)(4)(5) == 15 15 | ``` 16 | 17 | P.S. 提示:你可能需要创建自定义对象来为你的函数提供基本类型转换。 18 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/08-settimeout-setinterval/1-output-numbers-100ms/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 每秒输出一次 6 | 7 | 编写一个函数 `printNumbers(from, to)`,使其每秒输出一个数字,数字从 `from ` 开始,到 `to` 结束。 8 | 9 | 使用以下两种方法来实现。 10 | 11 | 1. 使用 `setInterval`。 12 | 2. 使用嵌套的 `setTimeout`。 13 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/08-settimeout-setinterval/4-settimeout-result/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 任何 `setTimeout` 都只会在当前代码执行完毕之后才会执行。 3 | 4 | 所以 `i` 的取值为:`100000000`。 5 | 6 | ```js run 7 | let i = 0; 8 | 9 | setTimeout(() => alert(i), 100); // 100000000 10 | 11 | // 假设这段代码的运行时间 >100ms 12 | for(let j = 0; j < 100000000; j++) { 13 | i++; 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function spy(func) { 2 | 3 | function wrapper(...args) { 4 | // using ...args instead of arguments to store "real" array in wrapper.calls 5 | wrapper.calls.push(args); 6 | return func.apply(this, args); 7 | } 8 | 9 | wrapper.calls = []; 10 | 11 | return wrapper; 12 | } 13 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js: -------------------------------------------------------------------------------- 1 | function spy(func) { 2 | // your code 3 | } 4 | 5 | 6 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md: -------------------------------------------------------------------------------- 1 | 由 `spy(f)` 返回的包装器应存储所有参数,然后使用 `f.apply` 转发调用。 2 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/09-call-apply-decorators/02-delay/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function delay(f, ms) { 2 | 3 | return function() { 4 | setTimeout(() => f.apply(this, arguments), ms); 5 | }; 6 | 7 | }; -------------------------------------------------------------------------------- /1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function debounce(func, ms) { 2 | let timeout; 3 | return function() { 4 | clearTimeout(timeout); 5 | timeout = setTimeout(() => func.apply(this, arguments), ms); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md: -------------------------------------------------------------------------------- 1 | ```js demo 2 | function debounce(func, ms) { 3 | let timeout; 4 | return function() { 5 | clearTimeout(timeout); 6 | timeout = setTimeout(() => func.apply(this, arguments), ms); 7 | }; 8 | } 9 | ``` 10 | 11 | 调用 `debounce` 会返回一个包装器。当它被调用时,它会安排一个在给定的 `ms` 之后对原始函数的调用,并取消之前的此类超时。 12 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/2-write-to-object-after-bind/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`null`。 2 | 3 | 4 | ```js run 5 | function f() { 6 | alert( this ); // null 7 | } 8 | 9 | let user = { 10 | g: f.bind(null) 11 | }; 12 | 13 | user.g(); 14 | ``` 15 | 16 | 绑定函数的上下文是硬绑定(hard-fixed)的。没有办法再修改它。 17 | 18 | 所以即使我们执行 `user.g()`,源方法调用时还是 `this=null`。 19 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/2-write-to-object-after-bind/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 作为方法的绑定函数 6 | 7 | 输出将会是什么? 8 | 9 | ```js 10 | function f() { 11 | alert( this ); // ? 12 | } 13 | 14 | let user = { 15 | g: f.bind(null) 16 | }; 17 | 18 | user.g(); 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/3-second-bind/solution.md: -------------------------------------------------------------------------------- 1 | 答案:**John**。 2 | 3 | ```js run no-beautify 4 | function f() { 5 | alert(this.name); 6 | } 7 | 8 | f = f.bind( {name: "John"} ).bind( {name: "Pete"} ); 9 | 10 | f(); // John 11 | ``` 12 | 13 | `f.bind(...)` 返回的外来(exotic)[绑定函数](https://tc39.github.io/ecma262/#sec-bound-function-exotic-objects) 对象仅在创建的时候记忆上下文(以及参数,如果提供了的话)。 14 | 15 | 一个函数不能被重绑定(re-bound)。 16 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/3-second-bind/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 二次 bind 6 | 7 | 我们可以通过额外的绑定改变 `this` 吗? 8 | 9 | 输出将会是什么? 10 | 11 | ```js no-beautify 12 | function f() { 13 | alert(this.name); 14 | } 15 | 16 | f = f.bind( {name: "John"} ).bind( {name: "Ann" } ); 17 | 18 | f(); 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/4-function-property-after-bind/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`undefined`。 2 | 3 | `bind` 的结果是另一个对象。它并没有 `test` 属性。 4 | 5 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/4-function-property-after-bind/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # bind 后的函数属性 6 | 7 | 函数的属性中有一个值。`bind` 之后它会改变吗?为什么,阐述一下? 8 | 9 | ```js run 10 | function sayHi() { 11 | alert( this.name ); 12 | } 13 | sayHi.test = 5; 14 | 15 | *!* 16 | let bound = sayHi.bind({ 17 | name: "John" 18 | }); 19 | 20 | alert( bound.test ); // 输出将会是什么?为什么? 21 | */!* 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/10-bind/6-ask-partial/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1. 使用包装(wapper)函数,箭头函数很简洁: 4 | 5 | ```js 6 | askPassword(() => user.login(true), () => user.login(false)); 7 | ``` 8 | 9 | 现在它从外部变量中获得了 `user`,然后以常规方式运行它。 10 | 11 | 2. 或者从 `user.login` 创建一个偏函数,该函数使用 `user` 作为上下文,并具有正确的第一个参数: 12 | 13 | 14 | ```js 15 | askPassword(user.login.bind(user, true), user.login.bind(user, false)); 16 | ``` 17 | -------------------------------------------------------------------------------- /1-js/06-advanced-functions/index.md: -------------------------------------------------------------------------------- 1 | # 函数进阶内容 2 | -------------------------------------------------------------------------------- /1-js/07-object-properties/index.md: -------------------------------------------------------------------------------- 1 | # 对象属性配置 2 | 3 | 在本节中,我们将回到对象,并更深入地研究其属性。 4 | -------------------------------------------------------------------------------- /1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 1. `true`,来自于 `rabbit`。 3 | 2. `null`,来自于 `animal`。 4 | 3. `undefined`,不再有这样的属性存在。 5 | -------------------------------------------------------------------------------- /1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md: -------------------------------------------------------------------------------- 1 | **答案:`rabbit`。** 2 | 3 | 这是因为 `this` 是点符号前面的这个对象,因此 `rabbit.eat()` 修改了 `rabbit`。 4 | 5 | 属性查找和执行是两回事儿。 6 | 7 | 首先在原型中找到 `rabbit.eat` 方法,然后在 `this=rabbit` 的情况下执行。 8 | -------------------------------------------------------------------------------- /1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 写在哪里? 6 | 7 | 我们有从 `animal` 中继承的 `rabbit`。 8 | 9 | 如果我们调用 `rabbit.eat()`,哪一个对象会接收到 `full` 属性:`animal` 还是 `rabbit`? 10 | 11 | ```js 12 | let animal = { 13 | eat() { 14 | this.full = true; 15 | } 16 | }; 17 | 18 | let rabbit = { 19 | __proto__: animal 20 | }; 21 | 22 | rabbit.eat(); 23 | ``` 24 | -------------------------------------------------------------------------------- /1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 使用相同的构造函数创建一个对象 6 | 7 | 想象一下,我们有一个由构造函数创建的对象 `obj` —— 我们不知道使用的是哪个构造函数,但是我们想使用它创建一个新对象。 8 | 9 | 我们可以这样做吗? 10 | 11 | ```js 12 | let obj2 = new obj.constructor(); 13 | ``` 14 | 15 | 请给出一个可以使这样的代码正常工作的 `obj` 的构造函数的例子。再给出会导致这样的代码无法正确工作的例子。 16 | -------------------------------------------------------------------------------- /1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```js run 4 | Function.prototype.defer = function(ms) { 5 | setTimeout(this, ms); 6 | }; 7 | 8 | function f() { 9 | alert("Hello!"); 10 | } 11 | 12 | f.defer(1000); // 1 秒后显示 "Hello!" 13 | ``` 14 | -------------------------------------------------------------------------------- /1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 给函数添加一个 "f.defer(ms)" 方法 6 | 7 | 在所有函数的原型中添加 `defer(ms)` 方法,该方法将在 `ms` 毫秒后运行该函数。 8 | 9 | 当你完成添加后,下面的代码应该是可执行的: 10 | 11 | ```js 12 | function f() { 13 | alert("Hello!"); 14 | } 15 | 16 | f.defer(1000); // 1 秒后显示 "Hello!" 17 | ``` 18 | -------------------------------------------------------------------------------- /1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 将装饰器 "defer()" 添加到函数 6 | 7 | 在所有函数的原型中添加 `defer(ms)` 方法,该方法返回一个包装器,将函数调用延迟 `ms` 毫秒。 8 | 9 | 下面是它应该如何执行的例子: 10 | 11 | ```js 12 | function f(a, b) { 13 | alert( a + b ); 14 | } 15 | 16 | f.defer(1000)(1, 2); // 1 秒后显示 3 17 | ``` 18 | 19 | 请注意,参数应该被传给原始函数。 20 | -------------------------------------------------------------------------------- /1-js/08-prototypes/03-native-prototypes/console_dir_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/08-prototypes/03-native-prototypes/console_dir_array.png -------------------------------------------------------------------------------- /1-js/08-prototypes/index.md: -------------------------------------------------------------------------------- 1 | # 原型,继承 2 | -------------------------------------------------------------------------------- /1-js/09-classes/01-class/1-rewrite-to-class/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/09-classes/01-class/1-rewrite-to-class/solution.md -------------------------------------------------------------------------------- /1-js/09-classes/01-class/1-rewrite-to-class/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 重写为 class 6 | 7 | `Clock` 类(请见沙箱)是以函数式编写的。请以 "class" 语法重写它。 8 | 9 | P.S. 时钟在控制台(console)中滴答,打开控制台即可查看。 10 | -------------------------------------------------------------------------------- /1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md: -------------------------------------------------------------------------------- 1 | [js src="solution.view/extended-clock.js"] 2 | -------------------------------------------------------------------------------- /1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js: -------------------------------------------------------------------------------- 1 | class ExtendedClock extends Clock { 2 | constructor(options) { 3 | super(options); 4 | let { precision = 1000 } = options; 5 | this.precision = precision; 6 | } 7 | 8 | start() { 9 | this.render(); 10 | this.timer = setInterval(() => this.render(), this.precision); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 扩展 clock 6 | 7 | 我们获得了一个 `Clock` 类。到目前为止,它每秒都会打印一次时间。 8 | 9 | 10 | [js src="source.view/clock.js"] 11 | 12 | 创建一个继承自 `Clock` 的新的类 `ExtendedClock`,并添加参数 `precision` — 每次 "ticks" 之间间隔的毫秒数,默认是 `1000`(1 秒)。 13 | 14 | - 你的代码应该在 `extended-clock.js` 文件里。 15 | - 不要修改原有的 `clock.js`。请扩展它。 16 | -------------------------------------------------------------------------------- /1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg -------------------------------------------------------------------------------- /1-js/09-classes/04-private-protected-properties-methods/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg -------------------------------------------------------------------------------- /1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md: -------------------------------------------------------------------------------- 1 | 是的,看起来确实很奇怪。 2 | 3 | `instanceof` 并不关心函数,而是关心函数的与原型链匹配的 `prototype`。 4 | 5 | 并且,这里 `a.__proto__ == B.prototype`,所以 `instanceof` 返回 `true`。 6 | 7 | 总之,根据 `instanceof` 的逻辑,真正决定类型的是 `prototype`,而不是构造函数。 8 | -------------------------------------------------------------------------------- /1-js/09-classes/06-instanceof/1-strange-instanceof/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 不按套路出牌的 instanceof 6 | 7 | 在下面的代码中,为什么 `instanceof` 会返回 `true`?我们可以明显看到,`a` 并不是通过 `B()` 创建的。 8 | 9 | ```js run 10 | function A() {} 11 | function B() {} 12 | 13 | A.prototype = B.prototype = {}; 14 | 15 | let a = new A(); 16 | 17 | *!* 18 | alert( a instanceof B ); // true 19 | */!* 20 | ``` 21 | -------------------------------------------------------------------------------- /1-js/09-classes/index.md: -------------------------------------------------------------------------------- 1 | # 类 2 | -------------------------------------------------------------------------------- /1-js/10-error-handling/2-custom-errors/1-format-error/solution.md: -------------------------------------------------------------------------------- 1 | ```js run untrusted 2 | class FormatError extends SyntaxError { 3 | constructor(message) { 4 | super(message); 5 | this.name = this.constructor.name; 6 | } 7 | } 8 | 9 | let err = new FormatError("formatting error"); 10 | 11 | alert( err.message ); // formatting error 12 | alert( err.name ); // FormatError 13 | alert( err.stack ); // stack 14 | 15 | alert( err instanceof SyntaxError ); // true 16 | ``` 17 | -------------------------------------------------------------------------------- /1-js/10-error-handling/index.md: -------------------------------------------------------------------------------- 1 | # 错误处理 2 | -------------------------------------------------------------------------------- /1-js/11-async/01-callbacks/one.js: -------------------------------------------------------------------------------- 1 | function one() { 2 | alert(1); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/01-re-resolve/solution.md: -------------------------------------------------------------------------------- 1 | 输出为:`1`。 2 | 3 | 第二个对 `resolve` 的调用会被忽略,因为只有第一次对 `reject/resolve` 的调用才会被处理。进一步的调用都会被忽略。 4 | -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/01-re-resolve/task.md: -------------------------------------------------------------------------------- 1 | 2 | # 用 promise 重新解决? 3 | 4 | 5 | 下列这段代码会输出什么? 6 | 7 | ```js 8 | let promise = new Promise(function(resolve, reject) { 9 | resolve(1); 10 | 11 | setTimeout(() => resolve(2), 1000); 12 | }); 13 | 14 | promise.then(alert); 15 | ``` 16 | -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/02-delay-promise/solution.md: -------------------------------------------------------------------------------- 1 | ```js run 2 | function delay(ms) { 3 | return new Promise(resolve => setTimeout(resolve, ms)); 4 | } 5 | 6 | delay(3000).then(() => alert('runs after 3 seconds')); 7 | ``` 8 | 9 | 请注意,在此任务中 `resolve` 是不带参数调用的。我们不从 `delay` 中返回任何值,只是确保延迟即可。 10 | -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/02-delay-promise/task.md: -------------------------------------------------------------------------------- 1 | 2 | # 基于 promise 的延时 3 | 4 | 内建函数 `setTimeout` 使用了回调函数。请创建一个基于 promise 的替代方案。 5 | 6 | 函数 `delay(ms)` 应该返回一个 promise。这个 promise 应该在 `ms` 毫秒后被 resolve,所以我们可以向其中添加 `.then`,像这样: 7 | 8 | ```js 9 | function delay(ms) { 10 | // 你的代码 11 | } 12 | 13 | delay(3000).then(() => alert('runs after 3 seconds')); 14 | ``` 15 | -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md: -------------------------------------------------------------------------------- 1 | 2 | # 带有 promise 的圆形动画 3 | 4 | 重写任务 的解决方案中的 `showCircle` 函数,以使其返回一个 promise,而不接受回调。 5 | 6 | 新的用法: 7 | 8 | ```js 9 | showCircle(150, 150, 100).then(div => { 10 | div.classList.add('message-ball'); 11 | div.append("Hello, world!"); 12 | }); 13 | ``` 14 | 15 | 以任务 的解决方案为基础。 16 | -------------------------------------------------------------------------------- /1-js/11-async/02-promise-basics/head.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md: -------------------------------------------------------------------------------- 1 | 简要回答就是:**不,它们不相等**: 2 | 3 | 不同之处在于,如果 `f1` 中出现 error,那么在这儿它会被 `.catch` 处理: 4 | 5 | ```js run 6 | promise 7 | .then(f1) 8 | .catch(f2); 9 | ``` 10 | 11 | ……在这儿则不会: 12 | 13 | ```js run 14 | promise 15 | .then(f1, f2); 16 | ``` 17 | 18 | 这是因为 error 是沿着链传递的,而在第二段代码中,`f1` 下面没有链。 19 | 20 | 换句话说,`.then` 将 result/error 传递给下一个 `.then/.catch`。所以在第一个例子中,在下面有一个 `catch`,而在第二个例子中并没有 `catch`,所以 error 未被处理。 21 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md: -------------------------------------------------------------------------------- 1 | # Promise:then 对比 catch 2 | 3 | 这两个代码片段是否相等?换句话说,对于任何处理程序(handler),它们在任何情况下的行为都相同吗? 4 | 5 | ```js 6 | promise.then(f1).catch(f2); 7 | ``` 8 | 9 | 对比: 10 | 11 | ```js 12 | promise.then(f1, f2); 13 | ``` 14 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/getMessage.js: -------------------------------------------------------------------------------- 1 | function getMessage() { 2 | return "Hello, world!"; 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/one.js: -------------------------------------------------------------------------------- 1 | function one() { 2 | alert(1); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/three.js: -------------------------------------------------------------------------------- 1 | function three() { 2 | alert(3); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/two.js: -------------------------------------------------------------------------------- 1 | function two() { 2 | alert(2); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/03-promise-chaining/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iliakan", 3 | "isAdmin": true 4 | } 5 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/01-error-async/solution.md: -------------------------------------------------------------------------------- 1 | 答案是:**不,它不会被触发**: 2 | 3 | ```js run 4 | new Promise(function(resolve, reject) { 5 | setTimeout(() => { 6 | throw new Error("Whoops!"); 7 | }, 1000); 8 | }).catch(alert); 9 | ``` 10 | 11 | 正如本章所讲,函数代码周围有个“隐式的 `try..catch`”。所以,所有同步错误都会得到处理。 12 | 13 | 但是这里的错误并不是在 executor 运行时生成的,而是在稍后生成的。因此,promise 无法处理它。 14 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/01-error-async/task.md: -------------------------------------------------------------------------------- 1 | # setTimeout 中的错误 2 | 3 | 你怎么看?`.catch` 会被触发么?解释你的答案。 4 | 5 | ```js 6 | new Promise(function(resolve, reject) { 7 | setTimeout(() => { 8 | throw new Error("Whoops!"); 9 | }, 1000); 10 | }).catch(alert); 11 | ``` 12 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/getMessage.js: -------------------------------------------------------------------------------- 1 | function getMessage() { 2 | return "Hello, world!"; 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/one.js: -------------------------------------------------------------------------------- 1 | function one() { 2 | alert(1); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/three.js: -------------------------------------------------------------------------------- 1 | function three() { 2 | alert(3); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/two.js: -------------------------------------------------------------------------------- 1 | function two() { 2 | alert(2); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/04-promise-error-handling/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iliakan", 3 | "isAdmin": true 4 | } 5 | -------------------------------------------------------------------------------- /1-js/11-async/05-promise-api/head.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /1-js/11-async/05-promise-api/iliakan.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iliakan", 3 | "isAdmin": true 4 | } 5 | -------------------------------------------------------------------------------- /1-js/11-async/05-promise-api/one.js: -------------------------------------------------------------------------------- 1 | function one() { 2 | alert(1); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/05-promise-api/two.js: -------------------------------------------------------------------------------- 1 | function two() { 2 | alert(2); 3 | } 4 | -------------------------------------------------------------------------------- /1-js/11-async/08-async-await/03-async-from-regular/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 在这种情况下,知道其内部工作原理会很有帮助。 3 | 4 | 只需要把 `async` 调用当作 promise 对待,并在它的后面加上 `.then` 即可: 5 | ```js run 6 | async function wait() { 7 | await new Promise(resolve => setTimeout(resolve, 1000)); 8 | 9 | return 10; 10 | } 11 | 12 | function f() { 13 | // 1 秒后显示 10 14 | *!* 15 | wait().then(result => alert(result)); 16 | */!* 17 | } 18 | 19 | f(); 20 | ``` 21 | -------------------------------------------------------------------------------- /1-js/11-async/08-async-await/head.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /1-js/11-async/index.md: -------------------------------------------------------------------------------- 1 | 2 | # Promise,async/await 3 | -------------------------------------------------------------------------------- /1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/solution.js: -------------------------------------------------------------------------------- 1 | function* pseudoRandom(seed) { 2 | let value = seed; 3 | 4 | while(true) { 5 | value = value * 16807 % 2147483647 6 | yield value; 7 | } 8 | 9 | }; 10 | -------------------------------------------------------------------------------- /1-js/12-generators-iterators/index.md: -------------------------------------------------------------------------------- 1 | 2 | # Generator,高级 iteration 3 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/say.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/say.view/say.js: -------------------------------------------------------------------------------- 1 | export function sayHi(user) { 2 | return `Hello, ${user}!`; 3 | } 4 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/scopes-working.view/hello.js: -------------------------------------------------------------------------------- 1 | import {user} from './user.js'; 2 | 3 | document.body.innerHTML = user; // John 4 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/scopes-working.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/scopes-working.view/user.js: -------------------------------------------------------------------------------- 1 | export let user = "John"; 2 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/scopes.view/hello.js: -------------------------------------------------------------------------------- 1 | alert(user); // no such variable (each module has independent variables) 2 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/scopes.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /1-js/13-modules/01-modules-intro/scopes.view/user.js: -------------------------------------------------------------------------------- 1 | let user = "John"; 2 | -------------------------------------------------------------------------------- /1-js/13-modules/03-modules-dynamic-imports/say.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | -------------------------------------------------------------------------------- /1-js/13-modules/03-modules-dynamic-imports/say.view/say.js: -------------------------------------------------------------------------------- 1 | export function hi() { 2 | alert(`Hello`); 3 | } 4 | 5 | export function bye() { 6 | alert(`Bye`); 7 | } 8 | 9 | export default function() { 10 | alert("Module loaded (export default)!"); 11 | } 12 | -------------------------------------------------------------------------------- /1-js/13-modules/index.md: -------------------------------------------------------------------------------- 1 | # 模块 2 | -------------------------------------------------------------------------------- /1-js/99-js-misc/01-proxy/02-array-negative/solution.md: -------------------------------------------------------------------------------- 1 | 2 | ```js run 3 | let array = [1, 2, 3]; 4 | 5 | array = new Proxy(array, { 6 | get(target, prop, receiver) { 7 | if (prop < 0) { 8 | // 即使我们像 arr[1] 这样访问它 9 | // prop 是一个字符串,所以我们需要将其转换成数字 10 | prop = +prop + target.length; 11 | } 12 | return Reflect.get(target, prop, receiver); 13 | } 14 | }); 15 | 16 | 17 | alert(array[-1]); // 3 18 | alert(array[-2]); // 2 19 | ``` 20 | -------------------------------------------------------------------------------- /1-js/99-js-misc/02-eval/1-eval-calculator/solution.md: -------------------------------------------------------------------------------- 1 | 让我们使用 `eval` 来计算数学表达式: 2 | 3 | ```js demo run 4 | let expr = prompt("Type an arithmetic expression?", '2*3+2'); 5 | 6 | alert( eval(expr) ); 7 | ``` 8 | 9 | 用户可以输入任意文本或代码。 10 | 11 | 安全起见,并限制其仅进行算术运算,我们可以使用 [正则表达式](info:regular-expressions) 来检查 `expr`,以限制输入的内容只能包含数字和运算符。 12 | -------------------------------------------------------------------------------- /1-js/99-js-misc/02-eval/1-eval-calculator/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # Eval-计算器 6 | 7 | 创建一个计算器,提示用户输入一个算术表达式,并返回其计算结果。 8 | 9 | 在本题中,你不需要检查表达式是否正确。只需要计算并返回结果。 10 | 11 | [demo] 12 | -------------------------------------------------------------------------------- /1-js/99-js-misc/04-reference-type/2-check-syntax/task.md: -------------------------------------------------------------------------------- 1 | importance: 2 2 | 3 | --- 4 | 5 | # 检查语法 6 | 7 | 这段代码的结果是什么? 8 | 9 | 10 | ```js no-beautify 11 | let user = { 12 | name: "John", 13 | go: function() { alert(this.name) } 14 | } 15 | 16 | (user.go)() 17 | ``` 18 | 19 | 提示:有一个陷阱哦 :) 20 | -------------------------------------------------------------------------------- /1-js/99-js-misc/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 杂项 3 | -------------------------------------------------------------------------------- /1-js/index.md: -------------------------------------------------------------------------------- 1 | # JavaScript 编程语言 2 | 3 | 在这儿我们将从头开始学习 JavaScript,也会学习 OOP 等相关高级概念。 4 | 5 | 本教程专注于语言本身,我们默认使用最小环境。 6 | -------------------------------------------------------------------------------- /2-ui/1-document/02-dom-nodes/elk.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The truth about elk. 5 |
    6 |
  1. An elk is a smart
  2. 7 | 8 |
  3. ...and cunning animal!
  4. 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /2-ui/1-document/02-dom-nodes/head.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /2-ui/1-document/03-dom-navigation/1-dom-children/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # DOM 子节点 6 | 7 | 对于这个页面: 8 | 9 | ```html 10 | 11 | 12 |
Users:
13 |
    14 |
  • John
  • 15 |
  • Pete
  • 16 |
17 | 18 | 19 | ``` 20 | 21 | 对于以下各项,请给出至少一种访问方式: 22 | - `
` DOM 节点? 23 | - `
    ` DOM 节点? 24 | - 第二个 `
  • ` 节点(即包含 Pete 的节点)? 25 | -------------------------------------------------------------------------------- /2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md: -------------------------------------------------------------------------------- 1 | 1. 是的,这是真的。`elem.lastChild` 就是最后一个节点,它没有 `nextSibling`。 2 | 2. 不,这是错的,因为 `elem.children[0]` 是元素中的第一个子元素。但是在它前面可能存在非元素的节点。所以 `previousSibling` 可能是一个文本节点。 3 | 4 | 请注意,对于这两种情况,如果没有子节点,那么就会报错。 5 | 6 | 如果这里没有子节点,那么 `elem.lastChild` 是 `null`,所以我们就访问不到 `elem.lastChild.nextSibling`。并且 `elem.children` 是空的(像一个空数组一样 `[]`)。 7 | -------------------------------------------------------------------------------- /2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 兄弟节点问题 6 | 7 | 如果 `elem` 是任意一个 DOM 元素节点…… 8 | 9 | - `elem.lastChild.nextSibling` 值一直都是 `null`,这个判定是不是真的? 10 | - `elem.children[0].previousSibling` 值一直都是 `null`,这个判定是不是真的? 11 | -------------------------------------------------------------------------------- /2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md: -------------------------------------------------------------------------------- 1 | 我们将使用 `rows` 和 `cells` 属性来获取表格中的对角单元格。 -------------------------------------------------------------------------------- /2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 选择所有对角单元格 6 | 7 | 编写代码来把表格中的对角单元格都绘制成红色。 8 | 9 | 你需要用代码从 `` 中获取所有的对角单元格 `
    `,然后绘制它们: 10 | 11 | ```js 12 | // td 表示的是对单元格的引用 13 | td.style.backgroundColor = 'red'; 14 | ``` 15 | 16 | 结果应该如下所示: 17 | 18 | [iframe src="solution" height=180] 19 | -------------------------------------------------------------------------------- /2-ui/1-document/03-dom-navigation/head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 搜索元素 6 | 7 | 这是带有表格(table)和表单(form)的文档。 8 | 9 | 如何查找?…… 10 | 11 | 1. 带有 `id="age-table"` 的表格。 12 | 2. 表格内的所有 `label` 元素(应该有三个)。 13 | 3. 表格中的第一个 `td`(带有 "Age" 字段)。 14 | 4. 带有 `name="search"` 的 `form`。 15 | 5. 表单中的第一个 `input`。 16 | 6. 表单中的最后一个 `input`。 17 | 18 | 在一个单独的窗口中打开 [table.html](table.html) 页面,并对此页面使用浏览器开发者工具。 19 | -------------------------------------------------------------------------------- /2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md: -------------------------------------------------------------------------------- 1 | 这里有一个陷阱。 2 | 3 | 在 ` 14 | 15 | 16 | 17 | ``` 18 | -------------------------------------------------------------------------------- /2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # nodeType 中是什么? 6 | 7 | 下面这个脚本会显示什么? 8 | 9 | ```html 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | ``` 20 | -------------------------------------------------------------------------------- /2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md: -------------------------------------------------------------------------------- 1 | 我们使用循环遍历 `
  • `: 2 | 3 | ```js 4 | for (let li of document.querySelectorAll('li')) { 5 | ... 6 | } 7 | ``` 8 | 9 | 循环时,我们需要获取每个 `li` 中的文本。 10 | 11 | 我们可以从 `li` 的第一个子节点读取文本,即文本节点: 12 | 13 | ```js 14 | for (let li of document.querySelectorAll('li')) { 15 | let title = li.firstChild.data; 16 | 17 | // title 是在
  • 中的任何其他节点之前的文本 18 | } 19 | ``` 20 | 21 | 然后我们就可以使用 `li.getElementsByTagName('li')` 来获取后代的数目了。 22 | -------------------------------------------------------------------------------- /2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 计数后代 6 | 7 | 这里有一个树结构嵌套的 `ul/li`。 8 | 9 | 编写代码,为每个 `
  • ` 显示: 10 | 11 | 1. 里面的文本内容是什么(没有子树) 12 | 2. 嵌套的 `
  • ` 的数量 —— 所有后代,包括深层嵌套的后代。 13 | 14 | [demo src="solution"] 15 | -------------------------------------------------------------------------------- /2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 注释中的标签 6 | 7 | 这段代码会显示什么? 8 | 9 | ```html 10 | 17 | ``` 18 | -------------------------------------------------------------------------------- /2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 层次结构中的 "document" 在哪里? 6 | 7 | `document` 属于哪一类? 8 | 9 | 它位于 DOM 层次结构(hierarchy)中的什么位置? 10 | 11 | 它继承自 `Node` 还是 `Element`,或者可能是 `HTMLElement`? 12 | -------------------------------------------------------------------------------- /2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 获取特性 6 | 7 | 编写代码,从文档(document)中获取带有 `data-widget-name` 特性(attribute)的元素,并读取它的值。 8 | 9 | ```html run 10 | 11 | 12 | 13 | 14 |
    Choose the genre
    15 | 16 | 19 | 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md: -------------------------------------------------------------------------------- 1 | 回答:**1 和 3**。 2 | 3 | 这两个命令都会将 `text` “作为文本”添加到 `elem` 中。 4 | 5 | 这是一个例子: 6 | 7 | ```html run height=80 8 |
    9 |
    10 |
    11 | 18 | ``` 19 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # createTextNode vs innerHTML vs textContent 6 | 7 | 我们有一个空的 DOM 元素 `elem` 和一个字符串 `text`。 8 | 9 | 下面这 3 个命令中的哪些命令会执行完全相同的操作? 10 | 11 | 1. `elem.append(document.createTextNode(text))` 12 | 2. `elem.innerHTML = text` 13 | 3. `elem.textContent = text` 14 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 使用 setInterval 的彩色时钟 6 | 7 | 创建一个像这样的彩色时钟: 8 | 9 | [iframe src="solution" height=60] 10 | 11 | 使用 HTML/CSS 进行样式设计,JavaScript 仅用来更新元素中的时间。 12 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/11-append-to-list/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 当我们需要在某处插入 HTML 时,`insertAdjacentHTML` 是最适合的方案。 3 | 4 | 解决方法: 5 | 6 | ```js 7 | one.insertAdjacentHTML('afterend', '
  • 2
  • 3
  • '); 8 | ``` 9 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/11-append-to-list/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 将 HTML 插入到列表中 6 | 7 | 编写代码,将 `
  • 2
  • 3
  • `,插入到两个 `
  • ` 之间: 8 | 9 | ```html 10 |
      11 |
    • 1
    • 12 |
    • 4
    • 13 |
    14 | ``` 15 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/4-clear-elem/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 清除元素 6 | 7 | 创建一个函数 `clear(elem)` 用来移除元素里的内容。 8 | 9 | ```html run height=60 10 |
      11 |
    1. Hello
    2. 12 |
    3. World
    4. 13 |
    14 | 15 | 20 | ``` 21 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/5-why-aaa/solution.md: -------------------------------------------------------------------------------- 1 | 这个题目中的 HTML 是错的。这就是造成怪异现象的原因。 2 | 3 | 浏览器必须自动修复它。但 `` 内可能会没有文本:根据规范,只允许特定于表格的标签。所以浏览器将 `"aaa"` 展示在了 `
    ` **前面**。 4 | 5 | 当我们删除表格后,文本 `"aaa"` 仍然存在的原因就很明显了吧。 6 | 7 | 通过使用浏览器开发者工具查看 DOM,就可以轻松地回答这个问题。从浏览器开发者工具中我们可以看到,`"aaa"` 在 `
    ` 前面。 8 | 9 | HTML 标准规范详细描述了如何处理错误的 HTML,并且浏览器的这种行为是正确的。 10 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/5-why-aaa/task.md: -------------------------------------------------------------------------------- 1 | importance: 1 2 | 3 | --- 4 | 5 | # 为什么留下 "aaa"? 6 | 7 | 在下面这个示例中,我们调用 `table.remove()` 从文档中删除表格。 8 | 9 | 但如果运行它,你就会看到文本 `"aaa"` 并没有被删除。 10 | 11 | 这是为什么? 12 | 13 | ```html height=100 run 14 |
    15 | aaa 16 | 17 | 18 | 19 |
    Test
    20 | 21 | 27 | ``` 28 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/6-create-list/solution.md: -------------------------------------------------------------------------------- 1 | 请注意使用 `textContent` 对 `
  • ` 的内容进行赋值的用法。 2 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/6-create-list/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 创建一个列表 6 | 7 | 编写一个接口,根据用户输入创建一个列表(list)。 8 | 9 | 对于每个列表项: 10 | 11 | 1. 使用 `prompt` 向用户询问列表项的内容。 12 | 2. 使用用户输入的内容创建 `
  • `,并添加到 `
      `。 13 | 3. 重复以上步骤,直到用户取消输入(按下 `key:Esc` 键,或输入一个空内容)。 14 | 15 | 所有元素应该都是动态创建的。 16 | 17 | 如果用户输入了 HTML 标签,那么这些内容应该被视为文本进行后续处理。 18 | 19 | [demo src="solution"] 20 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md: -------------------------------------------------------------------------------- 1 | 遍历对象的最简单的方法是使用递归。 2 | 3 | 1. [使用 innerHTML 的解决方案](sandbox:innerhtml)。 4 | 2. [使用 DOM 的解决方案](sandbox:build-tree-dom)。 5 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/8-tree-count/solution.md: -------------------------------------------------------------------------------- 1 | 为了将文本附加到每个 `
    • ` 中,我们可以改变文本节点的 `data`。 2 | -------------------------------------------------------------------------------- /2-ui/1-document/07-modifying-document/8-tree-count/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 在树中显示后代 6 | 7 | 这里有一棵由嵌套的 `ul/li` 组成的树。 8 | 9 | 编写代码,为每个 `
    • ` 添加其后代数量。跳过叶子节点(没有子代的节点)。 10 | 11 | 结果: 12 | 13 | [iframe border=1 src="solution"] 14 | -------------------------------------------------------------------------------- /2-ui/1-document/08-styles-and-classes/2-create-notification/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.md -------------------------------------------------------------------------------- /2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.css: -------------------------------------------------------------------------------- 1 | .notification { 2 | position: fixed; 3 | z-index: 1000; 4 | padding: 5px; 5 | border: 1px solid black; 6 | font-size: 20px; 7 | background: white; 8 | text-align: center; 9 | } 10 | 11 | .welcome { 12 | background: #b80000; 13 | color: yellow; 14 | } 15 | -------------------------------------------------------------------------------- /2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.css: -------------------------------------------------------------------------------- 1 | .notification { 2 | position: fixed; 3 | z-index: 1000; 4 | padding: 5px; 5 | border: 1px solid black; 6 | font-size: 20px; 7 | background: white; 8 | text-align: center; 9 | } 10 | 11 | .welcome { 12 | background: #b80000; 13 | color: yellow; 14 | } 15 | -------------------------------------------------------------------------------- /2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md: -------------------------------------------------------------------------------- 1 | 解决方案: 2 | 3 | ```js 4 | let scrollBottom = elem.scrollHeight - elem.scrollTop - elem.clientHeight; 5 | ``` 6 | 7 | 换句话说:(完全高度)减去(已滚出顶部的高度)减去(可见部分的高度)—— 得到的结果就是滚动出来的底部的部分。 8 | -------------------------------------------------------------------------------- /2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 相对于底部滚动了多少? 6 | 7 | `elem.scrollTop` 属性是从顶部滚动出来的部分的大小。如何获得底部滚动的大小(我们称其为 `scrollBottom`)? 8 | 9 | 编写适用于任意 `elem` 的代码。 10 | 11 | P.S. 请检查你的代码:如果没有滚动,或元素底部已经完全滚动完成,那么它应该返回 `0`。 12 | -------------------------------------------------------------------------------- /2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 滚动条的宽度是多少? 6 | 7 | 编写代码,返回标准滚动条宽度。 8 | 9 | 对于 Windows,它通常在 `12px` 和 `20px` 之间变化。如果浏览器没有为其保留任何空间(滚动条以半透明的形式处于文本上面,也是可能发生的),那么它可能是 `0px`。 10 | 11 | P.S. 该代码应适用于任何 HTML 文档,而不依赖于其内容。 12 | -------------------------------------------------------------------------------- /2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md: -------------------------------------------------------------------------------- 1 | 不同点: 2 | 3 | 1. `clientWidth` 值是数值,而 `getComputedStyle(elem).width` 返回一个以 `px` 作为后缀的字符串。 4 | 2. `getComputedStyle` 可能会返回非数值的 width,例如内联(inline)元素的 `"auto"`。 5 | 3. `clientWidth` 是元素的内部内容区域加上 padding,而 CSS width(具有标准的 `box-sizing`)是内部内容区域,**不包括 padding**。 6 | 4. 如果有滚动条,并且浏览器为其保留了空间,那么某些浏览器会从 CSS width 中减去该空间(因为它不再可用于内容),而有些则不会这样做。`clientWidth` 属性总是相同的:如果为滚动条保留了空间,那么将减去滚动条的大小。 7 | -------------------------------------------------------------------------------- /2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # CSS width 与 clientWidth 的不同点 6 | 7 | `getComputedStyle(elem).width` 与 `elem.clientWidth` 之间有什么不同点? 8 | 9 | 指出至少三种不同点。当然越多越好。 10 | -------------------------------------------------------------------------------- /2-ui/1-document/11-coordinates/2-position-at/solution.md: -------------------------------------------------------------------------------- 1 | 在这个任务中,我们只需要准确地计算坐标即可。具体细节,请参见代码。 2 | 3 | 请注意:元素必须在文档中才能读取 `offsetHeight` 和其它属性。 4 | 隐藏的(`display:none`)或者不在文档中的元素没有大小。 5 | -------------------------------------------------------------------------------- /2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md: -------------------------------------------------------------------------------- 1 | 解决方案实际上很简单: 2 | 3 | - 在 `.note` 的 CSS 中,使用 `position:absolute` 代替 `position:fixed`。 4 | - 使用在 一章中所讲的函数 [getCoords()](info:coordinates#getCoords) 来获取相对于文档的坐标。 5 | -------------------------------------------------------------------------------- /2-ui/1-document/11-coordinates/3-position-at-absolute/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 在元素旁(absolute)显示一个 note 6 | 7 | 修改 [上一个任务](info:task/position-at) 的解决方案,让 note 元素使用 `position:absolute` 来替代 `position:fixed`。 8 | 9 | 这可以防止页面滚动时元素的“失控”。 10 | 11 | 以上一个任务的解决方案为基础。为了测试页面滚动,请添加样式 ``。 12 | -------------------------------------------------------------------------------- /2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.md -------------------------------------------------------------------------------- /2-ui/1-document/index.md: -------------------------------------------------------------------------------- 1 | # Document 2 | 3 | 在这里,我们将学习如何使用 JavaScript 来操纵网页。 4 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.md -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
      Text
      13 | 14 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 点击隐藏 6 | 7 | 为 `button` 添加 JavaScript 代码,使得 `
      ` 在我们点击该按钮时消失。 8 | 9 | 示例: 10 | 11 | [iframe border=1 src="solution" height=80] 12 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md: -------------------------------------------------------------------------------- 1 | 可以在处理程序中使用 `this` 来引用“元素自身”: 2 | 3 | ```html run height=50 4 | 5 | ``` 6 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 隐藏自己 6 | 7 | 创建一个按钮,在被点击时,隐藏自己。 8 | 9 | ```online 10 | 就像这样: 11 | 12 | ``` 13 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`1` 和 `2`。 2 | 3 | 第一个处理程序会触发,因为它没有被 `removeEventListener` 移除。要移除处理程序,我们需要传递正确的所分配的函数。在代码中,传递了一个新的函数,该函数看起来相同,但仍然是另一个函数。 4 | 5 | 要移除一个函数对象,我们需要存储对它的引用,像这样: 6 | 7 | ```js 8 | function handler() { 9 | alert(1); 10 | } 11 | 12 | button.addEventListener("click", handler); 13 | button.removeEventListener("click", handler); 14 | ``` 15 | 16 | 无论 `addEventListener` 怎样,`button.onclick` 处理程序都会触发。 17 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 哪个处理程序会运行? 6 | 7 | 在变量中有一个按钮。它上面没有处理程序。 8 | 9 | 执行以下代码之后,哪些处理程序会在按钮被点击时运行?会显示哪些 alert? 10 | 11 | ```js no-beautify 12 | button.addEventListener("click", () => alert("1")); 13 | 14 | button.removeEventListener("click", () => alert("1")); 15 | 16 | button.onclick = () => alert(2); 17 | ``` 18 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 让球在球场中移动 6 | 7 | 点击球场中任意一点,让球在球场中移动。就像这样: 8 | 9 | [iframe src="solution" height="260" link] 10 | 11 | 要求: 12 | 13 | - 球的中心应该恰好在点击时鼠标指针位置的下方(如果在球不越过球场边缘的情况下,能实现的话)。 14 | - 最好添加一些 CSS 动画。 15 | - 球不能越过场地边界。 16 | - 页面滚动时,布局不能被破坏。 17 | 18 | 注意: 19 | 20 | - 代码还应该适用于不同大小的球和球场,而不应该绑定到任何固定值。 21 | - 使用 `event.clientX/event.clientY` 属性来获取点击坐标。 22 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/05-sliding-menu/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ▶ ▼ Sweeties (click me)! 9 |
        10 |
      • Cake
      • 11 |
      • Donut
      • 12 |
      • Honey
      • 13 |
      14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 创建滑动菜单 6 | 7 | 创建一个在点击时打开/折叠的菜单: 8 | 9 | [iframe border=1 height=100 src="solution"] 10 | 11 | P.S. 源文档的 HTML/CSS 将被修改。 12 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 添加关闭按钮 6 | 7 | 有一个消息列表。 8 | 9 | 使用 JavaScript 在每条消息的右上角添加一个关闭按钮。 10 | 11 | 结果应该如下所示: 12 | 13 | [iframe src="solution" height=450] 14 | -------------------------------------------------------------------------------- /2-ui/2-events/01-introduction-browser-events/07-carousel/task.md: -------------------------------------------------------------------------------- 1 | importance: 4 2 | 3 | --- 4 | 5 | # 轮播图 6 | 7 | 创建一个“轮播图(carousel)” —— 一条可以通过点击箭头来滚动图像的图像带。 8 | 9 | [iframe height=200 src="solution"] 10 | 11 | 之后,我们可以为其添加更多功能:无限滚动,动态加载等。 12 | 13 | P.S. 对于这个任务,HTML/CSS 结构实际上占解决方案的 90%。 14 | -------------------------------------------------------------------------------- /2-ui/2-events/02-bubbling-and-capturing/both.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
      FORM 12 |
      DIV 13 |

      P

      14 |
      15 |
      16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-ui/2-events/02-bubbling-and-capturing/both.view/script.js: -------------------------------------------------------------------------------- 1 | let elems = document.querySelectorAll('form,div,p'); 2 | 3 | for (let i = 0; i < elems.length; i++) { 4 | elems[i].addEventListener("click", highlightThis, true); 5 | elems[i].addEventListener("click", highlightThis, false); 6 | } 7 | 8 | function highlightThis() { 9 | this.style.backgroundColor = 'yellow'; 10 | alert(this.tagName); 11 | this.style.backgroundColor = ''; 12 | } -------------------------------------------------------------------------------- /2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | A click shows both event.target and this to compare: 11 | 12 |
      FORM 13 |
      DIV 14 |

      P

      15 |
      16 |
      17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js: -------------------------------------------------------------------------------- 1 | 2 | form.onclick = function(event) { 3 | event.target.style.backgroundColor = 'yellow'; 4 | 5 | // chrome needs some time to paint yellow 6 | setTimeout(() => { 7 | alert("target = " + event.target.tagName + ", this=" + this.tagName); 8 | event.target.style.backgroundColor = '' 9 | }, 0); 10 | }; 11 | -------------------------------------------------------------------------------- /2-ui/2-events/02-bubbling-and-capturing/capture.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
      FORM 8 |
      DIV 9 |

      P

      10 |
      11 |
      12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /2-ui/2-events/02-bubbling-and-capturing/capture.view/script.js: -------------------------------------------------------------------------------- 1 | let elems = document.querySelectorAll('form,div,p'); 2 | 3 | for (let i = 0; i < elems.length; i++) { 4 | elems[i].addEventListener("click", highlightThis, true); 5 | } 6 | 7 | function highlightThis() { 8 | this.style.backgroundColor = 'yellow'; 9 | alert(this.tagName); 10 | this.style.backgroundColor = ''; 11 | } -------------------------------------------------------------------------------- /2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.md -------------------------------------------------------------------------------- /2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 使用委托隐藏消息 6 | 7 | 有一个带有移除按钮 `[x]` 的消息列表。让按钮可以工作。 8 | 9 | 就像这样: 10 | 11 | [iframe src="solution" height=420] 12 | 13 | P.S. 在容器上应该只有一个事件监听器,请使用事件委托。 14 | -------------------------------------------------------------------------------- /2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md: -------------------------------------------------------------------------------- 1 | 解决方案分为两个部分。 2 | 3 | 1. 将每个树节点的标题都包装到 `` 中。然后我们可以在 `:hover` 上使用 CSS 样式,并精确地处理文本上的点击事件,因为 `` 的宽度恰好是文本的宽度(与没有宽度不同)。 4 | 2. 为 `tree` 的根节点设置一个处理程序,来处理 `` 标题上的点击事件。 5 | -------------------------------------------------------------------------------- /2-ui/2-events/03-event-delegation/2-sliding-tree/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 树形菜单 6 | 7 | 创建一个点击可以显示/隐藏子节点的树形菜单: 8 | 9 | [iframe border=1 src="solution"] 10 | 11 | 要求: 12 | 13 | - 只能有一个事件处理程序(使用委托)。 14 | - 对节点标题以外(在空白处)的点击不会做任何处理。 15 | -------------------------------------------------------------------------------- /2-ui/2-events/03-event-delegation/3-sortable-table/solution.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.md -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md: -------------------------------------------------------------------------------- 1 | importance: 3 2 | 3 | --- 4 | 5 | # 为什么 "return false" 不起作用? 6 | 7 | 为什么下面这段代码中的 `return false` 不起作用? 8 | 9 | ```html autorun run 10 | 16 | 17 | the browser will go to w3.org 18 | ``` 19 | 20 | 浏览器在点击时会根据 URL 进行跳转,但这不是我们想要的。 21 | 22 | 如何修复它? 23 | -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md: -------------------------------------------------------------------------------- 1 | 这是一个很好的使用事件委托模式的案例。 2 | 3 | 在现实生活中,我们可以向服务器发送一个 "logging" 请求而不是询问,该请求会保存关于访问者离开位置的信息。或者,我们可以加载内容,并将其显示在页面中(如果允许的话)。 4 | 5 | 我们只需要捕获 `contents.onclick`,然后使用 `confirm` 来询问用户。一个好主意是使用 `link.getAttribute('href')` 来代替 `link.href`。详情请参见解决方案。 6 | -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 捕获元素中的链接 6 | 7 | 使所有包含 `id="contents"` 的元素内的链接询问用户是否真的要离开。如果用户不想离开,那就不离开。 8 | 9 | 像这样: 10 | 11 | [iframe height=100 border=1 src="solution"] 12 | 13 | 细节: 14 | 15 | - 元素内的 HTML 可以被随时动态加载或重新生成,因此,我们无法找到所有链接并为其添加处理程序。这里使用事件委托。 16 | - 内容中可能有嵌套的标签。链接中也是,例如 `...`。 17 | -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md: -------------------------------------------------------------------------------- 1 | 解决方案是将处理程序分配给容器,并追踪点击。如果点击在 `` 链接上,则将 `#largeImg` 的 `src` 修改为该缩略图的 `href`。 2 | -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/3-image-gallery/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 图册 6 | 7 | 创建一个图册,通过点击缩略图可以更改主图片。 8 | 9 | 像这样: 10 | 11 | [iframe src="solution" height=600] 12 | 13 | P.S. 使用事件委托。 14 | -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/menu.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /2-ui/2-events/04-default-browser-action/menu.view/menu.js: -------------------------------------------------------------------------------- 1 | menu.onclick = function(event) { 2 | if (event.target.nodeName != 'A') return; 3 | 4 | let href = event.target.getAttribute('href'); 5 | alert(href); 6 | 7 | return false; // prevent url change 8 | }; 9 | -------------------------------------------------------------------------------- /2-ui/2-events/index.md: -------------------------------------------------------------------------------- 1 | # 事件简介 2 | 3 | 浏览器事件、事件属性和处理模式简介。 4 | -------------------------------------------------------------------------------- /2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.md -------------------------------------------------------------------------------- /2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 可选列表 6 | 7 | 创建一个可以选择元素的列表,例如在文件管理器中。 8 | 9 | - 点击列表元素,只选择该元素(添加 `.selected` 类),取消选择其他所有元素。 10 | - 如果点击时,按键 `key:Ctrl`(在 Mac 中为 `key:Cmd`)是被按下的,则选择会被切换到被点击的元素上,但其他元素不会被改动。 11 | 12 | 示例: 13 | 14 | [iframe border="1" src="solution" height=180] 15 | 16 | P.S. 对于此任务,我们可以假设列表项是纯文本的。没有嵌套标签。 17 | 18 | P.P.S. 防止点击时浏览器原生的文本选择。 19 | -------------------------------------------------------------------------------- /2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.md -------------------------------------------------------------------------------- /2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/script.js: -------------------------------------------------------------------------------- 1 | table.onmouseenter = table.onmouseleave = log; 2 | 3 | function log(event) { 4 | text.value += event.type + ' [target: ' + event.target.tagName + ']\n'; 5 | text.scrollTop = text.scrollHeight; 6 | } -------------------------------------------------------------------------------- /2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/script.js: -------------------------------------------------------------------------------- 1 | function mouselog(event) { 2 | let d = new Date(); 3 | text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2'); 4 | text.scrollTop = text.scrollHeight; 5 | } 6 | -------------------------------------------------------------------------------- /2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/script.js: -------------------------------------------------------------------------------- 1 | function mouselog(event) { 2 | let d = new Date(); 3 | text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2'); 4 | text.scrollTop = text.scrollHeight; 5 | } 6 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md: -------------------------------------------------------------------------------- 1 | 正如我们从 HTML/CSS 中所看到的,滑动条就是一个带有彩色背景的 `
      `,其中包含一个滑块 —— 另一个具有 `position:relative` 的 `
      `。 2 | 3 | 为了对滑块进行定位,我们使用 `position:relative` 来提供相对于其父元素的坐标,在这儿它比 `position:absolute` 更方便。 4 | 5 | 然后我们通过限制宽度来实现仅水平方向的拖放。 6 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/style.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | border-radius: 5px; 3 | background: #E0E0E0; 4 | background: linear-gradient(left top, #E0E0E0, #EEEEEE); 5 | width: 310px; 6 | height: 15px; 7 | margin: 5px; 8 | } 9 | 10 | .thumb { 11 | width: 10px; 12 | height: 25px; 13 | border-radius: 3px; 14 | position: relative; 15 | left: 10px; 16 | top: -5px; 17 | background: blue; 18 | cursor: pointer; 19 | } 20 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
      12 |
      13 |
      14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/style.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | border-radius: 5px; 3 | background: #E0E0E0; 4 | background: linear-gradient(left top, #E0E0E0, #EEEEEE); 5 | width: 310px; 6 | height: 15px; 7 | margin: 5px; 8 | } 9 | 10 | .thumb { 11 | width: 10px; 12 | height: 25px; 13 | border-radius: 3px; 14 | position: relative; 15 | left: 10px; 16 | top: -5px; 17 | background: blue; 18 | cursor: pointer; 19 | } 20 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 滑动条 6 | 7 | 创建一个滑动条(slider): 8 | 9 | [iframe src="solution" height=60 border=1] 10 | 11 | 用鼠标拖动蓝色的滑块(thumb)并移动它。 12 | 13 | 重要的细节: 14 | 15 | - 当鼠标按钮被按下时,在滑动过程中,鼠标指针可能会移动到滑块的上方或下方。此时滑块仍会继续移动(方便用户)。 16 | - 如果鼠标非常快地向左边或者向右边移动,那么滑块应该恰好停在边缘。 17 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md: -------------------------------------------------------------------------------- 1 | 要拖动元素,我们可以使用 `position:fixed`,它使坐标更易于管理。最后,我们应该将其切换回 `position:absolute`,以使元素放置到文档中。 2 | 3 | 当坐标位于窗口顶端/底端时,我们使用 `window.scrollTo` 来滚动它。 4 | 5 | 更多细节请见代码注释。 6 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/soccer.js: -------------------------------------------------------------------------------- 1 | // Your code 2 | -------------------------------------------------------------------------------- /2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/style.css: -------------------------------------------------------------------------------- 1 | 2 | #gate { 3 | cursor: pointer; 4 | margin-bottom: 100px; 5 | width: 83px; 6 | height: 46px; 7 | } 8 | 9 | #ball { 10 | cursor: pointer; 11 | width: 40px; 12 | height: 40px; 13 | } 14 | -------------------------------------------------------------------------------- /2-ui/3-event-details/6-pointer-events/slider-html.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
      5 |
      6 |
      -------------------------------------------------------------------------------- /2-ui/3-event-details/6-pointer-events/slider-html.view/style.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | border-radius: 5px; 3 | background: #E0E0E0; 4 | background: linear-gradient(left top, #E0E0E0, #EEEEEE); 5 | width: 310px; 6 | height: 15px; 7 | margin: 5px; 8 | } 9 | 10 | .thumb { 11 | width: 10px; 12 | height: 25px; 13 | border-radius: 3px; 14 | position: relative; 15 | left: 10px; 16 | top: -5px; 17 | background: blue; 18 | cursor: pointer; 19 | } -------------------------------------------------------------------------------- /2-ui/3-event-details/6-pointer-events/slider.view/style.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | border-radius: 5px; 3 | background: #E0E0E0; 4 | background: linear-gradient(left top, #E0E0E0, #EEEEEE); 5 | width: 310px; 6 | height: 15px; 7 | margin: 5px; 8 | } 9 | 10 | .thumb { 11 | touch-action: none; 12 | width: 10px; 13 | height: 25px; 14 | border-radius: 3px; 15 | position: relative; 16 | left: 10px; 17 | top: -5px; 18 | background: blue; 19 | cursor: pointer; 20 | } 21 | -------------------------------------------------------------------------------- /2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 我们应该使用两个处理程序:`document.onkeydown` 和 `document.onkeyup`。 3 | 4 | 让我们创建一个集合 `pressed = new Set()` 来保存当前被按下的键。 5 | 6 | 第一个处理程序把当前被按下的键添加到集合中,而第二个处理程序将被松开的按键从集合中移除。我们每次在 `keydown` 上检查我们是否按下了足够多的键,如果是,则运行函数 `func`。 7 | -------------------------------------------------------------------------------- /2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 扩展热键 6 | 7 | 创建一个 `runOnKeys(func, code1, code2, ... code_n)` 函数,在同时按下 `code1, code2, ... code_n` 键时运行函数 `func`。 8 | 9 | 例如,当按键 `"Q"` 和 `"W"` 被一起按下时(任何语言中,无论是否 CapsLock),下面的代码将显示 `alert`: 10 | 11 | ```js no-beautify 12 | runOnKeys( 13 | () => alert("Hello!"), 14 | "KeyQ", 15 | "KeyW" 16 | ); 17 | ``` 18 | 19 | [demo src="solution"] 20 | -------------------------------------------------------------------------------- /2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css: -------------------------------------------------------------------------------- 1 | #kinput { 2 | font-size: 150%; 3 | box-sizing: border-box; 4 | width: 95%; 5 | } 6 | 7 | #area { 8 | width: 95%; 9 | box-sizing: border-box; 10 | height: 250px; 11 | border: 1px solid black; 12 | display: block; 13 | } 14 | 15 | form label { 16 | display: inline; 17 | white-space: nowrap; 18 | } -------------------------------------------------------------------------------- /2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

      Scroll me

      10 | 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /2-ui/3-event-details/8-onscroll/2-updown-button/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/3-event-details/8-onscroll/2-updown-button/solution.md -------------------------------------------------------------------------------- /2-ui/3-event-details/8-onscroll/2-updown-button/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # Up/down 按钮 6 | 7 | 创建一个“到顶部”按钮来帮助页面滚动。 8 | 9 | 它应该像这样运行: 10 | - 页面向下滚动的距离没有超过窗口高度时 —— 按钮不可见。 11 | - 当页面向下滚动距离超过窗口高度时 —— 在左上角出现一个“向上”的箭头。如果页面回滚回去,箭头就会消失。 12 | - 单击箭头时,页面将滚动到顶部。 13 | 14 | 像这样(左上角,滚动查看): 15 | 16 | [iframe border="1" height="200" link src="solution"] 17 | -------------------------------------------------------------------------------- /2-ui/3-event-details/index.md: -------------------------------------------------------------------------------- 1 | # UI 事件 2 | 3 | 在这里,我们介绍了最重要的用户界面事件,以及如何使用它们。 4 | -------------------------------------------------------------------------------- /2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.md -------------------------------------------------------------------------------- /2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 可编辑的 div 6 | 7 | 创建一个 `
      `,它在被点击后变成 ` 3 |
      4 | 5 | 11 | -------------------------------------------------------------------------------- /6-data-storage/02-localstorage/1-form-autosave/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /6-data-storage/02-localstorage/1-form-autosave/task.md: -------------------------------------------------------------------------------- 1 | 2 | # 自动保存表单字段 3 | 4 | 创建一个 `textarea` 字段,每当其值发生变化时,可以将其“自动保存”。 5 | 6 | 因此,如果用户不小心关闭了页面,然后重新打开,他会发现之前未完成的输入仍然保留在那里。 7 | 8 | 像这样: 9 | 10 | [iframe src="solution" height=120] 11 | -------------------------------------------------------------------------------- /6-data-storage/02-localstorage/sessionstorage.view/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /6-data-storage/02-localstorage/sessionstorage.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /6-data-storage/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 在浏览器中存储数据 3 | -------------------------------------------------------------------------------- /7-animation/1-bezier-curve/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/7-animation/1-bezier-curve/pause.png -------------------------------------------------------------------------------- /7-animation/1-bezier-curve/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/7-animation/1-bezier-curve/play.png -------------------------------------------------------------------------------- /7-animation/2-css-animations/1-animate-logo-css/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 使用 CSS 为 `width` 和 `height` 属性生成动效: 3 | ```css 4 | /* 原始类 */ 5 | 6 | #flyjet { 7 | transition: all 3s; 8 | } 9 | 10 | /* JS 添加的 .growing */ 11 | #flyjet.growing { 12 | width: 400px; 13 | height: 240px; 14 | } 15 | ``` 16 | 17 | 请注意,`transitionend` 会被触发两次 —— 每个属性触发一次。因此,如果我们不进行额外检查的话,这条信息会显示两次。 18 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/1-animate-logo-css/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 让飞机动起来(CSS) 6 | 7 | 生成如下图的动画(点击显示): 8 | 9 | [iframe src="solution" height=300] 10 | 11 | - 点击后,图片会从 `40x24px` 变为 `400x240px` (变大十倍)。 12 | - 动画持续三秒。 13 | - 在动画结束后,输出:"Done!"。 14 | - 动画过程中,如果飞机被点击,这些操作不应该打断动画。 15 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md: -------------------------------------------------------------------------------- 1 | 我们需要为此选择合适的贝塞尔曲线。它应该在某个地方拥有 `y > 1`,来使得飞机『跳出来』。 2 | 3 | 举例来说,我们可以使两个控制点的 `y` 都大于 `1`:`cubic-bezier(0.25, 1.5, 0.75, 1.5)`。 4 | 5 | 如图: 6 | 7 | ![](bezier-up.svg) 8 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/2-animate-logo-bezier-css/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 为飞机生成动画(CSS) 6 | 7 | 修改前一个的任务 的解决方案,让飞机超过原有的大小 `400x240px`(跳脱出来),然后再回到之前的大小。 8 | 9 | 这里是效果演示(点击飞机): 10 | 11 | [iframe src="solution" height=350] 12 | 13 | 在前一个解决方案的基础上做修改。 14 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/3-animate-circle/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/7-animation/2-css-animations/3-animate-circle/solution.md -------------------------------------------------------------------------------- /7-animation/2-css-animations/3-animate-circle/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 圆圈动画 6 | 7 | 创建一个函数:`showCircle(cx, cy, radius)`,来显示一个不断变大的圆。 8 | 9 | - `cx,cy` 为圆心相对于窗口的位置。 10 | - `radius` 为圆的半径。 11 | 12 | 点击下方的按钮以演示效果: 13 | 14 | [iframe src="solution" height=260] 15 | 16 | 源文件中提供了一个具有合适样式的圆样例,因此你需要做的就是创建合适的动画。 17 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/4-animate-circle-callback/solution.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/7-animation/2-css-animations/4-animate-circle-callback/solution.md -------------------------------------------------------------------------------- /7-animation/2-css-animations/boat.view/style.css: -------------------------------------------------------------------------------- 1 | #boat { 2 | margin-left: 0; 3 | cursor: pointer; 4 | transition: margin-left 3s ease-in-out; 5 | } 6 | 7 | /* flipping the picture with CSS */ 8 | .back { 9 | transform: scaleX(-1); 10 | filter: fliph; 11 | } 12 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/digits-negative-delay.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Click below to animate: 12 |
      0123456789
      13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/digits-negative-delay.view/script.js: -------------------------------------------------------------------------------- 1 | stripe.onclick = function() { 2 | let sec = new Date().getSeconds() % 10; 3 | stripe.style.transitionDelay = '-' + sec + 's'; 4 | stripe.classList.add('animate'); 5 | }; -------------------------------------------------------------------------------- /7-animation/2-css-animations/digits-negative-delay.view/style.css: -------------------------------------------------------------------------------- 1 | #digit { 2 | width: .5em; 3 | overflow: hidden; 4 | font: 32px monospace; 5 | cursor: pointer; 6 | } 7 | 8 | #stripe { 9 | display: inline-block 10 | } 11 | 12 | #stripe.animate { 13 | transform: translate(-90%); 14 | transition-property: transform; 15 | transition-duration: 9s; 16 | transition-timing-function: linear; 17 | } 18 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/digits.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Click below to animate: 12 | 13 |
      0123456789
      14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/digits.view/script.js: -------------------------------------------------------------------------------- 1 | stripe.onclick = function() { 2 | stripe.classList.add('animate'); 3 | }; -------------------------------------------------------------------------------- /7-animation/2-css-animations/digits.view/style.css: -------------------------------------------------------------------------------- 1 | #digit { 2 | width: .5em; 3 | overflow: hidden; 4 | font: 32px monospace; 5 | cursor: pointer; 6 | } 7 | 8 | #stripe { 9 | display: inline-block 10 | } 11 | 12 | #stripe.animate { 13 | transform: translate(-90%); 14 | transition-property: transform; 15 | transition-duration: 9s; 16 | transition-timing-function: linear; 17 | } 18 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/step-end.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Click below to animate: 12 | 13 |
      0123456789
      14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/step-end.view/style.css: -------------------------------------------------------------------------------- 1 | #digit { 2 | width: .5em; 3 | overflow: hidden; 4 | font: 32px monospace; 5 | cursor: pointer; 6 | } 7 | 8 | #stripe { 9 | display: inline-block 10 | } 11 | 12 | #stripe.animate { 13 | transform: translate(-90%); 14 | transition-property: transform; 15 | transition-duration: 9s; 16 | transition-timing-function: steps(9, end); 17 | } 18 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/step-list.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
      0123456789
      12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/step-list.view/style.css: -------------------------------------------------------------------------------- 1 | #digit { 2 | border: 1px solid red; 3 | width: 1.2em; 4 | } 5 | 6 | #stripe { 7 | display: inline-block; 8 | font: 32px monospace; 9 | } 10 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/step.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Click below to animate: 12 | 13 |
      0123456789
      14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/step.view/style.css: -------------------------------------------------------------------------------- 1 | #digit { 2 | width: .5em; 3 | overflow: hidden; 4 | font: 32px monospace; 5 | cursor: pointer; 6 | } 7 | 8 | #stripe { 9 | display: inline-block 10 | } 11 | 12 | #stripe.animate { 13 | transform: translate(-90%); 14 | transition-property: transform; 15 | transition-duration: 9s; 16 | transition-timing-function: steps(9, start); 17 | } 18 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/train-linear.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/train-linear.view/style.css: -------------------------------------------------------------------------------- 1 | .train { 2 | position: relative; 3 | cursor: pointer; 4 | width: 177px; 5 | height: 160px; 6 | left: 0; 7 | transition: left 5s cubic-bezier(0, 0, 1, 1); 8 | } -------------------------------------------------------------------------------- /7-animation/2-css-animations/train-over.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/train-over.view/style.css: -------------------------------------------------------------------------------- 1 | .train { 2 | position: relative; 3 | cursor: pointer; 4 | width: 177px; 5 | height: 160px; 6 | left: 100px; 7 | transition: left 5s cubic-bezier(.5, -1, .5, 2); 8 | } -------------------------------------------------------------------------------- /7-animation/2-css-animations/train.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /7-animation/2-css-animations/train.view/style.css: -------------------------------------------------------------------------------- 1 | .train { 2 | position: relative; 3 | cursor: pointer; 4 | width: 177px; 5 | height: 160px; 6 | left: 0px; 7 | transition: left 5s cubic-bezier(0.0, 0.5, 0.5, 1.0); 8 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/1-animate-ball/solution.view/style.css: -------------------------------------------------------------------------------- 1 | #field { 2 | height: 200px; 3 | border-bottom: 3px black groove; 4 | position: relative; 5 | } 6 | 7 | #ball { 8 | position: absolute; 9 | cursor: pointer; 10 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/1-animate-ball/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
      13 | 14 |
      15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /7-animation/3-js-animation/1-animate-ball/source.view/style.css: -------------------------------------------------------------------------------- 1 | #field { 2 | height: 200px; 3 | border-bottom: 3px black groove; 4 | position: relative; 5 | } 6 | 7 | #ball { 8 | position: absolute; 9 | cursor: pointer; 10 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/1-animate-ball/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 为弹跳的球设置动画 6 | 7 | 做一个弹跳的球。点击查看应有的效果: 8 | 9 | [iframe height=250 src="solution"] 10 | -------------------------------------------------------------------------------- /7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css: -------------------------------------------------------------------------------- 1 | #field { 2 | height: 200px; 3 | border-bottom: 3px black groove; 4 | position: relative; 5 | } 6 | 7 | #ball { 8 | position: absolute; 9 | cursor: pointer; 10 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/2-animate-ball-hops/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 设置动画使球向右移动 6 | 7 | 让球向右移动。像这样: 8 | 9 | [iframe height=250 src="solution"] 10 | 11 | 编写动画代码。终止时球到左侧的距离是 `100px`。 12 | 13 | 从前一个任务 的答案开始。 14 | -------------------------------------------------------------------------------- /7-animation/3-js-animation/back.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/bounce-easeinout.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/bounce-easeout.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/bounce.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/circ.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/elastic.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/quad.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/quint.view/style.css: -------------------------------------------------------------------------------- 1 | #brick { 2 | width: 40px; 3 | height: 20px; 4 | background: #EE6B47; 5 | position: relative; 6 | cursor: pointer; 7 | } 8 | 9 | #path { 10 | outline: 1px solid #E8C48E; 11 | width: 540px; 12 | height: 20px; 13 | } -------------------------------------------------------------------------------- /7-animation/3-js-animation/text.view/style.css: -------------------------------------------------------------------------------- 1 | textarea { 2 | display: block; 3 | border: 1px solid #BBB; 4 | color: #444; 5 | font-size: 110%; 6 | } 7 | 8 | button { 9 | margin-top: 10px; 10 | } -------------------------------------------------------------------------------- /7-animation/index.md: -------------------------------------------------------------------------------- 1 | # 动画 2 | 3 | CSS 和 JavaScript 动画。 4 | -------------------------------------------------------------------------------- /8-web-components/1-webcomponents-intro/satellite-expanded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/1-webcomponents-intro/satellite-expanded.jpg -------------------------------------------------------------------------------- /8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg -------------------------------------------------------------------------------- /8-web-components/1-webcomponents-intro/satellite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/1-webcomponents-intro/satellite.jpg -------------------------------------------------------------------------------- /8-web-components/1-webcomponents-intro/satellite@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/1-webcomponents-intro/satellite@2x.jpg -------------------------------------------------------------------------------- /8-web-components/2-custom-elements/1-live-timer/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 请注意: 3 | 1. 在元素被从文档移除的时候,我们会清除 `setInterval` 的 timer。这非常重要,否则即使我们不再需要它了,它仍然会继续计时。这样浏览器就不能清除这个元素占用和被这个元素引用的内存了。 4 | 2. 我们可以通过 `elem.date` 属性得到当前时间。类所有的方法和属性天生就是元素的方法和属性。 5 | -------------------------------------------------------------------------------- /8-web-components/2-custom-elements/1-live-timer/solution.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /8-web-components/2-custom-elements/1-live-timer/source.view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js: -------------------------------------------------------------------------------- 1 | class LiveTimer extends HTMLElement { 2 | 3 | /* your code here */ 4 | 5 | } 6 | 7 | customElements.define("live-timer", LiveTimer); 8 | -------------------------------------------------------------------------------- /8-web-components/3-shadow-dom/shadow-dom-range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/3-shadow-dom/shadow-dom-range.png -------------------------------------------------------------------------------- /8-web-components/3-shadow-dom/shadow-dom-range@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/3-shadow-dom/shadow-dom-range@2x.png -------------------------------------------------------------------------------- /8-web-components/3-shadow-dom/shadow-dom-say-hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/3-shadow-dom/shadow-dom-say-hello.png -------------------------------------------------------------------------------- /8-web-components/3-shadow-dom/shadow-dom-say-hello@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascript-tutorial/zh.javascript.info/e41b584cd6b44d001d8ead96e9005f60d9f06dca/8-web-components/3-shadow-dom/shadow-dom-say-hello@2x.png -------------------------------------------------------------------------------- /8-web-components/index.md: -------------------------------------------------------------------------------- 1 | # Web components 2 | 3 | Web components 是用于创建独立组件的一组标准:自定义 HTML 元素,它们具有自己的属性和方法,封装好的 DOM 和样式。 4 | -------------------------------------------------------------------------------- /9-regular-expressions/04-regexp-anchors/1-start-end/solution.md: -------------------------------------------------------------------------------- 1 | 空字符串是唯一的匹配项:它开始并立即结束。 2 | 3 | 这个题目再次证明了锚点不是字符串,而是测试。 4 | 5 | 对于空字符串 `""`,正则表达式引擎将会首先匹配 `pattern:^`(输入开始),匹配成功,然后立即匹配结束 `pattern:$`,也匹配成功。所以空字符串是匹配项。 6 | -------------------------------------------------------------------------------- /9-regular-expressions/04-regexp-anchors/1-start-end/task.md: -------------------------------------------------------------------------------- 1 | # 正则表达式 ^$ 2 | 3 | 什么字符串可以匹配模式 `^$`? 4 | -------------------------------------------------------------------------------- /9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 答案是:`pattern:\b\d\d:\d\d\b`。 3 | 4 | ```js run 5 | alert( "Breakfast at 09:00 in the room 123:456.".match( /\b\d\d:\d\d\b/ ) ); // 09:00 6 | ``` 7 | -------------------------------------------------------------------------------- /9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md: -------------------------------------------------------------------------------- 1 | # 查找时间 2 | 3 | 时间的格式是:`小时:分钟`。小时和分钟都是两位数,例如 `09:00`。 4 | 5 | 编写正则表达式在字符串 `subject:Breakfast at 09:00 in the room 123:456.` 中查找时间。 6 | 7 | P.S. 在这个任务里没有必要校验时间的正确性,所以 `25:99` 也可算做有效的结果。 8 | 9 | P.P.S. 正则表达式不应该匹配 `123:456`。 10 | -------------------------------------------------------------------------------- /9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md: -------------------------------------------------------------------------------- 1 | # Java[^script] 2 | 3 | 我们有一个正则表达式 `pattern:/Java[^script]/`。 4 | 5 | 它会和字符串 `subject:Java` 中的任何一部分匹配吗?`subject:JavaScript` 呢? 6 | -------------------------------------------------------------------------------- /9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md: -------------------------------------------------------------------------------- 1 | 答案:`pattern:\d\d[-:]\d\d`。 2 | 3 | ```js run 4 | let regexp = /\d\d[-:]\d\d/g; 5 | alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30 6 | ``` 7 | 8 | 请注意,破折号 `pattern:'-'` 在方括号中有特殊含义,但只有当它位于其它字符之间而不是开头或结尾时这个含义才会起作用,所以我们不需要对其进行转义。 9 | -------------------------------------------------------------------------------- /9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md: -------------------------------------------------------------------------------- 1 | # 找到 hh:mm 或者 hh-mm 格式的时间 2 | 3 | 时间可以通过 `hours:minutes` 或 `hours-minutes` 格式来表示。小时和分钟都有两位数:`09:00` 或 `21-30`。 4 | 5 | 写一个正则表达式来找到它们: 6 | 7 | ```js 8 | let regexp = /your regexp/g; 9 | alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30 10 | ``` 11 | 12 | P.S. 在这个任务中,我们假设时间总是正确的,不需要过滤像 "45:67" 这样错误的时间字符串。稍后我们也会处理这个问题。 13 | -------------------------------------------------------------------------------- /9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 答案: 3 | 4 | ```js run 5 | let regexp = /\.{3,}/g; 6 | alert( "Hello!... How goes?.....".match(regexp) ); // ..., ..... 7 | ``` 8 | 9 | 请注意,点(.)是一个特殊字符,所以我们必须对其进行转义,即将其插入为 `\.`。 10 | -------------------------------------------------------------------------------- /9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md: -------------------------------------------------------------------------------- 1 | importance: 5 2 | 3 | --- 4 | 5 | # 如何找到省略号 "..."? 6 | 7 | 创建一个正则表达式来查找省略号:连续 3(或更多)个点。 8 | 9 | 例如: 10 | 11 | ```js 12 | let regexp = /你的正则表达式/g; 13 | alert( "Hello!... How goes?.....".match(regexp) ); // ..., ..... 14 | ``` 15 | -------------------------------------------------------------------------------- /9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md: -------------------------------------------------------------------------------- 1 | # 针对 HTML 颜色的正则表达式 2 | 3 | 创建一个正则表达式来查找格式为 `#ABCDEF` 的 HTML 颜色值:首个字符是 `#`,后面紧接着的是六位的十六进制字符。 4 | 5 | 用例: 6 | 7 | ```js 8 | let regexp = /...你的正则表达式.../ 9 | 10 | let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2 #12345678"; 11 | 12 | alert( str.match(regexp) ) // #121212,#AA00ef 13 | ``` 14 | 15 | P.S. 在这个任务中,我们不需要其他的颜色格式,例如 `#123` 或 `rgb(1,2,3)` 等。 16 | -------------------------------------------------------------------------------- /9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 结果是:`match:123 4`。 3 | 4 | 首先,惰性模式 `pattern:\d+?` 尝试去获取尽可能少的数字,但它必须到达空格,因此需要匹配到 `match:123`。 5 | 6 | 然后,第二个 `\d+?` 就只获取一个数字,因为这就已经足够了。 7 | -------------------------------------------------------------------------------- /9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md: -------------------------------------------------------------------------------- 1 | # /d+? d+?/ 的匹配项 2 | 3 | 匹配的结果是什么? 4 | 5 | ```js 6 | alert( "123 456".match(/\d+? \d+?/g) ); // ? 7 | ``` 8 | -------------------------------------------------------------------------------- /9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md: -------------------------------------------------------------------------------- 1 | 我们需要找到注释的起始位置 `match:`。 2 | 3 | 行得通的表达式可以是 `pattern:` —— 惰性量词使得点在 `match:-->` 之前 停止。我们还需要为点添加修饰符 `pattern:s` 以包含换行符。 4 | 5 | 否则找不到多行注释: 6 | 7 | ```js run 8 | let regexp = //gs; 9 | 10 | let str = `... .. .. 12 | `; 13 | 14 | alert( str.match(regexp) ); // '', '' 15 | ``` 16 | -------------------------------------------------------------------------------- /9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md: -------------------------------------------------------------------------------- 1 | # 查找 HTML 注释 2 | 3 | 找出文本中的所有 HTML 注释: 4 | 5 | ```js 6 | let regexp = /你的正则表达式/g; 7 | 8 | let str = `... .. .. 10 | `; 11 | 12 | alert( str.match(regexp) ); // '', '' 13 | ``` 14 | -------------------------------------------------------------------------------- /9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md: -------------------------------------------------------------------------------- 1 | 2 | 答案是 `pattern:<[^<>]+>`。 3 | 4 | ```js run 5 | let regexp = /<[^<>]+>/g; 6 | 7 | let str = '<> '; 8 | 9 | alert( str.match(regexp) ); // '', '', '' 10 | ``` 11 | -------------------------------------------------------------------------------- /9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md: -------------------------------------------------------------------------------- 1 | # 寻找 HTML 标签 2 | 3 | 创建一个正则表达式来寻找所有(开始和结束)HTML 标签及其特性。 4 | 5 | 用例: 6 | 7 | ```js run 8 | let regexp = /你的正则表达式/g; 9 | 10 | let str = '<> '; 11 | 12 | alert( str.match(regexp) ); // '', '', '' 13 | ``` 14 | 15 | 这里我们假设标签特征中不包含 `<` 和 `>`(包括被引号包裹的内容),这样就简单多了。 16 | -------------------------------------------------------------------------------- /9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md: -------------------------------------------------------------------------------- 1 | # 找出形如 #abc 或 #abcdef 的颜色值 2 | 3 | 编写一个匹配 `#abc` 或 `#abcdef` 格式的颜色值的正则表达式。即:`#` 后跟着 3 个或 6 个十六进制的数字。 4 | 5 | 用例: 6 | ```js 7 | let regexp = /你的正则表达式/g; 8 | 9 | let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; 10 | 11 | alert( str.match(regexp) ); // #3f3 #AA00ef 12 | ``` 13 | 14 | P.S. 必须只匹配 3 位或 6 位十六进制数字的颜色值。不应该匹配 4 位数字的值,例如 `#abcd`。 15 | -------------------------------------------------------------------------------- /9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md: -------------------------------------------------------------------------------- 1 | 带有可选小数部分的正数:`pattern:\d+(\.\d+)?`。 2 | 3 | 让我们在开头加上可选的 `pattern:-`: 4 | 5 | ```js run 6 | let regexp = /-?\d+(\.\d+)?/g; 7 | 8 | let str = "-1.5 0 2 -123.4."; 9 | 10 | alert( str.match(regexp) ); // -1.5, 0, 2, -123.4 11 | ``` 12 | -------------------------------------------------------------------------------- /9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md: -------------------------------------------------------------------------------- 1 | # 找出所有数字 2 | 3 | 编写一个正则表达式,找出所有十进制数字,包括整数、浮点数和负数。 4 | 5 | 用例: 6 | 7 | ```js 8 | let regexp = /你的正则表达式/g; 9 | 10 | let str = "-1.5 0 2 -123.4."; 11 | 12 | alert( str.match(regexp) ); // -1.5, 0, 2, -123.4 13 | ``` 14 | -------------------------------------------------------------------------------- /9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md: -------------------------------------------------------------------------------- 1 | # 查找编程语言 2 | 3 | 有很多编程语言,例如 Java、JavaScript、PHP、C 或 C++。 4 | 5 | 构建一个正则表达式,用来匹配字符串 `subject:Java JavaScript PHP C++ C` 中包含的编程语言: 6 | 7 | ```js 8 | let regexp = /你的正则表达式/g; 9 | 10 | alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C 11 | ``` 12 | -------------------------------------------------------------------------------- /9-regular-expressions/13-regexp-alternation/04-match-exact-tag/task.md: -------------------------------------------------------------------------------- 1 | # 查找完整标签 2 | 3 | 写出一个正则表达式,用于查找 `` 标签。它应该匹配完整的标签:该标签可能没有特性(attributes)`