Repository: loverajoel/jstips Branch: master Commit: 44a1df45df96 Files: 296 Total size: 603.4 KB Directory structure: gitextract_f1dq9svt/ ├── CONTRIBUTING.md ├── CONTRIBUTING_es_ES.md ├── CONTRIBUTING_zh_CN.md ├── CONTRIBUTING_zh_TW.md ├── LICENSE.md ├── POST_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md └── _posts/ ├── en/ │ ├── angular/ │ │ ├── 2016-01-01-angularjs-digest-vs-apply.md │ │ └── 2017-03-07-preventing-unwanted-scopes-creation-in-angularjs.md │ ├── javascript/ │ │ ├── 2015-12-29-insert-item-inside-an-array.md │ │ ├── 2016-01-03-improve-nested-conditionals.md │ │ ├── 2016-01-04-sorting-strings-with-accented-characters.md │ │ ├── 2016-01-05-differences-between-undefined-and-null.md │ │ ├── 2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md │ │ ├── 2016-01-07-use-strict-and-get-lazy.md │ │ ├── 2016-01-08-converting-a-node-list-to-an-array.md │ │ ├── 2016-01-09-template-strings.md │ │ ├── 2016-01-10-check-if-a-property-is-in-a-object.md │ │ ├── 2016-01-11-hoisting.md │ │ ├── 2016-01-12-pseudomandatory-parameters-in-es6-functions.md │ │ ├── 2016-01-13-tip-to-measure-performance-of-a-javascript-block.md │ │ ├── 2016-01-14-fat-arrow-functions.md │ │ ├── 2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md │ │ ├── 2016-01-16-passing-arguments-to-callback-functions.md │ │ ├── 2016-01-17-nodejs-run-a-module-if-it-is-not-required.md │ │ ├── 2016-01-18-rounding-the-fast-way.md │ │ ├── 2016-01-19-safe-string-concatenation.md │ │ ├── 2016-01-20-return-objects-to-enable-chaining-of-functions.md │ │ ├── 2016-01-21-shuffle-an-array.md │ │ ├── 2016-01-22-two-ways-to-empty-an-array.md │ │ ├── 2016-01-23-converting-to-number-fast-way.md │ │ ├── 2016-01-24-use_===_instead_of_==.md │ │ ├── 2016-01-25-Using-immediately-invoked-function-expression.md │ │ ├── 2016-01-26-filtering-and-sorting-a-list-of-strings.md │ │ ├── 2016-01-27-short-circuit-evaluation-in-js.md │ │ ├── 2016-01-28-curry-vs-partial-application.md │ │ ├── 2016-01-29-speed-up-recursive-functions-with-memoization.md │ │ ├── 2016-01-30-converting-truthy-falsy-values-to-boolean.md │ │ ├── 2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md │ │ ├── 2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md │ │ ├── 2016-02-02-create-range-0-n-easily-using-one-line.md │ │ ├── 2016-02-03-implementing-asynchronous-loops.md │ │ ├── 2016-02-04-assignment-shorthands.md │ │ ├── 2016-02-05-observe-dom-changes.md │ │ ├── 2016-02-06-deduplicate-an-array.md │ │ ├── 2016-02-07-flattening-multidimensional-arrays-in-javascript.md │ │ ├── 2016-02-08-advanced-properties.md │ │ ├── 2016-02-09-using-json-stringify.md │ │ ├── 2016-02-10-array-average-and-median.md │ │ ├── 2016-02-11-preventing-unapply-attacks.md │ │ ├── 2016-02-12-use-destructuring-in-function-parameters.md │ │ ├── 2016-02-13-know-the-passing-mechanism.md │ │ ├── 2016-02-14-calculate-the-max-min-value-from-an-array.md │ │ ├── 2016-02-15-detect-document-ready-in-pure-js.md │ │ ├── 2016-02-16-basics-declarations.md │ │ ├── 2016-02-17-reminders-about-reduce-function-usage.md │ │ ├── 2016-02-26-extract-unix-timestamp-easily.md │ │ ├── 2016-03-03-helpful-console-log-hacks.md │ │ ├── 2016-03-16-DOM-event-listening-made-easy.md │ │ ├── 2016-04-05-return-values-with-the-new-operator.md │ │ ├── 2016-04-21-get-file-extension.md │ │ ├── 2016-05-06-use-optional-arguments.md │ │ ├── 2016-05-12-make-easy-loop-on-array.md │ │ ├── 2016-08-02-copy-to-clipboard.md │ │ ├── 2016-08-10-comma-operaton-in-js.md │ │ ├── 2016-08-17-break-continue-loop-functional.md │ │ ├── 2016-08-25-keyword-var-vs-let.md │ │ ├── 2016-10-28-three-useful-hacks.md │ │ ├── 2017-01-19-binding-objects-to-functions.md │ │ ├── 2017-03-09-working-with-websocket-timeout.md │ │ ├── 2017-03-12-3-array-hacks.md │ │ ├── 2017-03-16-tapping-for-quick-debugging.md │ │ ├── 2017-03-29-recursion-iteration-and-tail-calls-in-js.md │ │ ├── 2017-04-03-why-you-should-use-Object.is()-in-equality-comparison.md │ │ ├── 2017-04-05-picking-and-rejecting-object-properties.md │ │ ├── 2017-04-11-protocols-for-the-brave.md │ │ ├── 2017-04-24-improving-your-async-functions-with-webworkers.md │ │ ├── 2017-06-14-closures-inside-loops.md │ │ ├── 2017-06-14-immutable-structures-and-cloning.md │ │ ├── 2017-06-15-looping-over-arrays.md │ │ ├── 2017-09-01-hash-maps-without-side-effects.md │ │ ├── 2018-11-25-creating-immutable-objects-in-native-javascript.md │ │ ├── 2020-10-13-what-is-a-functional-inheritance.md │ │ ├── 2020-10-15-what-is-a-currying-function.md │ │ ├── 2020-10-20-what-is-the-temporal-dead-zone.md │ │ ├── 2020-10-22-difference-between-target-and-currentTarget.md │ │ ├── 2020-10-27-what-is-a-spread-operator.md │ │ ├── 2020-11-04-what-is-a-void-operator.md │ │ ├── 2020-11-17-what-is-the-promise-executor.md │ │ └── 2021-02-02-what-is-the-javascript-ternary-operator.md │ ├── more/ │ │ └── 2017-04-06-vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding.md │ └── react/ │ ├── 2016-01-02-keys-in-children-components-are-important.md │ ├── 2017-03-27-state-to-props-maps-with-memory.md │ ├── 2017-04-04-enhancing-react-components-composition.md │ ├── 2017-04-10-adventurers-guide-to-react.md │ ├── 2017-05-29-upping-performance-by-appending-keying.md │ └── 2021-07-18-trace-the-reason-make-your-page-rerender.md ├── es_ES/ │ ├── angular/ │ │ └── 2016-01-01-angularjs-digest-vs-apply.md │ ├── javascript/ │ │ ├── 2015-12-29-insert-item-inside-an-array.md │ │ ├── 2016-01-03-improve-nested-conditionals.md │ │ ├── 2016-01-04-sorting-strings-with-accented-characters.md │ │ ├── 2016-01-05-differences-between-undefined-and-null.md │ │ ├── 2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md │ │ ├── 2016-01-07-use-strict-and-get-lazy.md │ │ ├── 2016-01-08-converting-a-node-list-to-an-array.md │ │ ├── 2016-01-09-template-strings.md │ │ ├── 2016-01-10-check-if-a-property-is-in-a-object.md │ │ ├── 2016-01-11-hoisting.md │ │ ├── 2016-01-12-pseudomandatory-parameters-in-es6-functions.md │ │ ├── 2016-01-13-tip-to-measure-performance-of-a-javascript-block.md │ │ ├── 2016-01-14-fat-arrow-functions.md │ │ ├── 2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md │ │ ├── 2016-01-16-passing-arguments-to-callback-functions.md │ │ ├── 2016-01-17-nodejs-run-a-module-if-it-is-not-required.md │ │ ├── 2016-01-18-rounding-the-fast-way.md │ │ ├── 2016-01-19-safe-string-concatenation.md │ │ ├── 2016-01-20-return-objects-to-enable-chaining-of-functions.md │ │ ├── 2016-01-21-shuffle-an-array.md │ │ ├── 2016-01-22-two-ways-to-empty-an-array.md │ │ ├── 2016-01-23-converting-to-number-fast-way.md │ │ ├── 2016-01-24-use_===_instead_of_==.md │ │ ├── 2016-01-25-Using-immediately-invoked-function-expression.md │ │ ├── 2016-01-26-filtering-and-sorting-a-list-of-strings.md │ │ ├── 2016-01-27-short-circiut-evaluation-in-js.md │ │ ├── 2016-01-28-curry-vs-partial-application.md │ │ ├── 2016-01-29-speed-up-recursive-functions-with-memoization.md │ │ ├── 2016-01-30-converting-truthy-falsy-values-to-boolean.md │ │ ├── 2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md │ │ ├── 2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md │ │ ├── 2016-02-02-create-range-0-n-easily-using-one-line.md │ │ ├── 2016-02-03-implementing-asynchronous-loops.md │ │ ├── 2016-02-04-assignment-shorthands.md │ │ ├── 2016-02-05-observe-dom-changes.md │ │ ├── 2016-02-06-deduplicate-an-array.md │ │ ├── 2016-02-07-flattening-multidimensional-arrays-in-javascript.md │ │ ├── 2016-02-08-advanced-properties.md │ │ ├── 2016-02-09-using-json-stringify.md │ │ ├── 2016-02-10-array-average-and-median.md │ │ ├── 2016-02-11-preventing-unapply-attacks.md │ │ ├── 2016-02-12-use-destructuring-in-function-parameters.md │ │ ├── 2016-02-13-know-the-passing-mechanism.md │ │ ├── 2016-02-14-calculate-the-max-min-value-from-an-array.md │ │ ├── 2016-02-15-detect-document-ready-in-pure-js.md │ │ ├── 2016-02-16-basics-declarations.md │ │ ├── 2016-02-17-reminders-about-reduce-function-usage.md │ │ ├── 2016-02-26-extract-unix-timestamp-easily.md │ │ ├── 2016-03-03-helpful-console-log-hacks.md │ │ ├── 2016-03-16-DOM-event-listening-made-easy.md │ │ ├── 2016-04-05-return-values-with-the-new-operator.md │ │ ├── 2016-04-21-get-file-extension.md │ │ ├── 2016-05-06-use-optional-arguments.md │ │ └── 2016-05-12-make-easy-loop-on-array.md │ └── react/ │ └── 2016-01-02-keys-in-children-components-are-important.md ├── zh_CN/ │ ├── angular/ │ │ └── 2016-01-01-angularjs-digest-vs-apply.md │ ├── javascript/ │ │ ├── 2015-12-29-insert-item-inside-an-array.md │ │ ├── 2016-01-03-improve-nested-conditionals.md │ │ ├── 2016-01-04-sorting-strings-with-accented-characters.md │ │ ├── 2016-01-05-differences-between-undefined-and-null.md │ │ ├── 2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md │ │ ├── 2016-01-07-use-strict-and-get-lazy.md │ │ ├── 2016-01-08-converting-a-node-list-to-an-array.md │ │ ├── 2016-01-09-template-strings.md │ │ ├── 2016-01-10-check-if-a-property-is-in-a-object.md │ │ ├── 2016-01-11-hoisting.md │ │ ├── 2016-01-12-pseudomandatory-parameters-in-es6-functions.md │ │ ├── 2016-01-13-tip-to-measure-performance-of-a-javascript-block.md │ │ ├── 2016-01-14-fat-arrow-functions.md │ │ ├── 2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md │ │ ├── 2016-01-16-passing-arguments-to-callback-functions.md │ │ ├── 2016-01-17-nodejs-run-a-module-if-it-is-not-required.md │ │ ├── 2016-01-18-rounding-the-fast-way.md │ │ ├── 2016-01-19-safe-string-concatenation.md │ │ ├── 2016-01-20-return-objects-to-enable-chaining-of-functions.md │ │ ├── 2016-01-21-shuffle-an-array.md │ │ ├── 2016-01-22-two-ways-to-empty-an-array.md │ │ ├── 2016-01-23-converting-to-number-fast-way.md │ │ ├── 2016-01-24-use_===_instead_of_==.md │ │ ├── 2016-01-25-Using-immediately-invoked-function-expression.md │ │ ├── 2016-01-26-filtering-and-sorting-a-list-of-strings.md │ │ ├── 2016-01-27-short-circuit-evaluation-in-js.md │ │ ├── 2016-01-28-curry-vs-partial-application.md │ │ ├── 2016-01-29-speed-up-recursive-functions-with-memoization.md │ │ ├── 2016-01-30-converting-truthy-falsy-values-to-boolean.md │ │ ├── 2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md │ │ ├── 2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md │ │ ├── 2016-02-02-create-range-0-n-easily-using-one-line.md │ │ ├── 2016-02-03-implementing-asynchronous-loops.md │ │ ├── 2016-02-04-assignment-shorthands.md │ │ ├── 2016-02-05-observe-dom-changes.md │ │ ├── 2016-02-06-deduplicate-an-array.md │ │ ├── 2016-02-07-flattening-multidimensional-arrays-in-javascript.md │ │ ├── 2016-02-08-advanced-properties.md │ │ ├── 2016-02-09-using-json-stringify.md │ │ ├── 2016-02-10-array-average-and-median.md │ │ ├── 2016-02-11-preventing-unapply-attacks.md │ │ ├── 2016-02-12-use-destructuring-in-function-parameters.md │ │ ├── 2016-02-13-know-the-passing-mechanism.md │ │ ├── 2016-02-14-calculate-the-max-min-value-from-an-array.md │ │ ├── 2016-02-15-detect-document-ready-in-pure-js.md │ │ ├── 2016-02-16-basics-declarations.md │ │ ├── 2016-02-17-reminders-about-reduce-function-usage.md │ │ ├── 2016-02-26-extract-unix-timestamp-easily.md │ │ ├── 2016-03-03-helpful-console-log-hacks.md │ │ ├── 2016-03-16-DOM-event-listening-made-easy.md │ │ ├── 2016-04-05-return-values-with-the-new-operator.md │ │ ├── 2016-04-21-get-file-extension.md │ │ ├── 2016-05-06-use-optional-arguments.md │ │ ├── 2016-05-12-make-easy-loop-on-array.md │ │ ├── 2016-08-02-copy-to-clipboard.md │ │ ├── 2016-08-10-comma-operaton-in-js.md │ │ ├── 2016-08-17-break-continue-loop-functional.md │ │ ├── 2016-08-25-keyword-var-vs-let.md │ │ ├── 2016-10-28-three-useful-hacks.md │ │ ├── 2017-01-19-binding-objects-to-functions.md │ │ ├── 2017-03-09-working-with-websocket-timeout.md │ │ ├── 2017-03-12-3-array-hacks.md │ │ ├── 2017-03-16-tapping-for-quick-debugging.md │ │ ├── 2017-04-03-why-you-should-use-Object.is()-in-equality-comparison.md │ │ └── 2017-04-05-picking-and-rejecting-object-properties.md │ ├── more/ │ │ └── 2017-04-06-vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding.md │ └── react/ │ └── 2016-01-02-keys-in-children-components-are-important.md └── zh_TW/ ├── angular/ │ ├── 2016-01-01-angularjs-digest-vs-apply.md │ └── 2017-03-07-preventing-unwanted-scopes-creation-in-angularjs.md ├── javascript/ │ ├── 2015-12-29-insert-item-inside-an-array.md │ ├── 2016-01-03-improve-nested-conditionals.md │ ├── 2016-01-04-sorting-strings-with-accented-characters.md │ ├── 2016-01-05-differences-between-undefined-and-null.md │ ├── 2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md │ ├── 2016-01-07-use-strict-and-get-lazy.md │ ├── 2016-01-08-converting-a-node-list-to-an-array.md │ ├── 2016-01-09-template-strings.md │ ├── 2016-01-10-check-if-a-property-is-in-a-object.md │ ├── 2016-01-11-hoisting.md │ ├── 2016-01-12-pseudomandatory-parameters-in-es6-functions.md │ ├── 2016-01-13-tip-to-measure-performance-of-a-javascript-block.md │ ├── 2016-01-14-fat-arrow-functions.md │ ├── 2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md │ ├── 2016-01-16-passing-arguments-to-callback-functions.md │ ├── 2016-01-17-nodejs-run-a-module-if-it-is-not-required.md │ ├── 2016-01-18-rounding-the-fast-way.md │ ├── 2016-01-19-safe-string-concatenation.md │ ├── 2016-01-20-return-objects-to-enable-chaining-of-functions.md │ ├── 2016-01-21-shuffle-an-array.md │ ├── 2016-01-22-two-ways-to-empty-an-array.md │ ├── 2016-01-23-converting-to-number-fast-way.md │ ├── 2016-01-24-use_===_instead_of_==.md │ ├── 2016-01-25-Using-immediately-invoked-function-expression.md │ ├── 2016-01-26-filtering-and-sorting-a-list-of-strings.md │ ├── 2016-01-27-short-circiut-evaluation-in-js.md │ ├── 2016-01-28-curry-vs-partial-application.md │ ├── 2016-01-29-speed-up-recursive-functions-with-memoization.md │ ├── 2016-01-30-converting-truthy-falsy-values-to-boolean.md │ ├── 2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md │ ├── 2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md │ ├── 2016-02-02-create-range-0-n-easily-using-one-line.md │ ├── 2016-02-03-implementing-asynchronous-loops.md │ ├── 2016-02-04-assignment-shorthands.md │ ├── 2016-02-05-observe-dom-changes.md │ ├── 2016-02-06-deduplicate-an-array.md │ ├── 2016-02-07-flattening-multidimensional-arrays-in-javascript.md │ ├── 2016-02-08-advanced-properties.md │ ├── 2016-02-09-using-json-stringify.md │ ├── 2016-02-10-array-average-and-median.md │ ├── 2016-02-11-preventing-unapply-attacks.md │ ├── 2016-02-12-use-destructuring-in-function-parameters.md │ ├── 2016-02-13-know-the-passing-mechanism.md │ ├── 2016-02-14-calculate-the-max-min-value-from-an-array.md │ ├── 2016-02-15-detect-document-ready-in-pure-js.md │ ├── 2016-02-16-basics-declarations.md │ ├── 2016-02-17-reminders-about-reduce-function-usage.md │ ├── 2016-02-26-extract-unix-timestamp-easily.md │ ├── 2016-03-03-helpful-console-log-hacks.md │ ├── 2016-03-16-DOM-event-listening-made-easy.md │ ├── 2016-04-05-return-values-with-the-new-operator.md │ ├── 2016-04-21-get-file-extension.md │ ├── 2016-05-06-use-optional-arguments.md │ ├── 2016-05-12-make-easy-loop-on-array.md │ ├── 2016-08-02-copy-to-clipboard.md │ ├── 2016-08-10-comma-operaton-in-js.md │ ├── 2016-08-17-break-continue-loop-functional.md │ ├── 2016-08-25-keyword-var-vs-let.md │ ├── 2016-10-28-three-useful-hacks.md │ ├── 2017-01-19-binding-objects-to-functions.md │ ├── 2017-03-09-working-with-websocket-timeout.md │ ├── 2017-03-12-3-array-hacks.md │ ├── 2017-03-16-tapping-for-quick-debugging.md │ ├── 2017-03-29-recursion-iteration-and-tail-calls-in-js.md │ ├── 2017-04-03-why-you-should-use-Object.is()-in-equality-comparison.md │ ├── 2017-04-05-picking-and-rejecting-object-properties.md │ └── 2017-06-15-looping-over-arrays.md ├── more/ │ └── 2017-04-06-vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding.md └── react/ ├── 2016-01-02-keys-in-children-components-are-important.md ├── 2017-03-27-state-to-props-maps-with-memory.md ├── 2017-04-04-enhancing-react-components-composition.md └── 2017-04-10-adventurers-guide-to-react.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: CONTRIBUTING.md ================================================ # How to submit your tip To submit a tip to the list, fork the repository and add your tip in a new file in the correct folder (according language). The name of the file should be `2016-xx-xx-name-of-your-tip`. Use [this format](POST_TEMPLATE.md) when writing your tip. ### Requirements - The tip should be readable in less than two minutes - Adding links to other sites or videos that give more insight is welcome - Mark JS code with ```js - Don't mention "JavaScript" in the title (as our tips are about it anyway) - Use backticks (`) to mark code in the **title** and/or **tip-tldr** fields. _Warning_: Both values must not start with backticks! - How you can be rewarded: If you have a PayPal, Coinbase or another account, type the url on tip-writer-support. That's all! Now your readers will be able to grab you a beer for your knowledge. Once your tip is ready, [issue a pull request](https://help.github.com/articles/using-pull-requests/) with this [PR template](PULL_REQUEST_TEMPLATE.md) and your tip will be reviewed (see below). ## Notes Leave the date and the tip number as **xx**. When the PR is `ready to merge`, we will tell you the correct numbers. Please also [squash](https://davidwalsh.name/squash-commits-git) your commits. ## Tip flow **Tip proposal** ⇒ **Tip under review** ⇒ **Tip ready to merge** - When you send a tip, it has to pass the review process and while that happens, its status is `under review`. - Once the tip is reviewed by 5 people and has been given the reviewer's ship it emote (:shipit:), the tip is `ready to merge`. We are looking forward to your contribution! ================================================ FILE: CONTRIBUTING_es_ES.md ================================================ # Como enviar tu tip Para agregar tu tip a la lista, debes forkear el repositorio y agregar tu tip en un nuevo archivos en el direcctorio correcto (de acuero el lenguage). El nombre del archivo deberia ser '2016-xx-xx-nombre-de-tu-tip'. Utilice [este formato](https://github.com/loverajoel/jstips/blob/gh-pages/POST_TEMPLATE.md) para escribir su tip. ### Requisitos - Su tip debe ser legible en menos de dos minutos. - Puede añadir enlaces a otros sitios o videos que dan una visión más clara es bienvenido. - Se marcará el código JS con ```js - No Mencione "JavaScript" en el título (como nuestros consejos son respecto de todos modos) - Use comillas invertidas (`) para marcar código en el campos **title** y/o **tip-tldr**. _Precaucion_: Ambos valores no deben comenzar con acentos abiertos! Una vez que su tip está listo, [issue a pull request](https://help.github.com/articles/using-pull-requests/) con esta [PR template](https://github.com/loverajoel/jstips/blob/gh-pages/PULL_REQUEST_TEMPLATE.md) y su tip será revisado (véase más adelante). # Notas Deje la fecha y el número de tip con **xx**. Cuando el PR es `ready to merge`, le diremos los números correctos. Por favor, también [squash] (https://davidwalsh.name/squash-commits-git) sus confirmaciones. # Flujo del Tip **Tip proposal** ⇒ **Tip under review** ⇒ **Tip ready to merge** - Cuando se envía un tip, tiene que pasar el proceso de revisión y mientras eso sucede, su estado es `under review`. - Después de que el tip sea revisado por 5 personas y han dado (:shipit:), el tip esta `ready to merge`. ================================================ FILE: CONTRIBUTING_zh_CN.md ================================================ # 如何提交小知识 将小知识提交到本列表,`fork`此仓库(repository)然后将的小知识放到对应文件夹(根据语言)的新文件中。新文件的名字应该像`2016-xx-xx-name-of-your-tip`这样。 书写小知识时请按照[这个格式](https://github.com/loverajoel/jstips/blob/gh-pages/POST_TEMPLATE.md)。 ### 要求 - 小知识可以在两分钟内阅读完。 - 增加其他网站或视频的链接提供更深讲解更佳 - 使用 ```js 标记JS代码 - 无序在标题处提及"JavaScript"(因为我们的小知识全是关于它的) - 使用反引号(`) 标记**标题**或**tip-tldr**中的代码 ‐ _警告_:它们都不能以反引号开头 当你的小知识准备就绪了, 根据这个[PR 模板](https://github.com/loverajoel/jstips/blob/gh-pages/GIT_TEMPLATE.md),[发布一个pull request](https://help.github.com/articles/using-pull-requests/)你的小知识将被审查(见下文)。 # 提示 将时间和小知识number保留为**xx**。当PR为`ready to merge`状态时,我们将告诉你正确的数字。请同时[squash](https://davidwalsh.name/squash-commits-git)你的commits. # 工作流程 **Tip 提交** ⇒ **Tip 审查** ⇒ **Tip 通过并发布** - 当你提交 tip 时,如果 tip 正在通过审查,此时 tip 状态为 `under review`。 - 如果 tip 经过5个人的审查,而且他們都给了 :shipit:,tip 将会被合并(`merge`)到 tip 列表中。 期待您的贡献! ================================================ FILE: CONTRIBUTING_zh_TW.md ================================================ # 如何提交你的 tip 如果要提交 tip 到目錄,fork 這個儲存庫(repository)並加入你的 tip 到檔案內,放入到正確的資料夾(根據語系)。檔案名稱應該為 `2016-xx-xx-name-of-your-tip`。 當你撰寫你的 tip 時,請使用[這個格式](POST_TEMPLATE.md)。 ### 要求 - tip 應該至少可以在兩分鐘內讀懂。 - 你可以連結到其他的網站或者是影片讓我們了解更多。 - 程式碼區塊使用 ```js。 - 避免在 title 提到「JavaScript」(因為我們的 tips 都是與 JavaScript 相關的)。 - 使用反引號(`)來標記程式碼 - _警告_:tip **標題**和 **tip-tldr** 不要使用反引號。 當你的 tip 準備好了,依據這個 [PR 樣板](PULL_REQUEST_TEMPLATE.md),[發送一個 PR](https://help.github.com/articles/using-pull-requests/) 你的 tip 將會被校閱。每天都會有 tip 從可用的 PR 中被合併(merged)。 ## 注意 使用 **xx** 為日期和 tip 的編號。當我們決定合併你的 PR 你可以把它們增加並 [squash](https://davidwalsh.name/squash-commits-git) 到你的 commits。 ## Tip 工作流程 **Tip 發送** ⇒ **Tip 審查** ⇒ **Tip 接受並發布** - 當你提交 tip 時,如果 tip 正在校閱流程,則 tip 狀態為 `under-review`。 - 如果 tip 經過 5 位專業的人士校閱,而且他們都給了 :shipit:,tip 將會被合併(`merge`)到 tip 清單。 ================================================ FILE: LICENSE.md ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: POST_TEMPLATE.md ================================================ --- layout: *post title: Demo post tip-number: xx tip-username: tip_js tip-username-profile: https://twitter.com/tips_js tip-tldr: Just a demo categories: - en --- content here ### Subtitles format ================================================ FILE: PULL_REQUEST_TEMPLATE.md ================================================ ## [Title here] ## TL;DR; [content here] ## Username [twitter account link or github account link ej: [@tips-js](https://twitter.com/tips_js)] ## Extra [content here] ================================================ FILE: README.md ================================================ [![header](https://pbs.twimg.com/profile_banners/4539400881/1613214971/1500x500)](https://www.jstips.co/book/?utm_source=github&utm_medium=header&utm_campaign=book&utm_content=pro) # JS Tips [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) > Useful JavaScript tips This is an awesome project about short and useful JavaScript tips that will help you improve your code writing. In less than 2 minutes, you can read about performance, conventions, hacks, interview questions and all the items that the future of this awesome language holds for us. Tips are added frequently (read further if you want to stay in touch). ## Support the community If you found a JS Tip useful or if you like a writer of the community, you can show your support and respect with a tip! ### Can you help us enrich it? Sure, you can help the project in two ways: sending a tip or reviewing future tips. Any improvements or suggestions are more than welcome! [Instructions are here](https://github.com/loverajoel/jstips/blob/master/CONTRIBUTING.md). ### Let’s keep in touch There are a lot of ways to get updates: - [Official Blog](http://www.jstips.co) - [Official Twitter Account](https://twitter.com/tips_js) - [Hubot](https://github.com/dggriffin/hubot-jstips) - [Slackbot](https://github.com/radibit/js-tips-slack-bot) - [iOS App](https://goo.gl/Y9WiBc) > Don't forget to Star★ the repo, as this helps promote the project! # Tips list - 74 - [Check the reason make your page re-render by changed props and state](http://www.jstips.co/en/react/trace-the-reason-make-your-page-rerender/) - 73 - [Hash maps without side effects](http://www.jstips.co/en/javascript/hash-maps-without-side-effects/) - 72 - [Adventurers Guide to React (Part I)](http://www.jstips.co/en/react/adventurers-guide-to-react/) - 71 - [VueJS, How VueJS makes a copy-update-replace inside the data binding](http://www.jstips.co/en/more/vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding/) - 70 - [Picking and rejecting object properties](http://www.jstips.co/en/javascript/picking-and-rejecting-object-properties/) - 69 - [Enhancing React components, Composition](http://www.jstips.co/en/react/enhancing-react-components-composition/) - 68 - [Why you should use Object.is() in equality comparison](http://www.jstips.co/en/javascript/why-you-should-use-Object.is()-in-equality-comparison/) - 67 - [Recursion, iteration and tail calls in JS](http://www.jstips.co/en/javascript/recursion-iteration-and-tail-calls-in-js/) - 66 - [State to Props maps with memory](http://www.jstips.co/en/react/state-to-props-maps-with-memory/) - 65 - [Tapping for quick debugging](http://www.jstips.co/en/javascript/tapping-for-quick-debugging/) - 64 - [3 Array Hacks](http://www.jstips.co/en/javascript/3-array-hacks/) - 63 - [Working With Websocket Timeout](http://www.jstips.co/en/javascript/working-with-websocket-timeout/) - 62 - [Preventing Unwanted Scopes Creation in AngularJs](http://www.jstips.co/en/angular/preventing-unwanted-scopes-creation-in-angularjs/) - 61 - [Binding objects to functions](http://www.jstips.co/en/javascript/binding-objects-to-functions/) - 60 - [Three useful hacks](http://www.jstips.co/en/javascript/three-useful-hacks/) - 59 - [ES6, var vs let](http://www.jstips.co/en/javascript/keyword-var-vs-let/) - 58 - [Breaking or continuing loop in functional programming](http://www.jstips.co/en/javascript/break-continue-loop-functional/) - 57 - [Comma operator in JS](http://www.jstips.co/en/javascript/comma-operaton-in-js/) - 56 - [Copy to Clipboard](http://www.jstips.co/en/javascript/copy-to-clipboard/) - 55 - [Create an easy loop using an array](http://www.jstips.co/en/javascript/make-easy-loop-on-array/) - 54 - [How to use optional arguments in functions (with optional callback)](http://www.jstips.co/en/javascript/use-optional-arguments/) - 53 - [Get File Extension](http://www.jstips.co/en/javascript/get-file-extension/) - 52 - [Return Values with the 'new' Operator](http://www.jstips.co/en/javascript/return-values-with-the-new-operator/) - 51 - [DOM event listening made easy](http://www.jstips.co/en/javascript/DOM-event-listening-made-easy/) - 50 - [Helpful Console Logging Tricks](http://www.jstips.co/en/javascript/helpful-console-log-hacks/) - 49 - [Easiest way to extract Unix timestamps](http://www.jstips.co/en/javascript/extract-unix-timestamp-easily/) - 48 - [Reduce builtin function usage](http://www.jstips.co/en/javascript/reminders-about-reduce-function-usage/) - 47 - [Basics: Declarations](http://www.jstips.co/en/javascript/basics-declarations/) - 46 - [Detect document ready in pure JS](http://www.jstips.co/en/javascript/detect-document-ready-in-pure-js/) - 45 - [Calculate the Max/Min value from an array](http://www.jstips.co/en/javascript/calculate-the-max-min-value-from-an-array/) - 44 - [Know the passing mechanism](http://www.jstips.co/en/javascript/know-the-passing-mechanism/) - 43 - [Use destructuring in function parameters](http://www.jstips.co/en/javascript/use-destructuring-in-function-parameters/) - 42 - [Preventing Unapply Attacks](http://www.jstips.co/en/javascript/preventing-unapply-attacks/) - 41 - [Array average and median](http://www.jstips.co/en/javascript/array-average-and-median/) - 40 - [Using JSON.stringify](http://www.jstips.co/en/javascript/using-json-stringify/) - 39 - [Advanced Properties](http://www.jstips.co/en/javascript/advanced-properties/) - 38 - [Flattening multidimensional Arrays](http://www.jstips.co/en/javascript/flattening-multidimensional-arrays-in-javascript/) - 37 - [Deduplicate an Array](http://www.jstips.co/en/javascript/deduplicate-an-array/) - 36 - [Observe DOM changes in extensions](http://www.jstips.co/en/javascript/observe-dom-changes/) - 35 - [Assignment Operators](http://www.jstips.co/en/javascript/assignment-shorthands/) - 34 - [Implementing asynchronous loop](http://www.jstips.co/en/javascript/implementing-asynchronous-loops/) - 33 - [Create Range 0...N easily using one line](http://www.jstips.co/en/javascript/create-range-0-n-easily-using-one-line/) - 32 - [`Map()` to the rescue: adding order to Object properties](http://www.jstips.co/en/javascript/map-to-the-rescue-adding-order-to-object-properties/) - 31 - [Avoid modifying or passing `arguments` into other functions — it kills optimization](http://www.jstips.co/en/javascript/avoid-modifying-or-passing-arguments-into-other-functions-it-kills-optimization/) - 30 - [Converting truthy/falsy values to boolean](http://www.jstips.co/en/javascript/converting-truthy-falsy-values-to-boolean/) - 29 - [Speed up recursive functions with memoization](http://www.jstips.co/en/javascript/speed-up-recursive-functions-with-memoization/) - 28 - [Currying vs partial application](http://www.jstips.co/en/javascript/curry-vs-partial-application/) - 27 - [Short circuit evaluation](http://www.jstips.co/en/javascript/short-circuit-evaluation-in-js/) - 26 - [Filtering and sorting a list of Strings](http://www.jstips.co/en/javascript/filtering-and-sorting-a-list-of-strings/) - 25 - [Using immediately invoked function expression](http://www.jstips.co/en/javascript/Using-immediately-invoked-function-expression/) - 24 - [Use `===` instead of `==`](http://www.jstips.co/en/javascript/use_===_instead_of_==/) - 23 - [Converting to number fast way](http://www.jstips.co/en/javascript/converting-to-number-fast-way/) - 22 - [Empty an Array](http://www.jstips.co/en/javascript/two-ways-to-empty-an-array/) - 21 - [Shuffle an Array](http://www.jstips.co/en/javascript/shuffle-an-array/) - 20 - [Return objects to enable chaining of functions](http://www.jstips.co/en/javascript/return-objects-to-enable-chaining-of-functions/) - 19 - [Safe String concatenation](http://www.jstips.co/en/javascript/safe-string-concatenation/) - 18 - [Truncating the fast (but risky) way](http://www.jstips.co/en/javascript/rounding-the-fast-way/) - 17 - [Node.js: Run a module if it is not "required"](http://www.jstips.co/en/javascript/nodejs-run-a-module-if-it-is-not-required/) - 16 - [Passing arguments to callback functions](http://www.jstips.co/en/javascript/passing-arguments-to-callback-functions/) - 15 - [Even simpler way of using `indexOf` as a contains clause](http://www.jstips.co/en/javascript/even-simpler-way-of-using-indexof-as-a-contains-clause/) - 14 - [Fat Arrow Functions #ES6](http://www.jstips.co/en/javascript/fat-arrow-functions/) - 13 - [Measure performance of a code block](http://www.jstips.co/en/javascript/tip-to-measure-performance-of-a-javascript-block/) - 12 - [Pseudomandatory parameters in ES6 functions #ES6](http://www.jstips.co/en/javascript/pseudomandatory-parameters-in-es6-functions/) - 11 - [Hoisting](http://www.jstips.co/en/javascript/hoisting/) - 10 - [Check if a property is in an Object](http://www.jstips.co/en/javascript/check-if-a-property-is-in-a-object/) - 09 - [Template Strings](http://www.jstips.co/en/javascript/template-strings/) - 08 - [Converting a Node List to an Array](http://www.jstips.co/en/javascript/converting-a-node-list-to-an-array/) - 07 - ["use strict" and get lazy](http://www.jstips.co/en/javascript/use-strict-and-get-lazy/) - 06 - [Writing a single method for arrays and a single element](http://www.jstips.co/en/javascript/writing-a-single-method-for-arrays-and-a-single-element/) - 05 - [Differences between `undefined` and `null`](http://www.jstips.co/en/javascript/differences-between-undefined-and-null/) - 04 - [Sorting Strings with accented characters](http://www.jstips.co/en/javascript/sorting-strings-with-accented-characters/) - 03 - [Improve Nested Conditionals](http://www.jstips.co/en/javascript/improve-nested-conditionals/) - 02 - [ReactJS - Keys in children components are important](http://www.jstips.co/en/react/keys-in-children-components-are-important/) - 01 - [AngularJS: `$digest` vs `$apply`](http://www.jstips.co/en/angular/angularjs-digest-vs-apply/) - 00 - [Insert item inside an Array](http://www.jstips.co/en/javascript/insert-item-inside-an-array/) ### License [GNU GENERAL PUBLIC LICENSE](https://github.com/loverajoel/jstips/blob/master/LICENSE.md) ================================================ FILE: _posts/en/angular/2016-01-01-angularjs-digest-vs-apply.md ================================================ --- layout: post title: AngularJs - `$digest` vs `$apply` tip-number: 01 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: JavaScript modules and build steps are getting more numerous and complicated, but what about boilerplate in new frameworks? tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/angularjs-digest-vs-apply/ categories: - en - angular --- One of the most appreciated features of AngularJs is the two-way data binding. In order to make this work AngularJs evaluates the changes between the model and the view through cycles(`$digest`). You need to understand this concept in order to understand how the framework works under the hood. Angular evaluates each watcher whenever one event is fired. This is the known `$digest` cycle. Sometimes you have to force it to run a new cycle manually and you must choose the correct option because this phase is one of the most influential in terms of performance. ### `$apply` This core method lets you to start the digestion cycle explicitly. That means that all watchers are checked; the entire application starts the `$digest loop`. Internally, after executing an optional function parameter, it calls `$rootScope.$digest();`. ### `$digest` In this case the `$digest` method starts the `$digest` cycle for the current scope and its children. You should notice that the parent's scopes will not be checked. and not be affected. ### Recommendations - Use `$apply` or `$digest` only when browser DOM events have triggered outside of AngularJS. - Pass a function expression to `$apply`, this has an error handling mechanism and allows integrating changes in the digest cycle. ```javascript $scope.$apply(() => { $scope.tip = 'Javascript Tip'; }); ``` - If you only need to update the current scope or its children, use `$digest`, and prevent a new digest cycle for the whole application. The performance benefit is self-evident. - `$apply()` is a hard process for the machine and can lead to performance issues when there is a lot of binding. - If you are using >AngularJS 1.2.X, use `$evalAsync`, which is a core method that will evaluate the expression during the current cycle or the next. This can improve your application's performance ================================================ FILE: _posts/en/angular/2017-03-07-preventing-unwanted-scopes-creation-in-angularjs.md ================================================ --- layout: post title: Preventing Unwanted Scopes Creation in AngularJs tip-number: 62 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: In this tip I am going to show how to pass data between scopes preventing unwanted scopes created by `ng-repeat` and `ng-if` tip-writer-support: https://www.coinbase.com/loverajoel categories: - en - angular --- One of the most appreciated features of AngularJs is thUnderstanding and preventing ```ng-model``` data scope is one of the main challenge you get quite often. While working with ```ng-model``` data, new unwanted scope can be created by ```ng-repeat``` or ```ng-if``` procedures. Take a look on following example- ```js

innerScope:{{data}}
outerScope:{{data}}
``` In the above example, ```innerScope``` inherits from ```outerScope``` and pass the value in ```outerScope```. If you input a value in ```innerScope``` it will reflect in ```outerScope```. But if you edit ```outerScope```, ```innerScope``` doesn’t reflect the same value as ```outerScope``` because ```innerScope``` creates its own field so no longer inherits from ```outerScope```. To prevent this to happen we can use “Controller As” approach instead of using scope as a container for all data and functions. But one more catchy solution is to keep everything in objects as shown is below example- ```js

inner scope:{{data.text}}
outer scope:{{data.text}}
``` Now ```innerScope``` is no longer creates a new field and editing value in either ```innerScope``` or ```outerScope``` will reflect in both ```innerScope``` and ```outerScope```. ================================================ FILE: _posts/en/javascript/2015-12-29-insert-item-inside-an-array.md ================================================ --- layout: post title: Insert item inside an Array tip-number: 00 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Inserting an item into an existing array is a daily common task. You can add elements to the end of an array using push, to the beginning using unshift, or to the middle using splice. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/insert-item-inside-an-array/ categories: - en - javascript --- # Inserting an item into an existing array Inserting an item into an existing array is a daily common task. You can add elements to the end of an array using push, to the beginning using unshift, or to the middle using splice. Those are known methods, but it doesn't mean there isn't a more performant way. Here we go: ## Adding an element at the end Adding an element at the end of the array is easy with push(), but it can be done in different ways. ```javascript var arr = [1,2,3,4,5]; var arr2 = []; arr.push(6); arr[arr.length] = 6; arr2 = arr.concat([6]); ``` Both first methods modify the original array. Don't believe me? Check the [jsperf](http://jsperf.com/push-item-inside-an-array) ### Performance on mobile : #### Android (v4.2.2) 1. _arr.push(6);_ and _arr[arr.length] = 6;_ have the same performance // 3 319 694 ops/sec 3. _arr2 = arr.concat([6]);_ 50.61 % slower than the other two methods #### Chrome Mobile (v33.0.0) 1. _arr[arr.length] = 6;_ // 6 125 975 ops/sec 2. _arr.push(6);_ 66.74 % slower 3. _arr2 = arr.concat([6]);_ 87.63 % slower #### Safari Mobile (v9) 1. _arr[arr.length] = 6;_ // 7 452 898 ops/sec 2. _arr.push(6);_ 40.19 % slower 3. _arr2 = arr.concat([6]);_ 49.78 % slower ```javascript Final victor 1. arr[arr.length] = 6; // with an average of 5 632 856 ops/sec 2. arr.push(6); // 35.64 % slower 3. arr2 = arr.concat([6]); // 62.67 % slower ``` ### Performance on desktop #### Chrome (v48.0.2564) 1. _arr[arr.length] = 6;_ // 21 602 722 ops/sec 2. _arr.push(6);_ 61.94 % slower 3. _arr2 = arr.concat([6]);_ 87.45 % slower #### Firefox (v44) 1. _arr.push(6);_ // 56 032 805 ops/sec 2. _arr[arr.length] = 6;_ 0.52 % slower 3. _arr2 = arr.concat([6]);_ 87.36 % slower #### IE (v11) 1. _arr[arr.length] = 6;_ // 67 197 046 ops/sec 2. _arr.push(6);_ 39.61 % slower 3. _arr2 = arr.concat([6]);_ 93.41 % slower #### Opera (v35.0.2066.68) 1. _arr[arr.length] = 6;_ // 30 775 071 ops/sec 2. _arr.push(6);_ 71.60 % slower 3. _arr2 = arr.concat([6]);_ 83.70 % slower #### Safari (v9.0.3) 1. _arr.push(6);_ // 42 670 978 ops/sec 2. _arr[arr.length] = 6;_ 0.80 % slower 3. _arr2 = arr.concat([6]);_ 76.07 % slower ```javascript Final victor 1. arr[arr.length] = 6; // with an average of 42 345 449 ops/sec 2. arr.push(6); // 34.66 % slower 3. arr2 = arr.concat([6]); // 85.79 % slower ``` ## Add an element at the beginning Now if we are trying to add an item to the beginning of the array: ```javascript var arr = [1,2,3,4,5]; arr.unshift(0); [0].concat(arr); ``` Here is a little more detail: unshift edits the original array; concat returns a new array. [jsperf](http://jsperf.com/unshift-item-inside-an-array) ### Performance on mobile : #### Android (v4.2.2) 1. _[0].concat(arr);_ // 1 808 717 ops/sec 2. _arr.unshift(0);_ 97.85 % slower #### Chrome Mobile (v33.0.0) 1. _[0].concat(arr);_ // 1 269 498 ops/sec 2. _arr.unshift(0);_ 99.86 % slower #### Safari Mobile (v9) 1. _arr.unshift(0);_ // 3 250 184 ops/sec 2. _[0].concat(arr);_ 33.67 % slower ```javascript Final victor 1. [0].concat(arr); // with an average of 4 972 622 ops/sec 2. arr.unshift(0); // 64.70 % slower ``` ### Performance on desktop #### Chrome (v48.0.2564) 1. _[0].concat(arr);_ // 2 656 685 ops/sec 2. _arr.unshift(0);_ 96.77 % slower #### Firefox (v44) 1. _[0].concat(arr);_ // 8 039 759 ops/sec 2. _arr.unshift(0);_ 99.72 % slower #### IE (v11) 1. _[0].concat(arr);_ // 3 604 226 ops/sec 2. _arr.unshift(0);_ 98.31 % slower #### Opera (v35.0.2066.68) 1. _[0].concat(arr);_ // 4 102 128 ops/sec 2. _arr.unshift(0);_ 97.44 % slower #### Safari (v9.0.3) 1. _arr.unshift(0);_ // 12 356 477 ops/sec 2. _[0].concat(arr);_ 15.17 % slower ```javascript Final victor 1. [0].concat(arr); // with an average of 6 032 573 ops/sec 2. arr.unshift(0); // 78.65 % slower ``` ## Add an element in the middle Adding items in the middle of an array is easy with splice, and it's the most performant way to do it. ```javascript var items = ['one', 'two', 'three', 'four']; items.splice(items.length / 2, 0, 'hello'); ``` I tried to run these tests in various Browsers and OS and the results were similar. I hope these tips will be useful for you and encourage to perform your own tests! ================================================ FILE: _posts/en/javascript/2016-01-03-improve-nested-conditionals.md ================================================ --- layout: post title: Improve Nested Conditionals tip-number: 03 tip-username: AlbertoFuente tip-username-profile: https://github.com/AlbertoFuente tip-tldr: How can we improve and make a more efficient nested `if` statement in javascript? redirect_from: - /en/improve-nested-conditionals/ categories: - en - javascript --- How can we improve and make a more efficient nested `if` statement in javascript? ```javascript if (color) { if (color === 'black') { printBlackBackground(); } else if (color === 'red') { printRedBackground(); } else if (color === 'blue') { printBlueBackground(); } else if (color === 'green') { printGreenBackground(); } else { printYellowBackground(); } } ``` One way to improve the nested `if` statement would be using the `switch` statement. Although it is less verbose and is more ordered, it's not recommended to use it because it's so difficult to debug errors. Here's [why](https://toddmotto.com/deprecating-the-switch-statement-for-object-literals). ```javascript switch(color) { case 'black': printBlackBackground(); break; case 'red': printRedBackground(); break; case 'blue': printBlueBackground(); break; case 'green': printGreenBackground(); break; default: printYellowBackground(); } ``` But what if we have a conditional with several checks in each statement? In this case, if we want it less verbose and more ordered, we can use the conditional `switch`. If we pass `true` as a parameter to the `switch` statement, it allows us to put a conditional in each case. ```javascript switch(true) { case (typeof color === 'string' && color === 'black'): printBlackBackground(); break; case (typeof color === 'string' && color === 'red'): printRedBackground(); break; case (typeof color === 'string' && color === 'blue'): printBlueBackground(); break; case (typeof color === 'string' && color === 'green'): printGreenBackground(); break; case (typeof color === 'string' && color === 'yellow'): printYellowBackground(); break; } ``` If refactoring is an option, we can try to simplify the functions themselves. For example instead of having a function for each background color we could have an function that takes the color as an argument. ```javascript function printBackground(color) { if (!color || typeof color !== 'string') { return; // Invalid color, return immediately } } ``` But if refactoring is not an option, we must always avoid having several checks in every condition and avoid using `switch` as much as possible. We also must take into account that the most efficient way to do this is through an `object`. ```javascript var colorObj = { 'black': printBlackBackground, 'red': printRedBackground, 'blue': printBlueBackground, 'green': printGreenBackground, 'yellow': printYellowBackground }; if (color in colorObj) { colorObj[color](); } ``` Here you can find more information about [this](http://www.nicoespeon.com/en/2015/01/oop-revisited-switch-in-js/). ================================================ FILE: _posts/en/javascript/2016-01-04-sorting-strings-with-accented-characters.md ================================================ --- layout: post title: Sorting strings with accented characters tip-number: 04 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Javascript has a native method **sort** that allows sorting arrays. Doing a simple `array.sort()` will treat each array entry as a string and sort it alphabetically. But when you try order an array of non ASCII characters you will obtain a strange result. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/sorting-strings-with-accented-characters/ categories: - en - javascript --- Javascript has a native method **[sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)** that allows sorting arrays. Doing a simple `array.sort()` will treat each array entry as a string and sort it alphabetically. Also you can provide your [own custom sorting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters) function. ```javascript ['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); // ["Buenos Aires", "Mumbai", "New York", "Shanghai"] ``` But when you try order an array of non ASCII characters like this `['é', 'a', 'ú', 'c']`, you will obtain a strange result `['c', 'e', 'á', 'ú']`. That happens because sort works only with the English language. See the next example: ```javascript // Spanish ['único','árbol', 'cosas', 'fútbol'].sort(); // ["cosas", "fútbol", "árbol", "único"] // bad order // German ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); // ["Wann", "Woche", "wäre", "wöchentlich"] // bad order ``` Fortunately, there are two ways to overcome this behavior [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare) and [Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) provided by ECMAScript Internationalization API. > Both methods have their own custom parameters in order to configure it to work adequately. ### Using `localeCompare()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { return a.localeCompare(b); }); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { return a.localeCompare(b); }); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` ### Using `Intl.Collator()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` - For each method you can customize the location. - According to [Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Performance) Intl.Collator is faster when comparing large numbers of strings. So when you are working with arrays of strings in a language other than English, remember to use this method to avoid unexpected sorting. ================================================ FILE: _posts/en/javascript/2016-01-05-differences-between-undefined-and-null.md ================================================ --- layout: post title: Differences between `undefined` and `null` tip-number: 05 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Understanding the differences between `undefined` and `null`. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/differences-between-undefined-and-null/ categories: - en - javascript --- - `undefined` means a variable has not been declared, or has been declared but has not yet been assigned a value - `null` is an assignment value that means "no value" - Javascript sets unassigned variables with a default value of `undefined` - Javascript never sets a value to `null`. It is used by programmers to indicate that a `var` has no value. - `undefined` is not valid in JSON while `null` is - `undefined` typeof is `undefined` - `null` typeof is an `object`. [Why?](http://www.2ality.com/2013/10/typeof-null.html) - Both are primitives - Both are [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) (`Boolean(undefined) // false`, `Boolean(null) // false`) - You can know if a variable is [undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined) ```javascript typeof variable === "undefined" ``` - You can check if a variable is [null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null) ```javascript variable === null ``` - The **equality** operator considers them equal, but the **identity** doesn't ```javascript null == undefined // true null === undefined // false ``` ================================================ FILE: _posts/en/javascript/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md ================================================ --- layout: post title: Writing a single method for arrays and a single element tip-number: 06 tip-username: mattfxyz tip-username-profile: https://twitter.com/mattfxyz tip-tldr: Rather than writing separate methods to handle an array and a single element parameter, write your functions so they can handle both. This is similar to how some of jQuery's functions work (`css` will modify everything matched by the selector). redirect_from: - /en/writing-a-single-method-for-arrays-and-a-single-element/ categories: - en - javascript --- Rather than writing separate methods to handle an array and a single element parameter, write your functions so they can handle both. This is similar to how some of jQuery's functions work (`css` will modify everything matched by the selector). You just have to concat everything into an array first. `Array.concat` will accept an array or a single element. ```javascript function printUpperCase(words) { var elements = [].concat(words || []); for (var i = 0; i < elements.length; i++) { console.log(elements[i].toUpperCase()); } } ``` `printUpperCase` is now ready to accept a single node or an array of nodes as its parameter. It also avoids the potential `TypeError` that would be thrown if no parameter was passed. ```javascript printUpperCase("cactus"); // => CACTUS printUpperCase(["cactus", "bear", "potato"]); // => CACTUS // BEAR // POTATO ``` ================================================ FILE: _posts/en/javascript/2016-01-07-use-strict-and-get-lazy.md ================================================ --- layout: post title: use strict and get lazy tip-number: 07 tip-username: nainslie tip-username-profile: https://twitter.com/nat5an tip-tldr: Strict-mode JavaScript makes it easier for the developer to write "secure" JavaScript. redirect_from: - /en/use-strict-and-get-lazy/ categories: - en - javascript --- Strict-mode JavaScript makes it easier for the developer to write "secure" JavaScript. By default, JavaScript allows the programmer to be pretty careless, for example, by not requiring us to declare our variables with "var" when we first introduce them. While this may seem like a convenience to the unseasoned developer, it's also the source of many errors when a variable name is misspelled or accidentally referred to out of its scope. Programmers like to make the computer do the boring stuff for us, and automatically check our work for mistakes. That's what the JavaScript "use strict" directive allows us to do, by turning our mistakes into JavaScript errors. We add this directive either by adding it at the top of a js file: ```javascript // Whole-script strict mode syntax "use strict"; var v = "Hi! I'm a strict mode script!"; ``` or inside a function: ```javascript function f() { // Function-level strict mode syntax 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function f2() { return "I'm not strict."; } ``` By including this directive in a JavaScript file or function, we will direct the JavaScript engine to execute in strict mode which disables a bunch of behaviors that are usually undesirable in larger JavaScript projects. Among other things, strict mode changes the following behaviors: * Variables can only be introduced when they are preceded with "var" * Attempting to write to read-only properties generates a noisy error * You have to call constructors with the "new" keyword * "this" is not implicitly bound to the global object * Very limited use of eval() allowed * Protects you from using reserved words or future reserved words as variable names Strict mode is great for new projects, but can be challenging to introduce into older projects that don't already use it in most places. It also can be problematic if your build chain concatenates all your js files into one big file, as this may cause all files to execute in strict mode. It is not a statement, but a literal expression, ignored by earlier versions of JavaScript. Strict mode is supported in: * Internet Explorer from version 10. * Firefox from version 4. * Chrome from version 13. * Safari from version 5.1. * Opera from version 12. [See MDN for a fuller description of strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode). ================================================ FILE: _posts/en/javascript/2016-01-08-converting-a-node-list-to-an-array.md ================================================ --- layout: post title: Converting a Node List to an Array tip-number: 08 tip-username: Tevko tip-username-profile: https://twitter.com/tevko tip-tldr: Here's a quick, safe, and reusable way to convert a node list into an array of DOM elements. redirect_from: - /en/converting-a-node-list-to-an-array/ categories: - en - javascript --- The `querySelectorAll` method returns an array-like object called a node list. These data structures are referred to as "Array-like", because they appear as an array, but can not be used with array methods like `map` and `forEach`. Here's a quick, safe, and reusable way to convert a node list into an array of DOM elements: ```javascript const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.apply(null, nodelist); //later on .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); //etc... ``` The `apply` method is used to pass an array of arguments to a function with a given `this` value. [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) states that `apply` will take an array-like object, which is exactly what `querySelectorAll` returns. Since we don't need to specify a value for `this` in the context of the function, we pass in `null` or `0`. The result is an actual array of DOM elements which contains all of the available array methods. Alternatively you can use `Array.prototype.slice` combined with `Function.prototype.call` or `Function.prototype.apply` passing the array-like object as the value of `this`: ```javascript const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.prototype.slice.call(nodelist); // or equivalently Array.prototype.slice.apply(nodelist); //later on .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); //etc... ``` Or if you are using ES2015 you can use the [spread operator `...`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) ```js const nodelist = [...document.querySelectorAll('div')]; // returns a real array //later on .. nodelist.forEach(...); nodelist.map(...); nodelist.slice(...); //etc... ``` ================================================ FILE: _posts/en/javascript/2016-01-09-template-strings.md ================================================ --- layout: post title: Template Strings tip-number: 09 tip-username: JakeRawr tip-username-profile: https://github.com/JakeRawr tip-tldr: As of ES6, JS now has template strings as an alternative to the classic end quotes strings. redirect_from: - /en/template-strings/ categories: - en - javascript --- As of ES6, JS now has template strings as an alternative to the classic end quotes strings. Ex: Normal string ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log('My name is ' + firstName + ' ' + lastName); // My name is Jake Rawr ``` Template String ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log(`My name is ${firstName} ${lastName}`); // My name is Jake Rawr ``` You can do multi-line strings without `\n`, perform simple logic (ie 2+3) or even use the [ternary operator](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) inside `${}` in template strings. ```javascript var val1 = 1, val2 = 2; console.log(`${val1} is ${val1 < val2 ? 'less than': 'greater than'} ${val2}`) // 1 is less than 2 ``` You are also able to modify the output of template strings using a function; they are called [tagged template strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings#Tagged_template_strings) for example usages of tagged template strings. You may also want to [read](https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2) to understand template strings more. ================================================ FILE: _posts/en/javascript/2016-01-10-check-if-a-property-is-in-a-object.md ================================================ --- layout: post title: Check if a property is in a Object tip-number: 10 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: These are ways to check if a property is present in an object. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/check-if-a-property-is-in-a-object/ categories: - en - javascript --- When you have to check if a property is present in an [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects), you probably are doing something like this: ```javascript var myObject = { name: '@tips_js' }; if (myObject.name) { ... } ``` That's ok, but you have to know that there are two native ways for this kind of thing, the [`in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) and [`Object.hasOwnProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty). Every object descended from `Object`, has both ways available. ### See the big Difference ```javascript var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true 'name' in myObject; // true myObject.hasOwnProperty('valueOf'); // false, valueOf is inherited from the prototype chain 'valueOf' in myObject; // true ``` Both differ in the depth at which they check the properties. In other words, `hasOwnProperty` will only return true if key is available on that object directly. However, the `in` operator doesn't discriminate between properties created on an object and properties inherited from the prototype chain. Here's another example: ```javascript var myFunc = function() { this.name = '@tips_js'; }; myFunc.prototype.age = '10 days'; var user = new myFunc(); user.hasOwnProperty('name'); // true user.hasOwnProperty('age'); // false, because age is from the prototype chain ``` Check the [live examples here](https://jsbin.com/tecoqa/edit?js,console)! I also recommend reading [this discussion](https://github.com/loverajoel/jstips/issues/62) about common mistakes made when checking a property's existence in objects. ================================================ FILE: _posts/en/javascript/2016-01-11-hoisting.md ================================================ --- layout: post title: Hoisting tip-number: 11 tip-username: squizzleflip tip-username-profile: https://twitter.com/squizzleflip tip-tldr: Understanding hoisting will help you organize your function scope. redirect_from: - /en/hoisting/ categories: - en - javascript --- Understanding [hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) will help you organize your function scope. Just remember, variable declarations and function definitions are hoisted to the top. Variable definitions are not, even if you declare and define a variable on the same line. Also, a variable **declaration** lets the system know that the variable exists while **definition** assigns it a value. ```javascript function doTheThing() { // ReferenceError: notDeclared is not defined console.log(notDeclared); // Outputs: undefined console.log(definedLater); var definedLater; definedLater = 'I am defined!' // Outputs: 'I am defined!' console.log(definedLater) // Outputs: undefined console.log(definedSimulateneously); var definedSimulateneously = 'I am defined!' // Outputs: 'I am defined!' console.log(definedSimulateneously) // Outputs: 'I did it!' doSomethingElse(); function doSomethingElse(){ console.log('I did it!'); } // TypeError: undefined is not a function functionVar(); var functionVar = function(){ console.log('I did it!'); } } ``` To make things easier to read, declare all of your variables at the top of your function scope so it is clear which scope the variables are coming from. Define your variables before you need to use them. Define your functions at the bottom of your scope to keep them out of your way. ================================================ FILE: _posts/en/javascript/2016-01-12-pseudomandatory-parameters-in-es6-functions.md ================================================ --- layout: post title: Pseudomandatory parameters in ES6 functions tip-number: 12 tip-username: Avraam Mavridis tip-username-profile: https://github.com/AvraamMavridis tip-tldr: In many programming languages the parameters of a function are by default mandatory and the developer has to explicitly define that a parameter is optional. redirect_from: - /en/pseudomandatory-parameters-in-es6-functions/ categories: - en - javascript --- In many programming languages the parameters of a function are by default mandatory and the developer has to explicitly define that a parameter is optional. In Javascript, every parameter is optional, but we can enforce this behavior without messing with the actual body of a function, taking advantage of [**es6's default values for parameters**] (http://exploringjs.com/es6/ch_parameter-handling.html#sec_parameter-default-values) feature. ```javascript const _err = function( message ){ throw new Error( message ); } const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b getSum( 10 ) // throws Error, b is not defined getSum( undefined, 10 ) // throws Error, a is not defined ``` `_err` is a function that immediately throws an Error. If no value is passed for one of the parameters, the default value is going to be used, `_err` will be called and an Error will be thrown. You can see more examples for the **default parameters feature** on [Mozilla's Developer Network ](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/default_parameters) ================================================ FILE: _posts/en/javascript/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md ================================================ --- layout: post title: Tip to measure performance of a javascript block tip-number: 13 tip-username: manmadareddy tip-username-profile: https://twitter.com/manmadareddy tip-tldr: For quickly measuring performance of a javascript block, we can use the console functions like `console.time(label)` and `console.timeEnd(label)` redirect_from: - /en/tip-to-measure-performance-of-a-javascript-block/ categories: - en - javascript --- For quickly measuring performance of a javascript block, we can use the console functions like [`console.time(label)`](https://developer.chrome.com/devtools/docs/console-api#consoletimelabel) and [`console.timeEnd(label)`](https://developer.chrome.com/devtools/docs/console-api#consoletimeendlabel) ```javascript console.time("Array initialize"); var arr = new Array(100), len = arr.length, i; for (i = 0; i < len; i++) { arr[i] = new Object(); }; console.timeEnd("Array initialize"); // Outputs: Array initialize: 0.711ms ``` More info: [Console object](https://github.com/DeveloperToolsWG/console-object), [Javascript benchmarking](https://mathiasbynens.be/notes/javascript-benchmarking) Demo: [jsfiddle](https://jsfiddle.net/meottb62/) - [codepen](http://codepen.io/anon/pen/JGJPoa) (outputs in browser console) > Note: As [Mozilla](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) suggested don't use this for production sites, use it for development purposes only. ================================================ FILE: _posts/en/javascript/2016-01-14-fat-arrow-functions.md ================================================ --- layout: post title: Fat Arrow Functions tip-number: 14 tip-username: pklinger tip-username-profile: https://github.com/pklinger/ tip-tldr: Introduced as a new feature in ES6, fat arrow functions may come as a handy tool to write more code in fewer lines redirect_from: - /en/fat-arrow-functions/ categories: - en - javascript --- Introduced as a new feature in ES6, fat arrow functions may come as a handy tool to write more code in fewer lines. The name comes from its syntax, `=>`, which is a 'fat arrow', as compared to a thin arrow `->`. Some programmers might already know this type of function from different languages such as Haskell, as 'lambda expressions', or as 'anonymous functions'. It is called anonymous, as these arrow functions do not have a descriptive function name. ### What are the benefits? * Syntax: fewer LOC; no more typing `function` keyword over and over again * Semantics: capturing the keyword `this` from the surrounding context ### Simple syntax example Have a look at these two code snippets, which do the exact same job, and you will quickly understand what fat arrow functions do: ```javascript // general syntax for fat arrow functions param => expression // may also be written with parentheses // parentheses are required on multiple params (param1 [, param2]) => expression // using functions var arr = [5,3,2,9,1]; var arrFunc = arr.map(function(x) { return x * x; }); console.log(arr) // using fat arrow var arr = [5,3,2,9,1]; var arrFunc = arr.map((x) => x*x); console.log(arr) ``` As you can see, the fat arrow function in this case can save you time typing out the parentheses as well as the function and return keywords. I would advise you to always write parentheses around the parameter inputs, as the parentheses will be needed for multiple input parameters, such as in `(x,y) => x+y`. It is just a way to cope with forgetting them in different use cases. But the code above would also work like this: `x => x*x`. So far, these are only syntactical improvements, which lead to fewer LOC and better readability. ### Lexically binding `this` There is another good reason to use fat arrow functions. There is the issue with the context of `this`. With arrow functions, you don't need to worry about `.bind(this)` or setting `that = this` anymore, as fat arrow functions pick the context of `this` from the lexical surrounding. Have a look at the next [example] (https://jsfiddle.net/pklinger/rw94oc11/): ```javascript // globally defined this.i this.i = 100; var counterA = new CounterA(); var counterB = new CounterB(); var counterC = new CounterC(); var counterD = new CounterD(); // bad example function CounterA() { // CounterA's `this` instance (!! gets ignored here) this.i = 0; setInterval(function () { // `this` refers to global object, not to CounterA's `this` // therefore starts counting with 100, not with 0 (local this.i) this.i++; document.getElementById("counterA").innerHTML = this.i; }, 500); } // manually binding that = this function CounterB() { this.i = 0; var that = this; setInterval(function() { that.i++; document.getElementById("counterB").innerHTML = that.i; }, 500); } // using .bind(this) function CounterC() { this.i = 0; setInterval(function() { this.i++; document.getElementById("counterC").innerHTML = this.i; }.bind(this), 500); } // fat arrow function function CounterD() { this.i = 0; setInterval(() => { this.i++; document.getElementById("counterD").innerHTML = this.i; }, 500); } ``` Further information about fat arrow functions may be found at [MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). To see different syntax options visit [this site] (http://jsrocks.org/2014/10/arrow-functions-and-their-scope/). ================================================ FILE: _posts/en/javascript/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md ================================================ --- layout: post title: Even simpler way of using `indexOf` as a contains clause tip-number: 15 tip-username: jhogoforbroke tip-username-profile: https://twitter.com/jhogoforbroke tip-tldr: JavaScript by default does not have a contains method. And for checking existence of a substring in a string or an item in an array you may do this. redirect_from: - /en/even-simpler-way-of-using-indexof-as-a-contains-clause/ categories: - en - javascript --- JavaScript by default does not have a contains method. And for checking existence of a substring in a string or an item in an array you may do this: ```javascript var someText = 'javascript rules'; if (someText.indexOf('javascript') !== -1) { } // or if (someText.indexOf('javascript') >= 0) { } ``` But let's look at these [Expressjs](https://github.com/strongloop/express) code snippets. [examples/mvc/lib/boot.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/mvc/lib/boot.js#L26) ```javascript for (var key in obj) { // "reserved" exports if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; ``` [lib/utils.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/lib/utils.js#L93) ```javascript exports.normalizeType = function(type){ return ~type.indexOf('/') ? acceptParams(type) : { value: mime.lookup(type), params: {} }; }; ``` [examples/web-service/index.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/web-service/index.js#L35) ```javascript // key is invalid if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key')); ``` The gotcha is the [bitwise operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) **~**, "Bitwise operators perform their operations on binary representations, but they return standard JavaScript numerical values." It transforms `-1` into `0`, and `0` evaluates to `false` in JavaScript: ```javascript var someText = 'text'; !!~someText.indexOf('tex'); // someText contains "tex" - true !~someText.indexOf('tex'); // someText NOT contains "tex" - false ~someText.indexOf('asd'); // someText doesn't contain "asd" - false ~someText.indexOf('ext'); // someText contains "ext" - true ``` ### String.prototype.includes() ES6 introduced the [includes() method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) and you can use it to determine whether or not a string includes another string: ```javascript 'something'.includes('thing'); // true ``` With ECMAScript 2016 (ES7) it is even possible to use these techniques with Arrays: ```javascript !!~[1, 2, 3].indexOf(1); // true [1, 2, 3].includes(1); // true ``` **Unfortunately, it is only supported in Chrome, Firefox, Safari 9 or above and Edge; not IE11 or lower.** **It's better used in controlled environments.** ================================================ FILE: _posts/en/javascript/2016-01-16-passing-arguments-to-callback-functions.md ================================================ --- layout: post title: Passing arguments to callback functions tip-number: 16 tip-username: minhazav tip-username-profile: https://twitter.com/minhazav tip-tldr: By default you cannot pass arguments to a callback function, but you can take advantage of the closure scope in Javascript to pass arguments to callback functions. redirect_from: - /en/passing-arguments-to-callback-functions/ categories: - en - javascript --- By default you cannot pass arguments to a callback function. For example: ```js function callback() { console.log('Hi human'); } document.getElementById('someelem').addEventListener('click', callback); ``` You can take advantage of the closure scope in Javascript to pass arguments to callback functions. Check this example: ```js function callback(a, b) { return function() { console.log('sum = ', (a+b)); } } var x = 1, y = 2; document.getElementById('someelem').addEventListener('click', callback(x, y)); ``` ### What are closures? Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created. [Check MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) to learn more. So this way the arguments `x` and `y` are in scope of the callback function when it is called. Another method to do this is using the `bind` method. For example: ```js var alertText = function(text) { alert(text); }; document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello')); ``` There is a very slight difference in performance of both methods, checkout [jsperf](http://jsperf.com/bind-vs-closure-23). ================================================ FILE: _posts/en/javascript/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md ================================================ --- layout: post title: Node.js - Run a module if it is not `required` tip-number: 17 tip-username: odsdq tip-username-profile: https://twitter.com/odsdq tip-tldr: In node, you can tell your program to do two different things depending on whether the code is run from `require('./something.js')` or `node something.js`. This is useful if you want to interact with one of your modules independently. redirect_from: - /en/nodejs-run-a-module-if-it-is-not-required/ categories: - en - javascript --- In node, you can tell your program to do two different things depending on whether the code is run from `require('./something.js')` or `node something.js`. This is useful if you want to interact with one of your modules independently. ```js if (!module.parent) { // ran with `node something.js` app.listen(8088, function() { console.log('app listening on port 8088'); }) } else { // used with `require('/.something.js')` module.exports = app; } ``` See [the documentation for modules](https://nodejs.org/api/modules.html#modules_module_parent) for more info. ================================================ FILE: _posts/en/javascript/2016-01-18-rounding-the-fast-way.md ================================================ --- layout: post title: Truncating the fast (but risky) way tip-number: 18 tip-username: pklinger tip-username-profile: https://github.com/pklinger tip-tldr: .`~~X` is usually a faster `Math.trunc(X)`, but can also make your code do nasty things. redirect_from: - /en/rounding-the-fast-way/ categories: - en - javascript --- This tip is about performance...with a hidden price tag. Have you ever come across the [double tilde `~~` operator](http://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript)? It's also often called the "double bitwise NOT" operator. You can often use it as a faster substitute for `Math.trunc()`. Why is that? One bitwise shift `~` first truncates `input` to 32 bits, then transforms it into `-(input+1)`. The double bitwise shift therefore transforms the input into `-(-(input + 1)+1)` making it a great tool to round towards zero. For numeric input, it therefore mimics `Math.trunc()`. On failure, `0` is returned, which might come in handy sometimes instead of `Math.trunc()`, which returns `NaN` on failure. ```js // single ~ console.log(~1337) // -1338 // numeric input console.log(~~47.11) // -> 47 console.log(~~1.9999) // -> 1 console.log(~~3) // -> 3 ``` However, while `~~` is probably a better performer, experienced programmers often stick with `Math.trunc()` instead. To understand why, here's a clinical view on this operator. ### INDICATIONS ##### When every CPU cycle counts `~~` is probably faster than `Math.trunc()` across the board, though you should [test that assumption](https://jsperf.com/jsfvsbitnot/10) on whichever platforms matter to you. Also, you'd generally have to perform millions of such operations to have any visible impact at run time. ##### When code clarity is not a concern If you're trying to confuse others, or get maximum utility from your minifier/uglifier, this is a relatively cheap way to do it. ### CONTRAINDICATIONS ##### When your code needs to be maintained Code clarity is of great importance in the long term, whether you work in a team, contribute to public code repos, or fly solo. As [the oft-quoted saying](http://c2.com/cgi/wiki?CodeForTheMaintainer) goes: > Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. For a solo programmer, that psychopath is inevitably "you in six months". ##### When you forget that `~~` always rounds to zero Newbie programmers may fixate on the cleverness of `~~`, forgetting the significance of "just drop the fractional portion of this number". This can easily lead to **fencepost errors** (a.k.a. "off-by-one") when transforming floats to array indices or related ordinal values, where a different kind of fractional rounding may actually be called for. (Lack of code clarity usually contributes to this problem.) For instance, if you're counting numbers on a "nearest integer" basis, you should use `Math.round()` instead of `~~`, but programmer laziness and the impact of **_10 whole characters saved per use_** on human fingers often triumph over cold logic, leading to incorrect results. In contrast, the very names of the `Math.xyz()` functions clearly communicate their effect, reducing the probability of accidental errors. ##### When dealing with large-magnitude numbers Because `~` first does a 32-bit conversion, `~~` results in bogus values around ±2.15 billion. If you don't properly range-check your input, a user could trigger unexpected behavior when the transformed value ends up being a great distance from the original: ```js a = 2147483647.123 // maximum positive 32-bit integer, plus a bit more console.log(~~a) // -> 2147483647 (ok) a += 10000 // -> 2147493647.123 (ok) console.log(~~a) // -> -2147483648 (huh?) ``` One particularly vulnerable area involves dealing with Unix epoch timestamps (measured in seconds from 1 Jan 1970 00:00:00 UTC). A quick way to get such values is: ```js epoch_int = ~~(+new Date() / 1000) // Date() epochs in milliseconds, so we scale accordingly ``` However, when dealing with timestamps after 19 Jan 2038 03:14:07 UTC (sometimes called the **Y2038 limit**), this breaks horribly: ```js // epoch timestamp for 1 Jan 2040 00:00:00.123 UTC epoch = +new Date('2040-01-01') / 1000 + 0.123 // -> 2208988800.123 // back to the future! epoch_int = ~~epoch // -> -2085978496 console.log(new Date(epoch_int * 1000)) // -> Wed Nov 25 1903 17:31:44 UTC // that was fun, now let's get real epoch_flr = Math.floor(epoch) // -> 2208988800 console.log(new Date(epoch_flr * 1000)) // -> Sun Jan 01 2040 00:00:00 UTC ``` ##### When the original input wasn't sanitized Because `~~` transforms every non-number into `0`: ```js console.log(~~[]) // -> 0 console.log(~~NaN) // -> 0 console.log(~~null) // -> 0 ``` some programmers treat it as alternative to proper input validation. However, this can lead to strange logic bugs down the line, since you're no longer distinguishing between invalid inputs and actual `0` values. This is therefore _not_ a recommended practice. ##### When so many people think `~~X == Math.floor(X)` Most people who write about "double bitwise NOT" incorrectly equate it with `Math.floor()` for some reason. If you can't write about it accurately, odds are good you'll eventually misuse it. Others are more careful to mention `Math.floor()` for positive inputs and `Math.ceil()` for negative ones, but that forces you to stop and think about the values you're dealing with. This defeats the purpose of `~~` as a handy no-gotchas shortcut. ### DOSAGE Avoid where possible. Use sparingly otherwise. ### ADMINISTRATION 1. Apply cautiously. 2. Sanitize values before applying. 3. Carefully document relevant assumptions about the values being transformed. 4. Review code to deal with, at minimum: * logic bugs where invalid inputs are instead passed to other code modules as valid `0` values * range errors on transformed inputs * fencepost errors due to incorrect rounding direction ================================================ FILE: _posts/en/javascript/2016-01-19-safe-string-concatenation.md ================================================ --- layout: post title: Safe string concatenation tip-number: 19 tip-username: gogainda tip-username-profile: https://twitter.com/gogainda tip-tldr: Suppose you have a couple of variables with unknown types and you want to concatenate them in a string. To be sure that the arithmetical operation is not be applied during concatenation, use concat redirect_from: - /en/safe-string-concatenation/ categories: - en - javascript --- Suppose you have a couple of variables with unknown types and you want to concatenate them in a string. To be sure that the arithmetical operation is not be applied during concatenation, use `concat`: ```javascript var one = 1; var two = 2; var three = '3'; var result = ''.concat(one, two, three); //"123" ``` This way of concatenting does exactly what you'd expect. In contrast, concatenation with pluses might lead to unexpected results: ```javascript var one = 1; var two = 2; var three = '3'; var result = one + two + three; //"33" instead of "123" ``` Speaking about performance, compared to the `join` [type](http://www.sitepoint.com/javascript-fast-string-concatenation/) of concatenation, the speed of `concat` is pretty much the same. You can read more about the `concat` function on MDN [page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat). ================================================ FILE: _posts/en/javascript/2016-01-20-return-objects-to-enable-chaining-of-functions.md ================================================ --- layout: post title: Return objects to enable chaining of functions tip-number: 20 tip-username: WakeskaterX tip-username-profile: https://twitter.com/WakeStudio tip-tldr: When creating functions on an object in Object Oriented Javascript, returning the object in the function will enable you to chain functions together. redirect_from: - /en/return-objects-to-enable-chaining-of-functions/ categories: - en - javascript --- When creating functions on an object in Object Oriented Javascript, returning the object in the function will enable you to chain functions together. ```js function Person(name) { this.name = name; this.sayName = function() { console.log("Hello my name is: ", this.name); return this; }; this.changeName = function(name) { this.name = name; return this; }; } var person = new Person("John"); person.sayName().changeName("Timmy").sayName(); ``` ================================================ FILE: _posts/en/javascript/2016-01-21-shuffle-an-array.md ================================================ --- layout: post title: Shuffle an Array tip-number: 21 tip-username: 0xmtn tip-username-profile: https://github.com/0xmtn/ tip-tldr: Fisher-Yates Shuffling it's an algorithm to shuffle an array. redirect_from: - /en/shuffle-an-array/ categories: - en - javascript --- This snippet here uses [Fisher-Yates Shuffling](https://www.wikiwand.com/en/Fisher%E2%80%93Yates_shuffle) Algorithm to shuffle a given array. ```javascript function shuffle(arr) { var i, j, temp; for (i = arr.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; }; ``` An example: ```javascript var a = [1, 2, 3, 4, 5, 6, 7, 8]; var b = shuffle(a); console.log(b); // [2, 7, 8, 6, 5, 3, 1, 4] ``` ================================================ FILE: _posts/en/javascript/2016-01-22-two-ways-to-empty-an-array.md ================================================ --- layout: post title: Two ways to empty an array tip-number: 22 tip-username: microlv tip-username-profile: https://github.com/microlv tip-tldr: In JavaScript when you want to empty an array, there are a lot ways, but this is the most performant. redirect_from: - /en/two-ways-to-empty-an-array/ categories: - en - javascript --- You define an array and want to empty its contents. Usually, you would do it like this: ```javascript // define Array var list = [1, 2, 3, 4]; function empty() { //empty your array list = []; } empty(); ``` But there is another way to empty an array that is more performant. You should use code like this: ```javascript var list = [1, 2, 3, 4]; function empty() { //empty your array list.length = 0; } empty(); ``` * `list = []` assigns a reference to a new array to a variable, while any other references are unaffected. which means that references to the contents of the previous array are still kept in memory, leading to memory leaks. * `list.length = 0` deletes everything in the array, which does hit other references. In other words, if you have two references to the same array (`a = [1,2,3]; a2 = a;`), and you delete the array's contents using `list.length = 0`, both references (a and a2) will now point to the same empty array. (So don't use this technique if you don't want a2 to hold an empty array!) Think about what this will output: ```js var foo = [1,2,3]; var bar = [1,2,3]; var foo2 = foo; var bar2 = bar; foo = []; bar.length = 0; console.log(foo, bar, foo2, bar2); // [] [] [1, 2, 3] [] ``` Stackoverflow more detail: [difference-between-array-length-0-and-array](http://stackoverflow.com/questions/4804235/difference-between-array-length-0-and-array) ================================================ FILE: _posts/en/javascript/2016-01-23-converting-to-number-fast-way.md ================================================ --- layout: post title: Converting to number fast way tip-number: 23 tip-username: sonnyt tip-username-profile: http://twitter.com/sonnyt tip-tldr: Converting strings to numbers is extremely common. The easiest and fastest way to achieve that would be using the + operator. redirect_from: - /en/converting-to-number-fast-way/ categories: - en - javascript --- Converting strings to numbers is extremely common. The easiest and fastest ([jsPerf](https://jsperf.com/number-vs-parseint-vs-plus/29)) way to achieve that would be using the `+` (plus) operator. ```javascript var one = '1'; var numberOne = +one; // Number 1 ``` You can also use the `-` (minus) operator which type-converts the value into number but also negates it. ```javascript var one = '1'; var negativeNumberOne = -one; // Number -1 ``` ================================================ FILE: _posts/en/javascript/2016-01-24-use_===_instead_of_==.md ================================================ --- layout: post title: Use === instead of == tip-number: 24 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: The `==` (or `!=`) operator performs an automatic type conversion if needed. The `===` (or `!==`) operator will not perform any conversion. It compares the value and the type, which could be considered faster ([jsPref](http://jsperf.com/strictcompare)) than `==`. redirect_from: - /en/use_===_instead_of_==/ categories: - en - javascript --- The `==` (or `!=`) operator performs an automatic type conversion if needed. The `===` (or `!==`) operator will not perform any conversion. It compares the value and the type, which could be considered faster ([jsPref](http://jsperf.com/strictcompare)) than `==`. ```js [10] == 10 // is true [10] === 10 // is false '10' == 10 // is true '10' === 10 // is false [] == 0 // is true [] === 0 // is false '' == false // is true but true == "a" is false '' === false // is false ``` ================================================ FILE: _posts/en/javascript/2016-01-25-Using-immediately-invoked-function-expression.md ================================================ --- layout: post title: Using immediately invoked function expression tip-number: 25 tip-username: rishantagarwal tip-username-profile: https://github.com/rishantagarwal tip-tldr: Called as "Iffy" ( IIFE - immediately invoked function expression) is an anonymous function expression that is immediately invoked and has some important uses in Javascript. redirect_from: - /en/Using-immediately-invoked-function-expression/ categories: - en - javascript --- Called as "Iffy" ( IIFE - immediately invoked function expression) is an anonymous function expression that is immediately invoked and has some important uses in Javascript. ```javascript (function() { // Do something​ } )() ``` It is an anonymous function expression that is immediately invoked, and it has some particularly important uses in JavaScript. The pair of parenthesis surrounding the anonymous function turns the anonymous function into a function expression or variable expression. So instead of a simple anonymous function in the global scope, or wherever it was defined, we now have an unnamed function expression. Similarly, we can even create a named, immediately invoked function expression: ```javascript (someNamedFunction = function(msg) { console.log(msg || "Nothing for today !!") }) (); // Output --> Nothing for today !!​ ​ someNamedFunction("Javascript rocks !!"); // Output --> Javascript rocks !! someNamedFunction(); // Output --> Nothing for today !!​ ``` For more details, check the following URL's - 1. [Link 1](https://blog.mariusschulz.com/2016/01/13/disassembling-javascripts-iife-syntax) 2. [Link 2](http://javascriptissexy.com/12-simple-yet-powerful-javascript-tips/) Performance: [jsPerf](http://jsperf.com/iife-with-call) ================================================ FILE: _posts/en/javascript/2016-01-26-filtering-and-sorting-a-list-of-strings.md ================================================ --- layout: post title: Filtering and Sorting a List of Strings tip-number: 26 tip-username: davegomez tip-username-profile: https://github.com/davegomez tip-tldr: You may have a big list of names you need to filter in order to remove duplicates and sort them alphabetically. credirect_from: - /en/filtering-and-sorting-a-list-of-strings/ categories: - en - javascript --- You may have a big list of names you need to filter in order to remove duplicates and sort them alphabetically. In our example we are going to use the list of **JavaScript reserved keywords** we can find across the different versions of the language, but as you can notice, there is a lot of duplicated keywords and they are not alphabetically organized. So this is a perfect list ([Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)) of strings to test out this JavaScript tip. ```js var keywords = ['do', 'if', 'in', 'for', 'new', 'try', 'var', 'case', 'else', 'enum', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'delete', 'export', 'import', 'return', 'switch', 'typeof', 'default', 'extends', 'finally', 'continue', 'debugger', 'function', 'do', 'if', 'in', 'for', 'int', 'new', 'try', 'var', 'byte', 'case', 'char', 'else', 'enum', 'goto', 'long', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'final', 'float', 'short', 'super', 'throw', 'while', 'delete', 'double', 'export', 'import', 'native', 'public', 'return', 'static', 'switch', 'throws', 'typeof', 'boolean', 'default', 'extends', 'finally', 'package', 'private', 'abstract', 'continue', 'debugger', 'function', 'volatile', 'interface', 'protected', 'transient', 'implements', 'instanceof', 'synchronized', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof']; ``` Since we don't want to change our original list, we are going to use a high order function named [`filter`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), which will return a new filter array based in a predicate (*function*) we pass to it. The predicate will compare the index of the current keyword in the original list with its `index` in the new list and will push it to the new array only if the indexes match. Finally we are going to sort the filtered list using the [`sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) function which takes a comparison function as the only argument, returning a alphabetically sorted list. ```js var filteredAndSortedKeywords = keywords .filter(function (keyword, index) { return keywords.lastIndexOf(keyword) === index; }) .sort(function (a, b) { return a < b ? -1 : 1; }); ``` The **ES6** (ECMAScript 2015) version using [arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) looks a little simpler: ```js const filteredAndSortedKeywords = keywords .filter((keyword, index) => keywords.lastIndexOf(keyword) === index) .sort((a, b) => a < b ? -1 : 1); ``` And this is the final filtered and sorted list of JavaScript reserved keywords: ```js console.log(filteredAndSortedKeywords); // ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield'] ``` *Thanks to [@nikshulipa](https://github.com/nikshulipa), [@kirilloid](https://twitter.com/kirilloid), [@lesterzone](https://twitter.com/lesterzone), [@tracker1](https://twitter.com/tracker1), [@manuel_del_pozo](https://twitter.com/manuel_del_pozo) for all the comments and suggestions!* ================================================ FILE: _posts/en/javascript/2016-01-27-short-circuit-evaluation-in-js.md ================================================ --- layout: post title: Short circuit evaluation in JS. tip-number: 27 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: Short-circuit evaluation says, the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression, when the first argument of the AND `&&` function evaluates to false, the overall value must be false, and when the first argument of the OR `||` function evaluates to true, the overall value must be true. redirect_from: - /en/short-circuit-evaluation-in-js/ categories: - en - javascript --- [Short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation) says, the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression: when the first argument of the AND (`&&`) function evaluates to false, the overall value must be false; and when the first argument of the OR (`||`) function evaluates to true, the overall value must be true. For the following `test` condition and `isTrue` and `isFalse` function. ```js var test = true; var isTrue = function(){ console.log('Test is true.'); }; var isFalse = function(){ console.log('Test is false.'); }; ``` Using logical AND - `&&`. ```js // A normal if statement. if(test){ isTrue(); // Test is true } // Above can be done using '&&' as - ( test && isTrue() ); // Test is true ``` Using logical OR - `||`. ```js test = false; if(!test){ isFalse(); // Test is false. } ( test || isFalse()); // Test is false. ``` The logical OR could also be used to set a default value for function argument. ```js function theSameOldFoo(name){ name = name || 'Bar' ; console.log("My best friend's name is " + name); } theSameOldFoo(); // My best friend's name is Bar theSameOldFoo('Bhaskar'); // My best friend's name is Bhaskar ``` The logical AND could be used to avoid exceptions when using properties of undefined. Example: ```js var dog = { bark: function(){ console.log('Woof Woof'); } }; // Calling dog.bark(); dog.bark(); // Woof Woof. // But if dog is not defined, dog.bark() will raise an error "Cannot read property 'bark' of undefined." // To prevent this, we can use &&. dog&&dog.bark(); // This will only call dog.bark(), if dog is defined. ``` ================================================ FILE: _posts/en/javascript/2016-01-28-curry-vs-partial-application.md ================================================ --- layout: post title: Currying vs partial application tip-number: 28 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: Currying and partial application are two ways of transforming a function into another function with a generally smaller arity. redirect_from: - /en/curry-vs-partial-application/ categories: - en - javascript --- **Currying** Currying takes a function f: X * Y -> R and turns it into a function f': X -> (Y -> R) Instead of calling f with two arguments, we invoke f' with the first argument. The result is a function that we then call with the second argument to produce the result. Thus, if the uncurried f is invoked as f(3,5) then the curried f' is invoked as f(3)(5) For example: Uncurried add() ```javascript function add(x, y) { return x + y; } add(3, 5); // returns 8 ``` Curried add() ```javascript function addC(x) { return function (y) { return x + y; } } addC(3)(5); // returns 8 ``` **The algorithm for currying.** Curry takes a binary function and returns a unary function that returns a unary function. curry: (X × Y → R) → (X → (Y → R)) Javascript Code: ```javascript function curry(f) { return function(x) { return function(y) { return f(x, y); } } } ``` **Partial application** Partial application takes a function f: X * Y -> R and a fixed value for the first argument to produce a new function f`: Y -> R f' does the same as f, but only has to fill in the second parameter which is why its arity is one less than the arity of f. For example: Binding the first argument of function add to 5 produces the function plus5. ```javascript function plus5(y) { return 5 + y; } plus5(3); // returns 8 ``` **The algorithm of partial application.*** partApply takes a binary function and a value and produces a unary function. partApply : ((X × Y → R) × X) → (Y → R) Javascript Code: ```javascript function partApply(f, x) { return function(y) { return f(x, y); } } ``` ================================================ FILE: _posts/en/javascript/2016-01-29-speed-up-recursive-functions-with-memoization.md ================================================ --- layout: post title: Speed up recursive functions with memoization tip-number: 29 tip-username: hingsir tip-username-profile: https://github.com/hingsir tip-tldr: Fibonacci sequence is very familiar to everybody. we can write the following function in 20 seconds.it works, but not efficient. it did lots of duplicate computing works, we can cache its previously computed results to speed it up. redirect_from: - /en/speed-up-recursive-functions-with-memoization/ categories: - en - javascript --- Fibonacci sequence is very familiar to everybody. We can write the following function in 20 seconds. ```js var fibonacci = function(n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); } ``` It works, but is not efficient. It did lots of duplicate computing works, we can cache its previously computed results to speed it up. ```js var fibonacci = (function() { var cache = [0, 1]; // cache the value at the n index return function(n) { if (cache[n] === undefined) { for (var i = cache.length; i <= n; ++i) { cache[i] = cache[i - 1] + cache[i - 2]; } } return cache[n]; } })(); ``` Also, we can define a higher-order function that accepts a function as its argument and returns a memoized version of the function. ```js var memoize = function(func) { var cache = {}; return function() { var key = JSON.stringify(Array.prototype.slice.call(arguments)); return key in cache ? cache[key] : (cache[key] = func.apply(this, arguments)); } } fibonacci = memoize(fibonacci); ``` And this is an ES6 version of the memoize function. ```js var memoize = function(func) { const cache = {}; return (...args) => { const key = JSON.stringify(args); return key in cache ? cache[key] : (cache[key] = func(...args)); } } fibonacci = memoize(fibonacci); ``` we can use `memoize()` in many other situations * GCD(Greatest Common Divisor) ```js var gcd = memoize(function(a, b) { var t; if (a < b) t = b, b = a, a = t; while (b != 0) t = b, b = a % b, a = t; return a; }); gcd(27, 183); //=> 3 ``` * Factorial calculation ```js var factorial = memoize(function(n) { return (n <= 1) ? 1 : n * factorial(n - 1); }) factorial(5); //=> 120 ``` Learn more about memoization: - [Memoization - Wikipedia](https://en.wikipedia.org/wiki/Memoization) - [Implementing Memoization in JavaScript](https://www.sitepoint.com/implementing-memoization-in-javascript/) ================================================ FILE: _posts/en/javascript/2016-01-30-converting-truthy-falsy-values-to-boolean.md ================================================ --- layout: post title: Converting truthy/falsy values to boolean tip-number: 30 tip-username: hakhag tip-username-profile: https://github.com/hakhag tip-tldr: Logical operators are a core part of JavaScript, here you can see a a way you always get a true or false no matter what was given to it. redirect_from: - /en/converting-truthy-falsy-values-to-boolean/ categories: - en - javascript --- You can convert a [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) or [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) value to true boolean with the `!!` operator. ```js !!"" // false !!0 // false !!null // false !!undefined // false !!NaN // false !!"hello" // true !!1 // true !!{} // true !![] // true ``` ================================================ FILE: _posts/en/javascript/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md ================================================ --- layout: post title: Avoid modifying or passing `arguments` into other functions — it kills optimization tip-number: 31 tip-username: berkana tip-username-profile: https://github.com/berkana tip-tldr: Within JavaScript functions, the variable name `arguments` lets you access all of the arguments passed to the function. `arguments` is an *array-like object*; `arguments` can be accessed using array notation, and it has the *length* property, but it doesn't have many of the built-in methods that arrays have such as `filter` and `map` and `forEach`. Because of this, it is a fairly common practice to convert `arguments` into an array using the following snipet redirect_from: - /en/avoid-modifying-or-passing-arguments-into-other-functions-it-kills-optimization/ categories: - en - javascript --- ###Background Within JavaScript functions, the variable name [`arguments`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) lets you access all of the arguments passed to the function. `arguments` is an *array-like object*; `arguments` can be accessed using array notation, and it has the *length* property, but it doesn't have many of the built-in methods that arrays have such as `filter` and `map` and `forEach`. Because of this, it is a fairly common practice to convert `arguments` into an array using the following: ```js var args = Array.prototype.slice.call(arguments); ``` This calls the `slice` method from the `Array` prototype, passing it `arguments`; the `slice` method returns a shallow copy of `arguments` as a new array object. A common shorthand for this is : ```js var args = [].slice.call(arguments); ``` In this case, instead of calling `slice` from the `Array` prototype, it is simply being called from an empty array literal. ###Optimization Unfortunately, passing `arguments` into any function call will cause the V8 JavaScript engine used in Chrome and Node to skip optimization on the function that does this, which can result in considerably slower performance. See this article on [optimization killers](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers). Passing `arguments` to any other function is known as *leaking `arguments`*. Instead, if you want an array of the arguments that lets you use you need to resort to this: ```js var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } ``` Yes it is more verbose, but in production code, it is worth it for the performance optimization. ================================================ FILE: _posts/en/javascript/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md ================================================ --- layout: post title: Map() to the rescue; adding order to Object properties tip-number: 32 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: An Object it is an unordered collection of properties... that means that if you are trying to save ordered data inside an Object, you have to review it because properties order in objects are not guaranteed. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/map-to-the-rescue-adding-order-to-object-properties/ categories: - en - javascript --- ## Object properties order > An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method. [ECMAScript](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf) Take a look in action ```js var myObject = { z: 1, '@': 2, b: 3, 1: 4, 5: 5 }; console.log(myObject) // Object {1: 4, 5: 5, z: 1, @: 2, b: 3} for (item in myObject) {... // 1 // 5 // z // @ // b ``` Each browser have his own rules about the order in objects bebause technically, order is unspecified. ## How to solve this? ### Map Using a new ES6 feature called Map. A [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object iterates its elements in insertion order — a `for...of` loop returns an array of [key, value] for each iteration. ```js var myObject = new Map(); myObject.set('z', 1); myObject.set('@', 2); myObject.set('b', 3); for (var [key, value] of myObject) { console.log(key, value); ... // z 1 // @ 2 // b 3 ``` ### Hack for old browsers Mozilla suggest: > So, if you want to simulate an ordered associative array in a cross-browser environment, you are forced to either use two separate arrays (one for the keys and the other for the values), or build an array of single-property objects, etc. ```js // Using two separate arrays var objectKeys = [z, @, b, 1, 5]; for (item in objectKeys) { myObject[item] ... // Build an array of single-property objects var myData = [{z: 1}, {'@': 2}, {b: 3}, {1: 4}, {5: 5}]; ``` ================================================ FILE: _posts/en/javascript/2016-02-02-create-range-0-n-easily-using-one-line.md ================================================ --- layout: post title: Create array sequence `[0, 1, ..., N-1]` in one line tip-number: 33 tip-username: SarjuHansaliya tip-username-profile: https://github.com/SarjuHansaliya tip-tldr: Compact one-liners that generate ordinal sequence arrays redirect_from: - /en/create-range-0-n-easily-using-one-line/ categories: - en - javascript --- Here are two compact code sequences to generate the `N`-element array `[0, 1, ..., N-1]`: ### Solution 1 (requires ES5) ```js Array.apply(null, {length: N}).map(Function.call, Number); ``` #### Brief explanation 1. `Array.apply(null, {length: N})` returns an `N`-element array filled with `undefined` (i.e. `A = [undefined, undefined, ...]`). 2. `A.map(Function.call, Number)` returns an `N`-element array, whose index `I` gets the result of `Function.call.call(Number, undefined, I, A)` 3. `Function.call.call(Number, undefined, I, A)` collapses into `Number(I)`, which is naturally `I`. 4. Result: `[0, 1, ..., N-1]`. For a more thorough explanation, go [here](https://github.com/gromgit/jstips-xe/blob/master/tips/33.md). ### Solution 2 (requires ES6) It uses `Array.from` [https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) ```js Array.from(new Array(N),(val,index)=>index); ``` ### Solution 3 (requires ES6) ```js Array.from(Array(N).keys()); ``` #### Brief explanation 1. `A = new Array(N)` returns an array with `N` _holes_ (i.e. `A = [,,,...]`, but `A[x] = undefined` for `x` in `0...N-1`). 2. `F = (val,index)=>index` is simply `function F (val, index) { return index; }` 3. `Array.from(A, F)` returns an `N`-element array, whose index `I` gets the results of `F(A[I], I)`, which is simply `I`. 4. Result: `[0, 1, ..., N-1]`. ### One More Thing If you actually want the sequence [1, 2, ..., N], **Solution 1** becomes: ```js Array.apply(null, {length: N}).map(function(value, index){ return index + 1; }); ``` and **Solution 2**: ```js Array.from(new Array(N),(val,index)=>index+1); ``` ================================================ FILE: _posts/en/javascript/2016-02-03-implementing-asynchronous-loops.md ================================================ --- layout: post title: Implementing asynchronous loop tip-number: 34 tip-username: madmantalking tip-username-profile: https://github.com/madmantalking tip-tldr: You may run into problems while implementing asynchronous loops. redirect_from: - /en/implementing-asynchronous-loops/ categories: - en - javascript --- Let's try out writing an asynchronous function which prints the value of the loop index every second. ```js for (var i=0; i<5; i++) { setTimeout(function(){ console.log(i); }, 1000 * (i+1)); } ``` The output of the above programs turns out to be ```js > 5 > 5 > 5 > 5 > 5 ``` So this definitely doesn't work. **Reason** Each timeout refers to the original `i`, not a copy. So the for loop increments `i` until it gets to 5, then the timeouts run and use the current value of `i` (which is 5). Well , this problem seems easy. An immediate solution that strikes is to cache the loop index in a temporary variable. ```js for (var i=0; i<5; i++) { var temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i+1)); } ``` But again the output of the above programs turns out to be ```js > 4 > 4 > 4 > 4 > 4 ``` So , that doesn't work either , because blocks don't create a scope and variables initializers are hoisted to the top of the scope. In fact, the previous block is the same as: ```js var temp; for (var i=0; i<5; i++) { temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i+1)); } ``` **Solution** There are a few different ways to copy `i`. The most common way is creating a closure by declaring a function and passing `i` as an argument. Here we do this as a self-calling function. ```js for (var i=0; i<5; i++) { (function(num){ setTimeout(function(){ console.log(num); }, 1000 * (i+1)); })(i); } ``` In JavaScript, arguments are passed by value to a function. So primitive types like numbers, dates, and strings are basically copied. If you change them inside the function, it does not affect the outside scope. Objects are special: if the inside function changes a property, the change is reflected in all scopes. Another approach for this would be with using `let`. With ES6 the `let` keyword is useful since it's block scoped unlike `var` ```js for (let i=0; i<5; i++) { setTimeout(function(){ console.log(i); }, 1000 * (i+1)); } ``` ================================================ FILE: _posts/en/javascript/2016-02-04-assignment-shorthands.md ================================================ --- layout: post title: Assignment Operators tip-number: 35 tip-username: hsleonis tip-username-profile: https://github.com/hsleonis tip-tldr: Assigning is very common. Sometimes typing becomes time consuming for us 'Lazy programmers'. So, we can use some tricks to help us and make our code cleaner and simpler. redirect_from: - /en/assignment-shorthands/ categories: - en - javascript --- Assigning is very common. Sometimes typing becomes time consuming for us 'Lazy programmers'. So, we can use some tricks to help us and make our code cleaner and simpler. This is the similar use of ````javascript x += 23; // x = x + 23; y -= 15; // y = y - 15; z *= 10; // z = z * 10; k /= 7; // k = k / 7; p %= 3; // p = p % 3; d **= 2; // d = d ** 2; m >>= 2; // m = m >> 2; n <<= 2; // n = n << 2; n ++; // n = n + 1; n --; n = n - 1; ```` ### `++` and `--` operators There is a special `++` operator. It's best to explain it with an example: ````javascript var a = 2; var b = a++; // Now a is 3 and b is 2 ```` The `a++` statement does this: 1. return the value of `a` 2. increment `a` by 1 But what if we wanted to increment the value first? It's simple: ````javascript var a = 2; var b = ++a; // Now both a and b are 3 ```` See? I put the operator _before_ the variable. The `--` operator is similar, except it decrements the value. ### If-else (Using ternary operator) This is what we write on regular basis. ````javascript var newValue; if(value > 10) newValue = 5; else newValue = 2; ```` We can user ternary operator to make it awesome: ````javascript var newValue = (value > 10) ? 5 : 2; ```` ### Null, Undefined, Empty Checks ````javascript if (variable1 !== null || variable1 !== undefined || variable1 !== '') { var variable2 = variable1; } ```` Shorthand here: ````javascript var variable2 = variable1 || ''; ```` P.S.: If variable1 is a number, then first check if it is 0. ### Object Array Notation Instead of using: ````javascript var a = new Array(); a[0] = "myString1"; a[1] = "myString2"; ```` Use this: ````javascript var a = ["myString1", "myString2"]; ```` ### Associative array Instead of using: ````javascript var skillSet = new Array(); skillSet['Document language'] = 'HTML5'; skillSet['Styling language'] = 'CSS3'; ```` Use this: ````javascript var skillSet = { 'Document language' : 'HTML5', 'Styling language' : 'CSS3' }; ```` ================================================ FILE: _posts/en/javascript/2016-02-05-observe-dom-changes.md ================================================ --- layout: post title: Observe DOM changes in extensions tip-number: 36 tip-username: beyondns tip-username-profile: https://github.com/beyondns tip-tldr: When you develop extensions to existent sites it's not so easy to play with DOM 'cause of modern dynamic javascript. redirect_from: - /en/observe-dom-changes/ categories: - en - javascript --- [MutationObserver](https://developer.mozilla.org/en/docs/Web/API/MutationObserver) is a solution to listen DOM changes and do what you want to do with elements when they changed. In following example there is some emulation of dynamic content loading with help of timers, after first "target" element creation goes "subTarget". In extension code firstly rootObserver works till targetElement appearance then elementObserver starts. This cascading observing helps finally get moment when subTargetElement found. This useful to develop extensions to complex sites with dynamic content loading. ```js const observeConfig = { attributes: true, childList: true, characterData: true, subtree: true }; function initExtension(rootElement, targetSelector, subTargetSelector) { var rootObserver = new MutationObserver(function(mutations) { console.log("Inside root observer"); targetElement = rootElement.querySelector(targetSelector); if (targetElement) { rootObserver.disconnect(); var elementObserver = new MutationObserver(function(mutations) { console.log("Inside element observer"); subTargetElement = targetElement.querySelector(subTargetSelector); if (subTargetElement) { elementObserver.disconnect(); console.log("subTargetElement found!"); } }); elementObserver.observe(targetElement, observeConfig); } }); rootObserver.observe(rootElement, observeConfig); } (function() { initExtension(document.body, "div.target", "div.subtarget"); setTimeout(function() { del = document.createElement("div"); del.innerHTML = "
target
"; document.body.appendChild(del); }, 3000); setTimeout(function() { var el = document.body.querySelector('div.target'); if (el) { del = document.createElement("div"); del.innerHTML = "
subtarget
"; el.appendChild(del); } }, 5000); })(); ``` ================================================ FILE: _posts/en/javascript/2016-02-06-deduplicate-an-array.md ================================================ --- layout: post title: Deduplicate an Array tip-number: 37 tip-username: danillouz tip-username-profile: https://www.twitter.com/danillouz tip-tldr: How to remove duplicate elements, of different data types, from an Array. redirect_from: - /en/deduplicate-an-array/ categories: - en - javascript --- # Primitives If an Array only contains primitive values, we can deduplicate it by only using the [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) and [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) methods. ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter(function (el, i, arr) { return arr.indexOf(el) === i; }); console.log(deduped); // [ 1, 'a' ] ``` ## ES2015 We can write this in a more compact way using an [arrow function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions). ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter( (el, i, arr) => arr.indexOf(el) === i); console.log(deduped); // [ 1, 'a' ] ``` But with the introduction of [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and the [`from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) method, we can achieve the same result in a more concise way. ```javascript var deduped = Array.from( new Set([ 1, 1, 'a', 'a' ]) ); console.log(deduped); // [ 1, 'a' ] ``` # Objects We can't use the same approach when the elements are Objects, because Objects are stored by reference and primitives are stored by value. ```javascript 1 === 1 // true 'a' === 'a' // true { a: 1 } === { a: 1 } // false ``` Therefore we need to change our approach and use a hash table. ```javascript function dedup(arr) { var hashTable = {}; return arr.filter(function (el) { var key = JSON.stringify(el); var match = Boolean(hashTable[key]); return (match ? false : hashTable[key] = true); }); } var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ] ]); console.log(deduped); // [ {a: 1}, [1, 2] ] ``` Because a hash table in javascript is simply an `Object`, the keys will always be of the type `String`. This means that normally we can't distinguish between strings and numbers of the same value, i.e. `1` and `'1'`. ```javascript var hashTable = {}; hashTable[1] = true; hashTable['1'] = true; console.log(hashTable); // { '1': true } ``` However, because we're using [`JSON.stringify`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), keys that are of the type `String`, will be stored as an escaped string value, giving us unique keys in our `hashTable`. ```javascript var hashTable = {}; hashTable[JSON.stringify(1)] = true; hashTable[JSON.stringify('1')] = true; console.log(hashTable); // { '1': true, '\'1\'': true } ``` This means duplicate elements of the same value, but of a different type, will still be deduplicated using the same implementation. ```javascript var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ], 1, 1, '1', '1' ]); console.log(deduped); // [ {a: 1}, [1, 2], 1, '1' ] ``` # Resources ## Methods * [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) * [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) * [`from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) * [`JSON.stringify`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ## ES2015 * [arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) * [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) ## Stack overflow * [remove duplicates from array](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array/9229821#9229821) ================================================ FILE: _posts/en/javascript/2016-02-07-flattening-multidimensional-arrays-in-javascript.md ================================================ --- layout: post title: Flattening multidimensional Arrays in JavaScript tip-number: 38 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Three different solutions to merge multidimensional array into a single array. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/flattening-multidimensional-arrays-in-javascript/ categories: - en - javascript --- These are the three known ways to merge multidimensional array into a single array. Given this array: ```js var myArray = [[1, 2],[3, 4, 5], [6, 7, 8, 9]]; ``` We wanna have this result: ```js [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solution 1: Using [`concat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) and [`apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) ```js var myNewArray = [].concat.apply([], myArray); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solution 2: Using [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Flatten_an_array_of_arrays) ```js var myNewArray = myArray.reduce(function(prev, curr) { return prev.concat(curr); }); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solution 3: ```js var myNewArray3 = []; for (var i = 0; i < myArray.length; ++i) { for (var j = 0; j < myArray[i].length; ++j) myNewArray3.push(myArray[i][j]); } console.log(myNewArray3); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solution 4: Using [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) in ES6 ```js var myNewArray4 = [].concat(...myArray); console.log(myNewArray4); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solution 5: Using [`flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) in ES10 ```js var myNewArray5 = myArray.flat(); console.log(myNewArray5); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` Take a look [here](https://jsbin.com/janana/edit?js,console) these 4 algorithms in action. For infinitely nested array try Lodash [flattenDeep()](https://lodash.com/docs#flattenDeep). If you are curious about performance, [here](http://jsperf.com/flatten-an-array-loop-vs-reduce/6) a test for check how it works. ================================================ FILE: _posts/en/javascript/2016-02-08-advanced-properties.md ================================================ --- layout: post title: Advanced Javascript Properties tip-number: 39 tip-username: mallowigi tip-username-profile: https://github.com/mallowigi tip-tldr: How to add private properties, getters and setters to objects. redirect_from: - /en/advanced-properties/ categories: - en - javascript --- It is possible to configure object properties in Javascript for example to set properties to be pseudo-private or readonly. This feature is available since ECMAScript 5.1, therefore supported by all recent browsers. To do so, you need to use the method `defineProperty` of the `Object` prototype like so: ```js var a = {}; Object.defineProperty(a, 'readonly', { value: 15, writable: false }); a.readonly = 20; console.log(a.readonly); // 15 ``` The syntax is as follows: ```js Object.defineProperty(dest, propName, options) ``` or for multiple definitions: ```js Object.defineProperties(dest, { propA: optionsA, propB: optionsB, //... }) ``` where options include the following attributes: - *value*: if the property is not a getter (see below), value is a mandatory attribute. `{a: 12}` === `Object.defineProperty(obj, 'a', {value: 12})` - *writable*: set the property as readonly. Note that if the property is a nested objects, its properties are still editable. - *enumerable*: set the property as hidden. That means that `for ... of` loops and `stringify` will not include the property in their result, but the property is still there. Note: That doesn't mean that the property is private! It can still be accessible from the outside, it just means that it won't be printed. - *configurable*: set the property as non modifiable, e.g. protected from deletion or redefinition. Again, if the property is a nested object, its properties are still configurable. So in order to create a private constant property, you can define it like so: ```js Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false}); ``` Besides configuring properties, `defineProperty` allows us to define *dynamic properties*, thanks to the second parameter being a string. For instance, let's say that I want to create properties according to some external configuration: ```js var obj = { getTypeFromExternal(): true // illegal in ES5.1 } Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok // For the example sake, ES6 introduced a new syntax: var obj = { [getTypeFromExternal()]: true } ``` But that's not all! Advanced properties allows us to create **getters** and **setters**, just like other OOP languages! In that case, one cannot use the `writable`, `enumerable` and `configurable` properties, but instead: ```js function Foobar () { var _foo; // true private property Object.defineProperty(obj, 'foo', { get: function () { return _foo; } set: function (value) { _foo = value } }); } var foobar = new Foobar(); foobar.foo; // 15 foobar.foo = 20; // _foo = 20 ``` Aside for the obvious advantage of encapsulation and advanced accessors, you will notice that we didn't "call" the getter, instead we just "get" the property without parentheses! This is awesome! For instance, let's imagine that we have an object with long nested properties, like so: ```js var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } }; ``` Now instead of doing `a.b.c[0].d` (where one of the properties can resolve to `undefined` and throw an error), we can instead create an alias: ```js Object.defineProperty(obj, 'firstD', { get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d } }) console.log(obj.firstD) // 10 ``` ### Note If you define a getter without a setter and still try to set a value, you will get an error! This is particularly important when using helper functions such as `$.extend` or `_.merge`. Be careful! ### Links - [defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) - [Defining properties in JavaScript](http://bdadam.com/blog/defining-properties-in-javascript.html) ================================================ FILE: _posts/en/javascript/2016-02-09-using-json-stringify.md ================================================ --- layout: post title: Using JSON.Stringify tip-number: 40 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: Create string from selected properties of JSON object. redirect_from: - /en/using-json-stringify/ categories: - en - javascript --- Let's say there is an object with properties "prop1", "prop2", "prop3". We can pass __additional params__ to __JSON.stringify__ to selectively write properties of the object to string like: ```javascript var obj = { 'prop1': 'value1', 'prop2': 'value2', 'prop3': 'value3' }; var selectedProperties = ['prop1', 'prop2']; var str = JSON.stringify(obj, selectedProperties); // str // {"prop1":"value1","prop2":"value2"} ``` The __"str"__ will contain only info on selected properties only. Instead of array we can pass a function also. ```javascript function selectedProperties(key, val) { // the first val will be the entire object, key is empty string if (!key) { return val; } if (key === 'prop1' || key === 'prop2') { return val; } return; } ``` The last optional param it takes is to modify the way it writes the object to string. ```javascript var str = JSON.stringify(obj, selectedProperties, '\t\t'); /* str output with double tabs in every line. { "prop1": "value1", "prop2": "value2" } */ ``` ================================================ FILE: _posts/en/javascript/2016-02-10-array-average-and-median.md ================================================ --- layout: post title: Array average and median tip-number: 41 tip-username: soyuka tip-username-profile: https://github.com/soyuka tip-tldr: Calculate the average and median from array values redirect_from: - /en/array-average-and-median/ categories: - en - javascript --- The following examples will be based on the following array: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; ``` To get the average, we have to sum up numbers and then divide by the number of values. Steps are: - get the array length - sum up values - get the average (`sum/length`) ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let sum = values.reduce((previous, current) => current += previous); let avg = sum / values.length; // avg = 28 ``` Or: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let count = values.length; values = values.reduce((previous, current) => current += previous); values /= count; // avg = 28 ``` Now, to get the median steps are: - sort the array - get the arethmic mean of the middle values ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let lowMiddle = Math.floor((values.length - 1) / 2); let highMiddle = Math.ceil((values.length - 1) / 2); let median = (values[lowMiddle] + values[highMiddle]) / 2; // median = 13,5 ``` With a bitwise operator: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let median = (values[(values.length - 1) >> 1] + values[values.length >> 1]) / 2 // median = 13,5 ``` ================================================ FILE: _posts/en/javascript/2016-02-11-preventing-unapply-attacks.md ================================================ --- layout: post title: Preventing Unapply Attacks tip-number: 42 tip-username: emars tip-username-profile: https://twitter.com/marseltov tip-tldr: Freeze the builtin prototypes. redirect_from: - /en/preventing-unapply-attacks/ categories: - en - javascript --- By overriding the builtin prototypes, external code can cause code to break by rewriting code to expose and change bound arguments. This can be an issue that seriously breaks applications that works by using polyfill es5 methods. ```js // example bind polyfill function bind(fn) { var prev = Array.prototype.slice.call(arguments, 1); return function bound() { var curr = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.concat.apply(prev, curr); return fn.apply(null, args); }; } // unapply-attack function unapplyAttack() { var concat = Array.prototype.concat; Array.prototype.concat = function replaceAll() { Array.prototype.concat = concat; // restore the correct version var curr = Array.prototype.slice.call(arguments, 0); var result = concat.apply([], curr); return result; }; } ``` The above function discards the `prev` array from the bind meaning that any `.concat` the first concat call following using the unapply attack will throw an error. By using [Object.freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), making an object immutable, you prevent any overriding of the builtin object prototypes. ```js (function freezePrototypes() { if (typeof Object.freeze !== 'function') { throw new Error('Missing Object.freeze'); } Object.freeze(Object.prototype); Object.freeze(Array.prototype); Object.freeze(Function.prototype); }()); ``` You can read more about unapply attacks [here](https://glebbahmutov.com/blog/unapply-attack/). Although this concept is called an 'unapply attack' due to some code being able to access closures that normally wouldn't be in scope, it is mostly wrong to consider this a security feature due to it not preventing an attacker with code execution from extending prototypes before the freezing happens and also still having the potential to read all scopes using various language features. ECMA modules would give realm based isolation which is much stronger than this solution however still doesn't fix the issues of third party scripts. ================================================ FILE: _posts/en/javascript/2016-02-12-use-destructuring-in-function-parameters.md ================================================ --- layout: post title: Use destructuring in function parameters tip-number: 43 tip-username: dislick tip-username-profile: https://github.com/dislick tip-tldr: Did you know that you can use destructuring in function parameters? redirect_from: - /en/use-destructuring-in-function-parameters/ categories: - en - javascript --- I am sure many of you are already familiar with the [ES6 Destructuring Assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment). Did you know that you can also use it in function parameters? ```js var sayHello = function({ name, surname }) { console.log(`Hello ${name} ${surname}! How are you?`); }; sayHello({ name: 'John', surname: 'Smith' }) // -> Hello John Smith! How are you? ``` This is great for functions which accept an options object. For this use case, you can also add [default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) to fill in whatever values the caller leaves out, or if the caller forgets to pass one at all: ```js var sayHello2 = function({ name = "Anony", surname = "Moose" } = {}) { console.log(`Hello ${name} ${surname}! How are you?`); }; ``` The `= {}` says that the default object to be destructured for this parameter is `{}`, in case the caller forgets to pass the parameter, or passes one of the wrong type (more on this below). ```js sayHello2() // -> Hello Anony Moose! How are you? sayHello2({ name: "Bull" }) // -> Hello Bull Moose! How are you? ``` ##### Argument Handling With plain destructuring assignment, if the the input parameter can't be matched with the function's specified object arguments, all the unmatched arguments are `undefined`, so you need to add code that handles this properly: ```js var sayHelloTimes = function({ name, surname }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); } sayHelloTimes({ name: "Pam" }, 5678) // -> Hello Pam undefined! I've seen you 5678 times before. sayHelloTimes(5678) // -> Hello undefined undefined! I've seen you undefined times before. ``` Worse, if the parameter to be destructured is missing, an exception is thrown, probably bringing your app to a screeching halt: ```js sayHelloTimes() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'... ``` It's conceptually similar to accessing a property of an undefined object, just with a different exception type. Destructuring assignment with default parameters hides all the above to a certain extent: ```js var sayHelloTimes2 = function({ name = "Anony", surname = "Moose" } = {}, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2() // -> Hello Anony Moose! I've seen you undefined times before. ``` As for `= {}`, it covers the case of a missing _object_, for which individual property defaults won't help at all: ```js var sayHelloTimes2a = function({ name = "Anony", surname = "Moose" }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2a({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2a(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2a() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'. ``` ##### Availability Note that destructuring assignment may not yet be available by default, in the version of Node.js or browser that you're using. For Node.js, you can try using the `--harmony-destructuring` flag on startup to activate this feature. ================================================ FILE: _posts/en/javascript/2016-02-13-know-the-passing-mechanism.md ================================================ --- layout: post title: Know the passing mechanism tip-number: 44 tip-username: bmkmanoj tip-username-profile: https://github.com/bmkmanoj tip-tldr: JavaScript technically only passes by value for both primitives and object (or reference) types. In case of reference types the reference value itself is passed by value. redirect_from: - /en/know-the-passing-mechanism/ categories: - en - javascript --- JavaScript is pass-by-value, technically. It is neither pass-by-value nor pass-by-reference, going by the truest sense of these terms. To understand this passing mechanism, take a look at the following two example code snippets and the explanations. ### Example 1 ```js var me = { // 1 'partOf' : 'A Team' }; function myTeam(me) { // 2 me = { // 3 'belongsTo' : 'A Group' }; } myTeam(me); console.log(me); // 4 : {'partOf' : 'A Team'} ``` In above example, when the `myTeam` gets invoked, JavaScript is *passing the reference to* `me` *object as value, as it is an object* and invocation itself creates two independent references to the same object, (though the name being same here i.e. `me`, is misleading and gives us an impression that it is the single reference) and hence, the reference variable themselves are independent. When we assigned a new object at #`3`, we are changing this reference value entirely within the `myTeam` function, and it will not have any impact on the original object outside this function scope, from where it was passed and the reference in the outside scope is going to retain the original object and hence the output from #`4`. ### Example 2 ```js var me = { // 1 'partOf' : 'A Team' }; function myGroup(me) { // 2 me.partOf = 'A Group'; // 3 } myGroup(me); console.log(me); // 4 : {'partOf' : 'A Group'} ``` In the case of `myGroup` invocation, we are passing the object `me`. But unlike the example 1 scenario, we are not assigning this `me` variable to any new object, effectively meaning the object reference value within the `myGroup` function scope still is the original object's reference value and when we are modifying the property within this scope, it is effectively modifying the original object's property. Hence, you get the output from #`4`. So does this later case not prove that javascript is pass-by-reference? No, it does not. Remember, *JavaScript passes the reference as value, in case of objects*. The confusion arises as we tend not to understand fully what pass by reference is. This is the exact reason, some prefer to call this as *call-by-sharing*. *Initially posted by the author on [js-by-examples](https://github.com/bmkmanoj/js-by-examples/blob/master/examples/js_pass_by_value_or_reference.md)* ================================================ FILE: _posts/en/javascript/2016-02-14-calculate-the-max-min-value-from-an-array.md ================================================ --- layout: post title: Calculate the Max/Min value from an array tip-number: 45 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Ways to use the built-in functions Math.max() and Math.min() with arrays of numbers tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/calculate-the-max-min-value-from-an-array/ categories: - en - javascript --- The built-in functions [Math.max()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max) and [Math.min()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min) find the maximum and minimum value of the arguments, respectively. ```js Math.max(1, 2, 3, 4); // 4 Math.min(1, 2, 3, 4); // 1 ``` These functions will not work as-is with arrays of numbers. However, there are some ways around this. [`Function.prototype.apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) allows you to call a function with a given `this` value and an _array_ of arguments. ```js var numbers = [1, 2, 3, 4]; Math.max.apply(null, numbers) // 4 Math.min.apply(null, numbers) // 1 ``` Passing the `numbers` array as the second argument of `apply()` results in the function being called with all values in the array as parameters. A simpler, ES2015 way of accomplishing this is with the new [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). ```js var numbers = [1, 2, 3, 4]; Math.max(...numbers) // 4 Math.min(...numbers) // 1 ``` This operator causes the values in the array to be expanded, or "spread", into the function's arguments. ================================================ FILE: _posts/en/javascript/2016-02-15-detect-document-ready-in-pure-js.md ================================================ --- layout: post title: Detect document ready in pure JS tip-number: 46 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: The cross-browser way to check if the document has loaded in pure JavaScript tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/detect-document-ready-in-pure-js/ categories: - en - javascript --- The cross-browser way to check if the document has loaded in pure JavaScript is using [`readyState`](https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState). ```js if (document.readyState === 'complete') { // The page is fully loaded } ``` You can detect when the document is ready... ```js let stateCheck = setInterval(() => { if (document.readyState === 'complete') { clearInterval(stateCheck); // document ready } }, 100); ``` or with [onreadystatechange](https://developer.mozilla.org/en-US/docs/Web/Events/readystatechange)... ```js document.onreadystatechange = () => { if (document.readyState === 'complete') { // document ready } }; ``` Use `document.readyState === 'interactive'` to detect when the DOM is ready. ================================================ FILE: _posts/en/javascript/2016-02-16-basics-declarations.md ================================================ --- layout: post title: Basics declarations tip-number: 47 tip-username: adaniloff tip-username-profile: https://github.com/adaniloff tip-tldr: Understand and work with declarations. redirect_from: - /en/basics-declarations/ categories: - en - javascript --- Below, different ways to declare variables in JavaScript. Comments and console.log should be enough to explain what's happening here: ```js var y, x = y = 1 //== var x; var y; x = y = 1 console.log('--> 1:', `x = ${x}, y = ${y}`) // Will print //--> 1: x = 1, y = 1 ``` First, we just set two variables. Nothing much here. ```js ;(() => { var x = y = 2 // == var x; x = y = 2; console.log('2.0:', `x = ${x}, y = ${y}`) })() console.log('--> 2.1:', `x = ${x}, y = ${y}`) // Will print //2.0: x = 2, y = 2 //--> 2.1: x = 1, y = 2 ``` As you can see, the code has only changed the global y, as we haven't declared the variable in the closure. ```js ;(() => { var x, y = 3 // == var x; var y = 3; console.log('3.0:', `x = ${x}, y = ${y}`) })() console.log('--> 3.1:', `x = ${x}, y = ${y}`) // Will print //3.0: x = undefined, y = 3 //--> 3.1: x = 1, y = 2 ``` Now we declare both variables through var. Meaning they only live in the context of the closure. ```js ;(() => { var y, x = y = 4 // == var x; var y; x = y = 4 console.log('4.0:', `x = ${x}, y = ${y}`) })() console.log('--> 4.1:', `x = ${x}, y = ${y}`) // Will print //4.0: x = 4, y = 4 //--> 4.1: x = 1, y = 2 ``` Both variables have been declared using var and only after that we've set their values. As local > global, x and y are local in the closure, meaning the global x and y are untouched. ```js x = 5 // == x = 5 console.log('--> 5:', `x = ${x}, y = ${y}`) // Will print //--> 5: x = 5, y = 2 ``` This last line is explicit by itself. You can test this and see the result [thanks to babel](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=var%20y%2C%20x%20%3D%20y%20%3D%201%20%2F%2F%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%201%0Aconsole.log('--%3E%201%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%201%3A%20x%20%3D%201%2C%20y%20%3D%201%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%20%3D%20y%20%3D%202%20%2F%2F%20%3D%3D%20var%20x%3B%20y%20%3D%202%3B%0A%20%20console.log('2.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%202.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F2.0%3A%20x%20%3D%202%2C%20y%20%3D%202%0A%2F%2F--%3E%202.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%2C%20y%20%3D%203%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%20%3D%203%3B%0A%20%20console.log('3.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%203.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F3.0%3A%20x%20%3D%20undefined%2C%20y%20%3D%203%0A%2F%2F--%3E%203.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20y%2C%20x%20%3D%20y%20%3D%204%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%203%0A%20%20console.log('4.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%204.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F4.0%3A%20x%20%3D%204%2C%20y%20%3D%204%0A%2F%2F--%3E%204.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0Ax%20%3D%205%20%2F%2F%20%3D%3D%20x%20%3D%205%0Aconsole.log('--%3E%205%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%205%3A%20x%20%3D%205%2C%20y%20%3D%202). More informations available on the [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var). Special thanks to @kurtextrem for his collaboration :)! ================================================ FILE: _posts/en/javascript/2016-02-17-reminders-about-reduce-function-usage.md ================================================ --- layout: post title: How to `reduce()` arrays tip-number: 48 tip-username: darul75 tip-username-profile: https://twitter.com/darul75 tip-tldr: Some reminders about using `reduce()` redirect_from: - /en/reminders-about-reduce-function-usage/ categories: - en - javascript --- As written in documentation the `reduce()` method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value. ### Signature [reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) function accepts 2 parameters (M: mandatory, O: optional): - (M) a callback **reducer function** to be applied that deals with a pair of previous (result of previous computation) and next element until end of the list. - (O) an **initial value** to be used as the first argument to the first call of the callback. So let's see a common usage and later a more sophisticated one. ### Common usage (accumulation, concatenation) We are on Amazon website (prices in $) and our caddy is quite full, let's compute total. ```javascript // my current amazon caddy purchases var items = [{price: 10}, {price: 120}, {price: 1000}]; // our reducer function var reducer = function add(sumSoFar, item) { return sumSoFar + item.price; }; // do the job var total = items.reduce(reducer, 0); console.log(total); // 1130 ``` Optional reduce function parameter was primitive integer type 0 in that first case, but it could have been an Object, an Array...instead of a primitive type, but we will see that later. Now, cool I received a discount coupon of 20$. ```javascript var total = items.reduce(reducer,-20); console.log(total); // 1110 ``` ### Advanced usage (combination) This second usage example is inspired by Redux [combineReducers](http://redux.js.org/docs/api/combineReducers.html) function [source](https://github.com/reactjs/redux/blob/master/src/combineReducers.js#L93). Idea behind is to separate reducer function into separate individual functions and at the end compute a new *single big reducer function*. To illustrate this, let's create a single object literal with some reducers function able to compute total prices in different currency $, €... ```javascript var reducers = { totalInDollar: function(state, item) { // specific statements... return state.dollars += item.price; }, totalInEuros : function(state, item) { return state.euros += item.price * 0.897424392; }, totalInPounds : function(state, item) { return state.pounds += item.price * 0.692688671; }, totalInYen : function(state, item) { return state.yens += item.price * 113.852; } // more... }; ``` Then, we create a new swiss knife function - responsible for applying each partial reduce functions. - that will return a new callback reducer function ```javascript var combineTotalPriceReducers = function(reducers) { return function(state, item) { return Object.keys(reducers).reduce( function(nextState, key) { reducers[key](state, item); return state; }, {} ); } }; ``` Now let's see how using it. ```javascript var bigTotalPriceReducer = combineTotalPriceReducers(reducers); var initialState = {dollars: 0, euros:0, yens: 0, pounds: 0}; var totals = items.reduce(bigTotalPriceReducer, initialState); console.log(totals); /* Object {dollars: 1130, euros: 1015.11531904, yens: 127524.24, pounds: 785.81131152} */ ``` I hope this approach can give you another idea of using reduce() function for your own needs. Your reduce function could handle an history of each computation by instance as it is done in Ramdajs with [scan](http://ramdajs.com/docs/#scan) function [JSFiddle to play with](https://jsfiddle.net/darul75/81tgt0cd/) ================================================ FILE: _posts/en/javascript/2016-02-26-extract-unix-timestamp-easily.md ================================================ --- layout: post title: Easiest way to extract unix timestamp in JS tip-number: 49 tip-username: nmrony tip-username-profile: https://github.com/nmrony tip-tldr: In Javascript you can easily get the unix timestamp redirect_from: - /en/extract-unix-timestamp-easily/ categories: - en - javascript --- We frequently need to calculate with unix timestamp. There are several ways to grab the timestamp. For current unix timestamp easiest and fastest way is ```js const dateTime = Date.now(); const timestamp = Math.floor(dateTime / 1000); ``` or ```js const dateTime = new Date().getTime(); const timestamp = Math.floor(dateTime / 1000); ``` To get unix timestamp of a specific date pass `YYYY-MM-DD` or `YYYY-MM-DDT00:00:00Z` as parameter of `Date` constructor. For example ```js const dateTime = new Date('2012-06-08').getTime(); const timestamp = Math.floor(dateTime / 1000); ``` You can just add a `+` sign also when declaring a `Date` object like below ```js const dateTime = +new Date(); const timestamp = Math.floor(dateTime / 1000); ``` or for specific date ```js const dateTime = +new Date('2012-06-08'); const timestamp = Math.floor(dateTime / 1000); ``` Under the hood the runtime calls `valueOf` method of the `Date` object. Then the unary `+` operator calls `toNumber()` with that returned value. For detailed explanation please check the following links * [Date.prototype.valueOf](http://es5.github.io/#x15.9.5.8) * [Unary + operator](http://es5.github.io/#x11.4.6) * [toNumber()](http://es5.github.io/#x9.3) * [Date Javascript MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) ================================================ FILE: _posts/en/javascript/2016-03-03-helpful-console-log-hacks.md ================================================ --- layout: post title: Helpful Console Logging Tricks tip-number: 50 tip-username: zackhall tip-username-profile: https://twitter.com/zthall tip-tldr: Helpful logging techniques using coercion and conditonal breakpoints. redirect_from: - /en/helpful-console-log-hacks/ categories: - en - javascript --- ## Using conditional breakpoints to log data If you wanted to log to the console a value each time a function is called, you can use conditional break points to do this. Open up your dev tools, find the function where you'd like to log data to the console and set a breakpoint with the following condition: ```js console.log(data.value) && false ``` A conditional breakpoint pauses the page thread only if the condition for the breakpoint evaluates to true. So by using a condition like console.log('foo') && false it's guaranteed to evaluate to false since you're putting the literal false in the AND condition. So this will not pause the page thread when it's hit, but it will log data to the console. This can also be used to count how many times a function or callback is called. Here's how you can set a conditional breakpoint in [Edge](https://dev.windows.com/en-us/microsoft-edge/platform/documentation/f12-devtools-guide/debugger/#setting-and-managing-breakpoints "Managing Breakpoints in Edge"), [Chrome](https://developer.chrome.com/devtools/docs/javascript-debugging#breakpoints "Managing Breakpoints in Chrome"), [Firefox](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Set_a_conditional_breakpoint "Managing Breakpoints in Firefox") and [Safari](https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/Debugger/Debugger.html "Managing Breakpoints in Safari"). ## Printing a function variable to console Have you ever logged a function variable to the console and weren't able to just view the function's code? The quickest way to see the function's code is to coerce it to a string using concatenation with an empty string. ```js console.log(funcVariable + ''); ``` ================================================ FILE: _posts/en/javascript/2016-03-16-DOM-event-listening-made-easy.md ================================================ --- layout: post title: DOM event listening made easy tip-number: 51 tip-username: octopitus tip-username-profile: https://github.com/octopitus tip-tldr: An elegant and easy way to handle DOM events redirect_from: - /en/DOM-event-listening-made-easy/ categories: - en - javascript --- Many of us are still doing these things: - `element.addEventListener('type', obj.method.bind(obj))` - `element.addEventListener('type', function (event) {})` - `element.addEventListener('type', (event) => {})` The above examples all create new anonymous event handlers that can't be removed when no longer needed. This may cause performance problems or unexpected logic bugs, when handlers that you no longer need still get accidentally triggered through unexpected user interactions or [event bubbling](http://www.javascripter.net/faq/eventbubbling.htm). Safer event-handling patterns include the following: Use a reference: ```js const handler = function () { console.log("Tada!") } element.addEventListener("click", handler) // Later on element.removeEventListener("click", handler) ``` Named function that removes itself: ```js element.addEventListener('click', function click(e) { if (someCondition) { return e.currentTarget.removeEventListener('click', click); } }); ``` A better approach: ```js function handleEvent (eventName, {onElement, withCallback, useCapture = false} = {}, thisArg) { const element = onElement || document.documentElement function handler (event) { if (typeof withCallback === 'function') { withCallback.call(thisArg, event) } } handler.destroy = function () { return element.removeEventListener(eventName, handler, useCapture) } element.addEventListener(eventName, handler, useCapture) return handler } // Anytime you need const handleClick = handleEvent('click', { onElement: element, withCallback: (event) => { console.log('Tada!') } }) // And anytime you want to remove it handleClick.destroy() ``` ================================================ FILE: _posts/en/javascript/2016-04-05-return-values-with-the-new-operator.md ================================================ --- layout: post title: Return Values with the 'new' Operator tip-number: 52 tip-username: Morklympious tip-username-profile: https://github.com/morklympious tip-tldr: Understand what gets returned when using new vs. not using new. redirect_from: - /en/return-values-with-the-new-operator/ categories: - en - javascript --- You're going to run into some instances where you'll be using `new` to allocate new objects in JavaScript. It's going to blow your mind unless you read this tip to understand what's happening behind the scenes. The `new` operator in JavaScript is an operator that, under reasonable circumstances, returns a new instance of an object. Let's say we have a constructor function: ````js function Thing() { this.one = 1; this.two = 2; } var myThing = new Thing(); myThing.one // 1 myThing.two // 2 ```` __Note__: `this` refers to the new object created by `new`. Otherwise if `Thing()` is called without `new`, __no object is created__, and `this` is going to point to the global object, which is `window`. This means that: 1. You'll suddenly have two new global variables named `one` and `two`. 2. `myThing` is now undefined, since nothing is returned in `Thing()`. Now that you get that example, here's where things get a little bit wonky. Let's say I add something to the constructor function, a little SPICE: ````js function Thing() { this.one = 1; this.two = 2; return 5; } var myThing = new Thing(); ```` Now, what does myThing equal? Is it 5? is it an object? Is it my crippled sense of self-worth? The world may never know! Except the world does know: ````js myThing.one // 1 myThing.two // 2 ```` Interestingly enough, we never actually see the five that we supposedly 'returned' from our constructor. That's weird, isn't it? What are you doing function? WHERE'S THE FIVE? Let's try it with something else. Let's return a non-primitive type instead, something like an object. ````js function Thing() { this.one = 1; this.two = 2; return { three: 3, four: 4 }; } var myThing = new Thing(); ```` Let's check it out. A quick console.log reveals all: ````js console.log(myThing); /* Object {three: 3, four: 4} What happened to this.one and this.two!? They've been stomped, my friend. */ ```` __Here's where we learn:__ When you invoke a function with the `new` keyword, you can set properties on it using the keyword `this` (but you probably already knew that). Returning a primitive value from a function you called with the `new` keyword will not return the value you specified, but instead will return the `this` instance of the function (the one you put properties on, like `this.one = 1;`). However, returning a non-primitive, like an `object`, `array`, or `function` will stomp on the `this` instance, and return that non-primitive instead, effectively ruining all the hard work you did assigning everything to `this`. ================================================ FILE: _posts/en/javascript/2016-04-21-get-file-extension.md ================================================ --- layout: post title: Get File Extension tip-number: 53 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: How to get the file extension more efficiently? redirect_from: - /en/get-file-extension/ categories: - en - javascript --- ### Question: How to get the file extension? ```javascript var file1 = "50.xsl"; var file2 = "30.doc"; getFileExtension(file1); //returs xsl getFileExtension(file2); //returs doc function getFileExtension(filename) { /*TODO*/ } ``` ### Solution 1: Regular Expression ```js function getFileExtension1(filename) { return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined; } ``` ### Solution 2: String `split` method ```js function getFileExtension2(filename) { return filename.split('.').pop(); } ``` Those two solutions couldnot handle some edge cases, here is another more robust solution. ### Solution3: String `slice`, `lastIndexOf` methods ```js function getFileExtension3(filename) { return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2); } console.log(getFileExtension3('')); // '' console.log(getFileExtension3('filename')); // '' console.log(getFileExtension3('filename.txt')); // 'txt' console.log(getFileExtension3('.hiddenfile')); // '' console.log(getFileExtension3('filename.with.many.dots.ext')); // 'ext' ``` _How does it works?_ - [String.lastIndexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf) method returns the last occurrence of the specified value (`'.'` in this case). Returns `-1` if the value is not found. - The return values of `lastIndexOf` for parameter `'filename'` and `'.hiddenfile'` are `-1` and `0` respectively. [Zero-fill right shift operator (>>>)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3E%3E%3E_%28Zero-fill_right_shift%29) will transform `-1` to `4294967295` and `-2` to `4294967294`, here is one trick to insure the filename unchanged in those edge cases. - [String.prototype.slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice) extracts file extension from the index that was calculated above. If the index is more than the length of the filename, the result is `""`. ### Comparison | Solution | Paramters | Results | | ----------------------------------------- |:-------------------:|:--------:| | Solution 1: Regular Expression | ''
'filename'
'filename.txt'
'.hiddenfile'
'filename.with.many.dots.ext' | undefined
undefined
'txt'
'hiddenfile'
'ext'
| | Solution 2: String `split` | ''
'filename'
'filename.txt'
'.hiddenfile'
'filename.with.many.dots.ext' | ''
'filename'
'txt'
'hiddenfile'
'ext'
| | Solution 3: String `slice`, `lastIndexOf` | ''
'filename'
'filename.txt'
'.hiddenfile'
'filename.with.many.dots.ext' | ''
''
'txt'
''
'ext'
| ### Live Demo and Performance [Here](https://jsbin.com/tipofu/edit?js,console) is the live demo of the above codes. [Here](http://jsperf.com/extract-file-extension) is the performance test of those 3 solutions. ### Source [How can I get file extensions with JavaScript](http://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript) ================================================ FILE: _posts/en/javascript/2016-05-06-use-optional-arguments.md ================================================ --- layout: post title: How to use optional arguments in functions (with optional callback) tip-number: 54 tip-username: alphashuro tip-username-profile: https://github.com/alphashuro tip-tldr: You can make function arguments and callback optional redirect_from: - /en/use-optional-arguments/ categories: - en - javascript --- Example function where arguments 2 and 3 are optional ```javascript function example( err, optionalA, optionalB, callback ) { // retrieve arguments as array var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; }; // first argument is the error object // shift() removes the first item from the // array and returns it err = args.shift(); // if last argument is a function then its the callback function. // pop() removes the last item in the array // and returns it if (typeof args[args.length-1] === 'function') { callback = args.pop(); } // if args still holds items, these are // your optional items which you could // retrieve one by one like this: if (args.length > 0) optionalA = args.shift(); else optionalA = null; if (args.length > 0) optionalB = args.shift(); else optionalB = null; // continue as usual: check for errors if (err) { return callback && callback(err); } // for tutorial purposes, log the optional parameters console.log('optionalA:', optionalA); console.log('optionalB:', optionalB); console.log('callback:', callback); /* do your thing */ } // ES6 with shorter, more terse code function example(...args) { // first argument is the error object const err = args.shift(); // if last argument is a function then its the callback function const callback = (typeof args[args.length-1] === 'function') ? args.pop() : null; // if args still holds items, these are your optional items which you could retrieve one by one like this: const optionalA = (args.length > 0) ? args.shift() : null; const optionalB = (args.length > 0) ? args.shift() : null; // ... repeat for more items if (err && callback) return callback(err); /* do your thing */ } // invoke example function with and without optional arguments example(null, 'AA'); example(null, function (err) { /* do something */ }); example(null, 'AA', function (err) {}); example(null, 'AAAA', 'BBBB', function (err) {}); ``` ### How do you determine if optionalA or optionalB is intended? Design your function to require optionalA in order to accept optionalB ================================================ FILE: _posts/en/javascript/2016-05-12-make-easy-loop-on-array.md ================================================ --- layout: post title: Create an easy loop using an array tip-number: 55 tip-username: jamet-julien tip-username-profile: https://github.com/jamet-julien tip-tldr: Sometimes, we need to loop endlessly over an array of items, like a carousel of images or an audio playlist. Here’s how to take an array and give it “looping powers” redirect_from: - /en/make-easy-loop-on-array/ categories: - en - javascript --- Sometimes, we need to loop endlessly over an array of items, like a carousel of images or an audio playlist. Here's how to take an array and give it "looping powers": ```js var aList = ['A','B','C','D','E']; function make_looper( arr ){ arr.loop_idx = 0; // return current item arr.current = function(){ if( this.loop_idx < 0 ){// First verification this.loop_idx = this.length - 1;// update loop_idx } if( this.loop_idx >= this.length ){// second verification this.loop_idx = 0;// update loop_idx } return arr[ this.loop_idx ];//return item }; // increment loop_idx AND return new current arr.next = function(){ this.loop_idx++; return this.current(); }; // decrement loop_idx AND return new current arr.prev = function(){ this.loop_idx--; return this.current(); }; } make_looper( aList); aList.current();// -> A aList.next();// -> B aList.next();// -> C aList.next();// -> D aList.next();// -> E aList.next();// -> A aList.pop() ;// -> E aList.prev();// -> D aList.prev();// -> C aList.prev();// -> B aList.prev();// -> A aList.prev();// -> D ``` Using the ```%``` ( Modulus ) operator is prettier.The modulus return division's rest ( ``` 2 % 5 = 1``` and ``` 5 % 5 = 0```): ```js var aList = ['A','B','C','D','E']; function make_looper( arr ){ arr.loop_idx = 0; // return current item arr.current = function(){ this.loop_idx = ( this.loop_idx ) % this.length;// no verification !! return arr[ this.loop_idx ]; }; // increment loop_idx AND return new current arr.next = function(){ this.loop_idx++; return this.current(); }; // decrement loop_idx AND return new current arr.prev = function(){ this.loop_idx += this.length - 1; return this.current(); }; } make_looper( aList); aList.current();// -> A aList.next();// -> B aList.next();// -> C aList.next();// -> D aList.next();// -> E aList.next();// -> A aList.pop() ;// -> E aList.prev();// -> D aList.prev();// -> C aList.prev();// -> B aList.prev();// -> A aList.prev();// -> D ``` ================================================ FILE: _posts/en/javascript/2016-08-02-copy-to-clipboard.md ================================================ --- layout: post title: Copy to Clipboard tip-number: 56 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: This week I had to create a common "Copy to Clipboard" button, I've never created one before and I want to share how I made it. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/copy-to-clipboard/ categories: - en - javascript --- This is a simple tip, this week I had to create a common "Copy to Clipboard" button, I've never created one before and I want to share how I made it. It's easy, the bad thing is that we must add an `` with the text to be copied to the DOM. Then, we selected the content and execute the copy command with [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). `execCommand('copy')` will copy the actual selected content. Also, this command that now is [supported](http://caniuse.com/#search=execCommand) by all the latest version of browsers, allows us to execute another system commands like `copy`, `cut`, `paste`, and make changes like fonts color, size, and much more. ```js document.querySelector('#input').select(); document.execCommand('copy'); ``` ##### Playground
JS Bin on jsbin.com
================================================ FILE: _posts/en/javascript/2016-08-10-comma-operaton-in-js.md ================================================ --- layout: post title: Comma operator in JS tip-number: 57 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: When placed in an expression, it evaluates every expression from left to right and returns the last one. redirect_from: - /en/comma-operaton-in-js/ categories: - en - javascript --- Apart from being just a delimiter, the comma operator allows you to put multiple statements in a place where one statement is expected. Eg:- ```js for(var i=0, j=0; i<5; i++, j++, j++){ console.log("i:"+i+", j:"+j); } ``` Output:- ```js i:0, j:0 i:1, j:2 i:2, j:4 i:3, j:6 i:4, j:8 ``` When placed in an expression, it evaluates every expression from left to right and returns the right most expression. Eg:- ```js function a(){console.log('a'); return 'a';} function b(){console.log('b'); return 'b';} function c(){console.log('c'); return 'c';} var x = (a(), b(), c()); console.log(x); // Outputs "c" ``` Output:- ```js "a" "b" "c" "c" ``` * Note: The comma(`,`) operator has the lowest priority of all javascript operators, so without the parenthesis the expression would become: `(x = a()), b(), c();`. ##### Playground
JS Bin on jsbin.com
================================================ FILE: _posts/en/javascript/2016-08-17-break-continue-loop-functional.md ================================================ --- layout: post title: Breaking or continuing loop in functional programming tip-number: 58 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: A common task for us is iterate over a list looking for a value or values, but we can't return from inside a loop so we will have to iterate the whole array, even if the item we search is the first in the list, in this tip we will see how to short circuit with `.some` and `.every`. redirect_from: - /en/break-continue-loop-functional/ categories: - en - javascript --- A common requirement of iteration is cancelation. Using `for` loops we can `break` to end iteration early. ```javascript const a = [0, 1, 2, 3, 4]; for (var i = 0; i < a.length; i++) { if (a[i] === 2) { break; // stop the loop } console.log(a[i]); } //> 0, 1 ``` Another common requirement is to close over our variables. A quick approach is to use `.forEach` but then we lack the ability to `break`. In this situation the closest we get is `continue` functionality through `return`. ```javascript [0, 1, 2, 3, 4].forEach(function(val, i) { if (val === 2) { // how do we stop? return true; } console.log(val); // your code }); //> 0, 1, 3, 4 ``` The `.some` is a method on Array prototype. It tests whether some element in the array passes the test implemented by the provided function. If any value is returning true, then it stops executing. Here is a [MDN link](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some) for more details. An example quoted from that link ```javascript const isBiggerThan10 = numb => numb > 10; [2, 5, 8, 1, 4].some(isBiggerThan10); // false [12, 5, 8, 1, 4].some(isBiggerThan10); // true ``` Using `.some` we get iteration functionally similar to `.forEach` but with the ability to `break` through `return` instead. ```javascript [0, 1, 2, 3, 4].some(function(val, i) { if (val === 2) { return true; } console.log(val); // your code }); //> 0, 1 ``` You keep returning `false` to make it `continue` to next item. When you return `true`, the loop will `break` and `a.some(..)` will `return` `true`. ```javascript // Array contains 2 const isTwoPresent = [0, 1, 2, 3, 4].some(function(val, i) { if (val === 2) { return true; // break } }); console.log(isTwoPresent); //> true ``` Also there is `.every`, which can be used. We have to return the opposite boolean compared to `.some`. ##### Playground
JS Bin on jsbin.com
================================================ FILE: _posts/en/javascript/2016-08-25-keyword-var-vs-let.md ================================================ --- layout: post title: ES6, var vs let tip-number: 59 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: In this tip, I will introduce the block-scope difference between keyword var and let. Should I replace var by let? let's take a look redirect_from: - /en/keyword-var-vs-let/ categories: - en - javascript --- ### Overview - The scope of a variable defined with `var` is function scope or declared outside any function, global. - The scope of a variable defined with `let` is block scope. ```js function varvslet() { console.log(i); // i is undefined due to hoisting // console.log(j); // ReferenceError: j is not defined for( var i = 0; i < 3; i++ ) { console.log(i); // 0, 1, 2 }; console.log(i); // 3 // console.log(j); // ReferenceError: j is not defined for( let j = 0; j < 3; j++ ) { console.log(j); }; console.log(i); // 3 // console.log(j); // ReferenceError: j is not defined } ``` ### Difference Details - Variable Hoisting `let` will not hoist to the entire scope of the block they appear in. By contrast, `var` could hoist as below. ```js { console.log(c); // undefined. Due to hoisting var c = 2; } { console.log(b); // ReferenceError: b is not defined let b = 3; } ``` - Closure in Loop `let` in the loop can re-binds it to each iteration of the loop, making sure to re-assign it the value from the end of the previous loop iteration, so it can be used to avoid issue with closures. ```js for (var i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); // output '5' 5 times }, 100); } ``` After replacing `var` with `let` ```js // print 1, 2, 3, 4, 5 for (let i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); // output 0, 1, 2, 3, 4 }, 100); } ``` ### Should I replace `var` with `let`? > NO, `let` is the new block scoping `var`. That statement emphasizes that `let` should replace `var` only when `var` was already signaling block scoping stylistically. Otherwise, leave `var` alone. `let` improves scoping options in JS, not replaces. `var` is still a useful signal for variables that are used throughout the function. ### `let` compatibility - In server side, such as Node.js, you can safely use the `let` statement now. - In client side, through a transpiler (like [Traceur](https://github.com/google/traceur-compiler)), you can safely use the `let` statement. Otherwise, please consider the browser support [here](http://caniuse.com/#search=let) ### Playground
JS Bin on jsbin.com
### More info - [Let keyword vs var keyword](http://stackoverflow.com/questions/762011/let-keyword-vs-var-keyword) - [For and against let](https://davidwalsh.name/for-and-against-let) - [Explanation of `let` and block scoping with for loops](http://stackoverflow.com/questions/30899612/explanation-of-let-and-block-scoping-with-for-loops/30900289#30900289). ================================================ FILE: _posts/en/javascript/2016-10-28-three-useful-hacks.md ================================================ --- layout: post title: Three useful hacks tip-number: 60 tip-username: leandrosimoes tip-username-profile: https://github.com/leandrosimoes tip-tldr: Three very useful and syntax sugar hacks to speed up your development. redirect_from: - /en/three-useful-hacks/ categories: - en - javascript --- #### Getting array items from behind to front If you want to get the array items from behind to front, just do this: ```javascript var newArray = [1, 2, 3, 4]; console.log(newArray.slice(-1)); // [4] console.log(newArray.slice(-2)); // [3, 4] console.log(newArray.slice(-3)); // [2, 3, 4] console.log(newArray.slice(-4)); // [1, 2, 3, 4] ``` #### Short-circuits conditionals If you have to execute a function just if a condition is `true`, like this: ```javascript if(condition){ dosomething(); } ``` You can use a short-circuit just like this: ```javascript condition && dosomething(); ``` #### Set variable default values using "||" If you have to set a default value to variables, you can simple do this: ```javascript var a; console.log(a); //undefined a = a || 'default value'; console.log(a); //default value a = a || 'new value'; console.log(a); //default value ``` ================================================ FILE: _posts/en/javascript/2017-01-19-binding-objects-to-functions.md ================================================ --- layout: post title: Binding objects to functions tip-number: 61 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Understanding how to use `Bind` method with objects and functions in JavaScript redirect_from: - /en/binding-objects-to-functions/ categories: - en - javascript --- More than often, we need to bind an object to a function’s this object. JS uses the bind method when this is specified explicitly and we need to invoke desired method. ### Bind syntax ```js fun.bind(thisArg[, arg1[, arg2[, ...]]]) ``` ## Parameters **thisArg** `this` parameter value to be passed to target function while calling the `bound` function. **arg1, arg2, ...** Prepended arguments to be passed to the `bound` function while invoking the target function. **Return value** A copy of the given function along with the specified `this` value and initial arguments. ### Bind method in action in JS ```js const myCar = { brand: 'Ford', type: 'Sedan', color: 'Red' }; const getBrand = function () { console.log(this.brand); }; const getType = function () { console.log(this.type); }; const getColor = function () { console.log(this.color); }; getBrand(); // object not bind,undefined getBrand(myCar); // object not bind,undefined getType.bind(myCar)(); // Sedan let boundGetColor = getColor.bind(myCar); boundGetColor(); // Red ``` ================================================ FILE: _posts/en/javascript/2017-03-09-working-with-websocket-timeout.md ================================================ --- layout: post title: Working With Websocket Timeout tip-number: 63 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: A trick to control the timeout categories: - en - javascript --- In case of established websocket connection, server or firewall could timeout and terminate the connection after a period of inactivity. To deal with this situation, we send periodic message to the server. To control the timeout we will add two functions in our code : one to make sure connection keep alive and another one to cancel the keep alive. Also we need a common ```timerID``` variable. Let’s have a look on implementation- ```js var timerID = 0; function keepAlive() { var timeout = 20000; if (webSocket.readyState == webSocket.OPEN) { webSocket.send(''); } timerId = setTimeout(keepAlive, timeout); } function cancelKeepAlive() { if (timerId) { clearTimeout(timerId); } } ``` Now as we have both of our desired function for the task, we will place ```keepAlive()``` function at the end of ```onOpen()``` method of websocket connection and ```cancelKeepAlive()``` function at the end of ```onClose()``` method of websocket connection. Yes! We have perfectly implemented hack for websocket timeout problem. ================================================ FILE: _posts/en/javascript/2017-03-12-3-array-hacks.md ================================================ --- layout: post title: 3 Array Hacks tip-number: 64 tip-username: hassanhelfi tip-username-profile: https://twitter.com/hassanhelfi tip-tldr: Arrays are everywhere and with the new spread operators introduced in ECMAScript 6, you can do awesome things with them. In this post I will show you 3 useful tricks you can use when programming. categories: - en - javascript --- Arrays are everywhere in JavaScript and with the new [spread operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator) introduced in ECMAScript 6, you can do awesome things with them. In this post I will show you 3 useful tricks you can use when programming. ### 1. Iterating through an empty array JavaScript arrays are sparse in nature in that there are a lot of holes in them. Try creating an array using the Array’s constructor and you will see what I mean. ```javascript > const arr = new Array(4); [undefined, undefined, undefined, undefined] ``` You may find that iterating over a sparse array to apply a certain transformation is hard. ```javascript > const arr = new Array(4); > arr.map((elem, index) => index); [undefined, undefined, undefined, undefined] ``` To solve this, you can use `Array.apply` when creating the array. ```javascript > const arr = Array.apply(null, new Array(4)); > arr.map((elem, index) => index); [0, 1, 2, 3] ``` ### 2. Passing an empty parameter to a method If you want to call a method and ignore one of its parameters, then JavaScript will complain if you keep it empty. ```javascript > method('parameter1', , 'parameter3'); Uncaught SyntaxError: Unexpected token , ``` A workaround that people usually resort to is to pass either `null` or `undefined`. ```javascript > method('parameter1', null, 'parameter3') // or > method('parameter1', undefined, 'parameter3'); ``` I personally don’t like using `null` since JavaScript treats it as an object and that’s just weird. With the introduction of spread operators in ES6, there is a neater way of passing empty parameters to a method. As previously mentioned, arrays are sparse in nature and so passing empty values to it is totally okay. We'll use this to our advantage. ```javascript > method(...['parameter1', , 'parameter3']); // works! ``` ### 3. Unique array values I always wonder why the Array constructor does not have a designated method to facilitate the use of unique array values. Spread operators are here for the rescue. Use spread operators with the `Set` constructor to generate unique array values. ```javascript > const arr = [...new Set([1, 2, 3, 3])]; [1, 2, 3] ``` ================================================ FILE: _posts/en/javascript/2017-03-16-tapping-for-quick-debugging.md ================================================ --- layout: post title: Tapping for quick debugging tip-number: 65 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: This little beastie here is tap. A really useful function for quick-debugging chains of function calls, anonymous functions and, actually, whatever you just want to print. tip-md-link: https://github.com/loverajoel/jstips/blob/master/_posts/en/javascript/2017-03-16-tapping-for-quick-debugging.md categories: - en - javascript --- This little beastie here is tap. A really useful function for quick-debugging chains of function calls, anonymous functions and, actually, whatever you just want to print. ``` javascript function tap(x) { console.log(x); return x; } ``` Why would you use instead of good old `console.log`? Let me show you an example: ``` javascript bank_totals_by_client(bank_info(1, banks), table) .filter(c => c.balance > 25000) .sort((c1, c2) => c1.balance <= c2.balance ? 1 : -1 ) .map(c => console.log(`${c.id} | ${c.tax_number} (${c.name}) => ${c.balance}`)); ``` Now, suppose you're getting nothing from this chain (possibly an error). Where is it failing? Maybe `bank_info` isn't returning anything, so we'll tap it: ``` javascript bank_totals_by_client(tap(bank_info(1, banks)), table) ``` Depending on our particular implementation, it might print something or not. I'll assume the information that we got from our tapping was correct and therefore, bank_info isn't causing anything. We must then move on to the next chain, filter. ``` javascript .filter(c => tap(c).balance > 25000) ``` Are we receiving any c's (clients actually)? If so, then bank_totals_by_client works alright. Maybe it's the condition within the filter? ``` javascript .filter(c => tap(c.balance > 25000)) ``` Ah! Sweet, we see nothing but `false` printed, so there's no client with >25000, that's why the function was returning nothing. ## (Bonus) A more advanced tap. ``` javascript function tap(x, fn = x => x) { console.log(fn(x)); return x; } ``` Now we're talking about a more advanced beast, what if we wanted to perform a certain operation *prior* to tapping? i.e, we want to access a certain object property, perform a logical operation, etc. with our tapped object? Then we call old good tap with an extra argument, a function to be applied at the moment of tapping. ``` javascript tap(3, x => x + 2) === 3; // prints 5, but expression evaluates to true, why :-)? ``` ================================================ FILE: _posts/en/javascript/2017-03-29-recursion-iteration-and-tail-calls-in-js.md ================================================ --- layout: post title: Recursion, iteration and tail calls in JS tip-number: 67 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: If you've been on the business for some time, you have, most likely, come across the definition of recursion, for which the factorial of a given number `n! = n * n - 1 * ... * 1` is a standard example... tip-md-link: https://github.com/loverajoel/jstips/blob/master/_posts/en/javascript/2017-03-29-recursion-iteration-and-tail-calls-in-js.md categories: - en - javascript --- If you've been on the business for some time, you have, most likely, come across the definition of recursion, for which the factorial of a given number `n! = n * (n - 1) * ... * 1` is a standard example. ``` javascript function factorial(n) { if (n === 0) { return 1; } return n * factorial(n - 1); } ``` The example shown above is but the most naive implementation of the factorial function. For the sake of completeness, let's look at how this executes for `n = 6`: - factorial(6) - 6 * factorial(5) - 5 * factorial (4) - 4 * factorial(3) - 3 * factorial(2) - 2 * factorial(1) - 1 * factorial(0) - 1 - (resuming previous execution) 1 * 1 = 1 - (resuming...) 2 * 1 = 2 - (...) 3 * 2 = 6 - ... 4 * 6 = 24 - 5 * 24 = 120 - 6 * 120 = 720 - factorial(6) = 720 Now, we must be very cautious as to what's happening so we can understand what is to come next. When we invoke a function, several things happen at once. The location to which we must return to after calling the function is saved, along with the information of the current frame (i.e, the value of n). Then space is allocated for the new function and a new frame is born. This goes on and on, we keep stacking these frames and then we unwind that stack, replacing function calls with values returned by them. Another thing to notice is the shape of the process generated by our function. You might not be surprised if I call this shape *recursive*. We have, thus, a *recursive process*. Let's take a look at a second implementation of this function. ``` javascript function factorial(n, res) { if (n === 0) { return res; } return factorial(n - 1, res * n); } ``` We can encapsulate functionality a bit further by defining an inner function. ``` javascript function factorial(n) { function inner_factorial(n, res) { if (n === 0) { return res; } return inner_factorial(n - 1, res * n); } return inner_factorial(n, 1); } ``` Let's take a look at how this gets executed: - factorial(6) - inner anonymous function (iaf) gets called with (n = 6, res = 1) - iaf(5, 1 * 6) - iaf(4, 6 * 5) - iaf(3, 30 * 4) - iaf(2, 120 * 3) - iaf(1, 360 * 2) - iaf(0, 720) - 720 - 720 - 720 - 720 - 720 - 720 - 720 - iaf (6, 1) = 720 - factorial(6) = 720 You might notice that we didn't need to perform any calculation after unwinding the stack. We just returned a value. But, according to our rules, we had to save the state as a stack frame, even if it weren't of any use later in the chain. Our rules, however, are not applied to every language out there. In fact, in Scheme it's mandatory for such chains to be optimized with tail call optimization. This ensures that our stack is not filled with unnecessary frames. Our previous calculation would look, thus, this way: - factorial(6) - iaf(6, 1) - iaf(5, 6) - iaf(4, 30) - iaf(3, 120) - iaf(2, 360) - iaf(1, 720) - iaf(0, 720) - 720 Which in turns, looks an awfully lot like ``` javascript res = 1; n = 6; while(n > 1) { res = res * n; n--; } ``` This means, we actually have an *iterative process*, even if we're using recursion. How cool is that? The good news is, this is a feature in ES6. As long as your recursive call is in tail position and your function has strict mode, tail call optimization will kick in and save you from having a `maximum stack size exceeded` error. UPDATE Dec 1, 2017: The only major browser with tail call optimization is Safari.1 V8 has an implentation2 but has not shipped it yet3 for the reasons listed. 1: https://kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation) 2: https://bugs.chromium.org/p/v8/issues/detail?id=4698 3: https://v8project.blogspot.com/2016/04/es6-es7-and-beyond.html ================================================ FILE: _posts/en/javascript/2017-04-03-why-you-should-use-Object.is()-in-equality-comparison.md ================================================ --- layout: post title: Why you should use Object.is() in equality comparison tip-number: 68 tip-username: TarekAlQaddy tip-username-profile: https://github.com/TarekAlQaddy tip-tldr: A good solution for the looseness of equality comparisons in javascript categories: - en - javascript --- We all know that JavaScript is loosely typed and in some cases it fall behind specially when it comes to quality comparison with '==', comparing with '==' gives unexpected results due to whats called coercion or casting "converting one of the 2 operands to the other's type then compare". ``` javascript 0 == ' ' //true null == undefined //true [1] == true //true ``` So they provided us with the triple equal operator '===' which is more strict and does not coerce operands, However comparing with '===' is not the best solution you can get: ``` javascript NaN === NaN //false ``` The great news that in ES6 there is the new 'Object.is()' which is better and more precise it has the same features as '===' and moreover it behaves well in some special cases: ``` javascript Object.is(0 , ' '); //false Object.is(null, undefined); //false Object.is([1], true); //false Object.is(NaN, NaN); //true ``` Mozilla team doesn't think that Object.is is "stricter" than '===', they say that we should think of how this method deal with NaN, -0 and +0 but overall I think it is now a good practice in real applications. Now this table illustrates.. ![differences of operators in equality comparisons javascript](http://i.imgur.com/pCyqkLc.png) ## References: [Equality comparisons and sameness](http://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness) ================================================ FILE: _posts/en/javascript/2017-04-05-picking-and-rejecting-object-properties.md ================================================ --- layout: post title: Picking and rejecting object properties tip-number: 70 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Sometimes we need to whitelist certain attributes from an object, say we've got an array representation of a database table and we need to `select` just a few fields for some function. categories: - en - javascript --- Sometimes we need to whitelist certain attributes from an object, say we've got an array representation of a database table and we need to `select` just a few fields for some function: ``` javascript function pick(obj, keys) { return keys.map(k => k in obj ? {[k]: obj[k]} : {}) .reduce((res, o) => Object.assign(res, o), {}); } const row = { 'accounts.id': 1, 'client.name': 'John Doe', 'bank.code': 'MDAKW213' }; const table = [ row, {'accounts.id': 3, 'client.name': 'Steve Doe', 'bank.code': 'STV12JB'} ]; pick(row, ['client.name']); // Get client name table.map(row => pick(row, ['client.name'])); // Get a list of client names ``` There's a bit of skulduggery going on in pick. First, we `map` a function over the keys that will return, each time, an object with only the attribute pointed by the current key (or an empty object if there's no such attribute in the object). Then, we `reduce` this collection of single-attribute objects by merging the objects. But what if we want to `reject` the attributes? Well, the function changes a bit ``` javascript function reject(obj, keys) { return Object.keys(obj) .filter(k => !keys.includes(k)) .map(k => Object.assign({}, {[k]: obj[k]})) .reduce((res, o) => Object.assign(res, o), {}); } // or, reusing pick function reject(obj, keys) { const vkeys = Object.keys(obj) .filter(k => !keys.includes(k)); return pick(obj, vkeys); } reject({a: 2, b: 3, c: 4}, ['a', 'b']); // => {c: 4} ``` ================================================ FILE: _posts/en/javascript/2017-04-11-protocols-for-the-brave.md ================================================ --- layout: post title: Protocols for the Brave tip-number: 73 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: You might have heard about the old ways gaining hype recently, and we don't mean praying to the gods of the north. Today we're introducing a feature found in Clojure which allows you to define interfaces for your classes. categories: - en - javascript --- You might have heard about the old ways gaining hype recently, and we don't mean praying to the gods of the north. Functional programming is the rediscovered toy which is bringing some sanity to the world of mutable state and global bindings. Today we're introducing a feature found in Clojure which allows you to define interfaces for your classes. Let's look at one-off implementation: ``` javascript const protocols = (...ps) => ps.reduce((c, p) => p(c), Object); const Mappable = (klass) => { return class extends klass { map() { throw 'Not implemented'; } }; }; const Foldable = (klass) => { return class extends klass { fold() { throw 'Not implemented'; } }; }; class NaturalNumbers extends protocols(Mappable, Foldable) { constructor() { super(); this.elements = [1,2,3,4,5,6,7,8,9]; } map (f) { return this.elements.map(f); } fold (f) { return this.elements.reduce(f, this.elements, 0); } } ``` Yes, we're building a chain of class inheritance up there with that reduce boy. It's pretty cool. We're doing it dynamically! You see, each protocol receives a base class (Object) and extends it somehow returning the new class. The idea is similar to that of interfaces. We supply method signatures for the protocol and make sure we provide implementations for it on our base classes. What's so cool about it? We get to write things like these: ``` javascript const map = f => o => o.map(f); const fold = f => o => o.fold(f); const compose = (... fns) => fns.reduce((acc, f) => (x) => acc(f(x)), id); ``` Ok, maybe we could have written those two functions without the above fuzz but, now that we know `NaturalNumbers` are `Mappable`, we can call `map` on them and trust it will return the right result. Furthermore, with our third function, we can *compose* any number of operations defined in protocols cleanly: ``` javascript const plus1 = x => x + 1; const div5 = x => x / 5; const plus_then_div = compose(map(div5), map(plus1)); console.log(plus_then_div(new NaturalNumbers)); // => [ 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2 ] ``` More important, if we know that an object of ours is `Mappable`, we know `map` will work on it. Protocols gives us an idea of what we can do with an object and help us abstract common operations between data types, thus reducing the overhead of dealing with a hundred functions. What is easier? To have a hundred functions for every different object or ten functions that work on a hundred objects? ================================================ FILE: _posts/en/javascript/2017-04-24-improving-your-async-functions-with-webworkers.md ================================================ --- layout: post title: Improving your Async functions with WebWorkers tip-number: 74 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: JS runs in a single thread in the browser, this is the truth. In this tip I'll show you how to unleash the full power of asynchronous processing with Web Workers. categories: - en - javascript --- > JS shall have but one Thread (in the browser at least) > > -- Thus spoke the master programmer. JS runs in a single thread in the browser, this is the truth. Somewhere in its own universe, there exists a Queue which holds messages and functions associated with them. Every time an event (i.e, a user clicks a button) is registered, there's a runtime check to see whether there's any listener attached to that event. If there's one, it will enqueue the message. Otherwise, it will be lost forever. Now, our event loop processes one message at a time, meaning that if you do some CPU intensive operation (i.e, number crunching) this will indeed 'block' the one Thread, rendering our application useless. This is true even for `async` functions, which will be queued as soon as invoked and executed as soon as possible (immediately given the queue is empty). I/O such as requests to external resources are non-blocking though, so you can request a file as large as you want without fear. The associated callback, however, will show the same characteristics of an `async` function. Strategies for processing lots of data vary a lot. You could partition data and set timeouts for processing bits of it a time for example. But to unleash the full power of asynchronous processing, you should use Web Workers. To do so, you separate the processing part in a different file (possibly 'my_worker.js'), create a worker with `newWorker = new Worker('my_worker.js');` and offload the processing to it. ``` javascript // my_worker.js const do_a_lot_of_processing = (data) => { .... } onmessage = (e) => { postMessage(do_a_lot_of_processing(e.data)); } // main.js const myWorker = new Worker('my_worker.js'); async function get_useful_data() { const raw_data = await request(some_url); myWorker.postmessage(raw_data); } const show_data => (e) { const data = e.data; ... } myWorker.onmessage(show_data); get_useful_data(); ``` Your mileage may vary of course, and there are many abstractions that can be built upon this model. ================================================ FILE: _posts/en/javascript/2017-06-14-closures-inside-loops.md ================================================ --- layout: post title: Closures inside loops tip-number: 76 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Closure in a loop is an interesting topic and this is the tip to be a master of it categories: - en - javascript --- If you ever come across the likes of ```javascript var funcs = []; for (var i = 0; i < 3; i++) { funcs[i] = function() { console.log("i value is " + i); }; } for (var k = 0; k < 3; k++) { funcs[k](); } ``` You will notice that the expected output of ``` i value is 0 i value is 1 i value is 2 ``` Doesn't match the actual output which will resemble ``` i value is 3 i value is 3 i value is 3 ``` This is because of how the capturing mechanism of closures work and how `i` is represented internally. To solve this situation you can do as follows: ```javascript for (var i = 0; i < 3; i++) { funcs[i] = (function(value) { console.log("i value is " + i); })(i); } ``` Which effectively copies i by value by handing it to our closure or ```javascript for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("i value is " + i); } } ``` Where `let` scopes the variable to our `for` loop and produces a new value each iteration, thus `i` will be bound to different values on our closures as expected. ================================================ FILE: _posts/en/javascript/2017-06-14-immutable-structures-and-cloning.md ================================================ --- layout: post title: Immutable structures and cloning tip-number: 78 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Object cloning is a tricky, full of edge-cases, endeavor. The reason is simple enough. Objects maintain internal state, and that is much abused. There are countless techniques, or better phrased, countless derivations of the same technique. categories: - en - javascript --- Object cloning is a tricky, full of edge-cases, endeavor. The reason is simple enough. Objects maintain internal state, and that is much abused. There are countless techniques, or better phrased, countless derivations of the same technique. Cloning an object is an indicator that your application is growing, and that you've got a complex object which you'd want to treat as an immutable value, i.e operate on it while maintaining a previous state. If the object is in your control, you're lucky. A bit of refactoring here and there might lead you to a point where you avoid the problem entirely by rethinking your object's structure and behavior. With the rediscovering of functional programming techniques, a myriad of debates have been held about immutable structures and how they offer exactly what you seek for. Mutable state is the root of all evil, some might argue. We encourage to reach **ImmutableJS** by Facebook which provides a nice set of immutable structures free for use. By rethinking your object's inner workings and separating state from behavior, making each function consume a state to produce a new one - much like the Haskell's **State** monad - you will reduce many nuisances. If the object is outside your control, you're partly out of luck. This can be circumvented by creating convoluted computations where you solve for yourself circular references and reach enlightenment. However, as you're using external objects anyways, and they must come, as their name says, from external sources, then you might be more comfortable handling the matter to yet another external library and focus on what matters the most, i.e, your application itself. One such library is [pvorb/clone](https://github.com/pvorb/clone), which has a very simple API. To clone an object you only have to ``` javascript var clone = require('clone'); var a = {foo: {bar: 'baz'}}; var b = clone(a); a.foo.bar = 'foo'; console.log(a); // {foo: {bar: 'foo'}} console.log(b); // {foo: {bar: 'baz'}} ``` There are, of course, many more libraries that allow you to do the same such as [Ramda](http://ramdajs.com/docs/#clone), [lodash.clonedeep](https://www.npmjs.com/package/lodash.clonedeep) and [lodash.clone](https://www.npmjs.com/package/lodash.clone). As an end note, if you are serious about dealing with immutable structures, you might want to check **ClojureScript** or (for those that feel that Haskell's worth a shot) **PureScript**. We neither encourage, nor condemn, the use of self made cloning mechanisms. Only noting that considerable work has been done on the area and that you'd probably be better of reusing than reinventing the wheel. ================================================ FILE: _posts/en/javascript/2017-06-15-looping-over-arrays.md ================================================ --- layout: post title: Looping over arrays tip-number: 79 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: There's a few methods for looping over arrays in Javascript. We'll start with the classical ones and move towards additions made to the standard. categories: - en - javascript --- # Looping over arrays There're few methods for looping over arrays in Javascript. We'll start with the classical ones and move towards additions made to the standard. ## while ```javascript let index = 0; const array = [1,2,3,4,5,6]; while (index < array.length) { console.log(array[index]); index++; } ``` ## for (classical) ```javascript const array = [1,2,3,4,5,6]; for (let index = 0; index < array.length; index++) { console.log(array[index]); } ``` ## forEach ```javascript const array = [1,2,3,4,5,6]; array.forEach(function(current_value, index, array) { console.log(`At index ${index} in array ${array} the value is ${current_value}`); }); // => undefined ``` ## map The last construct was useful, however, it doesn't return a new array which might be undesirable for your specific case. `map` solves this by applying a function over every element and then returning the new array. ```javascript const array = [1,2,3,4,5,6]; const square = x => Math.pow(x, 2); const squares = array.map(square); console.log(`Original array: ${array}`); console.log(`Squared array: ${squares}`); ``` The full signature for `map` is `.map(current_value, index, array)`. ## reduce From MDN: > The reduce() method applies a function against an accumulator and each element > in the array (from left to right) to reduce it to a single value. ```javascript const array = [1,2,3,4,5,6]; const sum = (x, y) => x + y; const array_sum = array.reduce(sum, 0); console.log(`The sum of array: ${array} is ${array_sum}`); ``` ## filter Filters elements on an array based on a boolean function. ```javascript const array = [1,2,3,4,5,6]; const even = x => x % 2 === 0; const even_array = array.filter(even); console.log(`Even numbers in array ${array}: ${even_array}`); ``` ## every Got an array and want to test if a given condition is met in every element? ```javascript const array = [1,2,3,4,5,6]; const under_seven = x => x < 7; if (array.every(under_seven)) { console.log('Every element in the array is less than 7'); } else { console.log('At least one element in the array was bigger than 7'); } ``` ## some Test if at least one element matches our boolean function. ```javascript const array = [1,2,3,9,5,6,4]; const over_seven = x => x > 7; if (array.some(over_seven)) { console.log('At least one element bigger than 7 was found'); } else { console.log('No element bigger than 7 was found'); } ``` ================================================ FILE: _posts/en/javascript/2017-09-01-hash-maps-without-side-effects.md ================================================ --- layout: post title: Hash maps without side effects tip-number: 73 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: Create hash maps(without side effects) using `Object.create(null)`. categories: - en - javascript --- # Hash maps without side effects When you want to use javascript object as a hash map(purely for storing data), you might want to create it as follows. ```javascript const map = Object.create(null); ``` When creating a map using object literal(`const map = {}`), the map inherits properties from Object by default. It is equivalent to `Object.create(Object.prototype)`. But by doing `Object.create(null)`, we explicitly specify `null` as its prototype. So it have absolutely no properties, not even constructor, toString, hasOwnProperty, etc. so you're free to use those keys in your data structure if you need to. ## Rationale: ```javascript const dirtyMap = {}; const cleanMap = Object.create(null); dirtyMap.constructor // function Object() { [native code] } cleanMap.constructor // undefined // Iterating maps const key; for(key in dirtyMap){ if (dirtyMap.hasOwnProperty(key)) { // Check to avoid iterating over inherited properties. console.log(key + " -> " + dirtyMap[key]); } } for(key in cleanMap){ console.log(key + " -> " + cleanMap[key]); // No need to add extra checks, as the object will always be clean } ``` ## Notes: * Object.create() was introduced in ES5: [Compatibility](http://kangax.github.io/compat-table/es5/) * ES6 introduced some new structures: [Map](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map), [WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap), [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and [Weak Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) ================================================ FILE: _posts/en/javascript/2018-11-25-creating-immutable-objects-in-native-javascript.md ================================================ --- layout: post title: Creating immutable objects in native JavaScript tip-number: 74 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: With the latest versions of JavaScript it’s possible to create immutable objects. I’ll walk you through how to do it in three different ways. categories: - en - javascript --- # Creating immutable objects in native JavaScript Javascript it’s a flexible language, you can redefine anything. But when projects get complex we find problems with mutable data structures. With the latest versions of JavaScript this situation changed. Now it’s possible to create immutable objects. I’ll walk you through how to do it in three different ways. ### Wait, what means immutable? > Immutability in object means we don’t want our objects to change in any ways once we create them i.e make them read-only type. Let’s suppose we need to define a car [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) and use its properties to perform operations throughout our entire project. We can’t allow modifying by mistake any data. ``` const myTesla = { maxSpeed: 250, batteryLife: 300, weight: 123 }; ``` ## [Object.preventExtensions()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) This method prevents the addition of new properties to our existing object. `preventExtensions()` is a irreversible operation. We can never add extra properties to the object again. ``` Object.isExtensible(myTesla); // true Object.preventExtensions(myTesla); Object.isExtensible(myTesla); // false myTesla.color = 'blue'; console.log(myTesla.color) // undefined ``` ## [Object.seal()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) It prevents additions or deletion of properties. `seal()` also prevents the modification of property descriptors. ``` Object.isSealed(myTesla); // false Object.seal(myTesla); Object.isSealed(myTesla); // true myTesla.color = 'blue'; console.log(myTesla.color); // undefined delete myTesla.batteryLife; // false console.log(myTesla.batteryLife); // 300 Object.defineProperty(myTesla, 'batteryLife'); // TypeError: Cannot redefine property: batteryLife ``` ## [Object.freeze()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) It does the same that `Object.seal()` plus it makes the properties non-writable. ``` Object.isFrozen(myTesla); // false Object.freeze(myTesla); Object.isFrozen(myTesla); // true myTesla.color = 'blue'; console.log(myTesla.color); // undefined delete myTesla.batteryLife; console.log(myTesla.batteryLife); // 300 Object.defineProperty(myTesla, 'batteryLife'); // TypeError: Cannot redefine property: batteryLife myTesla.batteryLife = 400; console.log(myTesla.batteryLife); // 300 ``` ## Extra Use `strict mode` if you want to throw an error when trying to modify an immutable object. ================================================ FILE: _posts/en/javascript/2020-10-13-what-is-a-functional-inheritance.md ================================================ --- layout: post title: What is Functional Inheritance? tip-number: 75 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Functional inheritance is the process of inheriting features by applying an augmenting function to an object instance. categories: - en - javascript --- Functional inheritance is the process of inheriting features by applying an augmenting function to an object instance. The function supplies a closure scope which you can use to keep some data private. The augmenting function uses dynamic object extension to extend the object instance with new properties and methods. Functional mixins are composable factory functions that add properties and behaviors to objects like stations in an assembly line. ```javascript // Base object constructor function function Animal(data) { var that = {}; // Create an empty object that.name = data.name; // Add it a "name" property return that; // Return the object }; // Create achild object, inheriting from the base Animal function Cat(data) { // Create the Animal object var that = Animal(data); // Extend base object that.sayHello = function() { return 'Hello, I\'m ' + that.name; }; return that; }; // Usage var myCat = Cat({ name: 'Rufi' }); console.log(myCat.sayHello()); // Output: "Hello, I'm Rufi" ``` ================================================ FILE: _posts/en/javascript/2020-10-15-what-is-a-currying-function.md ================================================ --- layout: post title: What is a currying function? tip-number: 75 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: A currying function is a function that takes multiple arguments and turns it into a sequence of functions having only one argument at a time. categories: - en - javascript --- A currying function is a function that takes multiple arguments and turns it into a sequence of functions having only one argument at a time. In this way, an n-ary function becomes a unary function, and the last function returns the result of all the arguments together in a function. ```javascript // Normal definition function multiply(a, b, c) { return a * b * c; }; console.log(multiply(1, 2, 3)); // Output: 6 // Simple curry function definition function multiply(a) { return (b) => { return (c) => { return a * b * c; }; }; }; console.log(multiply(1)(2)(3)); // Output: 6 ``` ### Further readings: - [Currying in JavaScript](https://dev.to/suprabhasupi/currying-in-javascript-1k3l) - [Lodash curry](https://lodash.com/docs/#curry) - [JavaScript currying](http://zetcode.com/javascript/currying/) ================================================ FILE: _posts/en/javascript/2020-10-20-what-is-the-temporal-dead-zone.md ================================================ --- layout: post title: What is the Temporal Dead Zone? tip-number: 76 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Temporal Dead Zone is a JavaScript behavior while using variables declared using `let` and `const` keywords. categories: - en - javascript --- Temporal Dead Zone is a JavaScript behavior while using variables declared using `let` and `const` keywords. Since the keywords are block-scoped, the variables declared these keywords could not be accessed before the declaration, and then you will have to witness where variables will be said to be `undefined`. ```javascript function myFunc(){ console.log(greeting); var greeting = 'Hello World!' }; myFunc(); // Output: undefined function myFunc() { console.log(greeting); let greeting = 'Hello World!'; }; myFunc(); // Output: ReferenceError: greeting is not defined function myFunc() { console.log(greeting); const greeting = 'Hello World!'; } myFunc(); // Output: ReferenceError: greeting is not defined ``` ================================================ FILE: _posts/en/javascript/2020-10-22-difference-between-target-and-currentTarget.md ================================================ --- layout: post title: What is the difference between Target and currentTarget in the event context? tip-number: 77 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: target refers to the element that triggers an event. currentTarget to the element that the event listener is listening on. categories: - en - javascript --- `target` refers to the DOM element that triggers an event. Otherwise, `currentTarget` refers to the DOM element that the event listener is listening on. ```html
  • Walk your dog
``` ```js const list = document.querySelector(".todo-list"); list.addEventListener("click", e => { console.log(e.target); // Output:
  • Walk your dog
  • console.log(e.currentTarget); // Output:
      }); ``` ================================================ FILE: _posts/en/javascript/2020-10-27-what-is-a-spread-operator.md ================================================ --- layout: post title: What is a spread operator? tip-number: 78 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: The spread operator is a useful syntax for manipulating arrays and objects. categories: - en - javascript --- The spread operator in JavaScript is a useful syntax for adding elements to an array, combining arrays into one larger one, spreading an array inside the arguments of a function, and more. ```js // Concatenating arrays and objects let arr1 = [1,2,3]; let arr2 = [4,5]; let newArray = [...arr1,...arr2]; console.log(newArray); // Output: [ 1, 2, 3, 4, 5 ] // Copying array elements let arr = ["a","b","c"]; let newArray = [...arr]; console.log(newArray); // Output: ["a", "b", "c"] // Expanding arrays let arr = ["a","b"]; let newArray = [...arr,"c","d"]; console.log(newArray); // Output: ["a", "b", "c", "d"] // Merging objects const userBasic = { name: "Jen", age: 22, }; const userMoreInfo = { country: "Argentina", city: "Córdoba", }; const user = {... userBasic, ... userMoreInfo}; // Output: { name: "Jen", age: 22, country: "Argentina", city: "Córdoba" } ``` ================================================ FILE: _posts/en/javascript/2020-11-04-what-is-a-void-operator.md ================================================ --- layout: post title: What is a void operator? tip-number: 79 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: The void operator returns an undefined value from an evaluated expression categories: - en - javascript --- The `void` operator returns an `undefined` value from an evaluated expression, or in other words; the `void` operator specifies an expression to be evaluated without returning a value. It is commonly used in client-side JavaScript, where the browser should not display the value. ```js function getYear() { return 2020; }; console.log(getYear()); // Output: 2020 console.log(void getYear()); // Output: undefined // Useful use case button.onclick = () => void getYear(); ``` ================================================ FILE: _posts/en/javascript/2020-11-17-what-is-the-promise-executor.md ================================================ --- layout: post title: What is the promise executor? tip-number: 80 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: The method received as an argument for the promise. categories: - en - javascript --- All `Promise` instances accept a method as an argument called the executor. This executor takes two methods as arguments: resolve and reject. Within the executor, if resolve is called, the `Promise` instance becomes fulfilled. If an exception is thrown, reject is called instead, and the `Promise` instance becomes rejected. ```js const executor = (resolve, reject) => { setTimeout(() => resolve("I'm done"), 1000); }; new Promise(executor).then(result => { console.log(result); // Output after 1000ms: I'm done }); ``` ================================================ FILE: _posts/en/javascript/2021-02-02-what-is-the-javascript-ternary-operator.md ================================================ --- layout: post title: What is the JavaScript ternary operator? tip-number: 81 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: The ternary operator is a shortcut for the if statement. categories: - en - javascript --- The ternary operator is a shortcut for the `if` statement. It consists of three operands; a question mark, a condition, and an expression to execute if the condition is true, followed by a colon and another expression to execute if it’s false. ```js let age = 26; // condition ? expression if true : expression if false let drink = (age >= 21) ? "Beer" : "Juice"; console.log(drink); // "Beer" // Equivalent to: let drink; if ((age >= 21)) { drink = "Beer"; } else { drink = "Juice"; } console.log(drink); // "Beer" ``` ================================================ FILE: _posts/en/more/2017-04-06-vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding.md ================================================ --- layout: post title: VueJS, How VueJS makes a copy-update-replace inside the data binding. tip-number: 71 tip-username: pansila tip-username-profile: https://github.com/pansil tip-tldr: In this tip, I will introduce an example to show you how it might conflict with other software. categories: - en - more --- ### Overview Vuejs is a wonderful piece of software that keeps simple yet powerful compared to other prevalent frameworks, like Angularjs and Reactjs. You can quickly master it before you give up by being in awe of the framework complexity unlike them above. But it bites you sometimes if you don't know how it works. Here is an example of the conflict with another simple and popular UI framework, Framework7. ```html

      {% raw %}{{content}}{% endraw %}

      ``` ```js var myApp = new Framework7(); myApp.onPageInit('test', function (page) { new Vue({ el: '#test', data: { content: 'hello world' } }); }); ``` You may be surprised that it doesn't work, nothing changed after new page loaded. In fact, Vue internally makes a copy of the target html template element and replaces the original one with the new copied one after the date binding to it. While during the page loading, Framework7 invokes the `PageInit` callback and then Vue kicks in and makes the update on the `` element. Now the DOM tree has the new copied `` element while Framework7 stays unconcerned and continues to perform the remaining initialization job on the old `` element, like show it eventually, that's the root cause. To work around it, don't let Vue selector targets on the `` element, on its child instead. Then the copy-update-replace won't blow away the whole page anymore. ```js var myApp = new Framework7(); myApp.onPageInit('test', function (page) { new Vue({ el: '#test1', data: { content: 'hello world' } }); }); ``` ### More info - [Vue](https://github.com/Vuejs/Vue) - [Framework7](https://framework7.io/) ================================================ FILE: _posts/en/react/2016-01-02-keys-in-children-components-are-important.md ================================================ --- layout: post title: Keys in children components are important tip-number: 02 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: The key is an attribute that you must pass to all components created dynamically from an array. It's a unique and constant id that React uses to identify each component in the DOM and to know whether it's a different component or the same one. Using keys ensures that the child component is preserved and not recreated and prevents weird things from happening. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /en/keys-in-children-components-are-important/ categories: - en - react --- The [key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children) is an attribute that you must pass to all components created dynamically from an array. It's a unique and constant id that React uses to identify each component in the DOM and to know whether it's a different component or the same one. Using keys ensures that the child component is preserved and not recreated and prevents weird things from happening. > Key is not really about performance, it's more about identity (which in turn leads to better performance). Randomly assigned and changing values do not form an identity [Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939) - Use an existing unique value of the object. - Define the keys in the parent components, not in child components ```javascript //bad ... render() {
      {% raw %}{{item.name}}{% endraw %}
      } ... //good ``` - [Using array index is a bad practice.](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318#.76co046o9) - `random()` will not work ```javascript //bad ``` - You can create your own unique id. Be sure that the method is fast and attach it to your object. - When the number of children is large or contains expensive components, use keys to improve performance. - [You must provide the key attribute for all children of ReactCSSTransitionGroup.](http://docs.reactjs-china.com/react/docs/animation.html) ================================================ FILE: _posts/en/react/2017-03-27-state-to-props-maps-with-memory.md ================================================ --- layout: post title: State to Props maps with memory tip-number: 66 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: You've been building your React apps with a Redux store for quite a while, yet you feel awkward when your components update so often. You've crafted your state thoroughly, and your architecture is such that each component gets just what it needs from it, no more no less. Yet, they update behind your back. Always mapping, always calculating. tip-writer-support: https://www.coinbase.com/loverajoel categories: - en - react --- You've been building your React apps with a Redux store for quite a while, yet you feel awkward when your components update so often. You've crafted your state thoroughly, and your architecture is such that each component gets just what it needs from it, no more no less. Yet, they update behind your back. Always mapping, always calculating. ## Reselector to the rescue How could would it be if you could just calculate what you need? If this part of the state tree changes, then yes, please, update this. Let's take a look at the code for a simple TODOs list with a visibility filter, specially the part in charge of getting the visible TODOs in our container component: ```javascript const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } } ``` Each time our component is updated, this needs be recalculated again. Reselector actually allows us to give our `getVisibleTodos` function memory. So it knows whether the parts of the state we need have changed. If they have, it will proceed, if they haven't, it will just return the last result. Let's bring selectors! ```javascript const getVisibilityFilter = (state) => state.visibilityFilter const getTodos = (state) => state.todos ``` Now that we can *select* which parts of the state we want to keep track of, we're ready to give memory to getVisibleTodos ```javascript import {createSelector} from 'reselect' const getVisibleTodos = createSelector( [getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filtler(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } ) ``` And change our map to give the full state to it: ```javascript const mapStateToProps = (state) => { return { todos: getVisibleTodos(state) } } ``` And that's it! We've given memory to our function. No extra calculations if the involved parts of the state haven't changed! ================================================ FILE: _posts/en/react/2017-04-04-enhancing-react-components-composition.md ================================================ --- layout: post title: Enhancing React components, Composition tip-number: 69 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: React is extremely flexible in terms of how you can structure your components, but this pattern will make your apps more efficient. tip-writer-support: https://www.coinbase.com/loverajoel categories: - en - react --- > At Facebook, we use React in thousands of components, and we haven't found any > use cases where we would recommend creating component inheritance hierarchies. *Long live to composition, inheritance is dead.* So, how do you *extend* a component in React? Well, it's pretty obvious that the guys @Facebook consider inappropriate to inherit from parent components. Let's look at alternatives: ## Planning ahead of time There might be some cases where you have a component which can't know what its children will be ahead of time (like most of us). For them, React gifts `props.children`: ``` javascript function List(props) { return (
        {props.children}
      ); } function DownloadMenu() { return (
    • Download SBCL
    • Download Racket
    • Download Haskell
    • ); } ``` As we can see, `props.children` receives everything that's in between the component's open and closing tag. Furthermore, we can exploit `props` to fill voids: ``` javascript function ListWithHeader(props) { return (
      • {props.header}
      • {props.children}
      ); } function DownloadMenuWithHeader() { return ( }>
    • Download SBCL
    • ...
      ); } ``` ## Generic components and specialization So, we've got this great *FolderView* component ``` javascript function FolderView(props) { return (

      {props.folderName}

        {props.availableActions}
        {props.files}
      ); } ``` This could represent any folder in our filesystem, however, we only want to *specialize* it so it shows only the Pictures and Desktop folders. ``` javascript function DesktopFolder(props) { return ( Create Folder
    • Create Document
    • } files={props.files} /> ); } function PicturesFolder(props) { return ( New Picture } files={props.files} /> ); } ``` And just like so, we've *specialized* our component, without creating any inheritance hierarchy! ================================================ FILE: _posts/en/react/2017-04-10-adventurers-guide-to-react.md ================================================ --- layout: post title: Adventurers Guide to React (Part I) tip-number: 72 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: So you've heard of this *React* thing, and you actually peeked at the docs, maybe even went through some of the pages. Then you suddenly came across mysterious runes that spelled somewhat along the lines of Webpack, Browserify, Yarn, Babel, NPM and yet much more. categories: - en - react --- > I used to be an adventurer like you, but then I took a RTFM in the knee... > > Guard outside of Castle Reducer in the Land of React So you've heard of this *React* thing, and you actually peeked at the docs, maybe even went through some of the pages. Then you suddenly came across mysterious runes that spelled somewhat along the lines of Webpack, Browserify, Yarn, Babel, NPM and yet much more. Thus, you went to their official sites and went through the docs, just to find yourself lost in the ever growing collection of modern tools (tm) to build web applications with JS. Afraid you must be not. ## Yarn, NPM They are just dependency managers, nothing to be afraid of. In fact, they are the less scary part. If you've ever used a GNU/Linux distro, FreeBSD, Homebrew on MacOSX, Ruby and Bundler, Lisp and Quicklisp just to name a few, you'll be familiar with the concept. Should you not be, it's a very simple thing, so don't worry. The Land of React is but a province in a much bigger world, which some call Javascript and others Ecmascript (JS/ES for short). Now suppose you are starting your own town, but you really want to use React's tools for your buildings, because, let's face it, it is popular. The traditional way would be to walk to the Land of React and ask the ruler to handle you some of his tools. The problem is you waste time, and you must keep contact with him so if any tools is faulty, you get a new one. However, with the advent of drones and near-instant travel for them, some smart guys started a registry of the different towns, cities and provinces. Furthermore, they built especial drones that could deliver the tools right to you. Nowadays, to require React in your project, you just have to go ```bash $ npm init . # create town $ npm install --save react # get react and put the materials in the store ``` Handy, isn't it? and free. ## Webpack, Browserify and friends In the old times, you had to build your town all by yourself. If you wanted a statue of John McCarthy you would have to fetch marble and some parentheses on your own. This improved with the advent of *dependency managers*, which could fetch the marble and the parentheses for you. Yet, you had to assemble them on your own. Then came some folks from Copy & Paste (inc) and developed some tools that allowed you to copy and paste your materials on some specific order, so you could have your parentheses over the statue or under it. That was actually pretty cool, you just placed all the material and specified how they were going to be built, and all was peaceful for a while. But this approach had a problem, which I'm sure other folks tried to circumvent. Namely, that if you wanted to replace the parentheses by cars you would have to rebuild your entire town. This was no problem for small towns, but large cities suffered by this approach. Then inspiration was given by the mighty Lord and building and module bundlers were born. This module bundlers allowed you to draw blueprints, and specify exactly how the town was to be constructed. Furthermore, they grew quickly and supported only rebuilding parts of the town, leaving the rest be. ## Babel Babel is a time traveler who can bring materials from the future for you. like ES6/7/8 features. ## How they all mix together Generally, you'll create a folder for your project, fetch dependencies, configure your module bundler so it knows where to search for your code and where to output the distributable. Furthermore, you may want to wire that to a development server so you can get instant feedback on what you're building. However, the documentation of module bundlers is a bit overwhelming. There are so many choices and options for the novice adventurer that he might lose his motivation. ## create-react-app and friends Thus yet another tool was created. ```bash $ npm install -g create-react-app # globally install create-react-app $ create-react-app my-shiny-new-app ``` And that's all. It comes pre-configured so you don't have to go through all of Webpack/Browserify docs just to test React. It also brings testing scripts, a development web server and much more. However, this is not yet the final word to our history. There exists a myriad of different builders and tools for React, which can be seen here https://github.com/facebook/react/wiki/Complementary-Tools ================================================ FILE: _posts/en/react/2017-05-29-upping-performance-by-appending-keying.md ================================================ --- layout: post title: Upping Performance by Appending/Keying tip-number: 75 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: React uses a mechanism called **reconciliation** for efficient rendering on update. categories: - en - react --- React uses a mechanism called **reconciliation** for efficient rendering on update. **Reconciliation** works by recursively comparing the tree of DOM elements, the specifics of the algorithm might be found on the official documents or the source, which is always the best source. This mechanism is partly hidden, and as such, there are some conventions that might be followed to ease its inner workings. One such example is **appending** vs **inserting**. **Reconciliation** compares the list of root's nodes children at the same time, if two children differ, then React will mutate every one. So, for example, if you've got the following: ``` html
      • Sherlock Holmes
      • John Hamish Watson
      ``` And you insert an element at the beginning ``` html
      • Mycroft Holmes
      • Sherlock Holmes
      • John Hamish Watson
      ``` React will start of by comparing the old tree, where `
    • Second
    • ` is the first child and the new tree where `
    • First
    • ` is the new first child. Because they are different, it will mutate the children. If, instead of inserting good Mycroft you appended him, React would perform better, not touching Sherlock nor John. But you can't always append, sometimes you've just got to insert. This is were **keys** come in. If you supply a **key** *attribute* then React will figure out an efficient transformation from the old tree to the new one. ``` html
      • Mycroft Holmes
      • Sherlock Holmes
      • John Hamish Watson
      ``` ================================================ FILE: _posts/en/react/2021-07-18-trace-the-reason-make-your-page-rerender.md ================================================ --- layout: post title: Check the reason make your page re-render by changed props and state tip-number: 74 tip-username: tranvanhuyhoang tip-username-profile: https://github.com/tranvanhuyhoang tip-tldr: The approach of the post is we will console.log at componentDidUpdate. Find out the reason make our component re-render (specifically is look for those states and props changed). categories: - en - react --- > Avoid your page re-render unnecessary is a thing very important in optimizing your app performance. > > I often trace it through the way watching console.log and determine which state and props changed unnecessarily. After that, I handle that problem. The approach of the post is we will console.log at `componentDidUpdate`. Find out the reason make our component re-render (specifically is look for those **states** and **props** changed). This post will example of both styles are **Class** and **Hook**. ## With Class We apply this block code to `componentDidUpdate`: ``` javascript componentDidUpdate(prevProps, prevState) { Object.entries(this.props).forEach(([key, val]) => prevProps[key] !== val && console.log(`Prop '${key}' changed `) ); if (this.state) { Object.entries(this.state).forEach(([key, val]) => prevState[key] !== val && console.log(`State '${key}' changed `) ); } } ``` ## With Hook We apply function `useTraceUpdate` to our main function need to trace: ``` javascript function useTraceUpdate(props) { const prev = useRef(props); useEffect(() => { const changedProps = Object.entries(props).reduce((ps, [k, v]) => { if (prev.current[k] !== v) { ps[k] = [prev.current[k], v]; } return ps; }, {}); if (Object.keys(changedProps).length > 0) { console.log('Changed props:', changedProps); } prev.current = props; }); } // Usage function MyComponent(props) { useTraceUpdate(props); return
      {props.children}
      ; } ``` ## Conclusion That is a way that can help us trace the reason why our component re-render unnecessarily. After look for the reason, we can solve it and improve the performance of the application. ================================================ FILE: _posts/es_ES/angular/2016-01-01-angularjs-digest-vs-apply.md ================================================ --- layout: post title: AngularJs - `$digest` vs `$apply` tip-number: 01 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Los pasos para crear modulos en Javascript son cada vez más numerosos y complicados, pero ¿qué hay de los boilerplate en los nuevos frameworks? tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/angularjs-digest-vs-apply/ categories: - es_ES - angular --- Una de las características más apreciadas de AngularJS es el de dos vías de enlace de datos. Con el fin de hacer de este trabajo AngularJS evalúa los cambios entre el modelo y la vista a través de ciclos (`$digest`). Es necesario comprender este concepto con el fin de entender cómo funciona el framework bajo el capó. Angular evalúa cada observador cuando se dispara un evento. Este es el conocido ciclo de `$digest`. A veces hay que obligarlo a realizar un nuevo ciclo de forma manual y debe elegir la opción correcta porque esta fase es uno de los más influyentes en términos de rendimiento. ### `$apply` Este método del core le permite iniciar el ciclo de digestión de forma explícita. Eso significa que todos los observadores son comprobados; toda la aplicación se inicia el `$digest loop`. Internamente, después de ejecutar un parámetro de la función opcional, se llama `$rootScope.$digest();`. ### `$digest` En este caso, el método `$digest` inicia el ciclo `$digest` para el scope actual y sus descendientes. Usted debe notar que los padres scopes, no serán revisadas, y no se verá afectada. ### Recomendaciones - Use `$apply` o `$digest` sólo cuando eventos del DOM han disparado fuera del AngularJS. - Pasar una expresión de función a `$apply`, esto tiene un mecanismo de control de errores y permite la integración de los cambios en el ciclo de digestión. ```javascript $scope.$apply(() => { $scope.tip = 'Javascript Tip'; }); ``` - Si sólo necesita actualizar el scope actual o sus hijos, usar `$digest`, y evitar un nuevo ciclo de digest para toda la aplicación. La ventaja de rendimiento es evidente por sí mismo. - `$apply()` es un proceso difícil para la máquina y puede conducir a problemas de rendimiento cuando hay una gran cantidad de unión. - Si está utilizando >AngularJS 1.2.X, usar `$evalAsync`, que es un método básico que evaluará la expresión durante el ciclo actual o el siguiente. Esto puede mejorar el rendimiento de su aplicación. ================================================ FILE: _posts/es_ES/javascript/2015-12-29-insert-item-inside-an-array.md ================================================ --- layout: post title: Insertar elemento dentro de un Array tip-number: 00 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: La inserción de un elemento en un array existente, es una tarea común diaria. Se pueden añadir elementos al final de un array mediante push, al principio usando unshift, al medio que usa splice. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/insert-item-inside-an-array/ categories: - es_ES - javascript --- La inserción de un elemento en un array existente, es una tarea común diaria. Se pueden añadir elementos al final de un array mediante push, al principio usando unshift, al medio que usa splice. Esos son los métodos conocidos, pero no quiere decir que no hay una manera más performante. Aquí vamos: Agragar un elemento al final de un array es fácil con push(), pero hay una manera más performante. ```javascript var arr = [1,2,3,4,5]; arr.push(6); arr[arr.length] = 6; // 43% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1 ``` Ambos métodos modifican el array original. No lo cree? [jsperf](http://jsperf.com/push-item-inside-an-array) Ahora bien, si estamos tratando de añadir un elemento al principio de un array: ```javascript var arr = [1,2,3,4,5]; arr.unshift(0); [0].concat(arr); // 98% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1 ``` Aquí un poco más de detalle: unshift editó el array original; concat devuelve un nuevo array.[jsperf](http://jsperf.com/unshift-item-inside-an-array) Para añadir elementos en el medio de un array es fácil con splice, y que es la forma mas potente para hacerlo. ```javascript var items = ['one', 'two', 'three', 'four']; items.splice(items.length / 2, 0, 'hello'); ``` Traté de correr estas pruebas en distintos navegadores y sistemas operativos y los resultados fueron similares. Espero que estos consejos les sea de utilidad para usted y animar a llevar a cabo sus propias pruebas! ================================================ FILE: _posts/es_ES/javascript/2016-01-03-improve-nested-conditionals.md ================================================ --- layout: post title: Mejorar anidaciones Condicionales tip-number: 03 tip-username: AlbertoFuente tip-username-profile: https://github.com/AlbertoFuente tip-tldr: ¿Cómo podemos mejorar y hacer una declaración anidada `if` más eficiente en javascript? redirect_from: - /es_es/improve-nested-conditionals/ categories: - es_ES - javascript --- ¿Cómo podemos mejorar y hacer una declaración anidada `if` más eficiente en javascript? ```javascript if (color) { if (color === 'black') { printBlackBackground(); } else if (color === 'red') { printRedBackground(); } else if (color === 'blue') { printBlueBackground(); } else if (color === 'green') { printGreenBackground(); } else { printYellowBackground(); } } ``` Una forma de mejorar el `if` anidado sería usar `switch`. Aunque es menos verbosa y está más ordenado, no se recomienda su uso porque es muy difícil de depurar errores. Aquí [por eso] (https://toddmotto.com/deprecating-the-switch-statement-for-object-literals/). ```javascript switch(color) { case 'black': printBlackBackground(); break; case 'red': printRedBackground(); break; case 'blue': printBlueBackground(); break; case 'green': printGreenBackground(); break; default: printYellowBackground(); } ``` Pero lo que si tenemos un condicional con varios controles en cada declaración? en este caso, si queremos que sean menos detalladas y más ordenada, podemos utilizar el condicional `switch`. Si pasamos `true` como parámetro a la declaración `switch`, que nos permite colocar un condicional en cada caso. ```javascript switch(true) { case (typeof color === 'string' && color === 'black'): printBlackBackground(); break; case (typeof color === 'string' && color === 'red'): printRedBackground(); break; case (typeof color === 'string' && color === 'blue'): printBlueBackground(); break; case (typeof color === 'string' && color === 'green'): printGreenBackground(); break; case (typeof color === 'string' && color === 'yellow'): printYellowBackground(); break; } ``` Pero siempre hay que evitar tener varios controles en todas las condiciones y evitar el uso de `switch` tanto como sea posible. También hay que tener en cuenta que la forma más eficiente de hacer esto es a través de un `object`. ```javascript var colorObj = { 'black': printBlackBackground, 'red': printRedBackground, 'blue': printBlueBackground, 'green': printGreenBackground, 'yellow': printYellowBackground }; if (color in colorObj) { colorObj[color](); } ``` Aqui puede obtener mas informacion [this](http://www.nicoespeon.com/en/2015/01/oop-revisited-switch-in-js/). ================================================ FILE: _posts/es_ES/javascript/2016-01-04-sorting-strings-with-accented-characters.md ================================================ --- layout: post title: Ordenando cadenas con caracteres acentuados tip-number: 04 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Javascript tiene un método nativo **sort** que permite ordenar matrices. Haciendo un simple `array.sort()` va a tratar a cada entrada del array como una cadena y va a tratar de ordenarla alfabéticamente. Pero cuando intenta ordenar un array de caracteres no ASCII obtendrá un resultado extraño. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/sorting-strings-with-accented-characters/ categories: - es_ES - javascript --- Javascript tiene un método nativo **[sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)** que permite ordenar arrays. Haciendo una simple `array.sort()` va a tratar a cada entrada de la matriz como una cadena y va a tratar de ordenarla alfabéticamente. También puede proporcionar la funcion [own custom sorting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters). ```javascript ['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); // ["Buenos Aires", "Mumbai", "New York", "Shanghai"] ``` Pero cuando intenta para un array de caracteres no ASCII como esto `['E', 'a', 'U', 'c']`, se obtendrá un resultado extraño `[' c ',' e ',' A ',' U ']'. Eso sucede porque sort sólo funciona con el idioma Inglés. Mire el siguiente ejemplo: ```javascript // Spanish ['único','árbol', 'cosas', 'fútbol'].sort(); // ["cosas", "fútbol", "árbol", "único"] // bad order // German ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); // ["Wann", "Woche", "wäre", "wöchentlich"] // bad order ``` Afortunadamente, hay dos maneras de superar este comportamiento [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare) and [Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator). > Ambos métodos tienen sus propios parámetros personalizados con el fin de configurarlo para que funcione adecuadamente. ### Usando `localeCompare()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { return a.localeCompare(b); }); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { return a.localeCompare(b); }); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` ### Usando `Intl.Collator()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` - Para cada método se puede personalizar la ubicación. - De acuerdo a [Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Performance) Intl.Collator es más rápida cuando se compara un gran número de cadenas. Así que cuando se trabaja con arrays de cadenas en un idioma distinto del Inglés, recuerde utilizar este método para evitar la clasificación inesperado. ================================================ FILE: _posts/es_ES/javascript/2016-01-05-differences-between-undefined-and-null.md ================================================ --- layout: post title: Diferencias entre `undefined` y `null` tip-number: 05 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Comprendiendo las diferencias entre `undefined` y `null`. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/differences-between-undefined-and-null/ categories: - es_ES - javascript --- - `undefined` significa una variable no se ha declarado, o se ha declarado pero aún no se le ha asignado un valor - `null` es un valor de asignación que significa "no value" - Javascript establece variables no asignadas con un valor por defecto de `undefined` - Javascript nunca se setea un valor de `null`. Es utilizado por los programadores para indicar que un `var` no tiene ningún valor. - `undefined` no es válido en JSON, mientras que `null` si lo es - `undefined` typeof es `undefined` - `null` typeof es un `object`. [porque?](http://www.2ality.com/2013/10/typeof-null.html) - Ambos son primitivos - Ambos son [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) (`Boolean(undefined) // false`, `Boolean(null) // false`) - Se puede saber si una variable es [undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined) ```javascript typeof variable === "undefined" ``` - Puede comprobar si una variable es [null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null) ```javascript variable === null ``` - The **equality** operator considers them equal, but the **identity** doesn't El operador **igualdad** considera iguales, pero la **identidad** no lo hace ```javascript null == undefined // true null === undefined // false ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md ================================================ --- layout: post title: Escribir un método único para los arrays y elemento unico tip-number: 06 tip-username: mattfxyz tip-username-profile: https://twitter.com/mattfxyz tip-tldr: En lugar de escribir métodos separados para manejar un array y un único parámetro elemento, escriba sus funciones para que puedan manejar ambos. Esto es similar a la forma en que algunas de las funciones de jQuery trabaja(`css` modificará todos los que coinciden con el selector). redirect_from: - /es_es/writing-a-single-method-for-arrays-and-a-single-element/ categories: - es_ES - javascript --- En lugar de escribir métodos separados para manejar un array y un único parámetro elemento, escriba sus funciones para que puedan manejar ambos. Esto es similar a la forma en que algunas de las funciones de jQuery trabaja(`css` modificará todos los que coinciden con el selector). Sólo tienes que concatenar todo en un array. `Array.concat` aceptará un array o un elemento único. ```javascript function printUpperCase(words) { var elements = [].concat(words); for (var i = 0; i < elements.length; i++) { console.log(elements[i].toUpperCase()); } } ``` `printUpperCase` ya está listo para aceptar un único nodo o un conjunto de nodos como su parámetro. ```javascript printUpperCase("cactus"); // => CACTUS printUpperCase(["cactus", "bear", "potato"]); // => CACTUS // BEAR // POTATO ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-07-use-strict-and-get-lazy.md ================================================ --- layout: post title: Utilice strict y obtenga lazy tip-number: 07 tip-username: nainslie tip-username-profile: https://twitter.com/nat5an tip-tldr: Modo estricto en JavaScript hace que sea más fácil para los desarrolladores para escribir "seguro". redirect_from: - /es_es/use-strict-and-get-lazy/ categories: - es_ES - javascript --- Modo estricto en JavaScript hace que sea más fácil para los desarrolladores para escribir "seguro". De forma predeterminada, JavaScript permite al programador ser bastante descuidado, por ejemplo, al no requerir que declaremos nuestras variables con "var". Si bien esto puede parecer como una conveniencia para el desarrollador experimentado, que es también la fuente de muchos errores cuando un nombre de variable está mal escrito o accidentalmente se refirió a salir de su scope de aplicación. Los programadores como para hacer que el ordenador haga las cosas aburridas para nosotros, y automáticamente comprobar nuestro trabajo por los errores. Eso es lo que el código JavaScript "use strict" directiva nos permite hacer, haciendo nuestros errores en errores de JavaScript. Añadimos esta directiva ya sea que en la parte superior de un archivo JS: ```javascript // Whole-script strict mode syntax "use strict"; var v = "Hi! I'm a strict mode script!"; ``` o dentro de la funcion: ```javascript function f() { // Function-level strict mode syntax 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function f2() { return "I'm not strict."; } ``` Con la inclusión de esta directiva en un archivo o una función de JavaScript, vamos a dirigir el motor de JavaScript que se ejecutará en modo estricto que desactiva un grupo de comportamientos que son por lo general no deseable en proyectos de JavaScript más grandes. Entre otras cosas, el modo estricto cambia los siguientes comportamientos: * Las variables sólo pueden ser introducidos cuando van precedidas con "var" * El intento de escribir las propiedades de sólo lectura genera un error de ruido * Tienes que llamar a los constructores con la palabra clave "new" * "this" no está vinculado de manera implícita al objeto global * Un uso permitido muy limitado de eval() * Protege el uso de palabras reservadas o futuros palabras reservadas como nombres de variables El modo estricto es ideal para nuevos proyectos, pero puede ser difícil de introducir en los proyectos más antiguos que aún no lo utilizan en la mayoría de lugares. También puede ser problemático si su cadena de acumulación concatena todos sus archivos js en un archivo grande, ya que esto puede causar todos los archivos que se ejecutan en modo estricto. No es una declaración, sino una expresión literal, ignorada por las versiones anteriores de JavaScript. El modo estricto se apoya en: * Internet Explorer desde version 10. * Firefox desde version 4. * Chrome desde version 13. * Safari desde version 5.1. * Opera desde version 12. [Ver MDN para una descripción más completa de modo estricto](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode). ================================================ FILE: _posts/es_ES/javascript/2016-01-08-converting-a-node-list-to-an-array.md ================================================ --- layout: post title: Convitiendo una Node List a un Array. tip-number: 08 tip-username: Tevko tip-username-profile: https://twitter.com/tevko tip-tldr: Aquí está una manera rápida, segura y reutilizable para convertir una lista de nodos en un Array de elementos del DOM. redirect_from: - /es_es/converting-a-node-list-to-an-array/ categories: - es_ES - javascript --- El método `querySelectorAll` devuelve un array similar a un objeto, llamado una lista de nodos. Estas estructuras de datos se denominan como "Array-like", porque aparecen como un array, pero no se pueden utilizar con los métodos de matriz como `map` y `forEach`. Aquí está una manera rápida, segura y reutilizable para convertir una lista de nodos en un Array de elementos del DOM: ```javascript const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.apply(null, nodelist); //later on .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); //etc... ``` El método `apply` se utiliza para pasar una serie de argumentos a una función con un determinado valor de `this`.[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) afirma que `apply` tendrá un objeto de tipo vector, que es exactamente lo que `querySelectorAll` retorna. Dado que no es necesario especificar un valor para `this` en el contexto de la función, se pasa en `null` o `0`. El resultado es una matriz real de los elementos DOM que contiene todos los métodos de arreglos disponibles. O si esta utilizando ES2015 puede usar esto [spread operator `...`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) ```js const nodelist = [...document.querySelectorAll('div')]; // returns a real array //later on .. nodelist.forEach(...); nodelist.map(...); nodelist.slice(...); //etc... ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-09-template-strings.md ================================================ --- layout: post title: Template Strings tip-number: 09 tip-username: JakeRawr tip-username-profile: https://github.com/JakeRawr tip-tldr: A partir de ES6, JS ahora tiene template strings. redirect_from: - /es_es/template-strings/ categories: - es_ES - javascript --- A partir de ES6, JS ahora tiene template strings Ejemplo: String normal ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log('My name is ' + firstName + ' ' + lastName); // My name is Jake Rawr ``` Template String ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log(`My name is ${firstName} ${lastName}`); // My name is Jake Rawr ``` Usted puede hacer strings multilínea sin `\n' y lógica simple (ie 2+3) en el interior `$ {}` en template strings. Usted también es capaz de modificar la salida de template strings utilizando una función; se les llama [template strings etiquetados](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings#Tagged_template_strings), por ejemplo los usos de cadenas de la plantilla etiquetados. Tambien puede leer mas sobre Template Strings [read](https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2). ================================================ FILE: _posts/es_ES/javascript/2016-01-10-check-if-a-property-is-in-a-object.md ================================================ --- layout: post title: Averiguar si una propiedad está en un Objeto tip-number: 10 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Estas son formas de comprobar si una propiedad está presente en un objeto. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/check-if-a-property-is-in-a-object/ categories: - es_ES - javascript --- Cuando usted tiene que comprobar si una propiedad está presente en un [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects), es probable que esté haciendo algo como esto: ```javascript var myObject = { name: '@tips_js' }; if (myObject.name) { ... } ``` Eso está bien, pero hay que saber que existen dos formas nativas para este tipo de cosas, el [`in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) y [`Object.hasOwnProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty). Cada objeto descendiente de `Object`. ### Observe esta gran diferencia ```javascript var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true 'name' in myObject; // true myObject.hasOwnProperty('valueOf'); // false, valueOf is inherited from the prototype chain 'valueOf' in myObject; // true ``` Ambos difieren en la profundidad a la que se comprueban las propiedades. En otras palabras, `hasOwnProperty` sólo devolverá verdadero si la clave está disponible en ese objeto directamente. Sin embargo, el operador `in` no discrimina entre las propiedades creadas en un objeto y las propiedades heredadas de la cadena de prototipo. Aqui otro ejemplo: ```javascript var myFunc = function() { this.name = '@tips_js'; }; myFunc.prototype.age = '10 days'; var user = new myFunc(); user.hasOwnProperty('name'); // true user.hasOwnProperty('age'); // false, because age is from the prototype chain ``` Mire este link [live examples here](https://jsbin.com/tecoqa/edit?js,console)! Recomiendo que lean [this discussion](https://github.com/loverajoel/jstips/issues/62) acerca de los errores comunes el momento del control de la existencia de una propiedad en objetos. ================================================ FILE: _posts/es_ES/javascript/2016-01-11-hoisting.md ================================================ --- layout: post title: Hoisting tip-number: 11 tip-username: squizzleflip tip-username-profile: https://twitter.com/squizzleflip tip-tldr: Los pasos para crear módulos en JavaScript y constructores son cada vez más numerosos y complicados, pero ¿qué pasa con los boilerplate en nuevos frameworks? redirect_from: - /es_es/hoisting/ categories: - es_ES - javascript --- Comprendiendo [hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) le ayudará a organizar su scope en una funcion. Sólo recuerde, las declaraciones de variables y definiciones de funciones se izan a la cima. Las definiciones de variables no lo son, incluso si se declara y define una variable en la misma línea. Además, una **declaración** de variable permite que el sistema sepa que existe la variable, mientras que **definición** asigne un valor. ```javascript function doTheThing() { // ReferenceError: notDeclared is not defined console.log(notDeclared); // Outputs: undefined console.log(definedLater); var definedLater; definedLater = 'I am defined!' // Outputs: 'I am defined!' console.log(definedLater) // Outputs: undefined console.log(definedSimulateneously); var definedSimulateneously = 'I am defined!' // Outputs: 'I am defined!' console.log(definedSimulateneously) // Outputs: 'I did it!' doSomethingElse(); function doSomethingElse(){ console.log('I did it!'); } // TypeError: undefined is not a function functionVar(); var functionVar = function(){ console.log('I did it!'); } } ``` Para hacer las cosas más fáciles de leer, declarar todas las variables en la parte superior de su función para que quede claro, que variables que estan en el scope están viniendo. Definir las variables antes que necesite utilizarlas. Definir sus funciones en la parte inferior de su scope para mantenerlos fuera de su camino. ================================================ FILE: _posts/es_ES/javascript/2016-01-12-pseudomandatory-parameters-in-es6-functions.md ================================================ --- layout: post title: Pseudo parámetros obligatorios en funciones ES6 tip-number: 12 tip-username: Avraam Mavridis tip-username-profile: https://github.com/AvraamMavridis tip-tldr: En muchos lenguajes de programación, los parámetros de una función por defecto son obligatorios y el desarrollador tiene que definir explícitamente que un parámetro es opcional. redirect_from: - /es_es/pseudomandatory-parameters-in-es6-functions/ categories: - es_ES - javascript --- En muchos lenguajes de programación, los parámetros de una función por defecto son obligatorios y el desarrollador tiene que definir explícitamente que un parámetro es opcional. En Javascript, cada parámetro es opcional, pero puede hacer cumplir este comportamiento sin jugar con el propio cuerpo de una función, aprovechando [** valores predeterminados para los parámetros de es6's **] (http://exploringjs.com/es6/ch_parameter -handling.html # sec_parameter-valores por defecto). ```javascript const _err = function( message ){ throw new Error( message ); } const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b getSum( 10 ) // throws Error, b is not defined getSum( undefined, 10 ) // throws Error, a is not defined ``` `_err` es una función que lanza inmediatamente un error. Si no se pasa ningún valor para uno de los parámetros, el valor predeterminado se va a utilizar, `_err` serán tratados y se emite un error. Se puede ver más ejemplos para **caracteristicas de parámetros por defecto** en [Mozilla's Developer Network ](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/default_parameters) ================================================ FILE: _posts/es_ES/javascript/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md ================================================ --- layout: post title: Tip para medir el rendimiento de un bloque de Javascript tip-number: 13 tip-username: manmadareddy tip-username-profile: https://twitter.com/manmadareddy tip-tldr: Para medir rápidamente el rendimiento de un bloque de Javascript, podemos utilizar las funciones de la consola como `console.time(label)` y `console.timeEnd(label)` redirect_from: - /es_es/tip-to-measure-performance-of-a-javascript-block/ categories: - es_ES - javascript --- Para medir rápidamente el rendimiento de un bloque de Javascript, podemos utilizar las funciones de la consola como [`console.time(label)`](https://developer.chrome.com/devtools/docs/console-api#consoletimelabel) y [`console.timeEnd(label)`](https://developer.chrome.com/devtools/docs/console-api#consoletimeendlabel) ```javascript console.time("Array initialize"); var arr = new Array(100), len = arr.length, i; for (i = 0; i < len; i++) { arr[i] = new Object(); }; console.timeEnd("Array initialize"); // Outputs: Array initialize: 0.711ms ``` Más informacion: [Console object](https://github.com/DeveloperToolsWG/console-object), [Javascript benchmarking](https://mathiasbynens.be/notes/javascript-benchmarking) Demo: [jsfiddle](https://jsfiddle.net/meottb62/) - [codepen](http://codepen.io/anon/pen/JGJPoa) (outputs in browser console) ================================================ FILE: _posts/es_ES/javascript/2016-01-14-fat-arrow-functions.md ================================================ --- layout: post title: Funciones de flechas de direcciones. tip-number: 14 tip-username: pklinger tip-username-profile: https://github.com/pklinger/ tip-tldr: Introducido como una nueva característica en ES6, funciones de dirección pueden ser una herramienta muy útil para escribir más código en un menor número de líneas redirect_from: - /es_es/fat-arrow-functions/ categories: - es_ES - javascript --- Introducido como una nueva característica en ES6, funciones de dirección pueden ser una herramienta muy útil para escribir más código en un menor número de líneas. El nombre proviene de su sintaxis, `=>`, que es una "flecha", en comparación con una flecha delgada `->`. Algunos programas ya reconocen a este tipo de función de diferentes lenguajes como Haskell, como 'lambda expressions', o 'anonymous functions'. Se le llama en el anonimato, ya que estas funciones de dirección no tienen un nombre de función descriptiva. ### ¿Cuales son los beneficios? * Sintaxis: menos LOC; no más escribir palabra clave `function` una y otra vez * Semántica: captura de la palabra clave `this` del contexto que lo rodea ### Ejemplo de sintaxis sencilla Have a look at these two code snippets, which do the exact same job, and you will quickly understand what fat arrow functions do: Echar un vistazo a estos dos fragmentos de código, que hacen exactamente el mismo trabajo, y usted entenderá rápidamente qué hacen las funciones de dirección: ```javascript // general syntax for fat arrow functions param => expression // may also be written with parentheses // parentheses are required on multiple params (param1 [, param2]) => expression // using functions var arr = [5,3,2,9,1]; var arrFunc = arr.map(function(x) { return x * x; }); console.log(arr) // using fat arrow var arr = [5,3,2,9,1]; var arrFunc = arr.map((x) => x*x); console.log(arr) ``` As you can see, the fat arrow function in this case can save you time typing out the parentheses as well as the function and return keywords. I would advise you to always write parentheses around the parameter inputs, as the parentheses will be needed for multiple input parameters, such as in `(x,y) => x+y`. It is just a way to cope with forgetting them in different use cases. But the code above would also work like this: `x => x*x`. So far, these are only syntactical improvements, which lead to fewer LOC and better readability. Como se puede ver, la función de flecha en este caso le puede ahorrar tiempo a escribir los paréntesis, así como la palabras clave function y return. Yo aconsejaría escribir siempre paréntesis en torno a los parámetros de entrada, como serán necesarios los paréntesis para varios parámetros de entrada, como en `(x,y) => x+y`. Pero el código anterior también sería el siguiente: `x => x*x`. Hasta ahora, estos son sólo mejoras sintácticas, que conducen a menos LOC y una mejor legibilidad. ### Léxico de unión `this` There is another good reason to use fat arrow functions. There is the issue with the context of `this`. With arrow functions, you don't need to worry about `.bind(this)` or setting `that = this` anymore, as fat arrow functions pick the context of `this` from the lexical surrounding. Have a look at the next [example] (https://jsfiddle.net/pklinger/rw94oc11/): Hay otra buena razón para utilizar las funciones de dirección. No es el problema con el contexto de `this`. Con las funciones de dirección, usted no necesita preocuparse por `.bind(this)` o el ajuste `that = this`, ya que las funciones de dirección recogen el contexto de `this` del léxico de los alrededores. Echar un vistazo a la siguiente [example] (https://jsfiddle.net/pklinger/rw94oc11/): ```javascript // globally defined this.i this.i = 100; var counterA = new CounterA(); var counterB = new CounterB(); var counterC = new CounterC(); var counterD = new CounterD(); // bad example function CounterA() { // CounterA's `this` instance (!! gets ignored here) this.i = 0; setInterval(function () { // `this` refers to global object, not to CounterA's `this` // therefore starts counting with 100, not with 0 (local this.i) this.i++; document.getElementById("counterA").innerHTML = this.i; }, 500); } // manually binding that = this function CounterB() { this.i = 0; var that = this; setInterval(function() { that.i++; document.getElementById("counterB").innerHTML = that.i; }, 500); } // using .bind(this) function CounterC() { this.i = 0; setInterval(function() { this.i++; document.getElementById("counterC").innerHTML = this.i; }.bind(this), 500); } // fat arrow function function CounterD() { this.i = 0; setInterval(() => { this.i++; document.getElementById("counterD").innerHTML = this.i; }, 500); } ``` Para más información acerca de las funciones de dirección se puede encontrar en [MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). To see different syntax options visit [this site] (http://jsrocks.org/2014/10/arrow-functions-and-their-scope/). ================================================ FILE: _posts/es_ES/javascript/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md ================================================ --- layout: post title: Manera aún más sencilla de contener la cláusula al usar `indexOf` tip-number: 15 tip-username: jhogoforbroke tip-username-profile: https://twitter.com/jhogoforbroke tip-tldr: JavaScript por defecto no tiene un método contains. Y para comprobar la existencia de una subcadena en una cadena o un elemento de un array puede hacer esto. redirect_from: - /es_es/even-simpler-way-of-using-indexof-as-a-contains-clause/ categories: - es_ES - javascript --- JavaScript por defecto no tiene un método contains. Y para comprobar la existencia de una subcadena en una cadena o un elemento de un array puede hacer esto: ```javascript var someText = 'javascript rules'; if (someText.indexOf('javascript') !== -1) { } // or if (someText.indexOf('javascript') >= 0) { } ``` Pero vea esto [Expressjs](https://github.com/strongloop/express) code snippets. [examples/mvc/lib/boot.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/mvc/lib/boot.js#L26) ```javascript for (var key in obj) { // "reserved" exports if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; ``` [lib/utils.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/lib/utils.js#L93) ```javascript exports.normalizeType = function(type){ return ~type.indexOf('/') ? acceptParams(type) : { value: mime.lookup(type), params: {} }; }; ``` [examples/web-service/index.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/web-service/index.js#L35) ```javascript // key is invalid if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key')); ``` El gotcha es el [bitwise operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) **~**, "Operadores bit a bit realizan sus operaciones en representaciones binarias, sino que devuelven valores numéricos estándar de JavaScript." Transforma `-1` en `0`, and `0` evalúa como false JavaScript: ```javascript var someText = 'text'; !!~someText.indexOf('tex'); // someText contains "tex" - true !~someText.indexOf('tex'); // someText NOT contains "tex" - false ~someText.indexOf('asd'); // someText doesn't contain "asd" - false ~someText.indexOf('ext'); // someText contains "ext" - true ``` ### String.prototype.includes() ES6 introdujo [includes() method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) y se puede usar para determinar si o no una cadena incluye otra cadena: ```javascript 'something'.includes('thing'); // true ``` Con ECMAScript 2016 (ES7) incluso es posible utilizar estas técnicas con Arrays: ```javascript !!~[1, 2, 3].indexOf(1); // true [1, 2, 3].includes(1); // true ``` **Sólo se admite en Chrome, Firefox, Safari 9 o superior y Edge; No EI11 o inferior.** **Es mejor utilizados en ambientes controlados.** ================================================ FILE: _posts/es_ES/javascript/2016-01-16-passing-arguments-to-callback-functions.md ================================================ --- layout: post title: Paso de argumentos a las funciones de callback. tip-number: 16 tip-username: minhazav tip-username-profile: https://twitter.com/minhazav tip-tldr: JavaScript modules and build steps are getting more numerous and complicated, but what about boilerplate in new frameworks? Modulos JavaScript y construir pasos son cada vez más numerosos y complicados, pero ¿qué pasa en nuevos frameworks? redirect_from: - /es_es/passing-arguments-to-callback-functions/ categories: - es_ES - javascript --- Por defecto no se puede pasar argumentos a una función de callback. Por ejemplo: ```js function callback() { console.log('Hi human'); } document.getElementById('someelem').addEventListener('click', callback); ``` You can take advantage of the closure scope in Javascript to pass arguments to callback functions. Check this example: Puede aprovechar el closure scope en Javascript para pasar argumentos a funciones de callback. Compruebe este ejemplo: ```js function callback(a, b) { return function() { console.log('sum = ', (a+b)); } } var x = 1, y = 2; document.getElementById('someelem').addEventListener('click', callback(x, y)); ``` ### Que son closures? Closures son funciones que hacen referencia a las variables independientes (gratis). En otras palabras, la función definida en el closure "recuerda" el scope en el que se creó. [Check MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) para conocer mas. Así de esta manera los argumentos `x` y `y` están en el scope de la función de callback cuando se le llama. Otro método para hacerlo es usando el método `bind`. Por ejemplo: ```js var alertText = function(text) { alert(text); }; document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello')); ``` Hay una ligera diferencia en el rendimiento de ambos métodos, ver [jsperf](http://jsperf.com/bind-vs-closure-23). ================================================ FILE: _posts/es_ES/javascript/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md ================================================ --- layout: post title: Node.js - ejecutar un módulo si no es `required` tip-number: 17 tip-username: odsdq tip-username-profile: https://twitter.com/odsdq tip-tldr: En Node, puede decir que su programa va a hacer dos cosas diferentes dependiendo de si se ejecuta el código `require('./something.js')` o `node something.js`. Esto es útil si desea interactuar con uno de sus módulos de forma independiente. redirect_from: - /es_es/nodejs-run-a-module-if-it-is-not-required/ categories: - es_ES - javascript --- En Node, puede decir que su programa va a hacer dos cosas diferentes dependiendo de si se ejecuta el código `require('./something.js')` o `node something.js`. Esto es útil si desea interactuar con uno de sus módulos de forma independiente. ```js if (!module.parent) { // ran with `node something.js` app.listen(8088, function() { console.log('app listening on port 8088'); }) } else { // used with `require('/.something.js')` module.exports = app; } ``` Ver [the documentation for modules](https://nodejs.org/api/modules.html#modules_module_parent) para mas informacion. ================================================ FILE: _posts/es_ES/javascript/2016-01-18-rounding-the-fast-way.md ================================================ --- layout: post title: Redondeo, la manera mas rápida tip-number: 18 tip-username: pklinger tip-username-profile: https://github.com/pklinger tip-tldr: El tip de hoy es sobre el rendimiento. Nunca llegó a través del operador doble tilde `~~`? A veces también se llama el doble operador NOT (operador de bits). Se puede utilizar como un sustituto más rápido para `Math.floor()`. ¿Porqué es eso? redirect_from: - /es_es/rounding-the-fast-way/ categories: - es_ES - javascript --- El tip de hoy es sobre el rendimiento. [Nunca llegó a través del operador dobletilde] (http://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript) `~~`? A veces también se llama el doble operador NOT (operador de bits). Se puede utilizar como un sustituto más rápido para `Math.floor()`. ¿Porqué es eso? One bitwise shift `~` transforms the 32 bit converted input into `-(input+1)`. The double bitwise shift therefore transforms the input into `-(-(input + 1)+1)` making it a great tool to round towards 0. For numeric input, it therefore mimics the `Math.ceil()` for negative and `Math.floor()` for positive input. On failure, `0` is returned, which might come in handy sometimes instead of `Math.floor()`, which returns a value of `NaN` on failure. Un desplazamiento en modo bit `~` transforma 32 bits de entrada `-(input+1)`. Por tanto, el cambio de doble bit a bit transforma la entrada en `-(-(input + 1)+1)` por lo que es una gran herramienta para redondear hacia 0. Para la entrada numérica, de este modo imita el `Math.ceil()` para el negativo y `Math.floor()` para la entrada positiva. En caso de error, se devuelve `0`, lo que podría ser útil a veces en lugar de `Math.floor()`,que devuelve un valor de `NaN` en caso de fallo. ```javascript // single ~ console.log(~1337) // -1338 // numeric input console.log(~~47.11) // -> 47 console.log(~~-12.88) // -> -12 console.log(~~1.9999) // -> 1 console.log(~~3) // -> 3 // on failure console.log(~~[]) // -> 0 console.log(~~NaN) // -> 0 console.log(~~null) // -> 0 // greater than 32 bit integer fails console.log(~~(2147483647 + 1) === (2147483647 + 1)) // -> 0 ``` A pesar de que `~~` puede funcionar mejor, por el bien de la legibilidad por favor utilice `Math.floor()`. ================================================ FILE: _posts/es_ES/javascript/2016-01-19-safe-string-concatenation.md ================================================ --- layout: post title: Concatenación de Strings segura tip-number: 19 tip-username: gogainda tip-username-profile: https://twitter.com/gogainda tip-tldr: Suponga que tiene un par de variables con tipos desconocidos y desea concatenar en una cadena. Para asegurarse de que la operación aritmética no se puede aplicar durante la concatenación, utilizar concat redirect_from: - /es_es/safe-string-concatenation/ categories: - es_ES - javascript --- Suponga que tiene un par de variables con tipos desconocidos y desea concatenar en una cadena. Para asegurarse de que la operación aritmética no se puede aplicar durante la concatenación, utilizar 'concat': ```javascript var one = 1; var two = 2; var three = '3'; var result = ''.concat(one, two, three); //"123" ``` Esta forma de concatenación hace exactamente lo que se espera. Por el contrario, la concatenación con ventajas podría conducir a resultados inesperados: ```javascript var one = 1; var two = 2; var three = '3'; var result = one + two + three; //"33" instead of "123" ``` Hablando de rendimiento, en comparación con el `join` [type](http://www.sitepoint.com/javascript-fast-string-concatenation/) de concatenación, la velocidad de` concat` es más o menos lo mismo. Puede leer mas sobre `concat` en MDN [page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat). ================================================ FILE: _posts/es_ES/javascript/2016-01-20-return-objects-to-enable-chaining-of-functions.md ================================================ --- layout: post title: Devolver los objetos que permiten el encadenamiento de las funciones tip-number: 20 tip-username: WakeskaterX tip-username-profile: https://twitter.com/WakeStudio tip-tldr: Al crear funciones en un objeto en Javascript orientado a objetos, devolver el objeto a la función le permitirá encadenar funciones. redirect_from: - /es_es/return-objects-to-enable-chaining-of-functions/ categories: - es_ES - javascript --- Al crear funciones en un objeto en Javascript orientado a objetos, devolver el objeto a la función le permitirá encadenar funciones. ```js function Person(name) { this.name = name; this.sayName = function() { console.log("Hello my name is: ", this.name); return this; }; this.changeName = function(name) { this.name = name; return this; }; } var person = new Person("John"); person.sayName().changeName("Timmy").sayName(); ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-21-shuffle-an-array.md ================================================ --- layout: post title: Mezclar un Array tip-number: 21 tip-username: 0xmtn tip-username-profile: https://github.com/0xmtn/ tip-tldr: Fisher-Yates Shuffling es un algoritmo para mezclar un array. redirect_from: - /es_es/shuffle-an-array/ categories: - es_ES - javascript --- Este codigo utiliza [Fisher-Yates Shuffling](https://www.wikiwand.com/en/Fisher%E2%80%93Yates_shuffle) Algoritmo para mezclar un array. ```javascript function shuffle(arr) { var i, j, temp; for (i = arr.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; }; ``` Ejemplo: ```javascript var a = [1, 2, 3, 4, 5, 6, 7, 8]; var b = shuffle(a); console.log(b); // [2, 7, 8, 6, 5, 3, 1, 4] ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-22-two-ways-to-empty-an-array.md ================================================ --- layout: post title: Dos formas de vacia un array tip-number: 22 tip-username: microlv tip-username-profile: https://github.com/microlv tip-tldr: En JavaScript cuando se quiere vaciar un array, hay una muchas maneras, pero esta es la mas potente. redirect_from: - /es_es/two-ways-to-empty-an-array/ categories: - es_ES - javascript --- Se define un array y desea vaciar su contenido. Por lo general, usted lo haría así: ```javascript // define Array var list = [1, 2, 3, 4]; function empty() { //empty your array list = []; } empty(); ``` Pero hay otra manera de vaciar un array que es más performante. Debe utilizar un código como este: ```javascript var list = [1, 2, 3, 4]; function empty() { //empty your array list.length = 0; } empty(); ``` * `list = []` asigna una referencia a un nuevo array a una variable, mientras que las otras referencias no se ven afectadas. Lo que significa que las referencias a los contenidos del array anterior todavía se mantienen en la memoria, lo que lleva a pérdidas de memoria. * `list.length = 0` borra todo el contenido del array, lo que afecta otras referencias. In other words, if you have two references to the same array (`a = [1,2,3]; a2 = a;`), and you delete the array's contents using `list.length = 0`, both references (a and a2) will now point to the same empty array. (So don't use this technique if you don't want a2 to hold an empty array!) En otras palabras, si usted tiene dos referencias a el mismo array (`a = [1,2,3]; a2 = a;`), y se elimina el contenido del array usando `list.length = 0`, ambas referencias (a and a2) se apuntan ahora al mismo array vacío. (Así que no se utilice esta técnica si usted no quiere a2 para sostener un array vacío!) Piense en lo que tendra esta salida: ```js var foo = [1,2,3]; var bar = [1,2,3]; var foo2 = foo; var bar2 = bar; foo = []; bar.length = 0; console.log(foo, bar, foo2, bar2); // [] [] [1, 2, 3] [] ``` Stackoverflow mas detalles: [difference-between-array-length-0-and-array](http://stackoverflow.com/questions/4804235/difference-between-array-length-0-and-array) ================================================ FILE: _posts/es_ES/javascript/2016-01-23-converting-to-number-fast-way.md ================================================ --- layout: post title: Convertir a numero de la forma mas rapida tip-number: 23 tip-username: sonnyt tip-username-profile: http://twitter.com/sonnyt tip-tldr: La conversión de cadenas en números es muy común. La forma más fácil y rápida de lograr sería utilizar el operador +. redirect_from: - /es_es/converting-to-number-fast-way/ categories: - es_ES - javascript --- La conversión de cadenas en números es muy común. La forma más rápida ([jsPref](https://jsperf.com/number-vs-parseint-vs-plus/29)) y más fácil de lograr que sería utilizar el operador `+` (mas). ```javascript var one = '1'; var numberOne = +one; // Number 1 ``` You can also use the `-` (minus) operator which type-converts the value into number but also negates it. También se puede utilizar el operador `-` (menos) qué convierte el valor en número, sino también la niega. ```javascript var one = '1'; var negativeNumberOne = -one; // Number -1 ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-24-use_===_instead_of_==.md ================================================ --- layout: post title: Utilizar === en lugar de == tip-number: 24 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: El operador `==` (or `!=`) lleva a cabo una conversión automática de tipos si es necesario. El operador `===` (or `!==`) no va a realizar ninguna conversión. En él se compara el valor y el tipo, que podría considerarse más rápido ([jsPref](http://jsperf.com/strictcompare)) que `==`. redirect_from: - /es_es/use_===_instead_of_==/ categories: - es_ES - javascript --- El operador `==` (or `!=`) lleva a cabo una conversión automática de tipos si es necesario. El operador `===` (or `!==`) no va a realizar ninguna conversión. En él se compara el valor y el tipo, que podría considerarse más rápido ([jsPref](http://jsperf.com/strictcompare)) que `==`. ```js [10] == 10 // is true [10] === 10 // is false '10' == 10 // is true '10' === 10 // is false [] == 0 // is true [] === 0 // is false '' == false // is true but true == "a" is false '' === false // is false ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-25-Using-immediately-invoked-function-expression.md ================================================ --- layout: post title: Invocar una funcion inmediatamente tip-number: 25 tip-username: rishantagarwal tip-username-profile: https://github.com/rishantagarwal tip-tldr: Denominado como "Iffy" >dudoso< (IIFE - expresión de la función invocada inmediatamente) es una expresión de la función anónima que se invoca inmediatamente y tiene algunos usos importantes en Javascript. redirect_from: - /es_es/Using-immediately-invoked-function-expression/ categories: - es_ES - javascript --- Denominado como "Iffy" >dudoso< (IIFE - expresión de la función invocada inmediatamente) es una expresión de la función anónima que se invoca inmediatamente y tiene algunos usos importantes en Javascript. ```javascript (function() { // Do something​ } )() ``` Es una expresión de función anónima que se invoca de inmediato, y tiene algunos usos particularmente importantes en JavaScript. El par de paréntesis que rodean la función anónima convierte la función anónima en una expresión de función o expresión variable. Así que en lugar de una simple función anónima en el scope global, o donde quiera que se definió, ahora tenemos una expresión de función sin nombre. Similarly, we can even create a named, immediately invoked function expression: Del mismo modo, podemos incluso crear una llamada expresión de función, inmediatamente invocado: ```javascript (someNamedFunction = function(msg) { console.log(msg || "Nothing for today !!") }) (); // Output --> Nothing for today !!​ ​ someNamedFunction("Javascript rocks !!"); // Output --> Javascript rocks !! someNamedFunction(); // Output --> Nothing for today !!​ ``` Mas detalles URL's - 1. [Link 1](https://blog.mariusschulz.com/2016/01/13/disassembling-javascripts-iife-syntax) 2. [Link 2](http://javascriptissexy.com/12-simple-yet-powerful-javascript-tips/) Performance: [jsPerf](http://jsperf.com/iife-with-call) ================================================ FILE: _posts/es_ES/javascript/2016-01-26-filtering-and-sorting-a-list-of-strings.md ================================================ --- layout: post title: Filtrar y ordenar una lista de Strings tip-number: 26 tip-username: davegomez tip-username-profile: https://github.com/davegomez tip-tldr: Es posible que tenga una gran lista de nombres que necesitas filtrar con el fin de eliminar los duplicados y ordenarlos alfabéticamente. redirect_from: - /filtering-and-sorting-a-list-of-strings/ categories: - es_ES - javascript --- Es posible que tenga una gran lista de nombres que necesita filtrar con el fin de eliminar los duplicados y ordenarlos alfabéticamente. En nuestro ejemplo vamos a utilizar una lista de **palabras clave reservadas de JavaScript** que podemos encontrar en las diferentes versiones del lenguage, pero como se puede observar, hay una gran cantidad de palabras clave duplicadas y no están organizados alfabéticamente. Así que esta es una lista perfecta de cadenas de probar este consejo JavaScript ([Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)). ```js var keywords = ['do', 'if', 'in', 'for', 'new', 'try', 'var', 'case', 'else', 'enum', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'delete', 'export', 'import', 'return', 'switch', 'typeof', 'default', 'extends', 'finally', 'continue', 'debugger', 'function', 'do', 'if', 'in', 'for', 'int', 'new', 'try', 'var', 'byte', 'case', 'char', 'else', 'enum', 'goto', 'long', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'final', 'float', 'short', 'super', 'throw', 'while', 'delete', 'double', 'export', 'import', 'native', 'public', 'return', 'static', 'switch', 'throws', 'typeof', 'boolean', 'default', 'extends', 'finally', 'package', 'private', 'abstract', 'continue', 'debugger', 'function', 'volatile', 'interface', 'protected', 'transient', 'implements', 'instanceof', 'synchronized', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof']; ``` Dado que no queremos cambiar nuestra lista original, vamos a utilizar una función de orden superior llamado [`filter`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), que devolverá un nuevo array filtro basado en un predicado (* Función *) que se pasa a ella. El predicado comparará el índice de la palabra clave actual en la lista original con su `index` en la nueva lista y la pondra en un nuevo array sólo si los índices coinciden. Por último vamos a ordenar la lista filtrada con ayuda de la funcion[`sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) que toma una función de comparación como el único argumento, devolviendo una lista ordenada alfabéticamente. ```js var filteredAndSortedKeywords = keywords .filter(function (keyword, index) { return keywords.indexOf(keyword) === index; }) .sort(function (a, b) { if (a < b) return -1; else if (a > b) return 1; return 0; }); ``` **ES6** (ECMAScript 2015) usa [arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) se ve un poco mas simple: ```js const filteredAndSortedKeywords = keywords .filter((keyword, index) => keywords.indexOf(keyword) === index) .sort((a, b) => { if (a < b) return -1; else if (a > b) return 1; return 0; }); ``` Y esta es la lista final filtrada y ordenada de palabras clave JavaScript reservados: ```js console.log(filteredAndSortedKeywords); // ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield'] ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-27-short-circiut-evaluation-in-js.md ================================================ --- layout: post title: Evaluación de Short circuit en JS. tip-number: 27 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: Evaluación de Short-circuit dice, el segundo argumento se ejecuta o evalúa sólo si el primer argumento no es suficiente para determinar el valor de la expresión, cuando el primer argumento de AND (&&) la función se evalúa como falsa, el valor total debe ser falso; y cuando el primer argumento de la función OR (||) da como resultado true, el valor total debe ser verdad. redirect_from: - /es_es/short-circiut-evaluation-in-js/ categories: - es_ES - javascript --- [Evaluación Short-circuit](https://en.wikipedia.org/wiki/Short-circuit_evaluation) dice, el segundo argumento se ejecuta o evalúa sólo si el primer argumento no es suficiente para determinar el valor de la expresión: cuando el primer argumento de la función AND ('&&') se evalúa como falsa, el valor total debe ser falso; y cuando el primer argumento de la función OR ('||') da como resultado true, el valor total debe ser verdad. Para el siguiente funcion `test` condición and `isTrue` y 'isFalse'. ```js var test = true; var isTrue = function(){ console.log('Test is true.'); }; var isFalse = function(){ console.log('Test is false.'); }; ``` Usar logico AND - `&&`. ```js // A normal if statement. if(test){ isTrue(); // Test is true } // Above can be done using '&&' as - ( test && isTrue() ); // Test is true ``` Usar logico OR - `||`. ```js test = false; if(!test){ isFalse(); // Test is false. } ( test || isFalse()); // Test is false. ``` La lógica OR también podría ser utilizado para establecer un valor predeterminado para el argumento de la función. ```js function theSameOldFoo(name){ name = name || 'Bar' ; console.log("My best friend's name is " + name); } theSameOldFoo(); // My best friend's name is Bar theSameOldFoo('Bhaskar'); // My best friend's name is Bhaskar ``` La lógica AND podría ser utilizado para evitar excepciones utilizando las propiedades de undefined. Ejemplo:- ```js var dog = { bark: function(){ console.log('Woof Woof'); } }; // Calling dog.bark(); dog.bark(); // Woof Woof. //But if dog is not defined, dog.bark() will raise an error "Cannot read property 'bark' of undefined." // To prevent this, we can you &&. dog&&dog.bark(); // This will only call dog.bark(), if dog is defined. ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-28-curry-vs-partial-application.md ================================================ --- layout: post title: Currying vs partial application tip-number: 28 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: Currying and partial application son dos maneras de transformar una funcion en otra funcion generalidad muy pequeña. redirect_from: - es_es/curry-vs-partial-application/ categories: - es_ES - javascript --- **Currying** Currying toma una función f: X * Y -> R y lo convierte en una función f': X -> (Y -> R) En lugar de llamar f con dos argumentos, invocamos f' con el primer argumento. El resultado es una función que entonces llamamos con el segundo argumento para producir el resultado. Por lo tanto, si el f no es comparable se invoca como f(3,5) entonces el f' comparables es invocada como f(3)(5) Por ejemplo: Uncurried add() ```javascript function add(x, y) { return x + y; } add(3, 5); // returns 8 ``` Curried add() ```javascript function addC(x) { return function (y) { return x + y; } } addC(3)(5); // returns 8 ``` **El algoritmo para currying.** Curry toma una función binaria y devuelve una función unaria, función que devuelve una función unaria. curry: (X × Y → R) → (X → (Y → R)) Codigo Javascript: ```javascript function curry(f) { return function(x) { return function(y) { return f(x, y); } } } ``` **Partial application** Partial application toma una funcion f: X * Y -> R y un valor fijo para el primer argumento para producir una nueva función f`: Y -> R f' hace lo mismo que f, pero sólo tiene que completar el segundo parámetro. Por ejemplo: almacenando el primer argumento de la función de añadir a 5 resulta la función más 5. ```javascript function plus5(y) { return 5 + y; } plus5(3); // returns 8 ``` **El algoritmo de partial application.** partApply toma una función y un valor binario y produce una función unaria. partApply : ((X × Y → R) × X) → (Y → R) Codigo Javascript: ```javascript function partApply(f, x) { return function(y) { return f(x, y); } } ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-29-speed-up-recursive-functions-with-memoization.md ================================================ --- layout: post title: Acelerar las funciones recursivas con memoization tip-number: 29 tip-username: hingsir tip-username-profile: https://github.com/hingsir tip-tldr: Fibonacci es muy familiar. Podemos escribir la siguiente función en 20 segundos, pero no eficiente. Se puede almacenar en caché los resultados previamente calculados para acelerarlo. redirect_from: - /es_es/speed-up-recursive-functions-with-memoization/ categories: - es_ES - javascript --- Fibonacci es muy familiar. podemos escribir la siguiente función en 20 segundos. ```js var fibonacci = function(n){ return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2); } ``` funciona, pero no es eficiente. Se pueden almacenar en caché los resultados previamente calculados para acelerarlo. ```js var fibonacci = (function(){ var cache = { 0: 0, 1: 1 }; return function self(n){ return n in cache ? cache[n] : (cache[n] = self(n-1) + self(n-2)); } })() ``` También, podemos definir una función de orden superior que acepta una función como argumento y devuelve una versión memoized de la función. ```js var memoize = function(func){ var cache = {}; return function(){ var key = Array.prototype.slice.call(arguments).toString(); return key in cache ? cache[key] : (cache[key] = func.apply(this,arguments)); } } fibonacci = memoize(fibonacci); ``` Y hay una versión ES6 de la función memoize. ```js var memoize = function(func){ const cache = {}; return (...args) => { const key = [...args].toString(); return key in cache ? cache[key] : (cache[key] = func(...args)); } } fibonacci = memoize(fibonacci); ``` podemos usar `memoize()` en muchas otras situciones * GCD(Greatest Common Divisor) ```js var gcd = memoize(function(a,b){ var t; if (a < b) t=b, b=a, a=t; while(b != 0) t=b, b = a%b, a=t; return a; }) gcd(27,183); //=> 3 ``` * Calculo Factorial ```js var factorial = memoize(function(n) { return (n <= 1) ? 1 : n * factorial(n-1); }) factorial(5); //=> 120 ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-30-converting-truthy-falsy-values-to-boolean.md ================================================ --- layout: post title: Convertir valores truthy/falsy a boolean tip-number: 30 tip-username: hakhag tip-username-profile: https://github.com/hakhag tip-tldr: Los operadores lógicos son una parte fundamental de JavaScript, aquí se puede ver una manera de obtener siempre un verdadero o falso, no importa lo que se le dio a él. redirect_from: - /es_es/converting-truthy-falsy-values-to-boolean/ categories: - es_ES - javascript --- Puede convertir un valor [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) o [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) a verdadero booleano con el `!!` operador. ```js !!"" // false !!0 // false !!null // false !!undefined // false !!NaN // false !!"hello" // true !!1 // true !!{} // true !![] // true ``` ================================================ FILE: _posts/es_ES/javascript/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md ================================================ --- layout: post title: Evitar la modificación o pasando `arguments` en otras funciones - mata la optimización tip-number: 31 tip-username: berkana tip-username-profile: https://github.com/berkana tip-tldr: Dentro de las funciones de JavaScript, el nombre de la variable `arguments` le permite acceder a todos los argumentos pasados a la función. `arguments` en un *array-like object*; `arguments` se puede acceder usando array notation, y tiene la propiedad *longitud*, pero no tiene muchos de los métodos incorporados en arrays que tienen como `filter` y `map` y `forEach`. Debido a esto, es una práctica bastante común para convertir `arguments` en una matriz utilizando el siguiente fragmento. redirect_from: - /es_es/avoid-modifying-or-passing-arguments-into-other-functions-it-kills-optimization/ categories: - es_ES - javascript --- ###Background Dentro de las funciones de JavaScript, el nombre de la variable [`arguments`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) le permite acceder a todos los argumentos pasados a la función. `arguments` es una *array-like object*; `arguments` se puede acceder usando la array notation, y tiene la propiedad *longitud*, pero no tiene por qué muchos de los métodos incorporados en los arrays que tienen como`filter` y `map` y `foreach`. Debido a esto, es una práctica bastante común para convertir `arguments` en un array utilizando: ```js var args = Array.prototype.slice.call(arguments); ``` Este método `slice` del prototipo `array`, pasándole `arguments`; el método `slice` devuelve una copia superficial del `arguments` como un nuevo objeto de matriz. A la abreviatura común para esto es: ```js var args = [].slice.call(arguments); ``` En este caso, en lugar de llamar `slice` del prototipo `Array`, se trata simplemente de ser llamado desde un array vacío literal. ###Optimización Desafortunadamente, pasando `arguments` en cualquier llamada de función hará que el motor V8 JavaScript utilizado en Chrome y NodeJS para omitir la optimización de la función que hace esto, lo que puede resultar en un rendimiento considerablemente más lento. Ver este artículo [optimization killers](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers). Pasando `arguments` a cualquier otra función que se conoce como *leaking `arguments`*. En cambio, si quieres un array de los argumentos que le permite utilizar lo que necesita recurrir a este: ```js var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } ``` Sí, es más prolija, pero en el código de producción, vale la pena para la optimización del rendimiento. ================================================ FILE: _posts/es_ES/javascript/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md ================================================ --- layout: post title: Map() al rescate; añadir orden a las propiedades de los Objetos. tip-number: 32 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: Un objeto es una colección desordenada de propiedades... que significa que si está tratando de guardar los datos ordenados dentro de un objeto, hay que revisarlo debido a que las propiedades de orden en los objetos no están garantizados. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/map-to-the-rescue-adding-order-to-object-properties/ categories: - es_ES - javascript --- ## Propiedades de orden del Objeto > Un objeto es un miembro del tipo de objeto. Es una colección desordenada de las propiedades de cada uno de los cuales contiene un valor primitivo, objeto o función. Una función almacenada en una propiedad de un objeto se llama un método.[ECMAScript](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf) Veamos en acción ```js var myObject = { z: 1, '@': 2, b: 3, 1: 4, 5: 5 }; console.log(myObject) // Object {1: 4, 5: 5, z: 1, @: 2, b: 3} for (item in myObject) {... // 1 // 5 // z // @ // b ``` Cada navegador tiene sus propias reglas sobre el orden en los objetos porque técnicamente, el orden no se especifica. ## Como solucionar esto? ### Map Usando una nueva característica de ES6 llamada Map. [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) objeto itera sus elementos con el fin de inserción - un `for...of` devuelve un array de [clave, valor] para cada iteración. ```js var myObject = new Map(); myObject.set('z', 1); myObject.set('@', 2); myObject.set('b', 3); for (var [key, value] of myObject) { console.log(key, value); ... // z 1 // @ 2 // b 3 ``` ### Hack para navegadores antiguos Sugerencias de Mozilla: > Por lo tanto, si desea simular el orden asociado al array en un entorno cross-browser, se ve obligado a usar dos array independientes (uno para las key y el otro para los valores), o construir una array de objetos de single-property, etc. ```js // usando dos arrays separados var objectKeys = [z, @, b, 1, 5]; for (item in objectKeys) { myObject[item] ... // Creando un array de objetos de single-property var myData = [{z: 1}, {'@': 2}, {b: 3}, {1: 4}, {5: 5}]; ``` ================================================ FILE: _posts/es_ES/javascript/2016-02-02-create-range-0-n-easily-using-one-line.md ================================================ --- layout: post title: Crear rangos 0...(N-1) facilmente usando una linea tip-number: 33 tip-username: SarjuHansaliya tip-username-profile: https://github.com/SarjuHansaliya tip-tldr: Podemos crear una función de rango que dará un rango entre 0...(N-1) utilizando una sola línea redirect_from: - /es_es/create-range-0-n-easily-using-one-line/ categories: - es_ES - javascript --- A continuación mostraremos como crear el rango 0...N-1). ```js Array.apply(null, {length: N}).map(Number.call, Number); ``` Vamos a descomponer esta línea en partes. Sabemos como la funcion `call ()` trabaja en javascript. Así que en `call ()` el primer argumento será contexto y los segundos argumentos, será una lista de argumentos de la función en la que estamos llamando `call()`. ```js function add(a, b){ return (a+b); } add.call(null, 5, 6); ``` Esto devolverá una suma de 5 y 6. `map()` del array en javascript toma dos argumentos, el primero `callback` y el segundo `thisArg(context)`. `callback` está tomando tres argumentos, `value`, `index` y todo el `array` sobre el que estamos iterando. Así es la sintaxis común: ```js [1, 2, 3].map(function(value, index, arr){ //Code }, this); ``` Debajo de la línea crea array de longitud dada. ```js Array.apply(null, {length: N}) ``` Poner todas las partes juntas a continuación es la solución. ```js Array.apply(null, {length: N}).map(Number.call, Number); ``` Si tiene un rango 1...N, que sera como este. ```js Array.apply(null, {length: N}).map(function(value, index){ return index+1; }); ``` ================================================ FILE: _posts/es_ES/javascript/2016-02-03-implementing-asynchronous-loops.md ================================================ --- layout: post title: Implementación de bucle asíncrono. tip-number: 34 tip-username: madmantalking tip-username-profile: https://github.com/madmantalking tip-tldr: Puedes tener problemas al implementar bucles asíncronos. redirect_from: - /es_es/implementing-asynchronous-loops/ categories: - es_ES - javascript --- Vamos a probar a escribir una función asíncrona que imprime el valor del índice del bucle cada segundo. ```js for (var i=0; i<5; i++) { setTimeout(function(){ console.log(i); }, 1000); } ``` La salida de los script antes mencionados resulta ser ```js > 5 > 5 > 5 > 5 > 5 ``` Así que esto definitivamente no funciona. **Explicación** Cada timeout se refiere a la original `i`, no una copia. Por lo que el bucle de incrementos `i` hasta que llega a 5, entonces comienza a dar timeuot y utilizar el valor actual de `i` (que es 5). Pues bien, este problema parece fácil. Una solución inmediata es almacenar en caché el índice de bucle en una variable temporal. ```js for (var i=0; i<5; i++) { var temp = i; setTimeout(function(){ console.log(temp); }, 1000); } ``` Pero de nuevo la salida de los script anteriores resulta ser ```js > 4 > 4 > 4 > 4 > 4 ``` Por lo tanto, eso no funciona bien, porque los bloques no crean un ámbito de aplicación y las variables de inicializadores se izan a la parte superior del scope. De hecho, el bloque anterior es el mismo que: ```js var temp; for (var i=0; i<5; i++) { temp = i; setTimeout(function(){ console.log(temp); }, 1000); } ``` **Solución** Hay algunas maneras diferentes para copiar `i`. La forma más común es la creación de un closure declarando una función que se le pasa `i` como un argumento. Aquí hacemos esto como una función de auto-llamada. ```js for (var i=0; i<5; i++) { (function(num){ setTimeout(function(){ console.log(num); }, 1000); })(i); } ``` En JavaScript, los argumentos se pasan por valor a una función. Así tipos primitivos como números, fechas, y las cadenas son básicamente copiados. Si los cambia dentro de la función, que no afecta el scope exterior. Los objetos son especiales: si la función cambia dentro de una propiedad, el cambio se refleja en todos los scopes. Otro camino para esto sería con el uso de `let`. ```js for (let i=0; i<5; i++) { var temp = i; setTimeout(function(){ console.log(temp); }, 1000); } ``` ================================================ FILE: _posts/es_ES/javascript/2016-02-04-assignment-shorthands.md ================================================ --- layout: post title: Operadores de Asignación tip-number: 35 tip-username: hsleonis tip-username-profile: https://github.com/hsleonis tip-tldr: Asignación es muy común. A veces volvemos a escribir, perdemos tiempo los 'Programadores perezosos'. Por lo tanto, podemos utilizar algunos trucos para ayudarnos y hacer nuestro código más claro y más simple. redirect_from: - /es_es/assignment-shorthands/ categories: - es_ES - javascript --- Asignación es muy común. A veces volvemos a escribir, perdemos tiempo los 'Programadores perezosos'. Por lo tanto, podemos utilizar algunos trucos para ayudarnos y hacer nuestro código más claro y más simple. Este es el uso similar de ````javascript x += 23; // x = x + 23; y -= 15; // y = y - 15; z *= 10; // z = z * 10; k /= 7; // k = k / 7; p %= 3; // p = p % 3; d **= 2; // d = d ** 2; m >>= 2; // m = m >> 2; n <<= 2; // n = n << 2; n ++; // n = n + 1; n --; n = n - 1; ```` ### If-else (Usando operador ternario) Esto es lo que se escribe de manera regular. ````javascript var newValue; if(value > 10) newValue = 5; else newValue = 2; ```` Podemos utilizar el operador ternario para que sea impresionante: ````javascript var newValue = (value > 10) ? 5 : 2; ```` ### Null, Undefined, Empty Checks ````javascript if (variable1 !== null || variable1 !== undefined || variable1 !== '') { var variable2 = variable1; } ```` Abreviación: ````javascript var variable2 = variable1 || ''; ```` P.D.: Si variable1 en un numero, luego controla primero si es 0 ### Object Array Notation En lugar de usar: ````javascript var a = new Array(); a[0] = "myString1"; a[1] = "myString2"; ```` Utilice: ````javascript var a = ["myString1", "myString2"]; ```` ### Associative array En lugar de usar: ````javascript var skillSet = new Array(); skillSet['Document language'] = 'HTML5'; skillSet['Styling language'] = 'CSS3'; ```` Utilice: ````javascript var skillSet = { 'Document language' : 'HTML5', 'Styling language' : 'CSS3' }; ```` ================================================ FILE: _posts/es_ES/javascript/2016-02-05-observe-dom-changes.md ================================================ --- layout: post title: Observar los cambios del DOM en extensiones. tip-number: 36 tip-username: beyondns tip-username-profile: https://github.com/beyondns tip-tldr: AL desarrollar extensiones de sitios existentes no es tan fácil interactuar con el DOM a causa de la moderna dinamica de javascript. redirect_from: - /es_es/observe-dom-changes/ categories: - es_ES - javascript --- [MutationObserver](https://developer.mozilla.org/en/docs/Web/API/MutationObserver) es una solución para escuchar a los cambios del DOM y hacer lo que quiere hacer con los elementos cuando cambiaron. En el siguiente ejemplo, hay una cierta emulación del contenido de la carga dinámica con una ayuda de contadores de tiempo, después de la primera creación del elemento "target" pasa "subTarget". En código de la extensión en primer lugar rootObserver funciona hasta la apariencia del targetElement luego empieza el elementObserver. Esta observación en cascada ayuda a obtener finalmente el momento en que se encuentra subTargetElement. Es útil para desarrollar extensiones a los sitios complejos con contenido de carga dinámica. ```js const observeConfig = { attributes: true, childList: true, characterData: true, subtree: true }; function initExtension(rootElement, targetSelector, subTargetSelector) { var rootObserver = new MutationObserver(function(mutations) { console.log("Inside root observer"); targetElement = rootElement.querySelector(targetSelector); if (targetElement) { rootObserver.disconnect(); var elementObserver = new MutationObserver(function(mutations) { console.log("Inside element observer") subTargetElement = targetElement.querySelector(subTargetSelector); if (subTargetElement) { elementObserver.disconnect(); console.log("subTargetElement found!") } }) elementObserver.observe(targetElement, observeConfig); } }) rootObserver.observe(rootElement, observeConfig); } (function() { initExtension(document.body, "div.target", "div.subtarget") setTimeout(function() { del = document.createElement("div"); del.innerHTML = "
      target
      " document.body.appendChild(del) }, 3000); setTimeout(function() { var el = document.body.querySelector('div.target') if (el) { del = document.createElement("div"); del.innerHTML = "
      subtarget
      " el.appendChild(del) } }, 5000); })() ``` ================================================ FILE: _posts/es_ES/javascript/2016-02-06-deduplicate-an-array.md ================================================ --- layout: post title: Deduplique un Array tip-number: 37 tip-username: danillouz tip-username-profile: https://www.twitter.com/danillouz tip-tldr: Cómo eliminar elementos duplicados, de diferentes tipos de datos, a partir de un Array. redirect_from: - /es_es/deduplicate-an-array/ categories: - es_ES - javascript --- # Primitivos Si una matriz sólo contiene valores primitivos, podemos desduplicar solo utilizando [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) and [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) methods. ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter(function (el, i, arr) { return arr.indexOf(el) === i; }); console.log(deduped); // [ 1, 'a' ] ``` ## ES2015 Podemos escribir esto de una manera más compacta utilizando [arrow function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions). ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter( (el, i, arr) => arr.indexOf(el) === i); console.log(deduped); // [ 1, 'a' ] ``` Sin embargo, con la introducción de [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) y de [`from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) se puede lograr lo mismo como resultado de una manera más concisa. ```javascript var deduped = Array.from( new Set([ 1, 1, 'a', 'a' ]) ); console.log(deduped); // [ 1, 'a' ] ``` # Objects No podemos usar el mismo enfoque cuando los elementos son Objects, Dado que los Objects se almacenan como referencia y se almacenan primitivas por valor. ```javascript 1 === 1 // true 'a' === 'a' // true { a: 1 } === { a: 1 } // false ``` Por lo tanto tenemos que cambiar nuestro enfoque y utilizar una tabla hash. ```javascript function dedup(arr) { var hashTable = {}; return arr.filter(function (el) { var key = JSON.stringify(el); var match = Boolean(hashTable[key]); return (match ? false : hashTable[key] = true); }); } var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ] ]); console.log(deduped); // [ {a: 1}, [1, 2] ] ``` Because a hash table in javascript is simply an `Object`, the keys will always be of the type `String`. This means that normally we can't distinguish between strings and numbers of the same value, i.e. `1` and `'1'`. Debido a una tabla hash en javascript es simplemente un `Object`, las claves siempre será del tipo `string`. Esto significa que normalmente no podemos distinguir entre cadenas y números del mismo valor, es decir `1` y `'1'`. ```javascript var hashTable = {}; hashTable[1] = true; hashTable['1'] = true; console.log(hashTable); // { '1': true } ``` Sin embargo, debido a que estamos utilizando [`JSON.stringify`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), key que son del tipo `String`, se almacena como un valor de cadena escapada, que nos da única key en nuestra `hashTable`. ```javascript var hashTable = {}; hashTable[JSON.stringify(1)] = true; hashTable[JSON.stringify('1')] = true; console.log(hashTable); // { '1': true, '\'1\'': true } ``` Esto significa elementos duplicados del mismo valor, pero de un tipo diferente, aún serán deduplicables utilizando la misma aplicación. ```javascript var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ], 1, 1, '1', '1' ]); console.log(deduped); // [ {a: 1}, [1, 2], 1, '1' ] ``` # Recursos ## Metodos * [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) * [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) * [`from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) * [`JSON.stringify`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ## ES2015 * [arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) * [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) ## Stack overflow * [remove duplicates from array](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array/9229821#9229821) ================================================ FILE: _posts/es_ES/javascript/2016-02-07-flattening-multidimensional-arrays-in-javascript.md ================================================ --- layout: post title: Arrays multidimensionales en JavaScript tip-number: 38 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Tres soluciones diferentes para combinar arrays multidimensional en un sola arrays. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/flattening-multidimensional-arrays-in-javascript/ categories: - es_ES - javascript --- Estas son las tres formas conocidas para fusionar arrays multidimensional en una sola arrays. Dado el array: ```js var myArray = [[1, 2],[3, 4, 5], [6, 7, 8, 9]]; ``` Queremos tener este resultado: ```js [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solucion 1: usando [`concat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) y [`apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) ```js var myNewArray = [].concat.apply([], myArray); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solucion 2: usando [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Flatten_an_array_of_arrays) ```js var myNewArray = myArray.reduce(function(prev, curr) { return prev.concat(curr); }); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### Solucion 3: ```js var myNewArray3 = []; for (var i = 0; i < myArray.length; ++i) { for (var j = 0; j < myArray[i].length; ++j) myNewArray3.push(myArray[i][j]); } console.log(myNewArray3); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` Echar un vistazo [here](https://jsbin.com/qeqicu/edit?js,console) estos 3 algoritmos en accion. Para array infinitamente anidado probar Underscore [flatten()](https://github.com/jashkenas/underscore/blob/master/underscore.js#L501). Si tiene curiosidad por la performance, [here](http://jsperf.com/flatten-an-array-loop-vs-reduce/6). ================================================ FILE: _posts/es_ES/javascript/2016-02-08-advanced-properties.md ================================================ --- layout: post title: Propiedades avanzadas de Javascript tip-number: 39 tip-username: mallowigi tip-username-profile: https://github.com/mallowigi tip-tldr: Cómo añadir propiedades privadas, getters y setters de objetos. redirect_from: - /es_es/advanced-properties/ categories: - es_ES - javascript --- Es posible configurar las propiedades del objeto en Javascript, por ejemplo, para establecer las propiedades de ser pseudo-privada o de sólo lectura. Esta característica está disponible desde ECMAScript 5.1, por lo tanto, es soportado por todos los navegadores recientes. Para ello, es necesario utilizar el método `defineProperty` del `Object` prototype de este modo: ```js var a = {}; Object.defineProperty(a, 'readonly', { value: 15, writable: false }); a.readonly = 20; console.log(a.readonly); // 15 ``` La sintaxis es la siguiente: ```js Object.defineProperty(dest, propName, options) ``` o para múltiples definiciones: ```js Object.defineProperties(dest, { propA: optionsA, propB: optionsB, //... }) ``` donde las opciones incluyen los siguientes atributos: - *value*: si la propiedad no es una getter (ver abajo), value es un atributo obligatorio. `{a: 12}` === `Object.defineProperty(obj, 'a', {value: 12})` - *writable*: establecer la propiedad como readonly. Tenga en cuenta que si la propiedad está en objetos anidados, sus propiedades siguen siendo editable. - *enumerable*: establecer la propiedad como ocultos. Eso significa que bucles `for ... of` y `stringify` no incluirá el establecimiento con su resultado, pero la propiedad sigue ahí. Nota: Esto no significa que la propiedad es privada! Todavía puede ser accesible desde el exterior, sólo significa que no será impreso. - *configurable*: establecer la propiedad como no modificable, por ejemplo, protegidos de la supresión o redefinición. Una vez más, si la propiedad es un objeto anidado, sus propiedades siguen siendo configurable. Así que con el fin de crear una propiedad privada constante, se puede definir así: ```js Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false}); ``` Además de la configuración de propiedades, `defineProperty` nos permite definir *propiedades dinámicas*, gracias al segundo parámetro que es una cadena. Por ejemplo, digamos que quiero crear propiedades de acuerdo con alguna configuración externa: ```js var obj = { getTypeFromExternal(): true // illegal in ES5.1 } Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok // For the example sake, ES6 introduced a new syntax: var obj = { [getTypeFromExternal()]: true } ``` ¡Pero eso no es todo! propiedades avanzadas nos permite crear **getters** y **setters**, al igual que otros lenguajes de programación orientada a objetos! En ese caso, no se puede utilizar `writable`, `enumerable` y `configurable`, pero en su lugar: ```js function Foobar () { var _foo; // true private property Object.defineProperty(obj, 'foo', { get: function () { return _foo; } set: function (value) { _foo = value } }); } var foobar = new Foobar(); foobar.foo; // 15 foobar.foo = 20; // _foo = 20 ``` Aparte de la ventaja obvia de encapsulación y metodos de acceso avanzados, se dará cuenta de que no teníamos "call" de getter, en cambio, sólo la propiedad "get" sin paréntesis! ¡Esto es increíble! Por ejemplo, imaginemos que tenemos un objeto con propiedades anidadas largas, así: ```js var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } }; ``` Ahora, en lugar de hacer `a.b.c[0].d` (donde una de las propiedades se pueden resolver como `undefined` y lanzar un error), que en su lugar puede crear un alias: ```js Object.defineProperty(obj, 'firstD', { get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d } }) console.log(obj.firstD) // 10 ``` ### Nota Si se define un getter sin un setter y todavía intenta establecer un valor, obtendrá un error! Esto es particularmente importante cuando se utilizan funciones auxiliares tales como `$.extend` o `_.merge`. ¡Ten cuidado! ### Links - [defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) - [Defining properties in JavaScript](http://bdadam.com/blog/defining-properties-in-javascript.html) ================================================ FILE: _posts/es_ES/javascript/2016-02-09-using-json-stringify.md ================================================ --- layout: post title: Usando JSON.Stringify tip-number: 40 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: Crear un string seleccionando propiedades de un objeto JSON. redirect_from: - /es_es/using-json-stringify/ categories: - es_ES - javascript --- Digamos que hay un objeto con propiedades"prop1", "prop2", "prop3". Podemos pasarle __patrametro adicional__ a __JSON.stringify__ a escribir propiedades selectiva del objeto a cadena como: ```javascript var obj = { 'prop1': 'value1', 'prop2': 'value2', 'prop3': 'value3' }; var selectedProperties = ['prop1', 'prop2']; var str = JSON.stringify(obj, selectedProperties); // str // {"prop1":"value1","prop2":"value2"} ``` __"str"__ sólo contendrá información sobre propiedades seleccionadas. En lugar de un array podemos pasar una función también. ```javascript function selectedProperties(key, val) { // the first val will be the entire object, key is empty string if (!key) { return val; } if (key === 'prop1' || key === 'prop2') { return val; } return; } ``` The last optional param it takes is to modify the way it writes the object to string. El último parámetro opcional se necesita si quiere modificar la forma en que se escribe el objeto de cadena. ```javascript var str = JSON.stringify(obj, selectedProperties, '\t\t'); /* str output with double tabs in every line. { "prop1": "value1", "prop2": "value2" } */ ``` ================================================ FILE: _posts/es_ES/javascript/2016-02-10-array-average-and-median.md ================================================ --- layout: post title: Array media y promedio. tip-number: 41 tip-username: soyuka tip-username-profile: https://github.com/soyuka tip-tldr: Calcula la media y el promedio de los valores de la matriz redirect_from: - /es_es/array-average-and-median/ categories: - es_ES - javascript --- Los siguientes ejemplos se basan en el siguiente array: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; ``` Para obtener el promedio, tenemos que resumir los números y luego dividir por el número de valores. Los pasos son los siguientes: - obtener la longitud del array - sumar los valores - obtener el promedio (`suma/length`) ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let sum = values.reduce((previous, current) => current += previous); let avg = sum / values.length; // avg = 28 ``` O bien: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let count = values.length; values = values.reduce((previous, current) => current += previous); values /= count; // avg = 28 ``` Ahora, para obtener media los pasos son los siguientes: - ordenar el array - obtener la media aritmética de los valores medios ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let lowMiddle = Math.floor((values.length - 1) / 2); let highMiddle = Math.ceil((values.length - 1) / 2); let median = (values[lowMiddle] + values[highMiddle]) / 2; // median = 13,5 ``` Con un operador de bits: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let median = (values[(values.length - 1) >> 1] + values[values.length >> 1]) / 2 // median = 13,5 ``` ================================================ FILE: _posts/es_ES/javascript/2016-02-11-preventing-unapply-attacks.md ================================================ --- layout: post title: La prevención de ataques de cancelar la aplicación tip-number: 42 tip-username: emars tip-username-profile: https://twitter.com/marseltov tip-tldr: Congelar la construcción en los prototypes. redirect_from: - /es_es/preventing-unapply-attacks/ categories: - es_ES - javascript --- Reemplazando el constructor de prototypes, código externo puede ocasionar que el código roto sea reescribiendo código para exponer y cambiar los argumentos ligados. Esto puede ser un problema serio que rompe las aplicaciones que funciona mediante el uso de métodos polyfill ES5. ```js // example bind polyfill function bind(fn) { var prev = Array.prototype.slice.call(arguments, 1); return function bound() { var curr = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.concat.apply(prev, curr); return fn.apply(null, args); }; } // unapply-attack function unapplyAttack() { var concat = Array.prototype.concat; Array.prototype.concat = function replaceAll() { Array.prototype.concat = concat; // restore the correct version var curr = Array.prototype.slice.call(arguments, 0); var result = concat.apply([], curr); return result; }; } ``` La función anterior descarta el array `prev` desde el enlace que significa que cualquier `.concat`, la primera llamada concat siguiente utilizando el ataque cancelar la aplicación generará un error. Usando [Object.freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze), haciendo un objeto inmutable, se impide que cualquier sobreescritura de los objetos prototype. ```js (function freezePrototypes() { if (typeof Object.freeze !== 'function') { throw new Error('Missing Object.freeze'); } Object.freeze(Object.prototype); Object.freeze(Array.prototype); Object.freeze(Function.prototype); }()); ``` Puede leer mas [here](https://glebbahmutov.com/blog/unapply-attack/). Aunque este concepto se llama 'ataque de cancelar la aplicación' debido a algún código de poder acceder a los closures que normalmente no estarían en su scope, es sobre todo un error considerar esto una característica de seguridad, debido a que no impedir que un atacante la ejecución de código con el que se extiende desde prototipos antes de la congelación ocurre, y también sigue teniendo el potencial para leer todos los scopes que utilizan diversas características del lenguaje. ECMA módulos darían aislamiento, que es mucho más fuerte que esta solución sin embargo sigue sin solucionar los problemas de los scripts de terceros. ================================================ FILE: _posts/es_ES/javascript/2016-02-12-use-destructuring-in-function-parameters.md ================================================ --- layout: post title: Utilice la desestructuración de los parámetros de función tip-number: 43 tip-username: dislick tip-username-profile: https://github.com/dislick tip-tldr: ¿Sabías que se puede utilizar una funcion de desestructuración de parametro parámetros? redirect_from: - /es_es/use-destructuring-in-function-parameters/ categories: - es_ES - javascript --- Estoy seguro que muchos de ustedes ya están familiarizados con el [ES6 Destructuring Assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment). ¿Sabías que también se puede utilizar en los parámetros de una función? ```javascript var sayHello = function({ name, surname }) { console.log(`Hello ${name} ${surname}! How are you?`); }; sayHello({ name: 'John', surname: 'Smith' }); ``` Esto es grande para las funciones que aceptan un objeto de opciones. > Tenga en cuenta que la asignación desestructurada aún no está disponible en Node.js y casi todos los navegadores. Sin embargo, puede usar `--harmony-destructuring` para Node.js si desea intentarlo por sí mismo ahora. ================================================ FILE: _posts/es_ES/javascript/2016-02-13-know-the-passing-mechanism.md ================================================ --- layout: post title: Conocer el mecanismo de paso tip-number: 44 tip-username: bmkmanoj tip-username-profile: https://github.com/bmkmanoj tip-tldr: JavaScript solamente pasa por valor para ambos tipos primitiva y objeto (o referencia). En el caso de referencia el valor de referencia en sí se pasa por valor. redirect_from: - /es_es/calculate-the-max-min-value-from-an-array/ categories: - es_ES - javascript --- JavaScript es pass-by-value, técnicamente. No es ni pass-by-value, ni pass-by-reference, pasando por el verdadero sentido de estos términos. Para entender este mecanismo de paso, echar un vistazo a los siguientes dos fragmentos de código de ejemplo y las explicaciones. ### Ejemplo 1 ```js var me = { // 1 'partOf' : 'A Team' }; function myTeam(me) { // 2 me = { // 3 'belongsTo' : 'A Group' }; } myTeam(me); console.log(me); // 4 : {'partOf' : 'A Team'} ``` En el ejemplo anterior, cuando `myTeam` se invoca, JavaScript es *pasar la referencia a* `me` *objeto como valor, ya que es un objeto* y la invocación de sí mismo crea dos referencias independientes para el mismo objeto, (aunque el nombre siendo el mismo aquí `me`, es engañosa y nos da la impresión de que es el único de referencia) y por lo tanto, la variable de referencia a sí mismos son independientes. Cuando asignamos un nuevo objeto en la posición #`3`, estamos cambiando el valor de referencia en su totalidad dentro de la función `myTeam`, y no va a tener ningún impacto en el objeto original fuera de este scope de la función, desde donde fue aprobada y la referencia en el scope exterior se va a conservar el objeto original y por lo tanto la salida de `# 4'. ### Ejemplo 2 ```js var me = { // 1 'partOf' : 'A Team' }; function myGroup(me) { // 2 me.partOf = 'A Group'; // 3 } myGroup(me); console.log(me); // 4 : {'partOf' : 'A Group'} ``` En el caso de invocar `myGroup`, estamos pasando el objeto `me`. Pero a diferencia del escenario de ejemplo 1, nosotros no estamos asignando esta variable `me` a cualquier nuevo objeto, lo que significa efectivamente el valor de referencia de objeto dentro del scope de la funcion `myGroup` todavía es el valor de referencia del objeto original y cuando estamos modificando la propiedad dentro de este scope, que está modificando de manera efectiva la propiedad del objeto original. Por lo tanto, se obtiene la salida de #`7`. Lo mismo ocurre si esto no prueba que Javascript es pass-by-reference? No, no lo es. Recuerde, *JavaScript pasa la referencia como valor, en el caso de objetos*. La confusión surge ya que se tiende a no entender completamente lo que pase por referencia. Esta es la razón exacta, algunos prefieren llamar a esto como *Call-by-sharing*. *Publicacion del autor [js-by-examples](https://github.com/bmkmanoj/js-by-examples/blob/master/examples/js_pass_by_value_or_reference.md)* ================================================ FILE: _posts/es_ES/javascript/2016-02-14-calculate-the-max-min-value-from-an-array.md ================================================ --- layout: post title: Calcular el valor Max/Min de un array tip-number: 45 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: Formas de utilizar las funciones Math.max() y Math.min() con array de números tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/detect-document-ready-in-pure-js/ categories: - es_ES - javascript --- Las funciones [Math.max()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max) and [Math.min()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min) encontrar el valor máximo y mínimo de los argumentos, respectivamente. ```js Math.max(1, 2, 3, 4); // 4 Math.min(1, 2, 3, 4); // 1 ``` Estas funciones no funcionarán como tal con arrays de números. Sin embargo, hay algunas maneras de evitar esto. [`Function.prototype.apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) le permite llamar a una función con un determinado valor de `this` y un _array_ de argumentos. ```js var numbers = [1, 2, 3, 4]; Math.max.apply(null, numbers) // 4 Math.min.apply(null, numbers) // 1 ``` Pasando el array `numbers` como el segundo argumento de `apply()` resulta en la función invocados con todos los valores en le array como parámetros. Una manera sencilla con ES2015 de conseguir esto [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). ```js var numbers = [1, 2, 3, 4]; Math.max(...numbers) // 4 Math.min(...numbers) // 1 ``` Este operador hace que los valores del array a ser ampliado, o "spread", dentro de argumentos de la función. ================================================ FILE: _posts/es_ES/javascript/2016-02-15-detect-document-ready-in-pure-js.md ================================================ --- layout: post title: Detectar cuando DOM esta listo en JS puro tip-number: 46 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: La forma cross-browser para comprobar si el DOM se ha cargado en JavaScript puro. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/basics-declarations/ categories: - es_ES - javascript --- La forma cross-browser para comprobar si el documento se ha cargado en JavaScript puro utiliza [`readyState`](https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState). ```js if (document.readyState === 'complete') { // The page is fully loaded } ``` Como detectar cuando el document esta listo... ```js let stateCheck = setInterval(() => { if (document.readyState === 'complete') { clearInterval(stateCheck); // document ready } }, 100); ``` o con [onreadystatechange](https://developer.mozilla.org/en-US/docs/Web/Events/readystatechange)... ```js document.onreadystatechange = () => { if (document.readyState === 'complete') { // document ready } }; ``` Use `document.readyState === 'interactive'` para detectar cuando el DOM esta listo. ================================================ FILE: _posts/es_ES/javascript/2016-02-16-basics-declarations.md ================================================ --- layout: post title: Declaraciones basicas tip-number: 47 tip-username: adaniloff tip-username-profile: https://github.com/adaniloff tip-tldr: Entender y trabajar con declaraciones. redirect_from: - /es_es/basics-declarations/ categories: - es_ES - javascript --- A continuación, las diferentes formas de declarar las variables en JavaScript. Comentarios y console.log debería ser suficiente para explicar lo que está pasando aquí: ```js var y, x = y = 1 //== var x; var y; x = y = 1 console.log('--> 1:', `x = ${x}, y = ${y}`) // Will print //--> 1: x = 1, y = 1 ``` En primer lugar, apenas fijamos dos variables. No hay mucho aquí. ```js ;(() => { var x = y = 2 // == var x; x = y = 2; console.log('2.0:', `x = ${x}, y = ${y}`) })() console.log('--> 2.1:', `x = ${x}, y = ${y}`) // Will print //2.0: x = 2, y = 2 //--> 2.1: x = 1, y = 2 ``` Como se puede ver, el código sólo ha cambiado la 'y' global, ya que no hemos declarado la variable en el closure. ```js ;(() => { var x, y = 3 // == var x; var y = 3; console.log('3.0:', `x = ${x}, y = ${y}`) })() console.log('--> 3.1:', `x = ${x}, y = ${y}`) // Will print //3.0: x = undefined, y = 3 //--> 3.1: x = 1, y = 2 ``` Ahora declaramos dos variables a través var. Lo que significa que sólo viven en el contexto del closure. ```js ;(() => { var y, x = y = 4 // == var x; var y; x = y = 4 console.log('4.0:', `x = ${x}, y = ${y}`) })() console.log('--> 4.1:', `x = ${x}, y = ${y}`) // Will print //4.0: x = 4, y = 4 //--> 4.1: x = 1, y = 2 ``` Ambas variables han sido declaradas utilizando var y sólo después de que han puesto sus valores. Como local > global, x e y son locales en el closure, es decir, el global x e y no se tocaron. ```js x = 5 // == x = 5 console.log('--> 5:', `x = ${x}, y = ${y}`) // Will print //--> 5: x = 5, y = 2 ``` Esta última línea es explícito por sí mismo. Puede probar esto y ver el resultado [thanks to babel](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=var%20y%2C%20x%20%3D%20y%20%3D%201%20%2F%2F%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%201%0Aconsole.log('--%3E%201%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%201%3A%20x%20%3D%201%2C%20y%20%3D%201%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%20%3D%20y%20%3D%202%20%2F%2F%20%3D%3D%20var%20x%3B%20y%20%3D%202%3B%0A%20%20console.log('2.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%202.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F2.0%3A%20x%20%3D%202%2C%20y%20%3D%202%0A%2F%2F--%3E%202.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%2C%20y%20%3D%203%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%20%3D%203%3B%0A%20%20console.log('3.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%203.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F3.0%3A%20x%20%3D%20undefined%2C%20y%20%3D%203%0A%2F%2F--%3E%203.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20y%2C%20x%20%3D%20y%20%3D%204%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%203%0A%20%20console.log('4.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%204.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F4.0%3A%20x%20%3D%204%2C%20y%20%3D%204%0A%2F%2F--%3E%204.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0Ax%20%3D%205%20%2F%2F%20%3D%3D%20x%20%3D%205%0Aconsole.log('--%3E%205%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%205%3A%20x%20%3D%205%2C%20y%20%3D%202). Mas información disponible en [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var). Agradecimientos especiales @kurtextrem por su colaboracion :)! ================================================ FILE: _posts/es_ES/javascript/2016-02-17-reminders-about-reduce-function-usage.md ================================================ --- layout: post title: Reducir el uso de la función incorporada tip-number: 48 tip-username: darul75 tip-username-profile: https://twitter.com/darul75 tip-tldr: Algunos recordatorios sobre cómo usar la función de reducir redirect_from: - /es_es/reminders-about-reduce-function-usage/ categories: - es_ES - javascript --- Como está escrito en la documentación del método `reduce()` aplica una función contra un acumulador y cada valor del array (de izquierda a derecha) para reducirla a un solo valor. ### `reduce()` [reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) la funcion acepta 2 parametros (M: mandatory, O: optional): - (M) la callback **reducer function** para ser aplicada se ocupa de un par de anterior (el resultado de cálculo anterior) y el próximo elemento hasta el final de la lista. - (O) un **initial value** para ser utilizado como el primer argumento de la primera llamada de la callback. Así que vamos a ver un uso común y luego una más sofisticada. ### Uso común (acumulacion, concatenacion) Estamos en el sitio web de Amazon (precios en $) y nuestro carrito esta completo, vamos a calcular el total. ```javascript // my current amazon caddy purchases var items = [{price: 10}, {price: 120}, {price: 1000}]; // our reducer function var reducer = function add(sumSoFar, nextPrice) { return sumSoFar + nextPrice.price; }; // do the job var total = items.reduce(reducer, 0); console.log(total); // 1130 ``` Reducir el parámetro de la función opcional era entero primitivo de tipo 0 en el primer caso, pero podría haber sido un objeto, un Array... en lugar de un tipo primitivo, pero vamos a ver esto más adelante. Ahora, recibí un cupón de descuento de $20. ```javascript var total = items.reduce(reducer,-20); console.log(total); // 1110 ``` ### Uso avanzado (combinacion) Este segundo ejemplo de sintaxis se inspira en Redux [combineReducers](http://redux.js.org/docs/api/combineReducers.html) function [source](https://github.com/reactjs/redux/blob/master/src/combineReducers.js#L93). La idea separar la función de reducer por detras en funciones individuales separadas y al final calcular una nueva *función reductor grande sola*. Para ilustrar esto, vamos a crear un único objeto literal con algunas funciones reductoras capaz de calcular los precios totales en moneda distinta $, € ... ```javascript var reducers = { totalInDollar: function(state, item) { state.dollars += item.price; return state; }, totalInEuros : function(state, item) { state.euros += item.price * 0.897424392; return state; }, totalInPounds : function(state, item) { state.pounds += item.price * 0.692688671; return state; }, totalInYen : function(state, item) { state.yens += item.price * 113.852; return state; } // more... }; ``` Luego, creamos una nueva función navaja suiza - Responsable de la aplicación de la funcion reduce en cada parial. - Que devolverá una nueva callback de funcion reductor ```javascript var combineTotalPriceReducers = function(reducers) { return function(state, item) { return Object.keys(reducers).reduce( function(nextState, key) { reducers[key](state, item); return state; }, {} ); } }; ``` Ahora vamos a ver cómo usarla. ```javascript var bigTotalPriceReducer = combineTotalPriceReducers(reducers); var initialState = {dollars: 0, euros:0, yens: 0, pounds: 0}; var totals = items.reduce(bigTotalPriceReducer, initialState); console.log(totals); /* Object {dollars: 1130, euros: 1015.11531904, yens: 127524.24, pounds: 785.81131152} */ ``` Espero que este approach le puede dar otra idea de la utilización de la función reduce() para sus propias necesidades. Su función reduce podía manejar un historial de cada cálculo por ejemplo, como se hace en Ramda Js [scan](http://ramdajs.com/docs/#scan) [JSFiddle to play with](https://jsfiddle.net/darul75/81tgt0cd/) ================================================ FILE: _posts/es_ES/javascript/2016-02-26-extract-unix-timestamp-easily.md ================================================ --- layout: post title: La manera más fácil de extraer marca de tiempo Unix en JS tip-number: 49 tip-username: nmrony tip-username-profile: https://github.com/nmrony tip-tldr: En Javascript se puede conseguir fácilmente la marca de tiempo Unix redirect_from: - /es_es/extract-unix-timestamp-easily/ categories: - es_ES - javascript --- Con frecuencia tenemos que calcular con marca de tiempo unix. Hay varias maneras de agarrar la marca de tiempo. La forma mas rapida y facil es ```js const dateTime = Date.now(); const timestamp = Math.floor(dateTime / 1000); ``` or ```js const dateTime = new Date().getTime(); const timestamp = Math.floor(dateTime / 1000); ``` Para conseguir marca de tiempo unix de una fecha específica pasar `YYYY-MM-DD` o `YYYY-MM-DDT00:00:00Z` como parámetro del constructor `Date`. Por ejemplo ```js const dateTime = new Date('2012-06-08').getTime(); const timestamp = Math.floor(dateTime / 1000); ``` Usted puede añadir un signo `+` también cuando se declara un objeto `Date`, como a continuación ```js const dateTime = +new Date(); const timestamp = Math.floor(dateTime / 1000); ``` o para una fecha especifica ```js const dateTime = +new Date('2012-06-08'); const timestamp = Math.floor(dateTime / 1000); ``` Bajo el capó la ejecucion de llamadas del metodo `valueOf` del objeto `Date`. A continuación, el unario `+` operador llama `toNumber()` con ese valor devuelto. Para una explicación más detallada, consultar los siguientes enlaces * [Date.prototype.valueOf](http://es5.github.io/#x15.9.5.8) * [Unary + operator](http://es5.github.io/#x11.4.6) * [toNumber()](http://es5.github.io/#x9.3) * [Date Javascript MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) ================================================ FILE: _posts/es_ES/javascript/2016-03-03-helpful-console-log-hacks.md ================================================ --- layout: post title: Utiles trucos para Console Logging tip-number: 50 tip-username: zackhall tip-username-profile: https://twitter.com/zthall tip-tldr: Utiles técnicas utilizando breakpoints de coacción y condicionales. redirect_from: - /es_es/helpful-console-log-hacks/ categories: - es_ES - javascript --- ## Usando breakpoints condicionales Si usted quiere observar un valor por consola cada vez que una función se llama, puede utilizar puntos de breakpoints condicionales para hacer esto. Abran sus herramientas dev, encontrar la función que desea registrar datos en la consola y un breakpoints con la siguiente condición: ```js console.log(data.value) && false ``` Un breakpoint condicional detiene el hilo página sólo si la condición se evalúa como verdadera. Así que mediante el uso de console.log('foo') && false está garantizado que dan como resultado false, ya que está poniendo el falso literal en la condición AND. Así que esto no va a hacer una pausa cuando es evaluado, pero va a registrar datos en la consola. Esto también se puede utilizar para contar cuántas veces se llama una función o una callback. He aquí cómo se puede establecer un punto de interrupción condicional en [Edge](https://dev.windows.com/en-us/microsoft-edge/platform/documentation/f12-devtools-guide/debugger/#setting-and-managing-breakpoints "Managing Breakpoints in Edge"), [Chrome](https://developer.chrome.com/devtools/docs/javascript-debugging#breakpoints "Managing Breakpoints in Chrome"), [Firefox](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Set_a_conditional_breakpoint "Managing Breakpoints in Firefox") y [Safari](https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/Debugger/Debugger.html "Managing Breakpoints in Safari"). ## Impresión de una variable a la consola ¿Alguna vez ha entrado una variable en la consola y no fueron capaces de simplemente ver el código de la función? La forma más rápida para ver el código de la función es la de coaccionar a una cadena mediante la concatenación con una cadena vacía. ```js console.log(funcVariable + ''); ``` ================================================ FILE: _posts/es_ES/javascript/2016-03-16-DOM-event-listening-made-easy.md ================================================ --- layout: post title: Escuchar eventos DOM mas simple tip-number: 51 tip-username: octopitus tip-username-profile: https://github.com/octopitus tip-tldr: Una forma elegante y fácil de manejar eventos DOM redirect_from: - /es_es/DOM-event-listening-made-easy/ categories: - es_ES - javascript --- Muchos de nosotros todavía están haciendo estas cosas: - `element.addEventListener('type', obj.method.bind(obj))` - `element.addEventListener('type', function (event) {})` - `element.addEventListener('type', (event) => {})` Los ejemplos anteriores, crean nuevos controladores de eventos anónimos que no pueden ser retirados cuando ya no sean necesarios. Esto puede causar problemas de rendimiento o errores lógicos inesperados, cuando los controladores que ya no es necesario puden ser accidentalmente activado mediante interacciones del usuario inesperados o [event bubbling](http://www.javascripter.net/faq/eventbubbling.htm) Patrones de gestión de eventos más seguras son las siguientes: Utilice una referencia: ```js const handler = function () { console.log("Tada!") } element.addEventListener("click", handler) // Later on element.removeEventListener("click", handler) ``` Función llamada que remueve a sí mismo: ```js element.addEventListener('click', function click(e) { if (someCondition) { return e.currentTarget.removeEventListener('click', click); } }); ``` Un mejor enfoque: ```js function handleEvent (eventName, {onElement, withCallback, useCapture = false} = {}, thisArg) { const element = onElement || document.documentElement function handler (event) { if (typeof withCallback === 'function') { withCallback.call(thisArg, event) } } handler.destroy = function () { return element.removeEventListener(eventName, handler, useCapture) } element.addEventListener(eventName, handler, useCapture) return handler } // Anytime you need const handleClick = handleEvent('click', { onElement: element, withCallback: (event) => { console.log('Tada!') } }) // And anytime you want to remove it handleClick.destroy() ``` ================================================ FILE: _posts/es_ES/javascript/2016-04-05-return-values-with-the-new-operator.md ================================================ --- layout: post title: Retorno de valores con el operador 'new' tip-number: 52 tip-username: Morklympious tip-username-profile: https://github.com/morklympious tip-tldr: Comprender lo que se devuelve cuando el uso de new vs. sin usar new. redirect_from: - /es_es/get-file-extension/ categories: - es_ES - javascript --- Usted va a correr dentro de algunas instancias en las que va a utilizar `new` para asignar nuevos objetos en JavaScript. Se va a volar tu mente a menos que lea este consejo para entender lo que está sucediendo por detrás. El operador `new` en JavaScript es un operador que, en circunstancias razonables, devuelve una nueva instancia de un objeto. Digamos que tenemos una función constructora: ````js function Thing() { this.one = 1; this.two = 2; } var myThing = new Thing(); myThing.one // 1 myThing.two // 2 ```` __Nota__: `This` se refiere al nuevo objeto creado por `new`. De lo contrario, si `Thing()` se llama sin `new`, __objeto no creado__, y `this` va a apuntar al objeto global, que es `window`. Esto significa que: 1. Usted repentinamente tiene dos nuevas variables globales con nombre `one` y 'two`. 2. `myThing` ahora es undefined, ya que no devuelve nada en `Thing()`. Ahora que se obtiene ese ejemplo, aquí es donde las cosas se ponen un poco torcidas. Digamos que quiero añadir algo a la función constructora, un poco de sabor: ````js function Thing() { this.one = 1; this.two = 2; return 5; } var myThing = new Thing(); ```` Ahora, que hace myThing equal? Es 5? es un objeto? Es mi sentido lisiado de autoestima? El mundo nunca puede saber! Excepto el mundo sabe: ````js myThing.one // 1 myThing.two // 2 ```` Curiosamente, nunca realmente vemos los cinco que supuestamente 'retorna' de nuestro constructor. Eso es raro, ¿verdad? ¿Qué hace la función? ¿Dónde está el cinco? Vamos a probar con otra cosa. Vamos a retornar un tipo no primitivo, algo así como un objeto. ````js function Thing() { this.one = 1; this.two = 2; return { three: 3, four: 4 }; } var myThing = new Thing(); ```` Vamos a ver. Un console.log revela todo: ````js console.log(myThing); /* Object {three: 3, four: 4} What happened to this.one and this.two!? They've been stomped, my friend. */ ```` __Aquí es donde aprendemos:__ Cuando se llama a una función con la palabra clave `new`, puede setear propiedades en el uso de la palabra clave `this` (pero probablemente ya lo sabía). La devolución de un valor simple de una función que llamó a la palabra clave `new` no devolverá el valor que ha especificado, pero en su lugar devolverá la instancia `this` de la función (el que se introdujo en propiedades, como `this.one = 1;`). Sin embargo, el retorno de un no-primitivo, como un `object`,` array`, o `function` será pisar fuerte en la instancia `this`, y volver ese no primitivo en su lugar, arruinando todo el duro trabajo que hizo todo lo posible para asignar `this`. ================================================ FILE: _posts/es_ES/javascript/2016-04-21-get-file-extension.md ================================================ --- layout: post title: Obtener extensión de archivo tip-number: 53 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: ¿Cómo conseguir la extensión del archivo de manera más eficiente? redirect_from: - /es_es/get-file-extension/ categories: - es_ES - javascript --- ### Pregunta: ¿Cómo conseguir la extensión de archivo? ```javascript var file1 = "50.xsl"; var file2 = "30.doc"; getFileExtension(file1); //returs xsl getFileExtension(file2); //returs doc function getFileExtension(filename) { /*TODO*/ } ``` ### Solucion 1: Expresion regular ```js function getFileExtension1(filename) { return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined; } ``` ### Solucion 2: Metodo `split` ```js function getFileExtension2(filename) { return filename.split('.').pop(); } ``` Estas dos soluciones no podían manejar algunos casos extremos, aquí hay otra solución más robusta. ### Solucion 3: Metodos `slice`, `lastIndexOf` ```js function getFileExtension3(filename) { return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2); } console.log(getFileExtension3('')); // '' console.log(getFileExtension3('filename')); // '' console.log(getFileExtension3('filename.txt')); // 'txt' console.log(getFileExtension3('.hiddenfile')); // '' console.log(getFileExtension3('filename.with.many.dots.ext')); // 'ext' ``` _¿Como funciona?_ - [String.lastIndexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf) el método devuelve la última aparición del valor especificado (`'.'` en este caso). Devuelve `-1` si no se encuentra el valor. - Los valores de retorno de `lastIndexOf` para el parámetro `'filename'` y `'.hiddenfile'` son `0` y `-1` respectivamente.[Zero-fill operador de desplazamiento a la derecha (>>>)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3E%3E%3E_%28Zero-fill_right_shift%29) transformará `-1` a `4294967295` y `-2` a `4294967294`, aquí es un truco para asegurar el nombre del archivo sin cambios en esos casos extremos. - [String.prototype.slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice) extrae la extensión del índice que se calculó anteriormente. Si el índice es mayor que la longitud del nombre de archivo, el resultado es `""`. ### Comparación | Solucion | Parametros | Resultados | | ----------------------------------------- |:-------------------:|:--------:| | Solucion 1: Expresion regular | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | undefined
      undefined
      'txt'
      'hiddenfile'
      'ext'
      | | Solucion 2: Metodo `split` | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | ''
      'filename'
      'txt'
      'hiddenfile'
      'ext'
      | | Solucion 3: Metodos `slice`, `lastIndexOf` | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | ''
      ''
      'txt'
      ''
      'ext'
      | ### Demo de performance [Here](https://jsbin.com/tipofu/edit?js,console) es la demostración de los códigos anteriores. [Here](http://jsperf.com/extract-file-extension) es la prueba de rendimiento de esos 3 soluciones. ### Codigo [How can I get file extensions with JavaScript](http://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript) ================================================ FILE: _posts/es_ES/javascript/2016-05-06-use-optional-arguments.md ================================================ --- layout: post title: Cómo utilizar argumentos opcionales en funciones (con callback opcional) tip-number: 54 tip-username: alphashuro tip-username-profile: https://github.com/alphashuro tip-tldr: Usted puede hacer argumentos de función y callback opcional redirect_from: - /es_es/use-optional-arguments/ categories: - es_ES - javascript --- Ejemplo de función donde los argumentos 2 y 3 son opcionales ```javascript function example( err, optionalA, optionalB, callback ) { // recuperar los argumentos como array var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; }; // Primer argumento es el objeto de error // shift() elimina el primer elemento del // array y lo devuelve err = args.shift(); // Si el último argumento es una función entonces es la funcion callback. // pop() elimina el último elemento del array // y lo devuelve if (typeof args[args.length-1] === 'function') { callback = args.pop(); } // Si args aún mantiene elementos, estos son         // sus elementos opcionales que se podía         // recuperar uno por uno como este: if (args.length > 0) optionalA = args.shift(); else optionalA = null; if (args.length > 0) optionalB = args.shift(); else optionalB = null; // continuar como de costumbre: comprobar si hay errores if (err) { return callback && callback(err); } // para los propósitos de tutoría, ingrese los parámetros opcionales console.log('optionalA:', optionalA); console.log('optionalB:', optionalB); console.log('callback:', callback); /* haz tus cosas */ } // ES6 con código más corto function example(...args) { // primer argumento es el objeto de error const err = args.shift(); // si el último argumento es una función entonces es la funcion callback. const callback = (typeof args[args.length-1] === 'function') ? args.pop() : null; // args si todavía tiene elementos, estos son los elementos opcionales que se podía recuperar de uno en uno como este: const optionalA = (args.length > 0) ? args.shift() : null; const optionalB = (args.length > 0) ? args.shift() : null; // ... repetir para mas items if (err && callback) return callback(err); /* haz tus cosas */ } // invocar la función de ejemplo con y sin argumentos opcionales example(null, 'AA'); example(null, function (err) { /* do something */ }); example(null, 'AA', function (err) {}); example(null, 'AAAA', 'BBBB', function (err) {}); ``` ### ¿Cómo se determina si optionalA o optionalB se destina? El diseño de su función para requerir optionalA con el fin de aceptar optionalB ================================================ FILE: _posts/es_ES/javascript/2016-05-12-make-easy-loop-on-array.md ================================================ --- layout: post title: Crear un bucle fácil usando un array tip-number: 55 tip-username: jamet-julien tip-username-profile: https://github.com/jamet-julien tip-tldr: A veces, necesitamos un ciclo sin fin sobre un array de items, como un carrusel de imágenes o un lista de reproducción de audio. Así es como tomar un array y darle "bucle poderosos" redirect_from: - /es_es/make-easy-loop-on-array/ categories: - es_ES - javascript --- A veces, necesitamos un ciclo sin fin sobre un array de items, como un carrusel de imágenes o un lista de reproducción de audio. Así es como tomar un array y darle "bucle poderosos" ```js var aList = ['A','B','C','D','E']; function make_looper( arr ){ arr.loop_idx = 0; // return current item arr.current = function(){ if( this.loop_idx < 0 ){// First verification this.loop_idx = this.length - 1;// update loop_idx } if( this.loop_idx >= this.length ){// second verification this.loop_idx = 0;// update loop_idx } return arr[ this.loop_idx ];//return item }; // increment loop_idx AND return new current arr.next = function(){ this.loop_idx++; return this.current(); }; // decrement loop_idx AND return new current arr.prev = function(){ this.loop_idx--; return this.current(); }; } make_looper( aList); aList.current();// -> A aList.next();// -> B aList.next();// -> C aList.next();// -> D aList.next();// -> E aList.next();// -> A aList.pop() ;// -> E aList.prev();// -> D aList.prev();// -> C aList.prev();// -> B aList.prev();// -> A aList.prev();// -> D ``` Utilizando el operador ```%``` (módulo) es más vistoso. El modulo retorna el resto de la division ( ``` 2 % 5 = 1``` y ``` 5 % 5 = 0```): ```js var aList = ['A','B','C','D','E']; function make_looper( arr ){ arr.loop_idx = 0; // return current item arr.current = function(){ this.loop_idx = ( this.loop_idx ) % this.length;// no verification !! return arr[ this.loop_idx ]; }; // increment loop_idx AND return new current arr.next = function(){ this.loop_idx++; return this.current(); }; // decrement loop_idx AND return new current arr.prev = function(){ this.loop_idx += this.length - 1; return this.current(); }; } make_looper( aList); aList.current();// -> A aList.next();// -> B aList.next();// -> C aList.next();// -> D aList.next();// -> E aList.next();// -> A aList.pop() ;// -> E aList.prev();// -> D aList.prev();// -> C aList.prev();// -> B aList.prev();// -> A aList.prev();// -> D ``` ================================================ FILE: _posts/es_ES/react/2016-01-02-keys-in-children-components-are-important.md ================================================ --- layout: post title: Keys en componentes secundarios son importantes tip-number: 02 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: La key es un atributo que se debe pasar a todos los componentes creados dinámicamente a partir de un array. Es un identificador único y constante que React usa para identificar cada componente en el DOM y saber si se trata de un componente diferente o el mismo. Utilizando keys asegura que el componente secundario se conserve y no se cree nuevamente y evita que cosas extrañas sucedan. tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /es_es/keys-in-children-components-are-important/ categories: - es_ES - react --- La [key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children) es un atributo que se debe pasar a todos los componentes creados dinámicamente a partir de un array. Es un identificador único y constante que React usa para identificar cada componente en el DOM y saber si se trata de un componente diferente o el mismo. Utilizando keys asegura que el componente secundario se conserve y no se cree nuevamente y evita que cosas extrañas sucedan. > La key no es realmente sobre el rendimiento, es más acerca de la identidad (que a su vez conduce a un mejor rendimiento). Aleatoriamente asignados y los valores cambiantes no forman una identidad [Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939) - Utilice un valor único existente del objeto. - Definir las keys de los componentes padre, no en los componentes secundarios ```javascript //bad ... render() {
      {% raw %}{{item.name}}{% endraw %}
      } ... //good ``` - [Usar el indice de un array es una mala practica.](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318#.76co046o9) - `random()` no funcionara. ```javascript //bad ``` - Usted puede crear su propia única id. Asegúrese de que el método es rápido y adjuntarlo a su objeto. - Cuando el número de children es grande o contiene componentes caros, utilice keys para mejorar el rendimiento. - [Debe proporcionar el atributo clave para todos los children de ReactCSSTransitionGroup.](http://docs.reactjs-china.com/react/docs/animation.html) ================================================ FILE: _posts/zh_CN/angular/2016-01-01-angularjs-digest-vs-apply.md ================================================ --- layout: post title: AngularJs - $digest vs $apply tip-number: 01 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: JavaScript模块和构建步骤越来越复杂和多样化,但是新框架里的样板是什么样子的呢? tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/angularjs-digest-vs-apply/ categories: - zh_CN - angular --- AngularJs最令人欣赏的特性之一就是双向数据绑定。AngularJs通过循环(`$digest`)检查model和view的变化实现此功能。想要理解框架底层的运行机制你需要理解这个概念。 当一个事件被触发时,Angular触发每个watcher. 这是我们已知的`$digest`循环。有时你需要强制手动运行一个新的循环,而且因为这是最影响性能的一方面,你必须选择一个正确的选项。 ### `$apply` 这个核心方法可以让你显式启动`digest`循环。这意味着所有的watcher将会被检测;整个应用启动`$digest loop`。在内部在会执行一个可选的方法之后,会调用`$rootScope.$digest();`. ### `$digest` 这种情况下`$digest`方法在当前作用域和它的子作用域启动`$digest`循环。你需要注意他的父作用域将不会被检测也不会被影响。 ### 推荐 - 仅当浏览器DOM事件在AngularJS之外被触发时使用`$apply`或`$digest`。 - 给`$apply`传递方法,它将包含错误处理机制而且允许整合在`digest`循环里的变化。 ```javascript $scope.$apply(() => { $scope.tip = 'Javascript Tip'; }); ``` - 如果你只需要更新当前的作用域或者它的子作用域的话,使用`$digest`,而且要防止在整个应用里运行新的`digest`循环。这在性能上的好处是显而易见的。 - `$apply()`对机器来说是一个困难的处理过程,在绑定过多的时候可能会引发性能问题。 - 如果你正使用`>AngularJS 1.2.X`版本,使用`$evalAsync`, 这个方法将在当前循环或下一个循环执行表达式,这能提高你的应用的性能。 ================================================ FILE: _posts/zh_CN/javascript/2015-12-29-insert-item-inside-an-array.md ================================================ --- layout: post title: 向数组中插入元素 tip-number: 00 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 向一个数组中插入元素是平时很常见的一件事情。你可以使用push在数组尾部插入元素,可以用unshift在数组头部插入元素,也可以用splice在数组中间插入元素。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/insert-item-inside-an-array/ categories: - zh_CN - javascript --- # 向一个数组中插入元素 向一个数组中插入元素是平时很常见的一件事情。你可以使用push在数组尾部插入元素,可以用unshift在数组头部插入元素,也可以用splice在数组中间插入元素。 但是这些已知的方法,并不意味着没有更加高效的方法。让我们接着往下看…… ## 向数组结尾添加元素 向数组结尾添加元素用push()很简单,但下面有一个更高效的方法 ```javascript var arr = [1,2,3,4,5]; var arr2 = []; arr.push(6); arr[arr.length] = 6; arr2 = arr.concat([6]); ``` 两种方法都是修改原始数组。不信?看看[jsperf](http://jsperf.com/push-item-inside-an-array) ### 手机上的效率 #### Android (v4.2.2) 1. _arr.push(6);_ and _arr[arr.length] = 6;_ 性能相同 // 3 319 694 ops/sec 3. _arr2 = arr.concat([6]);_ 比其他两个方法慢50.61% #### Chrome Mobile (v33.0.0) 1. _arr[arr.length] = 6;_ // 6 125 975 ops/sec 2. _arr.push(6);_ 慢66.74% 3. _arr2 = arr.concat([6]);_ 慢87.63% #### Safari Mobile (v9) 1. _arr[arr.length] = 6;_ // 7 452 898 ops/sec 2. _arr.push(6);_ 慢40.19% 3. _arr2 = arr.concat([6]);_ 慢49.78% ```javascript 最快的为 1. arr[arr.length] = 6; // 平均 5 632 856 ops/sec 2. arr.push(6); // 慢35.64% 3. arr2 = arr.concat([6]); // 慢62.67% ``` ### 桌面上的效率 #### Chrome (v48.0.2564) 1. _arr[arr.length] = 6;_ // 21 602 722 ops/sec 2. _arr.push(6);_ 慢61.94% 3. _arr2 = arr.concat([6]);_ 慢87.45% #### Firefox (v44) 1. _arr.push(6);_ // 56 032 805 ops/sec 2. _arr[arr.length] = 6;_ 慢0.52% 3. _arr2 = arr.concat([6]);_ 慢87.36% #### IE (v11) 1. _arr[arr.length] = 6;_ // 67 197 046 ops/sec 2. _arr.push(6);_ 慢39.61% 3. _arr2 = arr.concat([6]);_ 慢93.41% #### Opera (v35.0.2066.68) 1. _arr[arr.length] = 6;_ // 30 775 071 ops/sec 2. _arr.push(6);_ 慢71.60% 3. _arr2 = arr.concat([6]);_ 慢83.70% #### Safari (v9.0.3) 1. _arr.push(6);_ // 42 670 978 ops/sec 2. _arr[arr.length] = 6;_ 慢0.80% 3. _arr2 = arr.concat([6]);_ 慢76.07% ```javascript 最快的为 1. arr[arr.length] = 6; // 平均42 345 449 ops/sec 2. arr.push(6); // 慢34.66% 3. arr2 = arr.concat([6]); // 慢85.79% ``` ## 向数组的头部添加元素 现在我们试着向数组的头部添加元素: ```javascript var arr = [1,2,3,4,5]; arr.unshift(0); [0].concat(arr); ``` 这里有一些小区别,unshift操作的是原始数组,concat返回一个新数组,参考[jsperf](http://jsperf.com/unshift-item-inside-an-array) ### 手机上的效率 : #### Android (v4.2.2) 1. _[0].concat(arr);_ // 1 808 717 ops/sec 2. _arr.unshift(0);_ 慢97.85% #### Chrome Mobile (v33.0.0) 1. _[0].concat(arr);_ // 1 269 498 ops/sec 2. _arr.unshift(0);_ 慢99.86% #### Safari Mobile (v9) 1. _arr.unshift(0);_ // 3 250 184 ops/sec 2. _[0].concat(arr);_ 慢33.67% ```javascript 最快的为 1. [0].concat(arr); // 平均4 972 622 ops/sec 2. arr.unshift(0); // 慢64.70% ``` ### 桌面上的效率 #### Chrome (v48.0.2564) 1. _[0].concat(arr);_ // 2 656 685 ops/sec 2. _arr.unshift(0);_ 慢96.77% #### Firefox (v44) 1. _[0].concat(arr);_ // 8 039 759 ops/sec 2. _arr.unshift(0);_ 慢99.72% #### IE (v11) 1. _[0].concat(arr);_ // 3 604 226 ops/sec 2. _arr.unshift(0);_ 慢98.31% #### Opera (v35.0.2066.68) 1. _[0].concat(arr);_ // 4 102 128 ops/sec 2. _arr.unshift(0);_ 慢97.44% #### Safari (v9.0.3) 1. _arr.unshift(0);_ // 12 356 477 ops/sec 2. _[0].concat(arr);_ 慢15.17% ```javascript 最快的为 1. [0].concat(arr); // 平均6 032 573 ops/sec 2. arr.unshift(0); // 慢78.65% ``` ## 向数组中间添加元素 使用splice可以简单的向数组中间添加元素,这也是最高效的方法。 ```javascript var items = ['one', 'two', 'three', 'four']; items.splice(items.length / 2, 0, 'hello'); ``` 我在许多浏览器和系统中进行了测试,结果都是相似的。希望这条小知识可以帮到你,也欢迎大家自行测试。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-03-improve-nested-conditionals.md ================================================ --- layout: post title: 优化嵌套的条件语句 tip-number: 03 tip-username: AlbertoFuente tip-username-profile: https://github.com/AlbertoFuente tip-tldr: 我们怎样来提高和优化javascript里嵌套的`if`语句呢? redirect_from: - /zh_cn/improve-nested-conditionals/ categories: - zh_CN - javascript --- 我们怎样来提高和优化javascript里嵌套的`if`语句呢? ```javascript if (color) { if (color === 'black') { printBlackBackground(); } else if (color === 'red') { printRedBackground(); } else if (color === 'blue') { printBlueBackground(); } else if (color === 'green') { printGreenBackground(); } else { printYellowBackground(); } } ``` 一种方法来提高嵌套的`if`语句是用`switch`语句。虽然它不那么啰嗦而且排列整齐,但是并不建议使用它,因为这对于调试错误很困难。这告诉你[为什么](https://toddmotto.com/deprecating-the-switch-statement-for-object-literals). ```javascript switch(color) { case 'black': printBlackBackground(); break; case 'red': printRedBackground(); break; case 'blue': printBlueBackground(); break; case 'green': printGreenBackground(); break; default: printYellowBackground(); } ``` 如果可以重构的话,我们可以试着简化函数。比如不需要为每个颜色写一个函数,而是将颜色作为函数的参数。 ```javascript function printBackground(color) { if (!color || typeof color !== 'string') { return; // Invalid color, return immediately } } ``` 但是如果不能重构的话,我们必须避免过多的条件检查,避免过多使用`switch`。我们必须考虑最有效率的方法,使用`object`。 ```javascript switch(true) { case (typeof color === 'string' && color === 'black'): printBlackBackground(); break; case (typeof color === 'string' && color === 'red'): printRedBackground(); break; case (typeof color === 'string' && color === 'blue'): printBlueBackground(); break; case (typeof color === 'string' && color === 'green'): printGreenBackground(); break; case (typeof color === 'string' && color === 'yellow'): printYellowBackground(); break; } ``` 但是我们应该时刻注意避免太多判断在一个条件里,尽量少的使用`switch`,考虑最有效率的方法:借助`object`。 ```javascript var colorObj = { 'black': printBlackBackground, 'red': printRedBackground, 'blue': printBlueBackground, 'green': printGreenBackground, 'yellow': printYellowBackground }; if (color in colorObj) { colorObj[color](); } ``` 这里有更多相关的[内容](http://www.nicoespeon.com/en/2015/01/oop-revisited-switch-in-js/). ================================================ FILE: _posts/zh_CN/javascript/2016-01-04-sorting-strings-with-accented-characters.md ================================================ --- layout: post title: 排列含音节字母的字符串 tip-number: 04 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: Javascript有一个原生方法**sort**可以排列数组。一次简单的`array.sort()`将每一个数组元素视为字符串并按照字母表排列。但是当你试图整理一个非ASCII元素的数组时,你可能会得到一个奇怪的结果。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/sorting-strings-with-accented-characters/ categories: - zh_CN - javascript --- Javascript有一个原生方法**[sort](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)**可以排列数组。一次简单的`array.sort()`将每一个数组元素视为字符串并按照字母表排列。你也可以提供[自定义排列方法](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters)。 ```javascript ['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); // ["Buenos Aires", "Mumbai", "New York", "Shanghai"] ``` 但是当你试图整理一个如`['é', 'a', 'ú', 'c']`这样的非ASCII元素的数组时,你可能会得到一个奇怪的结果`['c', 'e', 'á', 'ú']`。这是因为排序方法只在英文下有用。 看一下下一个例子: ```javascript // 西班牙语 ['único','árbol', 'cosas', 'fútbol'].sort(); // ["cosas", "fútbol", "árbol", "único"] // bad order // 德语 ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); // ["Wann", "Woche", "wäre", "wöchentlich"] // bad order ``` 幸运的是,有两种方法可以解决这个问题,由ECMAScript国际化API提供的[localeCompare](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare)和[Intl.Collator](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Collator)。 > 两个方法都有自定义配置参数可以使其更好用。 ### 使用`localeCompare()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { return a.localeCompare(b); }); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { return a.localeCompare(b); }); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` ### 使用`Intl.Collator()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` - 两个方法都可以自定义区域位置。 - 根据[Firefox](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Performance),当比较大数量的字符串时,使用`Intl.Collator`更快。 所以当你处理一个由非英语组成的字符串数组时,记得使用此方法来避免排序出现意外。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-05-differences-between-undefined-and-null.md ================================================ --- layout: post title: undefined与null的区别 tip-number: 05 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 理解`undefined`与`null`的区别。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/differences-between-undefined-and-null/ categories: - zh_CN - javascript --- - `undefined`表示一个变量没有被声明,或者被声明了但没有被赋值 - `null`是一个表示“没有值”的值 - Javascript将未赋值的变量默认值设为`undefined` - Javascript从来不会将变量设为`null`。它是用来让程序员表明某个用`var`声明的变量时没有值的。 - `undefined`不是一个有效的JSON,而`null`是 - `undefined`的类型(typeof)是`undefined` - `null`的类型(typeof)是`object`. [为什么?](http://www.2ality.com/2013/10/typeof-null.html) - 它们都是基本类型 - 他们都是[falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) (`Boolean(undefined) // false`, `Boolean(null) // false`) - 你可以这样判断一个变量是否是[undefined](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined) ```javascript typeof variable === "undefined" ``` - 你可以这样判断一个变量是否是[null](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/null) ```javascript variable === null ``` - **双等号**比较时它们相等,但**三等号**比较时不相等 ```javascript null == undefined // true null === undefined // false ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md ================================================ --- layout: post title: 可以接受单参数与数组的方法 tip-number: 06 tip-username: mattfxyz tip-username-profile: https://twitter.com/mattfxyz tip-tldr: 写一个方法可以接受单个参数也可以接受一个数组,而不是分开写两个方法。这和jQuery的一些方法的工作原理很像(`css` 可以修改任何匹配到的选择器). redirect_from: - /zh_cn/writing-a-single-method-for-arrays-and-a-single-element/ categories: - zh_CN - javascript --- 写一个方法可以接受单个参数也可以接受一个数组,而不是分开写两个方法。这和jQuery的一些方法的工作原理很像(`css` 可以修改任何匹配到的选择器). 你只要把任何东西连接到一个数组. `Array.concat`可以接受一个数组也可以接受单个参数。 ```javascript function printUpperCase(words) { var elements = [].concat(words || []); for (var i = 0; i < elements.length; i++) { console.log(elements[i].toUpperCase()); } } ``` `printUpperCase`现在可以接受单个单词或多个单词的数组作为它的参数。同时也可以避免在不传递参数时抛出的`TypeError`错误的隐患。 ```javascript printUpperCase("cactus"); // => CACTUS printUpperCase(["cactus", "bear", "potato"]); // => CACTUS // BEAR // POTATO ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-07-use-strict-and-get-lazy.md ================================================ --- layout: post title: 使用"use strict" 变得懒惰 tip-number: 07 tip-username: nainslie tip-username-profile: https://twitter.com/nat5an tip-tldr: JavaScript的严格模式使开发者更容易写出“安全”的代码。 redirect_from: - /zh_cn/use-strict-and-get-lazy/ categories: - zh_CN - javascript --- (译者注:此片翻译较渣,欢迎指正,建议大家[阅读原文](http://www.jstips.co/en/use-strict-and-get-lazy/)或直接阅读[MDN对严格模式的中文介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode) 并欢迎PR) JavaScript的严格模式使开发者更容易写出“安全”的代码。 通常情况下,JavaScript允许程序员相当粗心,比如你可以引用一个从未用"var"声明的变量。或许对于一个刚入门的开发者来说这看起来很方便,但这也是变量拼写错误或者从作用域外引用变量时引发的一系列错误的原因。 程序员喜欢电脑帮我们做一些无聊的工作,喜欢它自动的检查我们工作上的错误。这就是"use strict"帮我们做的,它把我们的错误转变为了JavaScript错误。 我们把这个指令放在js文件顶端来使用它: ```javascript // 全脚本严格模式 "use strict"; var v = "Hi! I'm a strict mode script!"; ``` 或者放在一个方法内: ```javascript function f() { // 方法级严格模式 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function f2() { return "I'm not strict."; } ``` 通过在JavaScript文件或方法内引入此指令,使JavaScript引擎运行在严格模式下,这直接禁止了许多大项目中不受欢迎的操作。另外,严格模式也改变了以下行为: * 只有被"var"声明过的变量才可以引用 * 试图写只读变量时将会报错 * 只能通过"new"关键字调用构造方法 * "this"不再隐式的指向全局变量 * 对eval()有更严格的限制 * 防止你使用预保留关键字命名变量 严格模式对于新项目来说是很棒的,但对于一些并没有使用它的老项目来说,引入它也是很有挑战性的。如果你把所有js文件都连接到一个大文件中的话,可能导致所有文件都运行在严格模式下,这可能也会有一些问题。 它不是一个声明,而是一个表达式,被低版本的JavaScript忽略。 严格模式的支持情况: * Internet Explorer 10+ * Firefox 4+ * Chrome 13+ * Safari 5.1+ * Opera 12+ [MDN对严格模式的全面介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode) ================================================ FILE: _posts/zh_CN/javascript/2016-01-08-converting-a-node-list-to-an-array.md ================================================ --- layout: post title: 将Node List转换为数组(Array) tip-number: 08 tip-username: Tevko tip-username-profile: https://twitter.com/tevko tip-tldr: 这是一个快速、安全、可重用的方法将node list转换为DOM元素的数组。 redirect_from: - /zh_cn/converting-a-node-list-to-an-array/ categories: - zh_CN - javascript --- `querySelectorAll`方法返回一个类数组对象称为node list。这些数据结构被称为“类数组”,因为他们看似数组却没有类似`map`、`foreach`这样的数组方法。这是一个快速、安全、可重用的方法将node list转换为DOM元素的数组: ```javascript const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.apply(null, nodelist); //之后 .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); //等... ``` `apply`方法可以在指定`this`时以数组形式向方法传递参数。[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)规定`apply`可以接受类数组对象,恰巧就是`querySelectorAll`方法所返回的内容。如果我们不需要指定方法内`this`的值时传`null`或`0`即可。返回的结果即包含所有数组方法的DOM元素数组。 另外你可以使用`Array.prototype.slice`结合`Function.prototype.call`或`Function.prototype.apply`, 将类数组对象当做`this`传入: ```javascript const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.prototype.slice.call(nodelist); // or equivalently Array.prototype.slice.apply(nodelist); //之后 .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); //等... ``` 如果你正在用ES2015你可以使用[展开运算符 `...`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_operator) ```js const nodelist = [...document.querySelectorAll('div')]; // 返回一个真正的数组 //之后 .. nodelist.forEach(...); nodelist.map(...); nodelist.slice(...); //等... ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-09-template-strings.md ================================================ --- layout: post title: 模板字符串 tip-number: 09 tip-username: JakeRawr tip-username-profile: https://github.com/JakeRawr tip-tldr: ES6中,JS现在有了引号拼接字符串的替代品,模板字符串。 redirect_from: - /zh_cn/template-strings/ categories: - zh_CN - javascript --- ES6中,JS现在有了引号拼接字符串的替代品,模板字符串。 示例: 普通字符串 ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log('My name is ' + firstName + ' ' + lastName); // My name is Jake Rawr ``` 模板字符串 ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log(`My name is ${firstName} ${lastName}`); // My name is Jake Rawr ``` 在模板字符串中,你可以不用`\n`来生成多行字符串,在`${}`里做简单的逻辑运算(例如 2+3)甚至使用[逻辑运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)。 ```javascript var val1 = 1, val2 = 2; console.log(`${val1} is ${val1 < val2 ? 'less than': 'greater than'} ${val2}`) // 1 is less than 2 ``` 你也可以使用函数修改末班字符串的输出内容;这被称为[带标签的模板字符串](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings#带标签的模板字符串),其中包含了带标签的模板字符串的示例. 或许你还想[阅读更多内容](https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2)来了解模板字符串。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-10-check-if-a-property-is-in-a-object.md ================================================ --- layout: post title: 检查某对象是否有某属性 tip-number: 10 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 这是一些检查某对象是否有某属性的方法。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/check-if-a-property-is-in-a-object/ categories: - zh_CN - javascript --- 当你需要检查某属性是否存在于一个[对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects),你可能会这样做: ```javascript var myObject = { name: '@tips_js' }; if (myObject.name) { ... } ``` 这是可以的,但是你需要知道有两种原生方法可以解决此类问题。[`in` 操作符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in) 和 [`Object.hasOwnProperty`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty),任何继承自`Object`的对象都可以使用这两种方法。 ### 看一下较大的区别 ```javascript var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true 'name' in myObject; // true myObject.hasOwnProperty('valueOf'); // false, valueOf 继承自原型链 'valueOf' in myObject; // true ``` 两者检查属性的深度不同,换言之`hasOwnProperty`只在本身有此属性时返回true,而`in`操作符不区分属性来自于本身或继承自原型链。 这是另一个例子 ```javascript var myFunc = function() { this.name = '@tips_js'; }; myFunc.prototype.age = '10 days'; var user = new myFunc(); user.hasOwnProperty('name'); // true user.hasOwnProperty('age'); // false, 因为age来自于原型链 ``` [在线示例](https://jsbin.com/tecoqa/edit?js,console)! 同样建议阅读关于检查对象是否包含属性时常见错误的[讨论](https://github.com/loverajoel/jstips/issues/62)。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-11-hoisting.md ================================================ --- layout: post title: 变量提升 tip-number: 11 tip-username: squizzleflip tip-username-profile: https://twitter.com/squizzleflip tip-tldr: 理解变量提升有助于管理函数作用域 redirect_from: - /zh_cn/hoisting/ categories: - zh_CN - javascript --- 理解[变量提升](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var#var_hoisting)可以帮助你组织方法作用域。只要记住变量声明和方法声明都会被提升到顶部。变量的定义不会提升,即使你在同一行声明和定义一个变量。变量**声明**是让系统知道有这个变量存在而**定义**是给其赋值。 ```javascript function doTheThing() { // ReferenceError: notDeclared is not defined console.log(notDeclared); // Outputs: undefined console.log(definedLater); var definedLater; definedLater = 'I am defined!' // Outputs: 'I am defined!' console.log(definedLater) // Outputs: undefined console.log(definedSimulateneously); var definedSimulateneously = 'I am defined!' // Outputs: 'I am defined!' console.log(definedSimulateneously) // Outputs: 'I did it!' doSomethingElse(); function doSomethingElse(){ console.log('I did it!'); } // TypeError: undefined is not a function functionVar(); var functionVar = function(){ console.log('I did it!'); } } ``` 为了让你的代码更易读,将所有的变量声明在函数的顶端,这样可以更清楚的知道变量来自哪个作用域。在使用变量之前声明变量。将方法定义在函数的底部。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-12-pseudomandatory-parameters-in-es6-functions.md ================================================ --- layout: post title: ES6中的伪强制参数 tip-number: 12 tip-username: Avraam Mavridis tip-username-profile: https://github.com/AvraamMavridis tip-tldr: 在许多编程语言中,方法的参数时默认强制需要的,开发人员需要明确定义一个可选的参数。 redirect_from: - /zh_cn/tip-to-measure-performance-of-a-javascript-block/ categories: - zh_CN - javascript --- 在许多编程语言中,方法的参数是默认强制需要的,开发人员必须明确定义一个可选的参数。在Javascript 中每一个参数都是可选的,但是我们可以利用[**es6参数默认值**](http://exploringjs.com/es6/ch_parameter-handling.html#sec_parameter-default-values)特性的优点来达到强制要求这种目的,并且不污染函数体本身。 ``` javascript const _err = function( message ){ throw new Error( message ); } const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b getSum( 10 ) // throws Error, b is not defined getSum( undefined, 10 ) // throws Error, a is not defined ``` `_err` 是一个即时抛出错误的方法。如果参数中的任何一个没有值,参数默认的值将会被使用, `_err`方法将被调用,并且会抛出一个错误。你可以从[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Default_parameters)看到更多关于**默认参数特性**的例子。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md ================================================ --- layout: post title: 测量javascript代码块性能的小知识 tip-number: 13 tip-username: manmadareddy tip-username-profile: https://twitter.com/manmadareddy tip-tldr: 快速的测量javascript的性能,我们可以使用console的方法,例如 ```console.time(label)```和 ```console.timeEnd(label)``` redirect_from: - /zh_cn/fat-arrow-functions/ categories: - zh_CN - javascript --- 快速的测量javascript的性能,我们可以使用console的方法,例如 [```console.time(label)```](https://developer.chrome.com/devtools/docs/console-api#consoletimelabel) 和 [```console.timeEnd(label)```](https://developer.chrome.com/devtools/docs/console-api#consoletimeendlabel) ```javascript console.time("Array initialize"); var arr = new Array(100), len = arr.length, i; for (i = 0; i < len; i++) { arr[i] = new Object(); }; console.timeEnd("Array initialize"); // Outputs: Array initialize: 0.711ms ``` 更多内容: [Console object](https://github.com/DeveloperToolsWG/console-object), [Javascript benchmarking](https://mathiasbynens.be/notes/javascript-benchmarking) Demo: [jsfiddle](https://jsfiddle.net/meottb62/) - [codepen](http://codepen.io/anon/pen/JGJPoa) (在浏览器控制台输出) > 注意:由于[Mozilla](https://developer.mozilla.org/zh-CN/docs/Web/API/Console/time)不建议将其使用在线上项目中,建议仅在开发中使用。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-14-fat-arrow-functions.md ================================================ --- layout: post title: 箭头函数 tip-number: 14 tip-username: pklinger tip-username-profile: https://github.com/pklinger/ tip-tldr: 介绍一个ES6的新特性,箭头函数或许一个让你用更少行写更多代码的方便工具。 redirect_from: - /zh_cn/fat-arrow-functions/ categories: - zh_CN - javascript --- 介绍一个ES6的新特性,箭头函数或许一个让你用更少行写更多代码的方便工具。它的名字(fat arrow functions)来自于它的语法`=>`是一个比瘦箭头`->`要'胖的箭头'(译者注:但是国内貌似不分胖瘦就叫箭头函数)。Some programmers might already know this type of functions from different languages such as Haskell as 'lambda expressions' respectively 'anonymous functions'. It is called anonymous, as these arrow functions do not have a descriptive function name.(译者注:一些其他语言中的箭头函数,避免不准确就不翻译了 欢迎PR) ### 有什么益处呢? * 语法: 更少的代码行; 不再需要一遍一遍的打`function`了 * 语义: 从上下文中捕获`this`关键字 ### 简单的语法示例 观察一下这两个功能完全相同的代码片段。你将很快明白箭头函数做了什么。 ```javascript // 箭头函数的一般语法 param => expression // 也可以用用小括号 // 多参数时小括号是必须的 (param1 [, param2]) => expression // 使用functions var arr = [5,3,2,9,1]; var arrFunc = arr.map(function(x) { return x * x; }); console.log(arr) // 使用箭头函数 var arr = [5,3,2,9,1]; var arrFunc = arr.map((x) => x*x); console.log(arr) ``` 正如你所看到的,箭头函数在这种情况下省去了写小括号,function以及return的时间。我建议你总是使用小括号,因为对于像`(x,y) => x+y`这样多参数函数,小括号总是需要的。这仅是以防在不同使用场景下忘记小括号的一种方法。但是上面的代码和`x => x*x`是一样的。至此仅是语法上的提升,减少了代码行数并提高了可读性。 ### Lexically binding `this` 这是另一个使用箭头函数的好原因。这是一个关于`this`上下文的问题。使用箭头函数,你不需要再担心`.bind(this)`也不用再设置`that = this`了,因为箭头函数继承了外围作用域的`this`值。看一下下面的[示例](https://jsfiddle.net/pklinger/rw94oc11/): ```javascript // 全局定义 this.i this.i = 100; var counterA = new CounterA(); var counterB = new CounterB(); var counterC = new CounterC(); var counterD = new CounterD(); // 不好的例子 function CounterA() { // CounterA的`this`实例 (!!调用时忽略了此实例) this.i = 0; setInterval(function () { // `this` 指向全局(global)对象,而不是CounterA的`this` // 所以从100开始计数,而不是0 (本地的this.i) this.i++; document.getElementById("counterA").innerHTML = this.i; }, 500); } // 手动绑定 that = this function CounterB() { this.i = 0; var that = this; setInterval(function() { that.i++; document.getElementById("counterB").innerHTML = that.i; }, 500); } // 使用 .bind(this) function CounterC() { this.i = 0; setInterval(function() { this.i++; document.getElementById("counterC").innerHTML = this.i; }.bind(this), 500); } // 箭头函数 function CounterD() { this.i = 0; setInterval(() => { this.i++; document.getElementById("counterD").innerHTML = this.i; }, 500); } ``` 更多有关箭头函数的内容可以查看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions)。更多语法选项请看[这里](http://jsrocks.org/2014/10/arrow-functions-and-their-scope/). ================================================ FILE: _posts/zh_CN/javascript/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md ================================================ --- layout: post title: 更简单的使用indexOf实现contains功能 tip-number: 15 tip-username: jhogoforbroke tip-username-profile: https://twitter.com/jhogoforbroke tip-tldr: JavaScript并未提供contains方法。检测子字符串是否存在于字符串或者变量是否存在于数组你可能会这样做。 redirect_from: - /zh_cn/even-simpler-way-of-using-indexof-as-a-contains-clause/ categories: - zh_CN - javascript --- JavaScript并未提供contains方法。检测子字符串是否存在于字符串或者变量是否存在于数组你可能会这样做: ```javascript var someText = 'javascript rules'; if (someText.indexOf('javascript') !== -1) { } // or if (someText.indexOf('javascript') >= 0) { } ``` 但是让我们看一下这些 [Expressjs](https://github.com/strongloop/express)代码段。 [examples/mvc/lib/boot.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/mvc/lib/boot.js#L26) ```javascript for (var key in obj) { // "reserved" exports if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; ``` [lib/utils.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/lib/utils.js#L93) ```javascript exports.normalizeType = function(type){ return ~type.indexOf('/') ? acceptParams(type) : { value: mime.lookup(type), params: {} }; }; ``` [examples/web-service/index.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/web-service/index.js#L35) ```javascript // key is invalid if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key')); ``` 难点是 [位操作符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) **~**, “按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。” 它将`-1`转换为`0`,而`0`在javascript为`false`,所以: ```javascript var someText = 'text'; !!~someText.indexOf('tex'); // someText contains "tex" - true !~someText.indexOf('tex'); // someText NOT contains "tex" - false ~someText.indexOf('asd'); // someText doesn't contain "asd" - false ~someText.indexOf('ext'); // someText contains "ext" - true ``` ### String.prototype.includes() 在ES6中提供了[includes() 方法](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/includes)供我们判断一个字符串是否包含了另一个字符串: ```javascript 'something'.includes('thing'); // true ``` 在ECMAScript 2016 (ES7)甚至可能将其应用于数组,像indexOf一样: ```javascript !!~[1, 2, 3].indexOf(1); // true [1, 2, 3].includes(1); // true ``` **不幸的是, 只有Chrome、Firefox、Safari 9及其更高版本和Edge支持了这功能。IE11及其更低版本并不支持** **最好在受控的环境中使用此功能** ================================================ FILE: _posts/zh_CN/javascript/2016-01-16-passing-arguments-to-callback-functions.md ================================================ --- layout: post title: 向回调方法传递参数 tip-number: 16 tip-username: minhazav tip-username-profile: https://twitter.com/minhazav tip-tldr: 通常下,你并不能给回调函数传递参数,但是你可以借助Javascript闭包的优势来传递参数给回调函数。 redirect_from: - /zh_cn/passing-arguments-to-callback-functions/ categories: - zh_CN - javascript --- 通常下,你并不能给回调函数传递参数。 比如: ```js function callback() { console.log('Hi human'); } document.getElementById('someelem').addEventListener('click', callback); ``` 你可以借助Javascript闭包的优势来传递参数给回调函数。看这个例子: ```js function callback(a, b) { return function() { console.log('sum = ', (a+b)); } } var x = 1, y = 2; document.getElementById('someelem').addEventListener('click', callback(x, y)); ``` **什么是闭包?** 闭包是指函数有自由独立的变量。换句话说,定义在闭包中的函数可以“记忆”它创建时候的环境。想了解更多请[参考MDN的文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures)。 这种方法使参数`x`和`y`在回调方法被调用时处于其作用域内。 另一个办法是使用`bind`方法。比如: ```js var alertText = function(text) { alert(text); }; document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello')); ``` 两种方法之间有着微小的性能差异,请看[jsperf](http://jsperf.com/bind-vs-closure-23). ================================================ FILE: _posts/zh_CN/javascript/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md ================================================ --- layout: post title: Node.js - 运行未被引用的模块 tip-number: 17 tip-username: odsdq tip-username-profile: https://twitter.com/odsdq tip-tldr: 在Node里,你可以让你的程序根据其运行自`require('./something.js')`或者`node something.js`而做不同的处理。如果你想与你的一个独立的模块进行交互,这是非常有用的。 redirect_from: - /zh_cn/nodejs-run-a-module-if-it-is-not-required/ categories: - zh_CN - javascript --- 在Node里,你可以让你的程序根据其运行自`require('./something.js')`或者`node something.js`而做不同的处理。如果你想与你的一个独立的模块进行交互,这是非常有用的。 ```js if (!module.parent) { // 通过 `node something.js` 启动 app.listen(8088, function() { console.log('app listening on port 8088'); }) } else { // 通过 `require('/.something.js')` 被引用 module.exports = app; } ``` 更多内容请看 [modules的文档](https://nodejs.org/api/modules.html#modules_module_parent) ================================================ FILE: _posts/zh_CN/javascript/2016-01-18-rounding-the-fast-way.md ================================================ --- layout: post title: 快速(但危险)的取整方法 tip-number: 18 tip-username: pklinger tip-username-profile: https://github.com/pklinger tip-tldr: 通常情况下`~~X`比`Math.trunc(X)`要快,但同时也会使你的代码做一些讨厌的事情。 redirect_from: - /zh_cn/rounding-the-fast-way/ categories: - zh_CN - javascript --- 本条小知识关于性能... 你曾遇到过[双波浪线`~~`操作符](http://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript)吗?它也被称为“双按位非”操作符。你通常可以使用它作为代替`Math.trunc()`的更快的方法。为什么呢? 一个按位非操作符`~`首先将输入`input`截取为32位,然后将其转换为`-(input+1)`。因此双按位非操作符将输入转换为`-(-(input + 1)+1)`,使其成为一个趋向于0取整的好工具。对于数字的输入,它很像`Math.trunc()`。失败时返回`0`,这可能在解决`Math.trunc()`转换错误返回`NaN`时是一个很好的替代。 ```js // 单个 ~ console.log(~1337) // -1338 // 数字输入 console.log(~~47.11) // -> 47 console.log(~~1.9999) // -> 1 console.log(~~3) // -> 3 ``` 然而, 尽管`~~`可能有更好的性能,有经验的程序员通常坚持使用`Math.trunc()`。要明白为什么,这里有一个关于此操作符的冷静分析。 ### 适用的情况 ##### 当CPU资源很珍贵时 `~~`可能在各平台上都比`Math.trunc()`快,但是你应该在你所关心的所有平台上[测试这种猜想](https://jsperf.com/jsfvsbitnot/10)。同样,你通常需要执行数百万这样的操作来看看在运行时有没有明显的影响。 ##### 当不需要关心代码清晰度时 如果你想迷惑其他人,或者想在`minifier/uglifier`时取得更大功效,这是一种相对廉价的方式。 ### 禁用的情况 ##### 当你的代码需要维护时 代码可读性始终是最重要的。无论你工作在一个团队,或是贡献给开源仓库,或是单飞。正如[名言所说](http://c2.com/cgi/wiki?CodeForTheMaintainer): > Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.(写代码时,要始终认为一个有暴力倾向并知道你住在哪里的人会最终维护你的代码。) For a solo programmer, that psychopath is inevitably "you in six months".(这句不会翻译……) ##### 当你忘记`~~`永远趋向于0时 新手程序员或许更关注`~~`的聪明之处,却忘记了“只去掉小数部分”的意义。这在将浮点数转换为数组索引或关联有序的值时很容易导致**差一错误** ,这时明显需要一个不同的取整方法。 (代码可读性不高往往会导致此问题) 打个比方,如果你想得到离一个数“最近的整数”,你应该用`Math.round()`而不是`~~`,但是由于程序员的惰性和**_每次使用需要敲10个键_**的事实,人类的手指往往会战胜冷冷的逻辑,导致错误的结果。 相比之下,`Math.xyz()`(举例)函数的名字清楚的传达了它们的作用,减少了可能出现的意外的错误。 ##### 当处理大数时 因为`~`首先将数组转换为32位,`~~`的结果伪值在 ±2.15*10^12左右。如果你没有明确的检查输入值的范围,当转换的值最终与原始值有很大差距时,用户就可能触发未知的行为: ```js a = 2147483647.123 // 比32位最大正数,再多一点 console.log(~~a) // -> 2147483647 (ok) a += 10000 // -> 2147493647.123 (ok) console.log(~~a) // -> -2147483648 (huh?) ``` 一个特别容易中招的地方是在处理Unix时间戳时(从1970年1月1日 00:00:00 UTC开始以秒测量)。一个快速获取的方法: ```js epoch_int = ~~(+new Date() / 1000) // Date() 以毫秒计量,所以我们缩小它 ``` 然而,当处理2038年1月19日 03:14:07 UTC 之后的时间戳时(有时称为**Y2038 limit**), 可怕的事情发生了: ```js // 2040年1月1日 00:00:00.123 UTC的时间戳 epoch = +new Date('2040-01-01') / 1000 + 0.123 // -> 2208988800.123 // 回到未来! epoch_int = ~~epoch // -> -2085978496 console.log(new Date(epoch_int * 1000)) // -> Wed Nov 25 1903 17:31:44 UTC // 这很搞笑,让我们来取得正确答案 epoch_flr = Math.floor(epoch) // -> 2208988800 console.log(new Date(epoch_flr * 1000)) // -> Sun Jan 01 2040 00:00:00 UTC ``` ##### 当原始输入的数据类型不确定时 因为`~~`可以将任何非数字类型转换为`0`: ```js console.log(~~[]) // -> 0 console.log(~~NaN) // -> 0 console.log(~~null) // -> 0 ``` 一些程序员将其看作适当输入验证的替代品。然而,这将导致奇怪的逻辑问题,因此你不能辨别违法输入还是真正的`0`。因此这_并不_推荐。 ##### 当很多人认为`~~X == Math.floor(X)`时 很多人由于很多原因错误的把"双按位非"等同于`Math.floor()`。如果你不能准确地使用它,最终你很有可能会滥用它。 另一些人很细心的注意正数使用`Math.floor()`而负数使用`Math.ceil()`,但这又强制你在处理它的时候需要停下来想一想你处理的数是什么值。这又违背了使用`~~`快捷无陷阱的目的。 ### 结论 尽量避免,并有节制的使用。 ### 使用 1. 谨慎使用。 2. 在应用前检查值。 3. 仔细记录被转化值的相关假设。 4. 审查代码至少处理: * 逻辑错误,不合法的输入作为合法的`0`传入其他代码模块 * 输入转换后范围错误 * 错误的舍入方向导致差一错误 ================================================ FILE: _posts/zh_CN/javascript/2016-01-19-safe-string-concatenation.md ================================================ --- layout: post title: 安全的字符串拼接 tip-number: 19 tip-username: gogainda tip-username-profile: https://twitter.com/gogainda tip-tldr: 假如你需要拼接一些不确定类型的变量为字符串,你需要确保算术运算符在你拼接时不会起作用。使用concat redirect_from: - /zh_cn/safe-string-concatenation/ categories: - zh_CN - javascript --- 假如你需要拼接一些不确定类型的变量为字符串,你需要确保算术运算符在你拼接时不会起作用。使用concat: ```javascript var one = 1; var two = 2; var three = '3'; var result = ''.concat(one, two, three); //"123" ``` 这应该就是你所期望的拼接结果。如果不这样,拼接时加号可能会导致你意想不到的结果: ```javascript var one = 1; var two = 2; var three = '3'; var result = one + two + three; //"33" instead of "123" ``` 关于性能,与用[```join```](http://www.sitepoint.com/javascript-fast-string-concatenation/)来拼接字符串相比 ```concat```的效率是几乎一样的。 你可以在[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)了解更多关于```concat```方法的内容。 ================================================ FILE: _posts/zh_CN/javascript/2016-01-20-return-objects-to-enable-chaining-of-functions.md ================================================ --- layout: post title: 返回对象,使方法可以链式调用 tip-number: 20 tip-username: WakeskaterX tip-username-profile: https://twitter.com/WakeStudio tip-tldr: 在面向对象的Javascript中为对象建立一个方法时,返回当前对象可以让你在一条链上调用方法。 redirect_from: - /zh_cn/return-objects-to-enable-chaining-of-functions/ categories: - zh_CN - javascript --- 在面向对象的Javascript中为对象建立一个方法时,返回当前对象可以让你在一条链上调用方法。 ```js function Person(name) { this.name = name; this.sayName = function() { console.log("Hello my name is: ", this.name); return this; }; this.changeName = function(name) { this.name = name; return this; }; } var person = new Person("John"); person.sayName().changeName("Timmy").sayName(); ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-21-shuffle-an-array.md ================================================ --- layout: post title: 对数组洗牌 tip-number: 21 tip-username: 0xmtn tip-username-profile: https://github.com/0xmtn/ tip-tldr: Fisher-Yates Shuffling 算法对数组进行洗牌 redirect_from: - /zh_cn/shuffle-an-array/ categories: - zh_CN - javascript --- 这段代码运用了[Fisher-Yates Shuffling](https://www.wikiwand.com/en/Fisher%E2%80%93Yates_shuffle)算法对数组进行洗牌。 ```javascript function shuffle(arr) { var i, j, temp; for (i = arr.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; }; ``` 调用示例: ```javascript var a = [1, 2, 3, 4, 5, 6, 7, 8]; var b = shuffle(a); console.log(b); // [2, 7, 8, 6, 5, 3, 1, 4] ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-22-two-ways-to-empty-an-array.md ================================================ --- layout: post title: 清空数组的两种方法 tip-number: 22 tip-username: microlv tip-username-profile: https://github.com/microlv tip-tldr: 在JavaScript中清空一个数组有很多方法,但这是一个最高效的方法。 redirect_from: - /zh_cn/two-ways-to-empty-an-array/ categories: - zh_CN - javascript --- 如果你定义了一个数组,然后你想清空它。 通常,你会这样做: ```javascript // 定义一个数组 var list = [1, 2, 3, 4]; function empty() { //清空数组 list = []; } empty(); ``` 但是,这有一个效率更高的方法来清空数组。 你可以这样写: ```javascript var list = [1, 2, 3, 4]; function empty() { //empty your array list.length = 0; } empty(); ``` * `list = []` 将一个新的数组的引用赋值给变量,其他引用并不受影响。 这意味着以前数组的内容被引用的话将依旧存在于内存中,这将导致内存泄漏。 * `list.length = 0` 删除数组里的所有内容,也将影响到其他引用。 然而,如果你复制了一个数组(A 和 Copy-A),如果你用`list.length = 0`清空了它的内容,复制的数组也会清空它的内容。 考虑一下将会输出什么: ```js var foo = [1,2,3]; var bar = [1,2,3]; var foo2 = foo; var bar2 = bar; foo = []; bar.length = 0; console.log(foo, bar, foo2, bar2); //[] [] [1, 2, 3] [] ``` 更多内容请看Stackoverflow: [difference-between-array-length-0-and-array](http://stackoverflow.com/questions/4804235/difference-between-array-length-0-and-array) ================================================ FILE: _posts/zh_CN/javascript/2016-01-23-converting-to-number-fast-way.md ================================================ --- layout: post title: 转换为数字的更快方法 tip-number: 23 tip-username: sonnyt tip-username-profile: http://twitter.com/sonnyt tip-tldr: 将字符串转换为数字是极为常见的。最简单和快速的方法是使用`+`(加号) 来实现。 redirect_from: - /zh_cn/converting-to-number-fast-way/ categories: - zh_CN - javascript --- 将字符串转换为数字是极为常见的。最简单和快速的方法([jsPref](https://jsperf.com/number-vs-parseint-vs-plus/29))`+`(加号) 来实现。 ```javascript var one = '1'; var numberOne = +one; // Number 1 ``` 你也可以用`-`(减号)将其转化为负数值。 ```javascript var one = '1'; var negativeNumberOne = -one; // Number -1 ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-24-use_===_instead_of_==.md ================================================ --- layout: post title: 使用 === 而不是 == tip-number: 24 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: == (或者 `!=`) 操作在需要的情况下自动进行了类型转换。`===` (或 `!==`)操作不会执行任何转换。`===`在比较值和类型时,可以说比`==`更快。 redirect_from: - /zh_cn/use_===_instead_of_==/ categories: - zh_CN - javascript --- `==` (或者 `!=`) 操作在需要的情况下自动进行了类型转换。`===` (或 `!==`)操作不会执行任何转换。`===`在比较值和类型时,可以说比`==`更快([jsPref](http://jsperf.com/strictcompare))。 ```js [10] == 10 // 为 true [10] === 10 // 为 false '10' == 10 // 为 true '10' === 10 // 为 false [] == 0 // 为 true [] === 0 // 为 false '' == false // 为 true 但 true == "a" 为false '' === false // 为 false ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-25-Using-immediately-invoked-function-expression.md ================================================ --- layout: post title: 使用立即执行函数表达式 tip-number: 25 tip-username: rishantagarwal tip-username-profile: https://github.com/rishantagarwal tip-tldr: 立即执行函数表达式( IIFE - immediately invoked function expression)是一个立即执行的匿名函数表达式,它在Javascript中有一些很重要的用途。 redirect_from: - /zh_cn/Using-immediately-invoked-function-expression/ categories: - zh_CN - javascript --- 立即执行函数表达式( IIFE - immediately invoked function expression)是一个立即执行的匿名函数表达式,它在Javascript中有一些很重要的用途。 ```javascript (function() { // Do something​ } )() ``` 这是一个立即执行的匿名函数表达式,它在有JavaScript一些特别重要的用途。 两对括号包裹着一个匿名函数,使匿名函数变成了一个函数表达式。于是,我们现在拥有了一个未命名的函数表达式,而不是一个全局作用域下或在任何地方定义的的简单函数。 类似地,我们也可以创建一个命名过的立即执行函数表达式: ```javascript (someNamedFunction = function(msg) { console.log(msg || "Nothing for today !!") }) (); // 输出 --> Nothing for today !!​ ​ someNamedFunction("Javascript rocks !!"); // 输出 --> Javascript rocks !! someNamedFunction(); // 输出 --> Nothing for today !!​ ``` 更多内容, 请参考下面链接 - 1. [链接 1](https://blog.mariusschulz.com/2016/01/13/disassembling-javascripts-iife-syntax) 2. [链接 2](http://javascriptissexy.com/12-simple-yet-powerful-javascript-tips/) 效率: [jsPerf](http://jsperf.com/iife-with-call) ================================================ FILE: _posts/zh_CN/javascript/2016-01-26-filtering-and-sorting-a-list-of-strings.md ================================================ --- layout: post title: 过滤并排序字符串列表 tip-number: 26 tip-username: davegomez tip-username-profile: https://github.com/davegomez tip-tldr: 你可能有一个很多名字组成的列表,需要过滤掉重复的名字并按字母表将其排序。 redirect_from: - /zh_cn/filtering-and-sorting-a-list-of-strings/ categories: - zh_CN - javascript --- 你可能有一个很多名字组成的列表,需要过滤掉重复的名字并按字母表将其排序。 在我们的例子里准备用不同版本语言的**JavaScript 保留字**的列表,但是你能发现,有很多重复的关键字而且它们并没有按字母表顺序排列。所以这是一个完美的字符串列表([数组](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array))来测试我们的JavaScript小知识。 ```js var keywords = ['do', 'if', 'in', 'for', 'new', 'try', 'var', 'case', 'else', 'enum', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'delete', 'export', 'import', 'return', 'switch', 'typeof', 'default', 'extends', 'finally', 'continue', 'debugger', 'function', 'do', 'if', 'in', 'for', 'int', 'new', 'try', 'var', 'byte', 'case', 'char', 'else', 'enum', 'goto', 'long', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'final', 'float', 'short', 'super', 'throw', 'while', 'delete', 'double', 'export', 'import', 'native', 'public', 'return', 'static', 'switch', 'throws', 'typeof', 'boolean', 'default', 'extends', 'finally', 'package', 'private', 'abstract', 'continue', 'debugger', 'function', 'volatile', 'interface', 'protected', 'transient', 'implements', 'instanceof', 'synchronized', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof']; ``` 因为我们不想改变我们的原始列表,所以我们准备用高阶函数叫做[`filter`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),它将基于我们传递的回调方法返回一个新的过滤后的数组。回调方法将比较当前关键字在原始列表里的索引和新列表中的索引,仅当索引匹配时将当前关键字push到新数组。 最后我们准备使用[`sort`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)方法排序过滤后的列表,`sort`只接受一个比较方法作为参数,并返回按字母表排序后的列表。 ```js var filteredAndSortedKeywords = keywords .filter(function (keyword, index) { return keywords.lastIndexOf(keyword) === index; }) .sort(function (a, b) { return a < b ? -1 : 1; }); ``` 在**ES6** (ECMAScript 2015)版本下使用[箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions)看起来更简单: ```js const filteredAndSortedKeywords = keywords .filter((keyword, index) => keywords.lastIndexOf(keyword) === index) .sort((a, b) => a < b ? -1 : 1); ``` 这是最后过滤和排序后的JavaScript保留字列表: ```js console.log(filteredAndSortedKeywords); // ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield'] ``` *感谢[@nikshulipa](https://github.com/nikshulipa)、[@kirilloid](https://twitter.com/kirilloid)、[@lesterzone](https://twitter.com/lesterzone)、[@tracker1](https://twitter.com/tracker1)、[@manuel_del_pozo](https://twitter.com/manuel_del_pozo)所有的回复与建议!* ================================================ FILE: _posts/zh_CN/javascript/2016-01-27-short-circuit-evaluation-in-js.md ================================================ --- layout: post title: JS中的短路求值 tip-number: 27 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: 短路求值是说, 只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值:当AND(`&&`)的第一个运算数的值为false时,其结果必定为false;当OR(`||`)的第一个运算数为true时,最后结果必定为true。 redirect_from: - /zh_cn/short-circuit-evaluation-in-js/ categories: - zh_CN - javascript --- [短路求值](https://zh.wikipedia.org/wiki/%E7%9F%AD%E8%B7%AF%E6%B1%82%E5%80%BC)是说, 只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值:当AND(`&&`)的第一个运算数的值为false时,其结果必定为false;当OR(`||`)的第一个运算数为true时,最后结果必定为true。 对于下面的`test`条件和`isTrue`与`isFalse`方法 ```js var test = true; var isTrue = function(){ console.log('Test is true.'); }; var isFalse = function(){ console.log('Test is false.'); }; ``` 使用逻辑与 - `&&`. ```js // 普通的if语句 if(test){ isTrue(); // Test is true } // 上面的语句可以使用 '&&' 写为: ( test && isTrue() ); // Test is true ``` 使用逻辑或 - `||`. ```js test = false; if(!test){ isFalse(); // Test is false. } ( test || isFalse()); // Test is false. ``` 逻辑或可以用来给参数设置默认值。 ```js function theSameOldFoo(name){ name = name || 'Bar' ; console.log("My best friend's name is " + name); } theSameOldFoo(); // My best friend's name is Bar theSameOldFoo('Bhaskar'); // My best friend's name is Bhaskar ``` 逻辑与可以用来避免调用undefined参数的属性时报错 例如:- ```js var dog = { bark: function(){ console.log('Woof Woof'); } }; // 调用 dog.bark(); dog.bark(); // Woof Woof. // 但是当dog未定义时,dog.bark() 将会抛出"Cannot read property 'bark' of undefined." 错误 // 防止这种情况,我们可以使用 &&. dog&&dog.bark(); // This will only call dog.bark(), if dog is defined. ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-28-curry-vs-partial-application.md ================================================ --- layout: post title: 柯里化(currying)与部分应用(partial application) tip-number: 28 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: 柯里化(currying)与部分应用(partial application)是两种将一个函数转换为另一个有着较小普通参数个数的函数的方法。 redirect_from: - /zh_cn/curry-vs-partial-application/ categories: - zh_CN - javascript --- **柯里化(currying)** 柯里化是使一个函数 f: X * Y -> R 转变为 f': X -> (Y -> R) 与用两个参数调用f不同,我们用一个参数运行f'。返回的结果是一个函数,然后用第二个参数调用此函数,得到结果。 如此,如果未柯里化的函数f这样调用 f(3,5) 柯里化后的函数f'是这样调用的 f(3)(5) 比如: 未柯里化的函数add() ```javascript function add(x, y) { return x + y; } add(3, 5); // returns 8 ``` 柯里化后的add() ```javascript function addC(x) { return function (y) { return x + y; } } addC(3)(5); // returns 8 ``` **柯里化的规则** 柯里化将一个二元函数,转变为一元函数,这个函数将返回另一个一元函数。 curry: (X × Y → R) → (X → (Y → R)) Javascript Code: ```javascript function curry(f) { return function(x) { return function(y) { return f(x, y); } } } ``` **部分应用(partial application)** 部分应用将一个函数 f: X * Y -> R 的第一个参数固定而产生一个新的函数 f`: Y -> R f'与f不同,只需要填写第二个参数,这也是f'比f少一个参数的原因。 比如:将函数add的第一个参数绑定为5来产生函数plus5。 ```javascript function plus5(y) { return 5 + y; } plus5(3); // returns 8 ``` **部分应用的规则** 部分应用使用一个二元函数和一个值产生了一个一元函数。 partApply : ((X × Y → R) × X) → (Y → R) Javascript Code: ```javascript function partApply(f, x) { return function(y) { return f(x, y); } } ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-29-speed-up-recursive-functions-with-memoization.md ================================================ --- layout: post title: 运用存储加速递归 Speed up recursive functions with memoization tip-number: 29 tip-username: hingsir tip-username-profile: https://github.com/hingsir tip-tldr: 大家对斐波那契(Fibonacci)数列都很熟悉。我们可以再20秒内写出下面这样一个方法,它可以运行,但并不高效。它做了太多重复的运算,我们可以通过存储这些运算结果来使其加速。 redirect_from: - /zh_cn/speed-up-recursive-functions-with-memoization/ categories: - zh_CN - javascript --- 大家对斐波那契(Fibonacci)数列都很熟悉。我们可以再20秒内写出下面这样一个方法。 ```js var fibonacci = function(n){ return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2); } ``` 它可以运行,但并不高效。它做了太多重复的运算,我们可以通过存储这些运算结果来使其加速。 ```js var fibonacci = (function() { var cache = [0, 1]; // cache the value at the n index return function(n) { if (cache[n] === undefined) { for (var i = cache.length; i <= n; ++i) { cache[i] = cache[i-1] + cache[i-2]; } } return cache[n]; } })() ``` 我们也可以定义一个高阶函数,它接收一个方法作为参数,返回一个该方法运用存储后的新方法。 ```js var memoize = function(func){ var cache = {}; return function(){ var key = Array.prototype.slice.call(arguments).toString(); return key in cache ? cache[key] : (cache[key] = func.apply(this,arguments)); } } fibonacci = memoize(fibonacci); ``` ES6版本的memoize函数如下: ```js var memoize = function(func){ const cache = {}; return (...args) => { const key = [...args].toString(); return key in cache ? cache[key] : (cache[key] = func(...args)); } } fibonacci = memoize(fibonacci); ``` 我们可以将`memoize()`用在很多其他地方 * GCD(最大公约数) ```js var gcd = memoize(function(a,b){ var t; if (a < b) t=b, b=a, a=t; while(b != 0) t=b, b = a%b, a=t; return a; }) gcd(27,183); //=> 3 ``` * 阶乘运算 ```js var factorial = memoize(function(n) { return (n <= 1) ? 1 : n * factorial(n-1); }) factorial(5); //=> 120 ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-30-converting-truthy-falsy-values-to-boolean.md ================================================ --- layout: post title: 将truthy/falsy转换为布尔值 tip-number: 30 tip-username: hakhag tip-username-profile: https://github.com/hakhag tip-tldr: 逻辑运算符是JavaScript的核心之一,在这里你将看到一种无论你传什么值都可以总是得到true或false的方法。 redirect_from: - /zh_cn/converting-truthy-falsy-values-to-boolean/ categories: - zh_CN - javascript --- 你可以使用`!!`操作符将[truthy](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy)或[falsy](https://developer.mozilla.org/zh-CN/docs/Glossary/Falsy)值转换为布尔值。 ```js !!"" // false !!0 // false !!null // false !!undefined // false !!NaN // false !!"hello" // true !!1 // true !!{} // true !![] // true ``` ================================================ FILE: _posts/zh_CN/javascript/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md ================================================ --- layout: post title: 避免修改和传递`arguments`给其他方法 — 影响优化 tip-number: 31 tip-username: berkana tip-username-profile: https://github.com/berkana tip-tldr: 在JavaScript的方法里,`arguments`参数可以让你访问传递给该方法的所有参数。`arguments`是一个*类数组对象*;`arguments`可是使用数组标记访问,而且它有*length*参数,但是它没有`filter`、`map`和`forEach`这样内建到数组内的方法。因此,如下代码是一个非常常见的将`arguments`转换为数组的办法 redirect_from: - /zh_cn/avoid-modifying-or-passing-arguments-into-other-functions-it-kills-optimization/ categories: - zh_CN - javascript --- ### 背景 在JavaScript的方法里,[`arguments`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments)参数可以让你访问传递给该方法的所有参数。`arguments`是一个*类数组对象*;`arguments`可是使用数组标记访问,而且它有*length*参数,但是它没有`filter`、`map`和`forEach`这样内建到数组内的方法。因此,如下代码是一个非常常见的将`arguments`转换为数组的办法: ```js var args = Array.prototype.slice.call(arguments); ``` 将`arguments`传递给`Array`原型(prototype)上的`slice`方法;`slice`方法返回一个对`arguments`浅复制后的数组对象。更短的写法: ```js var args = [].slice.call(arguments); ``` 在这里,简单的调用了空数组的`slice`方法,而没有从`Array`的原型(prototype)上调用。 ### 系统优化 不幸的是,传递`arguments`给任何参数,将导致Chrome和Node中使用的V8引擎跳过对其的优化,这也将使性能相当慢。看一下这篇文章[optimization killers](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers)。传递`arguments`给任何方法被称为*leaking `arguments`*。 如果你想用一个包含参数(arguments)的数组,你需要求助于这个办法: ```js var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } ``` 没错,这很啰嗦,但是在生产环境中的代码里,为了系统性能优化,这是值得的。 ================================================ FILE: _posts/zh_CN/javascript/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md ================================================ --- layout: post title: Map()的营救;使对象属性有顺序 tip-number: 32 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 对象是一个无序的对象集合。这意味着如果你想在对象里保存有序的数据,你需要重新处理它,因为对象里的数据不保证是有序的。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/map-to-the-rescue-adding-order-to-object-properties/ categories: - zh_CN - javascript --- ## 对象属性顺序 > 一个对象是一个`Object`类型的实例。它是由一些未排序的元素组成的集合,其中包含了原始变量,对象,和函数。一个对象的属性所对应的函数被称为方法。[ECMAScript](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf) 实际看一下 ```js var myObject = { z: 1, '@': 2, b: 3, 1: 4, 5: 5 }; console.log(myObject) // Object {1: 4, 5: 5, z: 1, @: 2, b: 3} for (item in myObject) {... // 1 // 5 // z // @ // b ``` 因为技术实现,每个浏览器在排序时都有自己的规则,顺序是不确定的。 ## 怎么解决呢? ### Map 使用ES6的新特性Map。[Map](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map) 对象以插入的顺序遍历元素。`for...of`循环为每一次循环返回一个[key, value]数组。 ```js var myObject = new Map(); myObject.set('z', 1); myObject.set('@', 2); myObject.set('b', 3); for (var [key, value] of myObject) { console.log(key, value); ... // z 1 // @ 2 // b 3 ``` ### 攻克老浏览器 Mozilla 建议: > 所以,如果过你想在跨浏览器环境中模拟一个有序的关联数组,你要么使用两个分开的数组(一个保存key,另一个保存value),要么构建一个单属性对象(single-property objects)的数组。 ```js // 使用分开的数组 var objectKeys = [z, @, b, 1, 5]; for (item in objectKeys) { myObject[item] ... // 构建一个单属性对象(single-property objects)的数组 var myData = [{z: 1}, {'@': 2}, {b: 3}, {1: 4}, {5: 5}]; ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-02-create-range-0-n-easily-using-one-line.md ================================================ --- layout: post title: 仅用一行生成`[0, 1, ..., N-1]`数列 tip-number: 33 tip-username: SarjuHansaliya tip-username-profile: https://github.com/SarjuHansaliya tip-tldr: 我们可以创建一个函数,它可以仅用一行代码生成0...(N-1)数列。 redirect_from: - /zh_cn/create-range-0-n-easily-using-one-line/ categories: - zh_CN - javascript --- 使用下面一行代码,我们就可以生成0...(N-1)数列。 ### 方法1 (需要 ES5) ```js Array.apply(null, {length: N}).map(Function.call, Number); ``` #### 简要说明 1. `Array.apply(null, {length: N})` 返回一个由`undefined`填充的长度为`N`的数组(例如 `A = [undefined, undefined, ...]`)。 2. `A.map(Function.call, Number)` 返回一个长度为`N`的数组,它的索引为`I`的元素为`Function.call.call(Number, undefined, I, A)`的结果。 3. `Function.call.call(Number, undefined, I, A)`可转化为`Number(I)`,正好就是`I`。 4. 结果为:`[0, 1, ..., N-1]`。 更全面的介绍,请看[这里](https://github.com/gromgit/jstips-xe/blob/master/tips/33.md). ### 方法2 (需要 ES6) 这里用到了`Array.from` [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from) ```js Array.from(new Array(N),(val,index)=>index); ``` #### 简要说明 1. `A = new Array(N)` 返回一个有`N`个_小孔_的数组 (例如 `A = [,,,...]`, 但是对于`x` in `0...N-1`时`A[x] = undefined`)。 2. `F = (val,index)=>index` 即 `function F (val, index) { return index; }`。 3. `Array.from(A, F)` 返回一个长度为`N`的数组,它的索引为`I`的元素为`F(A[I], I)`的结果,也就是`I`。 4. 结果为:`[0, 1, ..., N-1]`。 ### One More Thing 如果你需要[1, 2, ..., N]序列, **方法1** 可改为: ```js Array.apply(null, {length: N}).map(function(value, index){ return index + 1; }); ``` **方法2**可改为: ```js Array.from(new Array(N),(val,index)=>index+1); ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-03-implementing-asynchronous-loops.md ================================================ --- layout: post title: 实现异步循环 tip-number: 34 tip-username: madmantalking tip-username-profile: https://github.com/madmantalking tip-tldr: 实现异步循环时,你可能会遇到问题。 redirect_from: - /zh_cn/implementing-asynchronous-loops/ categories: - zh_CN - javascript --- 让我们试着写一个异步方法,每秒打印一次循环的索引值。 ```js for (var i=0; i<5; i++) { setTimeout(function(){ console.log(i); }, 1000 * (i+1)); } ``` 如上程序的输出为: ```js > 5 > 5 > 5 > 5 > 5 ``` 这明显是有问题的。 **原因** 每次时间结束(timeout)都指向原始的`i`,而并非它的拷贝。所以,for循环使`i`增长到5,之后`timeout`运行并调用了当前`i`的值(也就是5)。 好吧,这个问题看起来很简单。最直接的解决方法是将循环的索引缓存在一个临时变量里。 ```js for (var i=0; i<5; i++) { var temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i+1)); } ``` 但是再次运行,如上的程序输出为: ```js > 4 > 4 > 4 > 4 > 4 ``` 这仍然有问题,这是因为并不存在块作用域,而且变量的声明被提升到了作用域顶端。实际上,如上代码和下面是一样的: ```js var temp; for (var i=0; i<5; i++) { temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i+1)); } ``` **解决方法** 有几个不同的方式可以拷贝`i`。最普通且常用方法是通过声明函数来建立一个闭包,并将`i`传给此函数。我们这里使用了自调用函数。 ```js for (var i=0; i<5; i++) { (function(num){ setTimeout(function(){ console.log(num); }, 1000 * (i+1)); })(i); } ``` 在JavaScript里,参数是按值传递给函数的。像`Number`、`Date`和`String`这些原始类型为基本复制。当你们在一个函数内改变它的值,并不影响外面的作用域。但`Object`类型不一样:如果你在函数内部修改了它的参数,将会影响到所有包含该`Object`的作用域内它的参数。 另一种方法是使用`let`。在ES6中的`let`关键字是可以实现的,它和`var`不一样,因为它支持块作用域的。 ```js for (let i=0; i<5; i++) { var temp = i; setTimeout(function(){ console.log(i); }, 1000 * (i+1)); } ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-04-assignment-shorthands.md ================================================ --- layout: post title: 赋值技巧 tip-number: 35 tip-username: hsleonis tip-username-profile: https://github.com/hsleonis tip-tldr: 赋值是很常见的。有时候打字对于我们这些“懒惰的程序员”来说是很费时间的。所以,我们可以使用一些小把戏来使我们的代码更清楚更简单。 redirect_from: - /zh_cn/assignment-shorthands/ categories: - zh_CN - javascript --- 赋值是很常见的。有时候打字对于我们这些“懒惰的程序员”来说是很费时间的。 所以,我们可以使用一些小把戏来使我们的代码更清楚更简单。 这类似于使用: ```js x += 23; // x = x + 23; y -= 15; // y = y - 15; z *= 10; // z = z * 10; k /= 7; // k = k / 7; p %= 3; // p = p % 3; d **= 2; // d = d ** 2; m >>= 2; // m = m >> 2; n <<= 2; // n = n << 2; n ++; // n = n + 1; n --; n = n - 1; ``` ### `++` 与 `--` 操作符 对于`++`操作符有些特殊。最好用下面的例子解释一下: ```js var a = 2; var b = a++; // 现在 a == 3 b == 2 ``` `a++`做了如下工作: 1. 返回`a`的值 2. `a`增加1 但是如果我们想让值先增加呢?这也很容易: ```js var a = 2; var b = ++a; // 现在a和b都是3 ``` 看明白了吗?我将操作符放在了参数_前面_。 `--`操作符除了使值减小外,其他功能是类似的。 ### If-else (使用三元运算符) 我们平时会这样写: ```js var newValue; if(value > 10) newValue = 5; else newValue = 2; ``` 我们可以使用三元运算符是它更简便: ```js var newValue = (value > 10) ? 5 : 2; ``` ### 检测Null、Undefined、空 ```js if (variable1 !== null || variable1 !== undefined || variable1 !== '') { var variable2 = variable1; } ``` 简便写法: ```js var variable2 = variable1 || ''; ``` P.S.:如果`variable1`是一个数字,则先检查他是否为0。 ### 对象数组表示法 不要用: ```js var a = new Array(); a[0] = "myString1"; a[1] = "myString2"; ``` 使用: ```js var a = ["myString1", "myString2"]; ``` ### 关联数组 不要用: ```js var skillSet = new Array(); skillSet['Document language'] = 'HTML5'; skillSet['Styling language'] = 'CSS3'; ``` 使用: ```js var skillSet = { 'Document language' : 'HTML5', 'Styling language' : 'CSS3' }; ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-05-observe-dom-changes.md ================================================ --- layout: post title: 扩展插件中观察DOM的变化 tip-number: 36 tip-username: beyondns tip-username-profile: https://github.com/beyondns tip-tldr: 当你为存在的网站开发扩展插件时,由于现代的动态Javascript,操作DOM并不是很容易。 redirect_from: - /zh_cn/observe-dom-changes/ categories: - zh_CN - javascript --- [MutationObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver)是监听DOM变化与当元素变化时做适当操作的一个解决方法。在下面的例子中我们使用计时器模拟了内容的动态加载,第一个元素"target"创建后,创建"subTarget"。 在扩展中的代码,`rootObserver`首先开始工作,直到`targetElement`被创建后`rootObserver`停止,然后`elementObserver`开始工作。这个级联观测可以在发现`subTargetElement`时提醒你。 这个方法在为动态加载内容的网站开发扩展插件时,是很有用的。 ```js const observeConfig = { attributes: true, childList: true, characterData: true, subtree: true }; function initExtension(rootElement, targetSelector, subTargetSelector) { var rootObserver = new MutationObserver(function(mutations) { console.log("Inside root observer"); targetElement = rootElement.querySelector(targetSelector); if (targetElement) { rootObserver.disconnect(); var elementObserver = new MutationObserver(function(mutations) { console.log("Inside element observer") subTargetElement = targetElement.querySelector(subTargetSelector); if (subTargetElement) { elementObserver.disconnect(); console.log("subTargetElement found!") } }) elementObserver.observe(targetElement, observeConfig); } }) rootObserver.observe(rootElement, observeConfig); } (function() { initExtension(document.body, "div.target", "div.subtarget") setTimeout(function() { del = document.createElement("div"); del.innerHTML = "
      target
      " document.body.appendChild(del) }, 3000); setTimeout(function() { var el = document.body.querySelector('div.target') if (el) { del = document.createElement("div"); del.innerHTML = "
      subtarget
      " el.appendChild(del) } }, 5000); })() ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-06-deduplicate-an-array.md ================================================ --- layout: post title: 数组去重 tip-number: 37 tip-username: danillouz tip-username-profile: https://www.twitter.com/danillouz tip-tldr: 移除包含不同类型数据的数组中重复的元素。 redirect_from: - /zh_cn/deduplicate-an-array/ categories: - zh_CN - javascript --- # 原始变量 如果一个数组只包含原始变量,我们可以使用[`filter`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)和[`indexOf`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)方法将其去重: ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter(function (el, i, arr) { return arr.indexOf(el) === i; }); console.log(deduped); // [ 1, 'a' ] ``` ## ES2015 我们可以使用[箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions)使写法更简明: ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter( (el, i, arr) => arr.indexOf(el) === i); console.log(deduped); // [ 1, 'a' ] ``` 但是根据[Sets](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set)和[`from`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from)方法的介绍,我们可以更简明的实现。 ```javascript var deduped = Array.from( new Set([ 1, 1, 'a', 'a' ]) ); console.log(deduped); // [ 1, 'a' ] ``` # Objects 当元素为对象(Object)时,我们就不能用这种办法了, 因为对象存储的是引用而原始变量存储的是值。 ```javascript 1 === 1 // true 'a' === 'a' // true { a: 1 } === { a: 1 } // false ``` 因此我们需要改变一下我们的实现方法,使用哈希表。 ```javascript function dedup(arr) { var hashTable = {}; return arr.filter(function (el) { var key = JSON.stringify(el); var match = Boolean(hashTable[key]); return (match ? false : hashTable[key] = true); }); } var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ] ]); console.log(deduped); // [ {a: 1}, [1, 2] ] ``` 因为哈希表在Javascript里是一个简单的`Object`,它的`key`永远是`String`类型。这意味着我们不能区分字符串和数字表示的相同的值,如`1`和`'1'`。 ```javascript var hashTable = {}; hashTable[1] = true; hashTable['1'] = true; console.log(hashTable); // { '1': true } ``` 然而,因为我们使用的[`JSON.stringify`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify),`String`类型的`key` 将会被存储为一个字符串值,这样`hashTable`的`key`就唯一了。 ```javascript var hashTable = {}; hashTable[JSON.stringify(1)] = true; hashTable[JSON.stringify('1')] = true; console.log(hashTable); // { '1': true, '\'1\'': true } ``` 这意味着相同的值,但不同类型的元素,将以原来的格式保留。 ```javascript var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ], 1, 1, '1', '1' ]); console.log(deduped); // [ {a: 1}, [1, 2], 1, '1' ] ``` # 阅读材料 ## 函数 * [`filter`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) * [`indexOf`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) * [`from`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from) * [`JSON.stringify`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ## ES2015 * [箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions) * [Set](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set) ## Stack overflow * [remove duplicates from array](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array/9229821#9229821) ================================================ FILE: _posts/zh_CN/javascript/2016-02-07-flattening-multidimensional-arrays-in-javascript.md ================================================ --- layout: post title: Javascript多维数组扁平化 tip-number: 38 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 将多位数组转化为单一数组的三种不同方法。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/flattening-multidimensional-arrays-in-javascript/ categories: - zh_CN - javascript --- 下面是将多位数组转化为单一数组的三种不同方法。 对于此数组: ```js var myArray = [[1, 2],[3, 4, 5], [6, 7, 8, 9]]; ``` 我们需要的结果是: ```js [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### 解决方案1:使用[`concat()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)和[`apply()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) ```js var myNewArray = [].concat.apply([], myArray); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### 解决方案2:使用[`reduce()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Flatten_an_array_of_arrays) ```js var myNewArray = myArray.reduce(function(prev, curr) { return prev.concat(curr); }); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### 解决方案3: ```js var myNewArray3 = []; for (var i = 0; i < myArray.length; ++i) { for (var j = 0; j < myArray[i].length; ++j) myNewArray3.push(myArray[i][j]); } console.log(myNewArray3); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` 在[这里](https://jsbin.com/qeqicu/edit?js,console)看一下三种逻辑的实际作用。 ### 方案四:使用 ES6 的[展开运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_operator) ```js var myNewArray4 = [].concat(...myArray); console.log(myNewArray4); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` 在[这里](https://jsbin.com/janana/edit?js,console) 查看这四种方法 对于无限嵌套的数组请使用 Lodash 的 [flattenDeep()](https://lodash.com/docs#flattenDeep)。 如果你担心性能问题的话,[这里](http://jsperf.com/flatten-an-array-loop-vs-reduce/6) 有一个测试让你确认他们是如何执行的。 对于较大的数组试一下Underscore的[flatten()](https://github.com/jashkenas/underscore/blob/master/underscore.js#L501). 如果你对性能好奇,[这里](http://jsperf.com/flatten-an-array-loop-vs-reduce/6)有一个测试。 ================================================ FILE: _posts/zh_CN/javascript/2016-02-08-advanced-properties.md ================================================ --- layout: post title: Javascript高级特性 tip-number: 39 tip-username: mallowigi tip-username-profile: https://github.com/mallowigi tip-tldr: 怎样给一个对象添加私有参数、`getter`或`setter`。 redirect_from: - /zh_cn/advanced-properties/ categories: - zh_CN - javascript --- 在Javascript里配置对象属性是可以实现的,比如将一个参数设为伪私有或者只读。这个特性从ECMAScript 5.1开始就可以使用了,因此近来的浏览器都是支持的。 要实现这些功能,你需要使用`Object`的原型方法`defineProperty`,像这样: ```js var a = {}; Object.defineProperty(a, 'readonly', { value: 15, writable: false }); a.readonly = 20; console.log(a.readonly); // 15 ``` 语法如下: ```js Object.defineProperty(dest, propName, options); ``` 或者定义多个: ```js Object.defineProperties(dest, { propA: optionsA, propB: optionsB, //... }); ``` `options`包含如下的属性: - *value*: 如果参数不是`getter`(请看下文),`value`是必须的。`{a: 12}` === `Object.defineProperty(obj, 'a', {value: 12})` - *writable*: 将参数设为只读。需要注意的是如果参数是嵌套对象,它的元素仍是能可修改的。 - *enumerable*: 将参数设为隐藏。这意味着`for ... of`循环和`stringify`的结果里不会包含这些参数,但是这个参数还是存在的。提示:这并不意味着参数是私有的!他依旧可以从外界访问,只是意味着不会被打印。 - *configurable*: 将属性设置为不能更改,比如:防止参数被删除或重新定义。如果此对象是一个嵌套对象,他的参数依旧是可配置的。 所以如果想创建私有静态变量,你可以这样定义: ```js Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false}); ``` 除了配置属性,由于`defineProperty`第二个参数是字符串,所以允许我们定义*动态变量(defineProperty)*。例如,我们可以说我们要根据一些外部配置创建一个属性: ```js var obj = { getTypeFromExternal(): true // illegal in ES5.1 }; Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok // For the example sake, ES6 introduced a new syntax: var obj = { [getTypeFromExternal()]: true }; ``` 还没有结束!高级特性允许我们创建**getter**和**setter**,就像其他面向对象(OOP)语言!这种情况下,我们不能使用`writable`、`enumerable`和`configurable`参数,而是: ```js function Foobar () { var _foo; // true private property Object.defineProperty(obj, 'foo', { get: function () { return _foo; } set: function (value) { _foo = value } }); }; var foobar = new Foobar(); foobar.foo; // 15 foobar.foo = 20; // _foo = 20 ``` 除了封装与先进的访问器这些明显的优点,你还发现我们并没有“调用”`getter`,而是不需要使用小括号直接“取得”了属性!这太棒了!例如,我们可以想象我们有一个多层嵌套对象,像这样: ```js var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } }; ``` 现在我们不需要调用`a.b.c[0].d`(其中某个属性可能是`undefined`且抛出错误),我们可以创建一个别名: ```js Object.defineProperty(obj, 'firstD', { get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d } }); console.log(obj.firstD); // 10 ``` ### 提示 如果你定义了`getter`而没有定义`setter`却仍要给它赋值,你将会得到一个错误。这在使用像`$.extend`或`_.merge`这样的辅助方法时是尤为重要的。要小心! ### 链接 - [defineProperty](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) - [Defining properties in JavaScript](http://bdadam.com/blog/defining-properties-in-javascript.html) ================================================ FILE: _posts/zh_CN/javascript/2016-02-09-using-json-stringify.md ================================================ --- layout: post title: 使用JSON.Stringify tip-number: 40 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: 将JSON对象的参数选择性地生成字符串。 redirect_from: - /zh_cn/using-json-stringify/ categories: - zh_CN - javascript --- 假如有一个对象具有参数"prop1", "prop2", "prop3"。 我们可以通过传递 __附加参数__ 给 __JSON.stringify__ 来选择性地将参数生成字符串,像这样: ```javascript var obj = { 'prop1': 'value1', 'prop2': 'value2', 'prop3': 'value3' }; var selectedProperties = ['prop1', 'prop2']; var str = JSON.stringify(obj, selectedProperties); // str // {"prop1":"value1","prop2":"value2"} ``` __"str"__ 将只包含选择的参数。 除了传递数组,我们也可以传递函数。 ```javascript function selectedProperties(key, val) { // the first val will be the entire object, key is empty string if (!key) { return val; } if (key === 'prop1' || key === 'prop2') { return val; } return; } ``` 最后一个参数,可以修改生成字符串的方式。 ```javascript var str = JSON.stringify(obj, selectedProperties, '\t\t'); /* str output with double tabs in every line. { "prop1": "value1", "prop2": "value2" } */ ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-10-array-average-and-median.md ================================================ --- layout: post title: 数组平均值与中值 tip-number: 41 tip-username: soyuka tip-username-profile: https://github.com/soyuka tip-tldr: 计算数组的平均值与中位数 redirect_from: - /zh_cn/array-average-and-median/ categories: - zh_CN - javascript --- 下面的例子都基于如下数组: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; ``` 要取得平均值,我们需要将数字求和,然后除以`values`的数目,步骤如下: - 取得数组长度(length) - 求和(sum) - 取得平均值(`sum/length`) ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let sum = values.reduce((previous, current) => current += previous); let avg = sum / values.length; // avg = 28 ``` 或者: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let count = values.length; values = values.reduce((previous, current) => current += previous); values /= count; // avg = 28 ``` 取得中值的步骤是: - 将数组排序 - 取得中位数 ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let lowMiddle = Math.floor((values.length - 1) / 2); let highMiddle = Math.ceil((values.length - 1) / 2); let median = (values[lowMiddle] + values[highMiddle]) / 2; // median = 13,5 ``` 或者使用[无符号右移](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift)操作符: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let median = (values[(values.length - 1) >> 1] + values[values.length >> 1]) / 2 // median = 23 ``` ================================================ FILE: _posts/zh_CN/javascript/2016-02-11-preventing-unapply-attacks.md ================================================ --- layout: post title: 预防unapply攻击 tip-number: 42 tip-username: emars tip-username-profile: https://twitter.com/marseltov tip-tldr: 冻结内置对象的原型方法。 redirect_from: - /zh_cn/preventing-unapply-attacks/ categories: - zh_CN - javascript --- 重写内置对象的原型方法,外部代码可以通过重写代码达到暴漏和修改已绑定参数的函数。这在es5的方法下使用`polyfill`时是一个严重的安全问题。 ```js // bind polyfill 示例 function bind(fn) { var prev = Array.prototype.slice.call(arguments, 1); return function bound() { var curr = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.concat.apply(prev, curr); return fn.apply(null, args); }; } // unapply攻击 function unapplyAttack() { var concat = Array.prototype.concat; Array.prototype.concat = function replaceAll() { Array.prototype.concat = concat; // restore the correct version var curr = Array.prototype.slice.call(arguments, 0); var result = concat.apply([], curr); return result; }; } ``` 上面的函数声明忽略了函数bind的`prev`参数,意味着调用`unapplyAttack`之后首次调用`.concat`将会抛出错误。 使用[Object.freeze](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze),可以使对象不可变,你可以防止任何内置对象原型方法被重写。 ```js (function freezePrototypes() { if (typeof Object.freeze !== 'function') { throw new Error('Missing Object.freeze'); } Object.freeze(Object.prototype); Object.freeze(Array.prototype); Object.freeze(Function.prototype); }()); ``` 你可以[在这里](https://glebbahmutov.com/blog/unapply-attack/)阅读更多关于unapply攻击。 ================================================ FILE: _posts/zh_CN/javascript/2016-02-12-use-destructuring-in-function-parameters.md ================================================ --- layout: post title: 函数参数内使用解构 tip-number: 43 tip-username: dislick tip-username-profile: https://github.com/dislick tip-tldr: 你知道在函数参数内也可以使用解构吗? redirect_from: - /zh_cn/use-destructuring-in-function-parameters/ categories: - zh_CN - javascript --- 大家一定对[ES6解构赋值](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)非常熟悉。但是你知道在函数参数里也可以使用它吗? ```js var sayHello = function({ name, surname }) { console.log(`Hello ${name} ${surname}! How are you?`); }; sayHello({ name: 'John', surname: 'Smith' }) // -> Hello John Smith! How are you? ``` 这对于接收可选参数的函数,是很棒的。对于这种用法,你也可以添加[默认参数值](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Default_parameters)来填充调用者没有传递或忘记传递的参数值: ```js var sayHello2 = function({ name = "Anony", surname = "Moose" } = {}) { console.log(`Hello ${name} ${surname}! How are you?`); }; ``` `= {}`表示此参数需要解构的默认对象是一个`{}`,以防调用者忘记传值,或传递了一个错误类型(大多情况为后者)。 ```js sayHello2() // -> Hello Anony Moose! How are you? sayHello2({ name: "Bull" }) // -> Hello Bull Moose! How are you? ``` ##### 参数处理 对于普通的解构,如果输入的参数与函数指定的对象参数不符,所有不符的参数都将为`undefined`,所以你需要增加代码来正确的处理这些情况: ```js var sayHelloTimes = function({ name, surname }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); } sayHelloTimes({ name: "Pam" }, 5678) // -> Hello Pam undefined! I've seen you 5678 times before. sayHelloTimes(5678) // -> Hello undefined undefined! I've seen you undefined times before. ``` 更糟糕的,如果没有传递需要解构的的参数,将会抛出错误,这可能使你的应用崩溃: ```js sayHelloTimes() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'... ``` 这与访问一个未定义对象的参数基本相似,只是错误类型不太一样。 为解构增加默认参数基本上解决了上面的所有问题: ```js var sayHelloTimes2 = function({ name = "Anony", surname = "Moose" } = {}, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2() // -> Hello Anony Moose! I've seen you undefined times before. ``` 对于`= {}`,它掩盖了_object_未传递时的情况,但对于个别属性默认值的情形下会抛出异常: ```js var sayHelloTimes2a = function({ name = "Anony", surname = "Moose" }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2a({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2a(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2a() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'. ``` ##### 可用性 需要注意解构赋值可能在你正在使用的Node.js或浏览器中默认情况下并不可用。对于Node.js,你可以在启动时使用`--harmony-destructuring`标记开启此特性。 ================================================ FILE: _posts/zh_CN/javascript/2016-02-13-know-the-passing-mechanism.md ================================================ --- layout: post title: 了解传值机制 tip-number: 44 tip-username: bmkmanoj tip-username-profile: https://github.com/bmkmanoj tip-tldr: JavaScript理论上说只传递原始类型和对象(或引用)类型的值。在引用类型的情况下引用值本身通过值传递。 redirect_from: - /zh_cn/know-the-passing-mechanism/ categories: - zh_CN - javascript --- 理论上,JavaScript通过值传递。它既不是值传递也不是引用传递,具体取决于它的真实场景。要理解传值机制,看一下下面两个实例代码和解释。 ### 实例 1 ```js var me = { // 1 'partOf' : 'A Team' }; function myTeam(me) { // 2 me = { // 3 'belongsTo' : 'A Group' }; } myTeam(me); console.log(me); // 4 : {'partOf' : 'A Team'} ``` 在上面的实例里`myTeam`被调用的时候,JavaScript *传递*`me`*对象的引用值,因为它是一个对象*。而且调用本身建立了同一个对象的两个独立的引用,(虽然在这里的的命名都是相同的,比如`me`, 这有些无调行,而且给我们一个这是单个引用的印象)因此,引用变量本身是独立的。 当我们在#`3`定义了一个新的对象,我们完全改变了`myTeam`函数内的引用值,这对此函数作用域外的原始对象是没有任何影响的,外作用域的引用仍保留在原始对象上,因此从#`4`输出去了。 ### 实例 2 ```js var me = { // 1 'partOf' : 'A Team' }; function myGroup(me) { // 2 me.partOf = 'A Group'; // 3 } myGroup(me); console.log(me); // 4 : {'partOf' : 'A Group'} ``` 当`myGroup`调用时,我们将对象`me`传给函数。但是与实例1的情况不同,我们没有指派`me`变量到任何新对象,有效的说明了`myGroup`函数作用域内的对象引用值依旧是原始对象的引用值,而且我们在作用域内修改对象的参数值同样有效的修改了原始对象的参数。因此你得到了#`7`的输出结果。 所以后面的例子是否说明javascript是引用传递呢?不,并没有。请记住,*如果是对象的话,JavaScript将引用按值传递*。这种混乱往往发生在我们没有完全理解什么通过引用传递的情况下。这就是确切的原因,有些人更愿意称它为*call-by-sharing*。 *此文最初被作者发表在[js-by-examples](https://github.com/bmkmanoj/js-by-examples/blob/master/examples/js_pass_by_value_or_reference.md)* ================================================ FILE: _posts/zh_CN/javascript/2016-02-14-calculate-the-max-min-value-from-an-array.md ================================================ --- layout: post title: 计算数组中的最大值/最小值 tip-number: 45 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 对于纯数字数组,使用内置函数Math.max()和Math.min()的方法。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/calculate-the-max-min-value-from-an-array/ categories: - zh_CN - javascript --- 内置函数[Math.max()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/max)和[Math.min()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/min)可以分别找出参数中的最大值和最小值。 ```js Math.max(1, 2, 3, 4); // 4 Math.min(1, 2, 3, 4); // 1 ``` 这些函数对于数字组成的数组是不能用的。但是,这有一些类似地方法。 [`Function.prototype.apply()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)让你可以使用提供的`this`与参数组成的_数组(array)_来调用函数。 ```js var numbers = [1, 2, 3, 4]; Math.max.apply(null, numbers) // 4 Math.min.apply(null, numbers) // 1 ``` 给`apply()`第二个参数传递`numbers`数组,等于使用数组中的所有值作为函数的参数。 一个更简单的,基于ES2015的方法来实现此功能,是使用[展开运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_operator). ```js var numbers = [1, 2, 3, 4]; Math.max(...numbers) // 4 Math.min(...numbers) // 1 ``` 此运算符使数组中的值在函数调用的位置展开。 ================================================ FILE: _posts/zh_CN/javascript/2016-02-15-detect-document-ready-in-pure-js.md ================================================ --- layout: post title: 纯JS监听document是否加载完成 tip-number: 46 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 跨浏览器且纯JavaScript检测document是否加载完成。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/detect-document-ready-in-pure-js/ categories: - zh_CN - javascript --- 跨浏览器且纯JavaScript检测document是否加载完成的方法是使用[`readyState`](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/readyState). ```js if (document.readyState === 'complete') { // 页面已完全加载 } ``` 这样可以在document完全加载时监测到…… ```js let stateCheck = setInterval(() => { if (document.readyState === 'complete') { clearInterval(stateCheck); // document ready } }, 100); ``` 或者使用[onreadystatechange](https://developer.mozilla.org/zh-CN/docs/Web/Events/readystatechange) ```js document.onreadystatechange = () => { if (document.readyState === 'complete') { // document ready } }; ``` 使用`document.readyState === 'interactive'`监听DOM是否加载完成。 ================================================ FILE: _posts/zh_CN/javascript/2016-02-16-basics-declarations.md ================================================ --- layout: post title: 变量声明 tip-number: 47 tip-username: adaniloff tip-username-profile: https://github.com/adaniloff tip-tldr: 理解并应用变量的声明。 redirect_from: - /zh_cn/basics-declarations/ categories: - zh_CN - javascript --- 下文是JavaScript中声明变量的不同方法。 注释与`console.log`足够说明这里发生了什么: ```js var y, x = y = 1 //== var x; var y; x = y = 1 console.log('--> 1:', `x = ${x}, y = ${y}`) // 将会输出 //--> 1: x = 1, y = 1 ``` 首先,我们只设置了两个变量。并没有很多。 ```js ;(() => { var x = y = 2 // == var x; y = 2; console.log('2.0:', `x = ${x}, y = ${y}`) })() console.log('--> 2.1:', `x = ${x}, y = ${y}`) // 将会输出 //2.0: x = 2, y = 2 //--> 2.1: x = 1, y = 2 ``` 正如你所看到的,代码只改变了全局的`y`,因为我们在闭包里并没有声明此变量。 ```js ;(() => { var x, y = 3 // == var x; var y = 3; console.log('3.0:', `x = ${x}, y = ${y}`) })() console.log('--> 3.1:', `x = ${x}, y = ${y}`) // 将会输出 //3.0: x = undefined, y = 3 //--> 3.1: x = 1, y = 2 ``` 现在我们用`var`声明了两个变量。意味着他们仅在闭包内有作用。 ```js ;(() => { var y, x = y = 4 // == var x; var y; x = y = 4 console.log('4.0:', `x = ${x}, y = ${y}`) })() console.log('--> 4.1:', `x = ${x}, y = ${y}`) // 将会输出 //4.0: x = 4, y = 4 //--> 4.1: x = 1, y = 2 ``` 两个变量都使用`var`声明了而且在之后又给它们赋值。由于`local > global`,闭包内声明了`x`和`y`,意味着闭包内是无法访问全局的`x`和`y`的。 ```js x = 5 // == x = 5 console.log('--> 5:', `x = ${x}, y = ${y}`) // 将会输出 //--> 5: x = 5, y = 2 ``` 最后一行的结果是很明显的。 你可以在这里测试并看到结果 [感谢babel](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=var%20y%2C%20x%20%3D%20y%20%3D%201%20%2F%2F%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%201%0Aconsole.log('--%3E%201%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%201%3A%20x%20%3D%201%2C%20y%20%3D%201%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%20%3D%20y%20%3D%202%20%2F%2F%20%3D%3D%20var%20x%3B%20y%20%3D%202%3B%0A%20%20console.log('2.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%202.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F2.0%3A%20x%20%3D%202%2C%20y%20%3D%202%0A%2F%2F--%3E%202.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%2C%20y%20%3D%203%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%20%3D%203%3B%0A%20%20console.log('3.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%203.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F3.0%3A%20x%20%3D%20undefined%2C%20y%20%3D%203%0A%2F%2F--%3E%203.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20y%2C%20x%20%3D%20y%20%3D%204%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%203%0A%20%20console.log('4.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%204.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F4.0%3A%20x%20%3D%204%2C%20y%20%3D%204%0A%2F%2F--%3E%204.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0Ax%20%3D%205%20%2F%2F%20%3D%3D%20x%20%3D%205%0Aconsole.log('--%3E%205%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%205%3A%20x%20%3D%205%2C%20y%20%3D%202). 更多相关内容请看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var). 特别感谢@kurtextrem的合作 :)! ================================================ FILE: _posts/zh_CN/javascript/2016-02-17-reminders-about-reduce-function-usage.md ================================================ --- layout: post title: 怎样`reduce()`数组 tip-number: 48 tip-username: darul75 tip-username-profile: https://twitter.com/darul75 tip-tldr: 使用`reduce()`函数时的一些建议 redirect_from: - /zh_cn/reminders-about-reduce-function-usage/ categories: - zh_CN - javascript --- 文档里说`reduce()`方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值。 ### `reduce()` [reduce()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) 函数接收2个参数(M: 必填, O: 可选): - (M) 回调**reducer 函数** 处理先前的结算结果和下一个元素直到序列结束。 - (O) **初值** 作为第一次调用回调时的第一个参数。 所以,让我们先看一个普通用法,之后再看一个复杂用法。 ### 普通用法 (累加,关联) 我们正在逛亚马逊(单价为美元$) 我们的购物车实在太满了,我们来计算一下总价吧: ```javascript // 当前的购物清单 var items = [{price: 10}, {price: 120}, {price: 1000}]; // reducer函数 var reducer = function add(sumSoFar, nextPrice) { return sumSoFar + nextPrice.price; }; // 开始运行 var total = items.reduce(reducer, 0); console.log(total); // 1130 ``` `reduce`函数可选的参数在第一个例子里是基本变量数字0,但是它也可以是一个对象,数组... 而不仅是基本类型,之后我们将会看到。 现在,我们收到一个20$的优惠券。 ```javascript var total = items.reduce(reducer,-20); console.log(total); // 1110 ``` ### 进阶用法(结合) 第二种用法的例子是`Redux`的[combineReducers](http://redux.js.org/docs/api/combineReducers.html)函数[源码](https://github.com/reactjs/redux/blob/master/src/combineReducers.js#L93)里用到的。 此创意是将`reducer`函数拆分为独立的函数,最后组合成一个新的*单一的大`reducer`函数*。 为了说明,我们创建一个单一的对象,包含一些可以计算不同货币($, €...)的总价值的`reducer`函数。 ```javascript var reducers = { totalInDollar: function(state, item) { state.dollars += item.price; return state; }, totalInEuros : function(state, item) { state.euros += item.price * 0.897424392; return state; }, totalInPounds : function(state, item) { state.pounds += item.price * 0.692688671; return state; }, totalInYen : function(state, item) { state.yens += item.price * 113.852; return state; } // more... }; ``` 然后我们建立一个瑞士军刀函数 - 能够调用每一部分的`reduce`函数 - 返回一个新的`reducer`回调函数 ```javascript var combineTotalPriceReducers = function(reducers) { return function(state, item) { return Object.keys(reducers).reduce( function(nextState, key) { reducers[key](state, item); return state; }, {} ); } }; ``` 现在,我们来看一下如何使用它。 ```javascript var bigTotalPriceReducer = combineTotalPriceReducers(reducers); var initialState = {dollars: 0, euros:0, yens: 0, pounds: 0}; var totals = items.reduce(bigTotalPriceReducer, initialState); console.log(totals); /* Object {dollars: 1130, euros: 1015.11531904, yens: 127524.24, pounds: 785.81131152} */ ``` 我希望这种方法可以使你在自己的需求内使用`reduce()`函数时有新的想法。 使用`reduce`函数也可以实现保存每一次计算结果的功能。这在`Ramdajs`里的[scan](http://ramdajs.com/docs/#scan)函数已经实现了。 [在JSFiddle里运行](https://jsfiddle.net/darul75/81tgt0cd/) ================================================ FILE: _posts/zh_CN/javascript/2016-02-26-extract-unix-timestamp-easily.md ================================================ --- layout: post title: 简单获取unix时间戳 tip-number: 49 tip-username: nmrony tip-username-profile: https://github.com/nmrony tip-tldr: 在Javascript里,你可以简单的取得unix时间戳 redirect_from: - /zh_cn/extract-unix-timestamp-easily/ categories: - zh_CN - javascript --- 我们经常需要使用unix时间戳计算。有很多方法可以取得unix时间戳。目前取得unix时间戳最简单最快的方法是: ```js const dateTime = Date.now(); const timestamp = Math.floor(dateTime / 1000); ``` 或 ```js const dateTime = new Date().getTime(); const timestamp = Math.floor(dateTime / 1000); ``` 要取得一个具体时间的unix时间戳,将`yyyy-mm-dd`或`YYYY-MM-DDT00:00:00Z`作为参数传递给`Date`构造函数。例如 ```js const dateTime = new Date('2012-06-08').getTime(); const timestamp = Math.floor(dateTime / 1000); ``` 你还可以像下面一样,在声明`Date`对象的时候添加一个`+`号 ```js const dateTime = +new Date(); const timestamp = Math.floor(dateTime / 1000); ``` 或者对于具体时间 ```js const dateTime = +new Date('2012-06-08'); const timestamp = Math.floor(dateTime / 1000); ``` 在底层,运行时调用了`Date`对象的`valueOf`方法。然后一元操作符`+`调用了之前返回值的`toNumber()`方法。想要了解更多内容请参考下面链接 * [Date.prototype.valueOf](http://es5.github.io/#x15.9.5.8) * [Unary + operator](http://es5.github.io/#x11.4.6) * [toNumber()](http://es5.github.io/#x9.3) * [Date Javascript MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) ================================================ FILE: _posts/zh_CN/javascript/2016-03-03-helpful-console-log-hacks.md ================================================ --- layout: post title: 实用的`log`技巧 tip-number: 50 tip-username: zackhall tip-username-profile: https://twitter.com/zthall tip-tldr: 运用`&&`与条件断点的实用的`log`技巧 redirect_from: - /zh_cn/helpful-console-log-hacks/ categories: - zh_CN - javascript --- ## 使用条件断点输出`log` 如果你想当函数每次被调用时都在控制台打印一个值,你可以应用条件断点来实现。打开你的开发工具,找到你准备打印的值所在的函数然后使用如下条件设置一个条件断点: ```js console.log(data.value) && false ``` 条件断点只有在条件运行的结果为`true`时才会中断页面。所以使用`console.log('foo') && false`这样的条件,由于你把`false`放在了`AND`条件中,所以结果肯定是`false`。因此这并不会中断页面但是会打印`log`到控制台。这也可以应用在计算某个函数或回调被调用了多少次上面。 这里有各个平台下设置条件断点的方法:[Edge](https://dev.windows.com/en-us/microsoft-edge/platform/documentation/f12-devtools-guide/debugger/#setting-and-managing-breakpoints "Managing Breakpoints in Edge")、[Chrome](https://developer.chrome.com/devtools/docs/javascript-debugging#breakpoints "Managing Breakpoints in Chrome")、[Firefox](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Set_a_conditional_breakpoint "Managing Breakpoints in Firefox")、[Safari](https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/Debugger/Debugger.html "Managing Breakpoints in Safari")。 ## 打印函数到控制台 你曾经有过打算打印函数到控制台却不能看到函数的代码的情况吗?最快的方法查看函数的代码是将其与空字符串连接,从而将其强制转换为字符串。 ```js console.log(funcVariable + ''); ``` ================================================ FILE: _posts/zh_CN/javascript/2016-03-16-DOM-event-listening-made-easy.md ================================================ --- layout: post title: 简单监听DOM事件 tip-number: 51 tip-username: octopitus tip-username-profile: https://github.com/octopitus tip-tldr: 简单而优雅地操作DOM事件的方法 redirect_from: - /zh_cn/DOM-event-listening-made-easy/ categories: - zh_CN - javascript --- 很多人还在这样做: - `element.addEventListener('type', obj.method.bind(obj))` - `element.addEventListener('type', function (event) {})` - `element.addEventListener('type', (event) => {})` 上面所有的例子都创建了一个匿名事件监控句柄,且在不需要时无法删除它。这在你不需要某句柄,而它却被用户或[事件冒泡](http://www.javascripter.net/faq/eventbubbling.htm)偶然触发时,可能会导致性能问题或不必要的逻辑问题。 更安全的事件处理方式如下: 使用引用: ```js const handler = function () { console.log("Tada!") } element.addEventListener("click", handler) // 之后 element.removeEventListener("click", handler) ``` 命名的函数移除它本身: ```js element.addEventListener('click', function click(e) { if (someCondition) { return e.currentTarget.removeEventListener('click', click); } }); ``` 更好的写法: ```js function handleEvent (eventName, {onElement, withCallback, useCapture = false} = {}, thisArg) { const element = onElement || document.documentElement function handler (event) { if (typeof withCallback === 'function') { withCallback.call(thisArg, event) } } handler.destroy = function () { return element.removeEventListener(eventName, handler, useCapture) } element.addEventListener(eventName, handler, useCapture) return handler } // 你需要的时候 const handleClick = handleEvent('click', { onElement: element, withCallback: (event) => { console.log('Tada!') } }) // 你想删除它的时候 handleClick.destroy() ``` ================================================ FILE: _posts/zh_CN/javascript/2016-04-05-return-values-with-the-new-operator.md ================================================ --- layout: post title: new的返回值 tip-number: 52 tip-username: Morklympious tip-username-profile: https://github.com/morklympious tip-tldr: 理解使用new与不使用new时将返回什么值 redirect_from: - /zh_cn/return-values-with-the-new-operator/ categories: - zh_CN - javascript --- 你将会遇到在JavaScript中使用`new`来分配新对象的一些情况。这将会扰乱你的思绪,除非你阅读了这篇文章并理解在内部发生了什么。 JavaScript中的`new`操作在合理的情况下然会一个新的对象实例。我们来看,我们有一个构造函数: ````js function Thing() { this.one = 1; this.two = 2; } var myThing = new Thing(); myThing.one // 1 myThing.two // 2 ```` __提示__: `this`指向`new`产生的新对象。否则如果`Thing()`不是用`new`调用, __将不会生成新对象__, 而且`this` 将会指向全局对象,也就是`window`。这意味着: 1. 你突然有两个全局变量`one`和`two`。 2. `myThing`现在为`undefined`,因为`Thing()`中没有返回任何东西。 现在我们又有一个例子,而它却有些让人搞不懂。我们看我在构造函数里加了一条语句: ````js function Thing() { this.one = 1; this.two = 2; return 5; } var myThing = new Thing(); ```` 现在`myThing`等于什么呢?5?一个对象?还是我受伤的自我价值观?或许永远不知道! 除了能知道: ````js myThing.one // 1 myThing.two // 2 ```` 很有趣,我们构造函数里`返回`的5怎么找不到了?这很奇怪不是吗?函数都做了什么?5呢?让我们再试试别的。 我们返回一个非原始类型试一下,比如一个对象: ````js function Thing() { this.one = 1; this.two = 2; return { three: 3, four: 4 }; } var myThing = new Thing(); ```` 让我们试一试。直接`console.log`出所有内容: ````js console.log(myThing); /* Object {three: 3, four: 4} this.one 和 this.two发生了什么!? 他们被覆盖了,朋友。 */ ```` __我们了解到:__ 当你使用`new`关键字调用一个函数的时候,你可以使用`this`关键字给其设置参数(但这些你应该已经知道了)。使用`new`关键字调用一个返回原始变量的函数将不会返回你指定的值,而是返回函数的实例`this`(你指定参数的那个对象,像 `this.one = 1;`). 然而,返回一个非原始变量像`object`、`array`或`function`将会覆盖`this`实例,并返回那个非原始变量,有效的破坏了你分配给`this`的所有工作。 ================================================ FILE: _posts/zh_CN/javascript/2016-04-21-get-file-extension.md ================================================ --- layout: post title: 取得文件扩展名 tip-number: 53 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: 如何更加高效的取得文件扩展名呢? redirect_from: - /zh_cn/get-file-extension/ categories: - zh_CN - javascript --- ### 问题 1: 怎样取得文件扩展名? ```javascript var file1 = "50.xsl"; var file2 = "30.doc"; getFileExtension(file1); //returs xsl getFileExtension(file2); //returs doc function getFileExtension(filename) { /*TODO*/ } ``` ### 解决方法 1: 正则表达式 ```js function getFileExtension1(filename) { return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined; } ``` ### 解决方法 2: String的`split`方法 ```js function getFileExtension2(filename) { return filename.split('.').pop(); } ``` 这两种解决方法不能解决一些边缘情况,这有另一个更加强大的解决方法。 ### 解决方法 3: String的`slice`、`lastIndexOf`方法 ```js function getFileExtension3(filename) { return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2); } console.log(getFileExtension3('')); // '' console.log(getFileExtension3('filename')); // '' console.log(getFileExtension3('filename.txt')); // 'txt' console.log(getFileExtension3('.hiddenfile')); // '' console.log(getFileExtension3('filename.with.many.dots.ext')); // 'ext' ``` _这是如何实现的呢?_ - [String.lastIndexOf()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf) 方法返回指定值(本例中的`'.'`)在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。 - 对于`'filename'`和`'.hiddenfile'`,`lastIndexOf`的返回值分别为`0`和`-1`[无符号右移操作符(>>>)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3E%3E%3E_%28Zero-fill_right_shift%29) 将`-1`转换为`4294967295`,将`-2`转换为`4294967294`,这个方法可以保证边缘情况时文件名不变。 - [String.prototype.slice()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/slice) 从上面计算的索引处提取文件的扩展名。如果索引比文件名的长度大,结果为`""`。 ### 对比 | 解决方法 | 参数 | 结果 | | ----------------------------------------- |:-------------------:|:--------:| | 解决方法 1: Regular Expression | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | undefined
      undefined
      'txt'
      'hiddenfile'
      'ext'
      | | 解决方法 2: String `split` | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | ''
      'filename'
      'txt'
      'hiddenfile'
      'ext'
      | | 解决方法 3: String `slice`, `lastIndexOf` | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | ''
      ''
      'txt'
      ''
      'ext'
      | ### 实例与性能 [这里](https://jsbin.com/tipofu/edit?js,console) 是上面解决方法的实例。 [这里](http://jsperf.com/extract-file-extension) 是上面三种解决方法的性能测试。 ### 来源 [How can I get file extensions with JavaScript](http://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript) ================================================ FILE: _posts/zh_CN/javascript/2016-05-06-use-optional-arguments.md ================================================ --- layout: post title: 函数中如何使用可选参数(包括可选回调函数) tip-number: 54 tip-username: alphashuro tip-username-profile: https://github.com/alphashuro tip-tldr: 使函数的参数与回调函数为可选参数。 redirect_from: - /zh_cn/use-optional-arguments/ categories: - zh_CN - javascript --- 实例函数中第2个与第3个参数为可选参数 ```javascript function example( err, optionalA, optionalB, callback ) { // 使用数组取出arguments var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; }; // 第一个参数为错误参数 // shift() 移除数组中第一个参数并将其返回 err = args.shift(); // 如果最后一个参数是函数,则它为回调函数 // pop() 移除数组中最后一个参数并将其返回 if (typeof args[args.length-1] === 'function') { callback = args.pop(); } // 如果args中仍有元素,那就是你需要的可选参数 // 你可以像这样一个一个的将其取出: if (args.length > 0) optionalA = args.shift(); else optionalA = null; if (args.length > 0) optionalB = args.shift(); else optionalB = null; // 像正常一样继续:检查是否有错误 if (err) { return callback && callback(err); } // 为了教程目的,打印可选参数 console.log('optionalA:', optionalA); console.log('optionalB:', optionalB); console.log('callback:', callback); /* 你想做的逻辑 */ } // ES6语法书写更简短 function example(...args) { // 第一个参数为错误参数 const err = args.shift(); // 如果最后一个参数是函数,则它为回调函数 const callback = (typeof args[args.length-1] === 'function') ? args.pop() : null; // 如果args中仍有元素,那就是你需要的可选参数你可以像这样一个一个的将其取出: const optionalA = (args.length > 0) ? args.shift() : null; const optionalB = (args.length > 0) ? args.shift() : null; // ... 重复取更多参数 if (err && callback) return callback(err); /* 你想做的逻辑 */ } // 使用或不适用可选参数调用实例函数 example(null, 'AA'); example(null, function (err) { /* do something */ }); example(null, 'AA', function (err) {}); example(null, 'AAAA', 'BBBB', function (err) {}); ``` ### 如何保证optionalA和optionalB是预期的值? 设计你的函数,使其在接收optionalB时optionalA为必选参数。 ================================================ FILE: _posts/zh_CN/javascript/2016-05-12-make-easy-loop-on-array.md ================================================ --- layout: post title: 用数组建立一个简单的循环 tip-number: 55 tip-username: jamet-julien tip-username-profile: https://github.com/jamet-julien tip-tldr: 有时我们需要不停的循环数组的元素,就像一组旋转的图片,或者音乐的播放列表。这里告诉你如何使一个数组拥有循环的能力。 redirect_from: - /zh_cn/make-easy-loop-on-array/ categories: - zh_CN - javascript --- 有时我们需要不停的循环数组的元素,就像一组旋转的图片,或者音乐的播放列表。这里告诉你如何使一个数组拥有循环的能力: ```js var aList = ['A','B','C','D','E']; function make_looper( arr ){ arr.loop_idx = 0; // 返回当前的元素 arr.current = function(){ if( this.loop_idx < 0 ){// 第一次检查 this.loop_idx = this.length - 1;// 更新 loop_idx } if( this.loop_idx >= this.length ){// 第二次检查 this.loop_idx = 0;// 更新 loop_idx } return arr[ this.loop_idx ];//返回元素 }; // 增加 loop_idx 然后返回新的当前元素 arr.next = function(){ this.loop_idx++; return this.current(); }; // 减少 loop_idx 然后返回新的当前元素 arr.prev = function(){ this.loop_idx--; return this.current(); }; } make_looper( aList); aList.current();// -> A aList.next();// -> B aList.next();// -> C aList.next();// -> D aList.next();// -> E aList.next();// -> A aList.pop() ;// -> E aList.prev();// -> D aList.prev();// -> C aList.prev();// -> B aList.prev();// -> A aList.prev();// -> D ``` 使用 ```%``` ( 取模 ) 操作符更优雅。取模返回除法的余数 ( ``` 2 % 5 = 1``` and ``` 5 % 5 = 0```): ```js var aList = ['A','B','C','D','E']; function make_looper( arr ){ arr.loop_idx = 0; // return current item arr.current = function(){ this.loop_idx = ( this.loop_idx ) % this.length;// 无需检查 !! return arr[ this.loop_idx ]; }; // 增加 loop_idx 然后返回新的当前元素 arr.next = function(){ this.loop_idx++; return this.current(); }; // 减少 loop_idx 然后返回新的当前元素 arr.prev = function(){ this.loop_idx += this.length - 1; return this.current(); }; } make_looper( aList); aList.current();// -> A aList.next();// -> B aList.next();// -> C aList.next();// -> D aList.next();// -> E aList.next();// -> A aList.pop() ;// -> E aList.prev();// -> D aList.prev();// -> C aList.prev();// -> B aList.prev();// -> A aList.prev();// -> D ``` ================================================ FILE: _posts/zh_CN/javascript/2016-08-02-copy-to-clipboard.md ================================================ --- layout: post title: 复制到粘贴板 tip-number: 56 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 本周我做了一个简单的“复制到剪贴板”按钮,这是我第一次做这种功能,向大家分享一下我的实现方法。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/copy-to-clipboard/ categories: - zh_CN - javascript --- 这是一个简单的小知识,本周我做了一个简单的“复制到剪贴板”按钮,这是我第一次做这种功能,向大家分享一下我的实现方法。 这很简单,比较麻烦的是我们必须为需要复制的文本增加``标签。之后我们选择要复制的内容然后调用复制命令[execCommand](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand). `execCommand('copy')` 将会复制被选择的内容。 此方法目前被所有最新版本的浏览器[支持](http://caniuse.com/#search=execCommand),它可以让我们执行如`复制`、`剪切`、`粘贴`等命令,还可以改变字体颜色、大小等。 ```js document.querySelector('#input').select(); document.execCommand('copy'); ``` 具体表现看[这里](https://jsbin.com/huhozu/edit?html,js,output) ================================================ FILE: _posts/zh_CN/javascript/2016-08-10-comma-operaton-in-js.md ================================================ --- layout: post title: JavaScript 的逗号操作符 tip-number: 57 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: 在一个表达式中,由左到右计算每个表达式并返回最后一个。 redirect_from: - /zh_cn/comma-operaton-in-js/ categories: - zh_CN - javascript --- 除了分号之外,逗号允许你在同一个地方放多个语句。 例如: ```js for(var i=0, j=0; i<5; i++, j++, j++){ console.log("i:"+i+", j:"+j); } ``` 輸出: ```js i:0, j:0 i:1, j:2 i:2, j:4 i:3, j:6 i:4, j:8 ``` 当放一个表达式时,它由左到右计算每个表达式,并传回最右边的表达式。 例如: ```js function a(){console.log('a'); return 'a';} function b(){console.log('b'); return 'b';} function c(){console.log('c'); return 'c';} var x = (a(), b(), c()); console.log(x); // 输出「c」 ``` 输出: ```js "a" "b" "c" "c" ``` * 注意:逗号(`,`)操作符在 JavaScript 中所有的操作符里是最低的优先顺序,所以没有括号表达式将变为:`(x = a()), b(), c();`。 ##### 实验 ================================================ FILE: _posts/zh_CN/javascript/2016-08-17-break-continue-loop-functional.md ================================================ --- layout: post title: break 或 continue 循环函数 tip-number: 58 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: 循环一个`list`从中寻找一个或一些值,是一个很常见的需求。但是即使我们要找的元素就是数组里的第一个,我们不也能从循环中直接`return`,只能遍历整个数组。本文教你如何使用`.some`和`.every`快速结束循环。 redirect_from: - /zh_cn/break-continue-loop-functional/ categories: - zh_CN - javascript --- 停止循环是循环中一个常见的需求。使用`for`循环我们可以用`break`提前结束循环。 ```javascript const a = [0, 1, 2, 3, 4]; for (var i = 0; i < a.length; i++) { if (a[i] === 2) { break; // stop the loop } console.log(a[i]); } //> 0, 1 ``` 另一个常见的需求使我们需要直接取得变量。 一个快速的方式是使用`.forEach`,但是这样我们就失去了`break`的能力。这种情况下,最接近的方式是使用`return`实现`continue`的功能。 ```javascript [0, 1, 2, 3, 4].forEach(function(val, i) { if (val === 2) { // 怎么停止呢? return true; } console.log(val); // your code }); //> 0, 1, 3, 4 ``` `.some`是一个原型方法。他用来检测是否某些元素满足所提供的函数。如果任何元素最终返回`true`,它就会停止运行。更多解释请看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some)。 引子上面链接的一个例子: ```javascript const isBiggerThan10 = numb => numb > 10; [2, 5, 8, 1, 4].some(isBiggerThan10); // false [12, 5, 8, 1, 4].some(isBiggerThan10); // true ``` 使用`.some`我们拥有了类似`.forEach`的功能,而且使用`return`实现了`break`的效果。 ```javascript [0, 1, 2, 3, 4].some(function(val, i) { if (val === 2) { return true; } console.log(val); // your code }); //> 0, 1 ``` 你可以返回`false`使循环`continue`到下一个元素。当你返回`true`时,循环将会`break`,此时`a.some(..)`将会`return` `true`。 ```javascript // Array contains 2 const isTwoPresent = [0, 1, 2, 3, 4].some(function(val, i) { if (val === 2) { return true; // break } }); console.log(isTwoPresent); //> true ``` 还有`.every`函数同样可以实现此功能。但此时我们需要返回与`.some`相反的布尔值。 ##### 示例 ================================================ FILE: _posts/zh_CN/javascript/2016-08-25-keyword-var-vs-let.md ================================================ --- layout: post title: var和ES6的let tip-number: 59 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: 在这个 tip,我将介绍 var 和 let 之间不同的作用域。我应该使用 let 替代 var 吗?让我们来看一下吧! redirect_from: - /zh_cn/keyword-var-vs-let/ categories: - zh_CN - javascript --- ### 概述 - 通过 `var` 定义的变量,它的作用域是在 function 或任何外部已经被声明的 function,是全域的 。 - 透過 `let` 定义的变量,它的作用域是在一個块(block)。 ```js function varvslet() { console.log(i); // i 是 undefined 的,因为变量提升 // console.log(j); // ReferenceError: j 没有被定义 for( var i = 0; i < 3; i++ ) { console.log(i); // 0, 1, 2 }; console.log(i); // 3 // console.log(j); // ReferenceError: j 没有被定义 for( let j = 0; j < 3; j++ ) { console.log(j); }; console.log(i); // 3 // console.log(j); // ReferenceError: j 没有被定义 } ``` ### 详细的区别 - 变量提升 `let` 不會被提升到整个块的作用域。相比之下,`var` 可以被提升。 ```js { console.log(c); // undefined。因为变量提升 var c = 2; } { console.log(b); // ReferenceError: b 没有被定义 let b = 3; } ``` - 循环中的闭包 `let` 在每次循环可以重新被 bind,确保在它之前结束的循环被重新赋值,所以在闭包中它被用來避免一些问题。 ```js for (var i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); // 输出 '5' 五次 }, 100); } ``` 使用 `let` 替换 `var` ```js // print 1, 2, 3, 4, 5 for (let i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); // 输出 0, 1, 2, 3, 4 }, 100); } ``` ### 我们应该用 `let` 替代 `var` 嗎? > 不是的,`let` 是新的块作用域。语法强调在 `var` 已经是区块作用域时時,`let` 应该替换 `var` ,否则请不要替换 `var`。`let` 改善了在 JS 作用域的选项,而不是取代。`var` 对于变量依旧是有用的,可被用在整個 function 之中。 ### `let` 兼容性 - 在 server 端,比如 Node.js,你现在可以安心的使用 `let`。 - 在 client 端,通过 transpiler(比如 [Traceur](https://github.com/google/traceur-compiler)),可以安心的使用 `let` 语法。否则请在[这里](http://caniuse.com/#search=let)确认你的浏览器是否支持。 ### Playground ### 更多信息 - [Let keyword vs var keyword](http://stackoverflow.com/questions/762011/let-keyword-vs-var-keyword) - [For and against let](https://davidwalsh.name/for-and-against-let) - [Explanation of `let` and block scoping with for loops](http://stackoverflow.com/questions/30899612/explanation-of-let-and-block-scoping-with-for-loops/30900289#30900289). ================================================ FILE: _posts/zh_CN/javascript/2016-10-28-three-useful-hacks.md ================================================ --- layout: post title: 三个实用的javascript小技巧 tip-number: 60 tip-username: leandrosimoes tip-username-profile: https://github.com/leandrosimoes tip-tldr: 分享三个让开发变得更高效的实用语法糖 redirect_from: - /zh_cn/three-useful-hacks/ categories: - zh_CN - javascript --- #### 从后向前获取数组元素 如果你想从后向前获取一个数组的元素,可以这样写: ```javascript var newArray = [1, 2, 3, 4] console.log(newArray.slice(-1)) // [4] console.log(newArray.slice(-2)) // [3, 4] console.log(newArray.slice(-3)) // [2, 3, 4] console.log(newArray.slice(-4)) // [1, 2, 3, 4] ``` #### 短路条件句 如果你想在某个条件逻辑值为`true`时,执行某个函数,就像这样: ```javascript if (condition) { dosomething() } ``` 这时,你可以这样子运用短路: ```javascript condition && dosomething() ``` #### 用操作符 "||" 来设置默认值 如果你必须给一个变量赋默认值,可以简单的这样写: ```javascript var a console.log(a) // undefined a = a || 'default value' console.log(a) // default value a = a || 'new value' console.log(a) // default value ``` ================================================ FILE: _posts/zh_CN/javascript/2017-01-19-binding-objects-to-functions.md ================================================ --- layout: post title: 给函数 Bind 对象 tip-number: 61 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 理解在 JavaScript 中如何使用 `Bind` 方法绑定对象和函数 redirect_from: - /zh_cn/binding-objects-to-functions/ categories: - zh_CN - javascript --- 我们常常需要将一个对象绑定到一个方法的 `this` 上。在 JS 中,如果你想要调用一个函数并指定它的 `this` 时可以使用 `bind` 方法。 ### Bind 语法 ```js fun.bind(thisArg[, arg1[, arg2[, ...]]]) ``` ## 参数 **thisArg** 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。 **arg1, arg2, ...** 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。 **返回值** 返回由指定的this值和初始化参数改造的原函数拷贝 ### JS 中的实例 ```js const myCar = { brand: 'Ford', type: 'Sedan', color: 'Red' }; const getBrand = function () { console.log(this.brand); }; const getType = function () { console.log(this.type); }; const getColor = function () { console.log(this.color); }; getBrand(); // object not bind,undefined getBrand(myCar); // object not bind,undefined getType.bind(myCar)(); // Sedan let boundGetColor = getColor.bind(myCar); boundGetColor(); // Red ``` ================================================ FILE: _posts/zh_CN/javascript/2017-03-09-working-with-websocket-timeout.md ================================================ --- layout: post title: 处理 Websocket 超时问题 tip-number: 63 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 一个控制超时的技巧 categories: - zh_CN - javascript --- 在 websocket 连接被建立后,如果一段时间未活动,服务器或防火墙可能会超时或终止连接。想要解决这个问题, 我们可以周期性地给服务器发消息。我们需要两个方法实现:一个来确保连接不会中断,,另一个用来取消此设定。同我们也需要一个 ```timerID``` 变量. 让我们来看一下具体实现: ```js var timerID = 0; function keepAlive() { var timeout = 20000; if (webSocket.readyState == webSocket.OPEN) { webSocket.send(''); } timerId = setTimeout(keepAlive, timeout); } function cancelKeepAlive() { if (timerId) { clearTimeout(timerId); } } ``` 现在我们实现了我们需要的两个方法,我们可以在 ```onOpen()``` 的最后面调用 ```keepAlive()``` ,在```onClose()``` 的组后面调用 ```cancelKeepAlive()```。 好了!我们我们完美的解决了 websocket 超时的问题。 ================================================ FILE: _posts/zh_CN/javascript/2017-03-12-3-array-hacks.md ================================================ --- layout: post title: Array 的三个技巧 tip-number: 64 tip-username: hassanhelfi tip-username-profile: https://twitter.com/hassanhelfi tip-tldr: 在 JavaScript 中 数组(Array)随处可见,使用ECMAScript 6 中的新特性 扩展运算符 你可以做很多很棒事情。在这边文章中,我将为你介绍在编码中有用的3个技巧。 categories: - zh_CN - javascript --- 在 JavaScript 中 数组(Array)随处可见,使用ECMAScript 6 中的新特性 [扩展运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_operator) 你可以做很多很棒事情。在这边文章中,我将为你介绍在编码中有用的3个技巧。 ### 1. 迭代一个空数组 JavaScript 中直接创建的数组是松散的,以至于会有很多坑。试着用数组的构造方法创建一个数组,你就会明白我的意思。 ```javascript > const arr = new Array(4); [undefined, undefined, undefined, undefined] ``` 你会发现,通过一个松散的数组去循环调用一些转换是非常难的。 ```javascript > const arr = new Array(4); > arr.map((elem, index) => index); [undefined, undefined, undefined, undefined] ``` 想要解决这个问题,你可以使用在创建新数组的时候使用 `Array.apply`。 ```javascript > const arr = Array.apply(null, new Array(4)); > arr.map((elem, index) => index); [0, 1, 2, 3] ``` ### 2. 给方法传一个空参数 如果你想调用一个方法,并不填其中的一个参数时,JavaScript 就会报错。 ```javascript > method('parameter1', , 'parameter3'); Uncaught SyntaxError: Unexpected token , ``` 一个人们常用的解决方法是传递 `null` 或 `undefined`. ```javascript > method('parameter1', null, 'parameter3') // or > method('parameter1', undefined, 'parameter3'); ``` 自从 JavaScript 把 `null` 当做一个 object 的时候, 我个人就不太喜欢使用它了。根据 ES6 中对扩展运算符的介绍,有一个更简洁的方法可以将空参数传递给一个方法。正如前文所提到的,数组是松散的,所以给它传空值是可以的,我们正式用到了这个优点。 ```javascript > method(...['parameter1', , 'parameter3']); // works! ``` ### 数组去重 我一直不明白为什么数组不提供一个内置函数可以让我们方便的取到去重以后的值。扩展运算符帮到了我们,使用扩展运算符配合 `Set` Spread operators are here for the rescue. Use spread operators with the `Set` 可以生成一个不重复的数组。 ```javascript > const arr = [...new Set([1, 2, 3, 3])]; [1, 2, 3] ``` ================================================ FILE: _posts/zh_CN/javascript/2017-03-16-tapping-for-quick-debugging.md ================================================ --- layout: post title: 使用 tap 来快速 debug tip-number: 65 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 在这里 tap 是一个小怪物。一个可以用来快速调试、链式调用、匿名函数,还可以打印任何你想打印的东西的函数。 tip-md-link: https://github.com/loverajoel/jstips/blob/master/_posts/en/javascript/2017-03-16-tapping-for-quick-debugging.md categories: - zh_CN - javascript --- 在这里 tap 是一个小怪物。一个可以用来快速调试、链式调用、匿名函数,还可以打印任何你想打印的东西的函数。 ``` javascript function tap(x) { console.log(x); return x; } ``` 为什么我们不用 `console.log` 这个老方式了?让我来示范一个例子: ``` javascript bank_totals_by_client(bank_info(1, banks), table) .filter(c => c.balance > 25000) .sort((c1, c2) => c1.balance <= c2.balance ? 1 : -1 ) .map(c => console.log(`${c.id} | ${c.tax_number} (${c.name}) => ${c.balance}`)); ``` 现在,加入你从这个链式调用中没有得到任何返回。 在哪里除了问题呢?或许 `bank_info` 没有返回东西,我们需要监听(tap)它: ``` javascript bank_totals_by_client(tap(bank_info(1, banks)), table) ``` 基于我们特殊的实现,它可能会打印一些东西,也可能什么也不打印。 我们假设,打印出来的东西是正确的,因此, `bank_info` 没有问题。 我们需要继续调试下一个函数, filter. ``` javascript .filter(c => tap(c).balance > 25000) ``` 我们可以得到 `c` 吗?如果可以,说明 `bank_totals_by_client` 运行正常。 可能是 filter 内的条件有问题? ``` javascript .filter(c => tap(c.balance > 25000)) ``` 啊哈!我们发现除了 `false` 没有打印其他东西,所以说明没有一个 client >25000, 这就是为什么方法什么也没返回的原因。 ## (附) 更先进的 tap ``` javascript function tap(x, fn = x => x) { console.log(fn(x)); return x; } ``` 让我们来看一下一个更强大的怪物,如果我们想在监听(tap)之前*事先*做一些操作应该怎么办?比如,我们只想方位某个对象特定的参数,位于一个逻辑运算,等等。使用上面的方法,在调用的时候增加一个额外参数,这个函数在被监听(tap)的时候就会被执行。 ``` javascript tap(3, x => x + 2) === 3; // prints 5, but expression evaluates to true, why :-)? ``` ================================================ FILE: _posts/zh_CN/javascript/2017-04-03-why-you-should-use-Object.is()-in-equality-comparison.md ================================================ --- layout: post title: 为什么你应该在相等比较中使用 Object.is() tip-number: 68 tip-username: TarekAlQaddy tip-username-profile: https://github.com/TarekAlQaddy tip-tldr: JavaScript 中一个很棒的相等比较解决方案 categories: - zh_CN - javascript --- 我们都知道 JavasSript 是弱类型的,并且当我们使用 `==` 作比较时,在一些情况下由于类型转换或者说“把两个操作数中的一个转换成另一个,然后在比较”,会出现意想不到的结果。 ``` javascript 0 == ' ' //true null == undefined //true [1] == true //true ``` 因此 JavaScript 中给我们提供了全等操作符 `===`, 它比不全等操作符更加严格并且不会发生类型转换。但是用 `===` 来进行比较并不是最好的解决方案。你可能会得到: ``` javascript NaN === NaN //false ``` 好消息是 ES6 中提供了新的 `Object.is()` 方法,它具有 `===` 的一些特点,而且更好、更精确,在一些特殊案例中表现的很好: ``` javascript Object.is(0 , ' '); //false Object.is(null, undefined); //false Object.is([1], true); //false Object.is(NaN, NaN); //true ``` Mozilla 团队并不认为 Object.is 比 `===` 更加“严格”,他们说我们应该考虑的是这个方法如何处理 NaN, -0 和 +0。但是总的来说, 我认为它在实际应用中是一个很好的实践。 现在来看看这张图表的对比... ![differences of operators in equality comparisons javascript](http://i.imgur.com/pCyqkLc.png) ## References: [Equality comparisons and sameness](http://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness) ================================================ FILE: _posts/zh_CN/javascript/2017-04-05-picking-and-rejecting-object-properties.md ================================================ --- layout: post title: 选择(picking)和反选(rejecting)对象的属性 tip-number: 70 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 有时候我们需要将一个对象的某些属性放到白名单里,这样来说,我们有一个数组代表了一张数据库表,并且为了一些功能我们需要从中选出(`select`)一些字段。 categories: - zh_CN - javascript --- 有时候我们需要将一个对象的某些属性放到白名单里,这样来说,我们有一个数组代表了一张数据库表,并且为了一些功能我们需要从中选出(`select`)一些字段: ``` javascript function pick(obj, keys) { return keys.map(k => k in obj ? {[k]: obj[k]} : {}) .reduce((res, o) => Object.assign(res, o), {}); } const row = { 'accounts.id': 1, 'client.name': 'John Doe', 'bank.code': 'MDAKW213' }; const table = [ row, {'accounts.id': 3, 'client.name': 'Steve Doe', 'bank.code': 'STV12JB'} ]; pick(row, ['client.name']); // 取到了 client name table.map(row => pick(row, ['client.name'])); // 取到了一系列 client name ``` 在 pick 函数中用到了一点‘诡计’。首先,我们用 `map` 遍历了键名数组(keys), 每次都会返回一个包含当前键名(key)的对象(如果在目标对象(obj)中没有当前键名,就会返回空对象)。然后我们用 `reduce` 把返回的所有单个键-值对象和合并到一个对象中。 但是,如果我们想反选(`reject`)属性/键名呢?改造一下我们的函数就好了: ``` javascript function reject(obj, keys) { return Object.keys(obj) .filter(k => !keys.includes(k)) .map(k => ({[k]: obj[k]})) .reduce((res, o) => Object.assign(res, o), {}); } // 或者, 利用 pick function reject(obj, keys) { const vkeys = Object.keys(obj) .filter(k => !keys.includes(k)); return pick(obj, vkeys); } reject({a: 2, b: 3, c: 4}, ['a', 'b']); // => {c: 4} ``` ================================================ FILE: _posts/zh_CN/more/2017-04-06-vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding.md ================================================ --- layout: post title: Vuejs在資料綁定時會複製更新並替換目標元素 tip-number: 71 tip-username: pansila tip-username-profile: https://github.com/pansila tip-tldr: 在這個提示中,我會通過一個例子向您展示Vue會如何與其它軟體衝突如果你不知道這一點。 categories: - zh_CN --- ### 概述 Vuejs是一款簡單而強大的軟體傑作,類似其它流行的UI框架,Angularjs和Reactjs,但不像這兩者令人生畏的複雜性,Vue非常簡單,在從入門到放棄之前,你能很快掌握它的全部知識並投入生產。 但是如果你不知道它怎麼工作的,有時候它也會難為你。這裡是一個和其它UI框架(Framework7)衝突的例子。 ```html

      {% raw %}{{content}}{% endraw %}

      ``` ```js var myApp = new Framework7(); myApp.onPageInit('test', function (page) { new Vue({ el: '#test', data: { content: 'hello world' } }); }); ``` 你可能會驚訝它竟然無法工作,新的page點擊後並沒有顯示出來。事實上,Vue內部會複製目標HTML元素,然後根據綁定的資料更新並替換原來的元素。當Framework7載入新的頁面時,它會調用`PageInit`回呼函數,這裡我們又調用了Vue在``元素上資料綁定,這之後DOM樹裡面包含的已經是新的``元素,但Framework7對此並不知情又接著在舊的``元素上完成剩下的初始化工作,比如最終顯示這個新的頁面,這就是根本原因。 為了繞過這個問題,不要讓Vue的元素選擇器錨定在``元素上,而是定在它的子元素,這樣Vue做資料綁定時就不會影響到整個頁面顯示。 ```js var myApp = new Framework7(); myApp.onPageInit('test', function (page) { new Vue({ el: '#test1', data: { content: 'hello world' } }); }); ``` ### 更多資訊 - [Vue](https://github.com/Vuejs/Vue) - [Vue教程] (https://cn.vuejs.org/) - [Framework7](https://framework7.io/) ================================================ FILE: _posts/zh_CN/react/2016-01-02-keys-in-children-components-are-important.md ================================================ --- layout: post title: 子容器的Key是很重要的 tip-number: 02 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: key是必须传递给从数组中动态创建的所有组件的一个值。它是一个唯一且固定的id,用来识别DOM中的每个组件,也可以让我们区别它是否是同一个组件。使用key可以确保子容器是可保存而且不需要重复创建的,还可以防止奇怪的事情发生。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_cn/keys-in-children-components-are-important/ categories: - zh_CN - react --- [key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children)必须传递给从数组中动态创建的所有组件的一个值。它是一个唯一且固定的id,用来识别DOM中的每个组件,也可以让我们区别它是否是同一个组件。使用key可以确保子容器是可保存而且不需要重复创建的,还可以防止奇怪的事情发生。 > key跟效率不是很相关,它更与身份有关系(这间接的使效率更好)。随机的赋值或改变值将不能识别身份[Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939) - 使用对象存在的的唯一值。 - 在父组件定义key,而不是子组件。 ```javascript //bad ... render() {
      {% raw %}{{item.name}}{% endraw %}
      } ... //good ``` - [使用数组索引是一个坏习惯](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318#.76co046o9) - `random()` 不会起作用 ```javascript //bad ``` - 你可以创建以自己的唯一id。确定这个方法运行速度够快,把它附着到你的对象上。 - 当子组件的数量很大或者包含重量级的组件时,使用key来提高性能。 - [你必须提供key值给ReactCSSTransitionGroup的每个子组件](http://docs.reactjs-china.com/react/docs/animation.html) ================================================ FILE: _posts/zh_TW/angular/2016-01-01-angularjs-digest-vs-apply.md ================================================ --- layout: post title: AngularJs - $digest vs $apply tip-number: 01 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: JavaScript 模組以及建構步驟變得更多更複雜,但對於新的框架樣板呢? tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/angularjs-digest-vs-apply/ categories: - zh_TW - angular --- AngularJs 最令人欣賞的特性之一是雙向資料綁定。AngularJS 透過循環方式(`$digest`)來檢查模型和視圖變化來實現這個功能。想要理解框架底層的工作方式,你必須了解這個概念。 當一個事件被觸發時,Angular 會檢查每個 watcher 變化,這是我們所知的 `$digest` 循環。 有時候你需要強迫手動執行一個新的循環,你必須選擇正確的選項,因為這個階段是最影響效能之一。 ### `$apply` 這個核心方法可以讓你明確地啟動 `$digest` 循環。意思說所有的 watchers 都會被確認;整個應用程式啟動 `$digest loop`。在內部,執行一個可選的功能參數,它會呼叫 `$rootScope.$digest();`。 ### `$digest` 在這個情況下,`$digest` 方法在目前的 scope 和子 scope 啟動 `$digeset` 循環。你應該注意到父 scope 不會被檢查,也不會受影響。 ### 建議 - 當瀏覽器 DOM 事件觸發外部的 AngularJS 使用 `$apply` 或 `$digest`。 - 傳送一個函式表達式給 `$apply`,這裡有一個錯誤處理機制,並且允許整合所有在 `$digest` 循環的變化。 ```javascript $scope.$apply(() => { $scope.tip = 'Javascript Tip'; }); ``` - 如果你只要更新目前的 scope 和子 scope,使用 `$digest` 防止在整個應用程式執行新的 digest 循環。這在性能上的好處是相當明顯的。 - `$apply()` 對電腦來說是一個相當複雜的處理程序,如果過多的 binding 會造成性能上的問題。 - 如果你使用 >AngularJS 1.2.X,使用 `$evalAsync`,這是一個核心方法,可以在目前的循環或下一個循環執行表達式,可以增加你的應用程式效能。 ================================================ FILE: _posts/zh_TW/angular/2017-03-07-preventing-unwanted-scopes-creation-in-angularjs.md ================================================ --- layout: post title: 在 AngularJs 防止不必要的 scope 建立 tip-number: 62 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 在這個 tip 我會示範如何在 scope 之件傳送資料,防止 `ng-repeat` 和 `ng-if` 建立不必要的 scope tip-writer-support: https://www.coinbase.com/loverajoel categories: - zh_TW - angular --- AngularJs 最受歡迎的特性之一是理解和防止 ```ng-model``` 資料的 scope,這會是你經常遇到的主要挑戰之一。 在處理 ```ng-model``` 資料時,新的不必要的 scope 透過 ```ng-repeat``` 或 ```ng-if``` 程序被建立。 看看下面的例子: ```js

      innerScope:{{data}}
      outerScope:{{data}}
      ``` 在上面的範例中,```innerScope``` 繼承了 ```outerScope``` 並傳送值到 ```outerScope```。 如果你輸入一個值到 ```innerScope``` 它將反映在 ```outerScope```。但是如果你編輯 ```outerScope```, ```innerScope``` 並不會反映與 ```outerScope``` 相同的值,因為 ```innerScope``` 建立了它本身的作用域,所以不再繼承自 ```outerScope```。 為了防止發生這件事,我們可以使用「Controller As」方式而不是使用 scope 作為一個 container 給所有資料和 function。但是一個更吸引人的解決方法是保持所有事物在 object,如下面範例: ```js

      inner scope:{{data.text}}
      outer scope:{{data.text}}
      ``` 現在 ```innerScope``` 不再建立一個新的作用域,在 ```innerScope``` 或 ```outerScope``` 編輯數值將會反映到 ```innerScope``` 和 ```outerScope```。 ================================================ FILE: _posts/zh_TW/javascript/2015-12-29-insert-item-inside-an-array.md ================================================ --- layout: post title: 在陣列中加入元素 tip-number: 00 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 在一個存在的陣列加入新的元素是一件很常見的事情,你可以使用 push 將元素加入到陣列的末端,或是使用 unshift 在陣列起始位置加入元素,也可以使用 splice 在陣列中間的地方加入元素。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/insert-item-inside-an-array/ categories: - zh_TW - javascript --- # 在存在的陣列加入新的元素 在一個存在的陣列加入新的元素是一件很常見的事情,你可以使用 push 將元素加入到陣列的末端,或是使用 unshift 在陣列起始位置加入元素,也可以使用 splice 在陣列中間的地方加入元素。 那些都是已知的方法,但這並不代表沒有更好的效能的方法。讓我們開始吧: ## 在陣列最後加入新的元素 在陣列最後加入新的元素我們可以簡單地透過 push() 方式,但以下是更能提升效能方法。 ```javascript var arr = [1, 2, 3, 4, 5]; var arr2 = []; arr.push(6); arr[arr.length] = 6; arr2 = arr.concat([6]); ``` 這兩種方法都是修改原始的陣列。不相信嗎?請參考 [jsperf](http://jsperf.com/push-item-inside-an-array)。 ### 在 mobile 上的性能表現: #### Android (v4.2.2) 1. _arr.push(6);_ 和 _arr[arr.length] = 6;_ 有相同的性能表現 // 3 319 694 ops/sec 3. _arr2 = arr.concat([6]);_ 較以上兩個方法慢 50.61 % #### Chrome Mobile (v33.0.0) 1. _arr[arr.length] = 6;_ // 6 125 975 ops/sec 2. _arr.push(6);_ 慢 66.74 % 3. _arr2 = arr.concat([6]);_ 慢 87.63 % #### Safari Mobile (v9) 1. _arr[arr.length] = 6;_ // 7 452 898 ops/sec 2. _arr.push(6);_ 慢 40.19 % 3. _arr2 = arr.concat([6]);_ 慢 49.78 % ```javascript 最後勝利者 1. arr[arr.length] = 6; // 平均 5 632 856 ops/sec 2. arr.push(6); // 慢 35.64 % 3. arr2 = arr.concat([6]); // 慢 62.67 % ``` ### 在 desktop 的性能表現 #### Chrome (v48.0.2564) 1. _arr[arr.length] = 6;_ // 21 602 722 ops/sec 2. _arr.push(6);_ 慢 61.94 % 3. _arr2 = arr.concat([6]);_ 慢 87.45 % #### Firefox (v44) 1. _arr.push(6);_ // 56 032 805 ops/sec 2. _arr[arr.length] = 6;_ 慢 0.52 % 3. _arr2 = arr.concat([6]);_ 慢 87.36 % #### IE (v11) 1. _arr[arr.length] = 6;_ // 67 197 046 ops/sec 2. _arr.push(6);_ 慢 39.61 % 3. _arr2 = arr.concat([6]);_ 慢 93.41 % #### Opera (v35.0.2066.68) 1. _arr[arr.length] = 6;_ // 30 775 071 ops/sec 2. _arr.push(6);_ 慢 71.60 % 3. _arr2 = arr.concat([6]);_ 慢 83.70 % #### Safari (v9.0.3) 1. _arr.push(6);_ // 42 670 978 ops/sec 2. _arr[arr.length] = 6;_ 慢 0.80 % 3. _arr2 = arr.concat([6]);_ 慢 76.07 % ```javascript 最後勝利者 1. arr[arr.length] = 6; // 平均 42 345 449 ops/sec 2. arr.push(6); // 慢 34.66 % 3. arr2 = arr.concat([6]); // 慢 85.79 % ``` ## 在陣列起始加入元素 現在我們嘗試在陣列的起始加入元素: ```javascript var arr = [1, 2, 3, 4, 5]; arr.unshift(0); [0].concat(arr); ``` 這裡有更多小細項:unshift 修改原始陣列;concat 回傳一個新的陣列。[jsperf](http://jsperf.com/unshift-item-inside-an-array) ### 在 mobile 的性能表現: #### Android (v4.2.2) 1. _[0].concat(arr);_ // 1 808 717 ops/sec 2. _arr.unshift(0);_ 慢 97.85 % #### Chrome Mobile (v33.0.0) 1. _[0].concat(arr);_ // 1 269 498 ops/sec 2. _arr.unshift(0);_ 慢 99.86 % #### Safari Mobile (v9) 1. _arr.unshift(0);_ // 3 250 184 ops/sec 2. _[0].concat(arr);_ 慢 33.67 % ```javascript 最後勝利者 1. [0].concat(arr); // 平均 4 972 622 ops/sec 2. arr.unshift(0); // 慢 64.70 % ``` ### 在 desktop 的性能表現 #### Chrome (v48.0.2564) 1. _[0].concat(arr);_ // 2 656 685 ops/sec 2. _arr.unshift(0);_ 慢 96.77 % #### Firefox (v44) 1. _[0].concat(arr);_ // 8 039 759 ops/sec 2. _arr.unshift(0);_ 慢 99.72 % #### IE (v11) 1. _[0].concat(arr);_ // 3 604 226 ops/sec 2. _arr.unshift(0);_ 慢 98.31 % #### Opera (v35.0.2066.68) 1. _[0].concat(arr);_ // 4 102 128 ops/sec 2. _arr.unshift(0);_ 慢 97.44 % #### Safari (v9.0.3) 1. _arr.unshift(0);_ // 12 356 477 ops/sec 2. _[0].concat(arr);_ 慢 15.17 % ```javascript 最後勝利者 1. [0].concat(arr); // 平均 6 032 573 ops/sec 2. arr.unshift(0); // 慢 78.65 % ``` ## 在陣列中間加入元素 在陣列中間可以簡單透過 splice 來加入元素,這樣的做法是最具效能的方式。 ```javascript var items = ['one', 'two', 'three', 'four']; items.splice(items.length / 2, 0, 'hello'); ``` 我嘗試在不同的瀏覽器和 OS 執行這些測試,他們的結果是相似的。我希望這些知識對你是有幫助的,也鼓勵你自己進行測試! ================================================ FILE: _posts/zh_TW/javascript/2016-01-03-improve-nested-conditionals.md ================================================ --- layout: post title: 改善巢狀化的條件式 tip-number: 03 tip-username: AlbertoFuente tip-username-profile: https://github.com/AlbertoFuente tip-tldr: 我們要如何在 JavaScript 改善巢狀化的 `if` 條件式? redirect_from: - o/zh_tw/improve-nested-conditionals/ categories: - zh_TW - javascript --- 我們要如何在 JavaScript 改善巢狀化的 `if` 條件式? ```javascript if (color) { if (color === 'black') { printBlackBackground(); } else if (color === 'red') { printRedBackground(); } else if (color === 'blue') { printBlueBackground(); } else if (color === 'green') { printGreenBackground(); } else { printYellowBackground(); } } ``` 有一種方式可以改善巢狀化的 `if` 條件式是使用 `switch` 陳述式。雖然更簡潔有序,但是不建議使用,因為它不容易 debug 錯誤。告訴你[為什麼](https://toddmotto.com/deprecating-the-switch-statement-for-object-literals)。 ```javascript switch(color) { case 'black': printBlackBackground(); break; case 'red': printRedBackground(); break; case 'blue': printBlueBackground(); break; case 'green': printGreenBackground(); break; default: printYellowBackground(); } ``` 但是,如果我們語句中都有很多條件檢查呢?在這個情況下,如果我們想要讓它更簡潔有序,我們可以使用有條件的 `switch`。 如果我們傳送 `true` 當作參數給 `switch` 陳述式,允許我們在每個 case 下使用條件式。 ```javascript switch(true) { case (typeof color === 'string' && color === 'black'): printBlackBackground(); break; case (typeof color === 'string' && color === 'red'): printRedBackground(); break; case (typeof color === 'string' && color === 'blue'): printBlueBackground(); break; case (typeof color === 'string' && color === 'green'): printGreenBackground(); break; case (typeof color === 'string' && color === 'yellow'): printYellowBackground(); break; } ``` 如果重構是一個選擇的話,我們可以嘗試的簡化這些函式。例如,不需要為每個顏色寫一個函式,我們可以寫一個以顏色作為參數的函式。 ```javascript function printBackground(color) { if (!color || typeof color !== 'string') { return; // 不正確的顏色,立即返回 } } ``` 如果重構不是一個選擇的話,我們必須避免使用過多的條件檢查以及避免使用 switch。我們使用最有效率的方式,透過 object。 ```javascript var colorObj = { 'black': printBlackBackground, 'red': printRedBackground, 'blue': printBlueBackground, 'green': printGreenBackground, 'yellow': printYellowBackground }; if (color in colorObj) { colorObj[color](); } ``` 這裡你可以找到更多的資訊關於 [switch](http://www.nicoespeon.com/en/2015/01/oop-revisited-switch-in-js/)。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-04-sorting-strings-with-accented-characters.md ================================================ --- layout: post title: 將帶有音節字元的字串進行排序 tip-number: 04 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: JavaScript 原生的 **sort** 方法讓我們可以排序陣列。做一個簡單的 `array.sort()`, 將每一個陣列元素視為字串並依字母排序。但是當你嘗試排序一個非 ASCII 字元陣列時,你會得到一個奇怪的結果。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/sorting-strings-with-accented-characters/ categories: - zh_TW - javascript --- JavaScript 原生的 **[sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)** 方法讓我們可以排序陣列。做一個簡單的 `array.sort()`, 將每一個陣列元素視為字串並依字母排序。你可以提供你的[自定義排序](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters)函式。 ```javascript ['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); // ["Buenos Aires", "Mumbai", "New York", "Shanghai"] ``` 但是當你嘗試排序一個非 ASCII 字元陣列像是: `['é', 'a', 'ú', 'c']`,你會得到奇怪的結果: `['c', 'e', 'á', 'ú']`。這個問題是因為排序功能只適用於英語。 請看以下的範例: ```javascript // 西班牙語 ['único','árbol', 'cosas', 'fútbol'].sort(); // ["cosas", "fútbol", "árbol", "único"] // bad order // 德文 ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); // ["Wann", "Woche", "wäre", "wöchentlich"] // bad order ``` 幸運的是,有兩種方法可以解決這個問題,由 ECMAScript 國際化 API 提供的 [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare) 和 [Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator)。 > 這兩種方法都有他們自己的自定義參數設置可以更有效的解決這個問題。 ### 使用 `localeCompare()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { return a.localeCompare(b); }); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { return a.localeCompare(b); }); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` ### 使用 `Intl.Collator()` ```javascript ['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); // ["Wann", "wäre", "Woche", "wöchentlich"] ``` - 對於每一個方法,你可以自定義位置。 - 根據 [Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Performance),當比對大量的字串時,Intl.Collator 會更加快速。 所以,當你在處理非英文語系的字串陣列時,記得使用這個方法來避免排序出現異常。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-05-differences-between-undefined-and-null.md ================================================ --- layout: post title: undefined 和 null 的差別 tip-number: 05 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 了解 `undefined` 和 `null` 的差別。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/differences-between-undefined-and-null/ categories: - zh_TW - javascript --- - `undefined` 意思是變數沒有被宣告,或者是已經宣告了,但是沒有賦值。 - `null` 意思是「沒有值」的值。 - Javascript 將未賦值的變數的預設值設為 `undefined`。 - Javascript 從來不會將值設定為 `null`。這是讓開發者用來宣告 `var` 是沒有值的。 - `undefined` 不是一個有效的 JSON,而 `null` 是有效的。 - `undefined` 的類型(typeof) 是 `undefined`。 - `null` 的類型(typeof)是一個 `object`。[為什麼?](http://www.2ality.com/2013/10/typeof-null.html) - 它們都是原始(primitives)型別。 - 它們都是 [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) (`Boolean(undefined) // false`, `Boolean(null) // false`)。 - 你可以判斷一個變數是否為 [undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)。 ```javascript typeof variable === "undefined" ``` - 你可以判斷一個變數是否為 [null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null)。 ```javascript variable === null ``` - **雙等號**運算符認為它們是相等的,但是**三等號**比較時是不相等的。 ```javascript null == undefined // true null === undefined // false ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md ================================================ --- layout: post title: 撰寫一個可以接受單一參數和陣列的方法 tip-number: 06 tip-username: mattfxyz tip-username-profile: https://twitter.com/mattfxyz tip-tldr: 你撰寫的函式要可以處理陣列或單一元素參數,而不是透過分開的方法來處理陣列和單一元素參數。這和 jQuery 一些函式工作原理相似(`css` 將會修改所有和 selector matched 的)。 redirect_from: - /zh_tw/writing-a-single-method-for-arrays-and-a-single-element/ categories: - zh_TW - javascript --- 你撰寫的函式要可以處理陣列或單一元素參數,而不是透過分開的方法來處理陣列和單一元素參數。這和 jQuery 一些函式工作原理相似(`css` 將會修改所有和 selector matched 的)。 首先,你只要將任何的東西 concat 到陣列上。`Array.concat` 將會接受陣列或是單一元素。 ```javascript function printUpperCase(words) { var elements = [].concat(words || []); for (var i = 0; i < elements.length; i++) { console.log(elements[i].toUpperCase()); } } ``` `printUpperCase` 現在已經可以接收單一的 node 或是一個陣列 nodes 當作它的參數了。如果沒有傳送參數,它可以避免拋出潛在的 `TypeError`。 ```javascript printUpperCase("cactus"); // => CACTUS printUpperCase(["cactus", "bear", "potato"]); // => CACTUS // BEAR // POTATO ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-07-use-strict-and-get-lazy.md ================================================ --- layout: post title: 在 JavaScript 使用嚴格模式 tip-number: 07 tip-username: nainslie tip-username-profile: https://twitter.com/nat5an tip-tldr: JavaScript 嚴格模式讓開發者可以寫出更「安全」的 JavaScript 程式碼。 redirect_from: - /zh_tw/use-strict-and-get-lazy/ categories: - zh_TW - javascript --- JavaScript 嚴格模式讓開發者可以寫出更「安全」的 JavaScript 程式碼。 預設情況下,JavaScript 允許開發者的粗心行為,例如,當我們引用一個沒有要求我們由「var」宣告的變數。或許這個對於一個剛入門的開發者相當方便,當變數名稱拼寫錯誤或者是不小心參考到其他的 scope,這些都是許多錯誤的來源。 開發者喜歡讓電腦幫我們做一些無聊的工作,然後自動幫我們確認工作上的錯誤。這就是 JavaScript 的 「嚴格模式」(use strict)幫我們做的,將錯誤轉換成 JavaScript 錯誤。 我們把這個指令放在 js 檔案的頂端: ```javascript // 整個 script 使用嚴格模式 "use strict"; var v = "Hi! I'm a strict mode script!"; ``` 或是在函式內: ```javascript function f() { // 函式內使用嚴格模式 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function f2() { return "I'm not strict."; } ``` 透過 JavaScript 檔案或函式內引入這個指令,我們直接讓 JavaScript 引擎執行嚴格模式來禁止一些在大型 JavaScript 專案不良的行為。除了其他之外,嚴格模式改變了以下的行為: * 只有被宣告的「var」變數才可以被引用。 * 嘗試寫入唯讀的變數會造成錯誤。 * 你只能透過「new」keyword 才可以呼叫建構子。 * 「this」不再隱式的綁定到全域的物件。 * 對 eval() 有嚴格的限制。 * 防止你使用保留字元或特殊字元當作變數名稱。 嚴格模式對於你的新專案是很棒的,但是對於引入一些大部分沒有使用舊的專案是一個挑戰。如果你將 js 檔案編譯在一起到你的大項目的話,可能會造成所有的檔案都執行在嚴格模式下,造成一些問題。 它不是一個語法,但它是一個文字上的表達,在早期版本的 JavaScript 被忽略了。 嚴格模式支援以下: * Internet Explorer from version 10. * Firefox from version 4. * Chrome from version 13. * Safari from version 5.1. * Opera from version 12. [嚴格模式的詳細說明,請參考 MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-08-converting-a-node-list-to-an-array.md ================================================ --- layout: post title: 將 Node List 轉換成陣列 tip-number: 08 tip-username: Tevko tip-username-profile: https://twitter.com/tevko tip-tldr: 這是快速,安全,可重複使用的方式,將 Node List 轉換成 DOM 元素的陣列。 redirect_from: - /zh_tw/converting-a-node-list-to-an-array/ categories: - zh_TW - javascript --- `querySelectorAll` 方法回傳一個類似陣列的物件稱為 Node List。這些資料結構簡稱為「類陣列」,因為他們和陣列很相似,但是不能使用陣列的方法像是 `map` 和 `forEach`。這是快速,安全,可重複使用的方式,將 Node List 轉換成 DOM 元素的陣列: ```javascript const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.apply(null, nodelist); // 之後 .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); // 等等... ``` `apply` 方法將參數陣列傳送給一個函式與給定 this 的值。根據 [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) ,`apply` 採用類陣列物件,而這剛好就是 `querySelectorAll` 方法所回傳的內容。如果我們不需要在函式的上下文(context)中指定 `this` ,我們可以傳送 `null` 或 `0`。這個結果實際上是一個 DOM 元素陣列,包含所有可用的陣列方法。 或者,假設你使用 ES2015 你可以使用[展開運算符 `...`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator)。 ```js const nodelist = [...document.querySelectorAll('div')]; // 回傳一個實際的陣列 // 之後 .. nodelist.forEach(...); nodelist.map(...); nodelist.slice(...); // 等等... ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-09-template-strings.md ================================================ --- layout: post title: 模板字串 tip-number: 09 tip-username: JakeRawr tip-username-profile: https://github.com/JakeRawr tip-tldr: 由於 ES6 中有了模板字串,JavaScript 可以使用模板字串來替代原本我們使用的引號字元。 redirect_from: - /zh_tw/template-strings/ categories: - zh_TW - javascript --- 由於 ES6 中有了模板字串,JavaScript 可以使用模板字串來替代原本我們使用的引號字元。 Ex: 正常的字串 ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log('My name is ' + firstName + ' ' + lastName); // My name is Jake Rawr ``` 模板字串 ```javascript var firstName = 'Jake'; var lastName = 'Rawr'; console.log(`My name is ${firstName} ${lastName}`); // My name is Jake Rawr ``` 在模板字串中,你不需要透過 `\n` 來產生多行的字串,只要簡單透過 `${}` 來替代就可以了,還可以計算簡單的邏輯,例如:`${2 + 3}`。 你也可以使用函式來修改你的輸出的內容;例如使用標籤模板字串,它們被稱為[標籤模板字串](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings#Tagged_template_strings)。 你或許想要[閱讀](https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2)和了解更多關於模板字串。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-10-check-if-a-property-is-in-a-object.md ================================================ --- layout: post title: 檢查屬性是否存在物件內 tip-number: 10 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 這些方法都是檢查屬性是否存在目前的物件內。 redirect_from: - /zh_tw/check-if-a-property-is-in-a-object/ categories: - zh_TW - javascript --- 當你檢查屬性是否存在目前的[物件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects)內,你或許可以這麼做: ```javascript var myObject = { name: '@tips_js' }; if (myObject.name) { ... } ``` 以上的方法是沒問題的,但是你必須知道對於這個問題有兩個原生的方法,[`in` 運算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in)和 [`Object.hasOwnProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)。任何繼承 `Object` 的都可以使用這兩種方法。 ### 觀察之間較大的差別 ```javascript var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true 'name' in myObject; // true myObject.hasOwnProperty('valueOf'); // false, valueOf 繼承自原型鏈結 'valueOf' in myObject; // true ``` 兩者不同的地方在於確認的屬性深度不同。換句話說,如果直接在物件內確認 key 是可用的話,`hasOwnProperty` 只會回傳 true。然而,在 `in` 運算符沒辦法分辨之間的屬性是建立在物件或是繼承自原型鏈結的。 這裡有其他的範例: ```javascript var myFunc = function() { this.name = '@tips_js'; }; myFunc.prototype.age = '10 days'; var user = new myFunc(); user.hasOwnProperty('name'); // true user.hasOwnProperty('age'); // false, 因為 age 是繼承自原型鏈結 ``` 在[線上範例](https://jsbin.com/tecoqa/edit?js,console)確認吧! 確認屬性是否存在在物件內的問題常見錯誤,我推薦閱讀關於這個問題的[討論](https://github.com/loverajoel/jstips/issues/62)。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-11-hoisting.md ================================================ --- layout: post title: 提升變數 tip-number: 11 tip-username: squizzleflip tip-username-profile: https://twitter.com/squizzleflip tip-tldr: 了解 hoisting 可以幫助你組織你的函式 scope。 redirect_from: - /zh_tw/hoisting/ categories: - zh_TW - javascript --- 了解 [hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) 將會幫助你編寫函式 scope。只要記得,變數宣告和函式定義都會被提升到頂端。變數定義則不會,即使你宣告和定義一個變數再同一行。變數**宣告**是讓系統知道變數的存在,而**定義**只是分配給它一個值。 ```javascript function doTheThing() { // ReferenceError: notDeclared 沒有被定義 console.log(notDeclared); // 輸出:undefine console.log(definedLater); var definedLater; definedLater = 'I am defined!' // 輸出:'I am defined!' console.log(definedLater) // 輸出:undefined console.log(definedSimulateneously); var definedSimulateneously = 'I am defined!' // 輸出:'I am defined!' console.log(definedSimulateneously) // 輸出:'I did it!' doSomethingElse(); function doSomethingElse(){ console.log('I did it!'); } // TypeError: undefined 不是一個函式 functionVar(); var functionVar = function(){ console.log('I did it!'); } } ``` 為了讓你的程式碼更容易閱讀,將你所有的變數宣告在你的函式 scope 頂端,這樣可以更清楚知道變數是來自哪個 scope。在你使用變數之前請先定義。在你的 scope 底部定義函式,來保持它們的方式。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-12-pseudomandatory-parameters-in-es6-functions.md ================================================ --- layout: post title: 在 ES6 函式內的預設參數 tip-number: 12 tip-username: Avraam Mavridis tip-username-profile: https://github.com/AvraamMavridis tip-tldr: 在許多程式設計語言函數的參數預設是強制需要的,而開發者也會明確定義一個可選的參數。 redirect_from: - /zh_tw/pseudomandatory-parameters-in-es6-functions/ categories: - zh_TW - javascript --- 在許多程式設計語言函數的參數預設值是強制需要的,而開發者也會明確定義一個可選的參數。在 JavaScript 中,每個參數都是可選的,利用 [**es6 參數預設值**](http://exploringjs.com/es6/ch_parameter-handling.html#sec_parameter-default-values)的特性,我們可以強制執行這個行為,而不會弄亂函數的主體。 ```javascript const _err = function( message ){ throw new Error( message ); } const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b getSum( 10 ) // 拋出錯誤,b 沒有被定義 getSum( undefined, 10 ) // 拋出錯誤,a 沒有被定義 ``` `_err` 是一個函式可以立即拋出錯誤。如果沒有傳送其中一個參數,函數預設值就會被使用,`_err` 將會被呼叫而且會拋出錯誤。你可以在 [Mozilla's Developer Network ](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/default_parameters) 看更多關於 **預設參數值特性** 的範例。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md ================================================ --- layout: post title: 測量 JavaScript 程式碼區塊性能的 tip tip-number: 13 tip-username: manmadareddy tip-username-profile: https://twitter.com/manmadareddy tip-tldr: 如果要快速的測量 JavaScript 程式碼區塊性能的話,我們可以使用 console 函式像是 `console.time(label)` 和 `console.timeEnd(label)`。 redirect_from: - /zh_tw/tip-to-measure-performance-of-a-javascript-block/ categories: - zh_TW - javascript --- 如果要快速的測量 JavaScript 程式碼區塊性能的話,我們可以使用 console 函式像是 [`console.time(label)`](https://developer.chrome.com/devtools/docs/console-api#consoletimelabel) 和 [`console.timeEnd(label)`](https://developer.chrome.com/devtools/docs/console-api#consoletimeendlabel)。 ```javascript console.time("Array initialize"); var arr = new Array(100), len = arr.length, i; for (i = 0; i < len; i++) { arr[i] = new Object(); }; console.timeEnd("Array initialize"); // 輸出:陣列初始化:0.711ms ``` 更多資訊: [Console object](https://github.com/DeveloperToolsWG/console-object)、 [Javascript benchmarking](https://mathiasbynens.be/notes/javascript-benchmarking)。 範例:[jsfiddle](https://jsfiddle.net/meottb62/) - [codepen](http://codepen.io/anon/pen/JGJPoa)(在瀏覽器 console 下輸出)。 > 注意:根據 [Mozilla](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) 建議,不該使用在上線環境下使用此方法,只能使用在開發環境。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-14-fat-arrow-functions.md ================================================ --- layout: post title: 箭頭函式 tip-number: 14 tip-username: pklinger tip-username-profile: https://github.com/pklinger/ tip-tldr: 介紹一個 ES6 的新特性 - 箭頭函式是一個方便的語法讓你用更少的程式碼做更多事。 redirect_from: - /zh_tw/fat-arrow-functions/ categories: - zh_TW - javascript --- 介紹一個 ES6 的新特性 - 箭頭函式,它是一個方便的語法讓你用更少的程式碼做更多事。它的名稱來自它的語法,`=>`,它是一個「fat arrow」, 相較於 `->`。有些開發者或許已經知道這個類型的函式是從不同的程式語言來的,像是 Haskell,作為「lambda 表達式」或者是「匿名函式」。它被稱作匿名函式是因為沒有一個名稱作為函數的名稱。 ### 這有什麼好處? * 語法:更少的程式碼;不需要重複再打出 `function`。 * 語義:從上下文(contex)取得 `this`。 ### 簡單的語法範例 你可以觀察這兩個程式碼的片段,它們做的是相同的工作,你很快地可以了解到箭頭函式做了什麼: ```javascript // 箭頭函式的一般語法 param => expression // 也可以寫入括號 // 在多個參數時,括號是必須的 (param1 [, param2]) => expression // 使用函式 var arr = [5,3,2,9,1]; var arrFunc = arr.map(function(x) { return x * x; }); console.log(arr) // 使用箭頭函式 var arr = [5,3,2,9,1]; var arrFunc = arr.map((x) => x*x); console.log(arr) ``` 正如你所見的,在這個範例箭頭函式省去了打出函式的括號和 return。我建議你在參數周圍寫出小括號,如果你需要多個輸入參數,像是 `(x, y) => x + y`。這是一個用來應付在不同情況下忘記使用小括號的方法。上面的程式碼也是這樣執行的:`x => x * x`。到目前為止,這些只是語法上的改進,減少的程式碼以及提高可讀性。 ### 詞彙綁定 - `this` 這是一個另一個更棒的理由來使用箭頭函式。這裡有一個 `this` context 的問題。使用箭頭函數,你不需要擔心 `.bind(this)` 或設定 `that = this` 的問題了,在箭頭函式會幫你從詞彙的範圍綁定 `this` 的 context。看以下的[範例](https://jsfiddle.net/pklinger/rw94oc11/): ```javascript // 在全域定義 this.i this.i = 100; var counterA = new CounterA(); var counterB = new CounterB(); var counterC = new CounterC(); var counterD = new CounterD(); // bad example function CounterA() { // CounterA 的 `this` 實例(!!這裡被忽略了) this.i = 0; setInterval(function () { // `this` 參考到全域的物件,而不是 CounterA's 的 `this`, // 因此,起始的計數是 100,而不是 0(CounterA 的 this.i)。 this.i++; document.getElementById("counterA").innerHTML = this.i; }, 500); } // 手動綁定 that = this function CounterB() { this.i = 0; var that = this; setInterval(function() { that.i++; document.getElementById("counterB").innerHTML = that.i; }, 500); } // 使用 .bind(this) function CounterC() { this.i = 0; setInterval(function() { this.i++; document.getElementById("counterC").innerHTML = this.i; }.bind(this), 500); } // 箭頭函式 function CounterD() { this.i = 0; setInterval(() => { this.i++; document.getElementById("counterD").innerHTML = this.i; }, 500); } ``` 更多關於箭頭函式的資訊你可以在 [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 找到。如果查看不同的語法選項拜訪[這個網站](http://jsrocks.org/2014/10/arrow-functions-and-their-scope/)。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md ================================================ --- layout: post title: 更簡單的方式將 indexOf 當作 contains 使用 tip-number: 15 tip-username: jhogoforbroke tip-username-profile: https://twitter.com/jhogoforbroke tip-tldr: JavaScript 預設沒有 contain 的方法。如果要檢查字串或是陣列內是否有子字串的存在你可以這樣做。 redirect_from: - /zh_tw/even-simpler-way-of-using-indexof-as-a-contains-clause/ categories: - zh_TW - javascript --- JavaScript 預設沒有 contain 的方法。如果要檢查字串或是陣列內是否有子字串的存在你可以這樣做: ```javascript var someText = 'javascript rules'; if (someText.indexOf('javascript') !== -1) { } // or if (someText.indexOf('javascript') >= 0) { } ``` 但是,讓我看一下這些 [Expressjs](https://github.com/strongloop/express) 程式碼片段。 [examples/mvc/lib/boot.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/mvc/lib/boot.js#L26) ```javascript for (var key in obj) { // "reserved" exports if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; ``` [lib/utils.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/lib/utils.js#L93) ```javascript exports.normalizeType = function(type){ return ~type.indexOf('/') ? acceptParams(type) : { value: mime.lookup(type), params: {} }; }; ``` [examples/web-service/index.js](https://github.com/strongloop/express/blob/2f8ac6726fa20ab5b4a05c112c886752868ac8ce/examples/web-service/index.js#L35) ```javascript // key 是無效的 if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key')); ``` 困難的地方是[位元運算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) **~**,「位元運算符在二進制執行它們的操作,但是它們回傳的是標準 JavaScript 數值」。 它將 `-1` 轉換成 `0`,而 `0` 在 JavaScript 當作 `false`: ```javascript var someText = 'text'; !!~someText.indexOf('tex'); // someText 包含 "tex" - true !~someText.indexOf('tex'); // someText 不包含 "tex" - false ~someText.indexOf('asd'); // someText 不包含 "asd" - false ~someText.indexOf('ext'); // someText 包含 "ext" - true ``` ### String.prototype.includes() ES6 提供了 [includes() 方法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes),你可以使用它來判斷字串是否包含在其他字串: ```javascript 'something'.includes('thing'); // true ``` 在 ECMAScript 2016(ES7)中,在陣列甚至有可能可以使用這些方法: ```javascript !!~[1, 2, 3].indexOf(1); // true [1, 2, 3].includes(1); // true ``` **不幸的是,這只支援在 Chrome、Firefox、Safari 9 或是更高版本的 Edge;在 IE11 或更低版本則無法使用。** **它最好在被控制的環境下使用。** ================================================ FILE: _posts/zh_TW/javascript/2016-01-16-passing-arguments-to-callback-functions.md ================================================ --- layout: post title: 將參數傳送到 callback 函式 tip-number: 16 tip-username: minhazav tip-username-profile: https://twitter.com/minhazav tip-tldr: 預設情況下,你不能傳送參數給 callback 函式,但是你可以利用 JavaScrip closure scope 的優點將參數傳給 callback 函式。 redirect_from: - /zh_tw/passing-arguments-to-callback-functions/ categories: - zh_TW - javascript --- 預設情況下,你不能傳送參數給 callback 函式。例如: ```js function callback() { console.log('Hi human'); } document.getElementById('someelem').addEventListener('click', callback); ``` 你可以利用 JavaScript 閉包(closure)scope 的優點傳送參數給 callback 函式。看一下這個範例: ```js function callback(a, b) { return function() { console.log('sum = ', (a+b)); } } var x = 1, y = 2; document.getElementById('someelem').addEventListener('click', callback(x, y)); ``` ### 什麼是閉包(closure)? Closures 指的是一個獨立的變數函式。換句話說,在閉包中定義的函式「記得」它被建立時的環境。在 [MDN 文件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)了解更多。 當 callback 函式被呼叫時,這些方法的參數 `x` 和 `y` 都會在 callback 函式的範圍內。 另一個方法你可以使用 `bind`。例如: ```js var alertText = function(text) { alert(text); }; document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello')); ``` 兩種方法上有細微的性能上的差別,請參考 [jsperf](http://jsperf.com/bind-vs-closure-23)。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md ================================================ --- layout: post title: Node.js - 執行尚未被 required 的模組 tip-number: 17 tip-username: odsdq tip-username-profile: https://twitter.com/odsdq tip-tldr: 在 nodejs,你可以讓你的程式取決於 `require('./something.js')` 或 `node something.js` 這兩種不同的方式來執行你的程式碼。如果你想要將你的獨立模組交互使用是非常有用的。 redirect_from: - /zh_tw/nodejs-run-a-module-if-it-is-not-required/ categories: - zh_TW - javascript --- 在 nodejs,你可以讓你的程式取決於 `require('./something.js')` 或 `node something.js` 這兩種不同的方式來執行你的程式碼。如果你想要將你的獨立模組交互使用是非常有用的。 ```js if (!module.parent) { // 透過 `node something.js` 執行 app.listen(8088, function() { console.log('app listening on port 8088'); }) } else { // 使用 `require('/.something.js')` 方法 module.exports = app; } ``` 更多資訊請參考 [nodejs modules 官方文件](https://nodejs.org/api/modules.html#modules_module_parent)。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-18-rounding-the-fast-way.md ================================================ --- layout: post title: Truncating 最快的方式(含有風險) tip-number: 18 tip-username: pklinger tip-username-profile: https://github.com/pklinger tip-tldr: 通常 `~~X` 速度比 `Math.trunc(X)` 快,但是會造成你的程式碼變得很混亂。 redirect_from: - /zh_tw/rounding-the-fast-way/ categories: - zh_TW - javascript --- 今天的 tip 是關於性能的部分。 [曾經使用過雙波浪](http://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript) `~~` 運算符嗎?有時候也被稱為 雙 NOT 位元運算符。你可以使用它來替代 `Math.trunc()` 更為快速。這是為什麼呢? 位元移位運算符 `~` 將輸入的 32 位元轉換成 `-(input + 1)`。因此,雙位元移位運算成為更好的工具,將輸入轉換成 `-(-(input + 1) + 1)` 更趨近 0。對於數字輸入,它類似 `Math.trunc()`。若失敗的話,則回傳 `0`,這或許是解決 `Math.trunc()` 失敗時回傳 `NaN` 的替代方法。 ```js // 單 ~ console.log(~1337) // -1338 // 數字輸入 console.log(~~47.11) // -> 47 console.log(~~1.9999) // -> 1 console.log(~~3) // -> 3 ``` 雖然 ~~ 可以讓性能更好,但是為了增加程式碼可讀性,請使用 Math.trunc()。如果要了解為什麼,這裡有一些關於 `~~` 運算子的分析。 ### 適用情況 ##### 每個 CPU 的週期次數 `~~` 整體來說或許比 `Math.trunc()` 來的快,這種[測試的假設](https://jsperf.com/jsfvsbitnot/10)在哪個平台下是很重要的。此外,你通常必須執行數以百萬計的這種操作才可以看到明顯的影響。 ##### 當程式碼的清晰度不是這麼重要時 如果你想要 confuse 他人,或者從你的 minifier/uglifier 取得最大的效用,這是一個相對廉價的方式。 ### 禁止的情況 ##### 當你的程式碼需要維護時 程式碼的清晰是一直以來都是相當重要的,無論你是不是和一個團隊一起工作,或是對公開的 repo 做 contribute。正如[俗話常說的](http://c2.com/cgi/wiki?CodeForTheMaintainer): > Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. ##### 當你忘記 `~~` 總是趨近於零時 或許新手開發者更關注在 `~~` 優秀之處,卻忘記了「只去掉小數點」的重要性。當我們將浮點數轉換成陣列索引,或是相關順序值,這容易導致 **fencepost 錯誤**(a.k.a 「off-by-one」),實際上可能需要不同類型的分數做四捨五入(程式碼缺乏清晰度很容易造成這個問題。) 舉個例子,如果你基於在「最靠近整數」的數字上做計算,你應該使用 `Math.round()` 而不是 `~~`,但因為開發者的懶惰往往勝過於冰冷的邏輯,而導致不正確的結果。 相反的,許多名稱類似 `Math.xyz()` 的函式反而清楚表達他們的功用,減少了錯誤的可能性。 ##### 當處理大量的位元數時 因為 `~` 首先是進行 32 位元的轉換,`~~` 範圍值的結果在 ±2.15 億位元左右。如果你沒有明確的檢查你的輸入範圍,當轉換後的值和原始值有很大的差距時,使用者可能會觸發 unexpected 的行為: ```js a = 2147483647.123 // 最大 32 位正整數,再多一點 console.log(~~a) // -> 2147483647 (ok) a += 10000 // -> 2147493647.123 (ok) console.log(~~a) // -> -2147483648 (huh?) ``` 一個容易出現問題的地方是在處理 Unix 時間戳記的地方(以秒為單位,從 1970 年 1 月 1 日 00:00:00 UTC)。一個快速取得這個值的方式是: ```js epoch_int = ~~(+new Date() / 1000) // Date() 以毫秒為單位 ``` 然而,當我們處理在 2038 年 1 月 19 03:14:07 UTC(有時候稱為 **Y2038 limit**)之後的時間戳記,發生可怕的事: ```js // 2040 年 1 月 1 日 00:00:00.123 UTC 的時間戳記 epoch = +new Date('2040-01-01') / 1000 + 0.123 // -> 2208988800.123 // 回到未來! epoch_int = ~~epoch // -> -2085978496 console.log(new Date(epoch_int * 1000)) // -> Wed Nov 25 1903 17:31:44 UTC // 這很有趣,讓我們取得正確的答案 epoch_flr = Math.floor(epoch) // -> 2208988800 console.log(new Date(epoch_flr * 1000)) // -> Sun Jan 01 2040 00:00:00 UTC ``` ##### 當原始輸入值沒經過處理 因為 `~~` 將每個非數字轉換成 `0`: ```js console.log(~~[]) // -> 0 console.log(~~NaN) // -> 0 console.log(~~null) // -> 0 ``` 有些開發者把它作為替代輸入驗證。然而,這可能會導致奇怪的邏輯錯誤,你沒辦法區別他們之間誰是無效的輸入,或者是 `0`。因此這_不是_推薦的做法。 ##### 當許多人認為 `~~X == Math.floor(X)` 時 許多人因為錯誤的原因以為「雙位元 NOT」等同於 `Math.floor()`。如果你沒辦法準確的使用它,最終你可能會濫用它。 有些人很細心地提到 `Math.floor()` 是用在正數輸入,而 `Math.ceil()` 是用在負數輸入。但是,這又會強制讓你停下並思考關於你該如何處理數值。這違背了使用 `~~` 做為方便無害的目的。 ### 結論 避免在可能的情況。否則請謹慎使用。 ### 管理 1. 謹慎使用。 2. 在應用前請先處理數值。 3. 仔細記錄關於數值被轉換的相關假設。 4. 至少檢查程式碼並處理: * 不正確輸入反而傳送給其他程式碼模組作為有效的 `0` 的邏輯 Bug。 * 在轉換輸入的範圍錯誤。 * 因為不正確的四捨五入方向,造成 fencepost 錯誤。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-19-safe-string-concatenation.md ================================================ --- layout: post title: 安全的使用字串串接 tip-number: 19 tip-username: gogainda tip-username-profile: https://twitter.com/gogainda tip-tldr: 假設你有一些不確定的變數類型,而你想將它們串接成字串。可以確定的是,算術運算不是應用在串接的地方,使用 concat 來串接。 redirect_from: - /zh_tw/safe-string-concatenation/ categories: - zh_TW - javascript --- 假設你有一些不確定的變數類型,而你想將它們串接成字串。可以肯定的是,算術運算符不是在串接過程的運用,請使用 `concat`: ```javascript var one = 1; var two = 2; var three = '3'; var result = ''.concat(one, two, three); // "123" ``` 這個方法串接結果是你預期的。相反的,透過加號來串接會造成非預期的結果: ```javascript var one = 1; var two = 2; var three = '3'; var result = one + two + three; // "33" 而不是 "123" ``` 關於性能部分,與 `join` [類型](http://www.sitepoint.com/javascript-fast-string-concatenation/)比較,串接速度和 `concat` 是差不多的。 你可以在 MDN [網頁](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat)上閱讀到更多關於 `concat` 函式的資訊。 ================================================ FILE: _posts/zh_TW/javascript/2016-01-20-return-objects-to-enable-chaining-of-functions.md ================================================ --- layout: post title: 回傳物件並使用函式鏈結 tip-number: 20 tip-username: WakeskaterX tip-username-profile: https://twitter.com/WakeStudio tip-tldr: 在 JavaScript 物件導向中,我們在物件上建立函式,函式回傳的物件讓你可以將函式鏈結在一起。 redirect_from: - /zh_tw/return-objects-to-enable-chaining-of-functions/ categories: - zh_TW - javascript --- 在 JavaScript 物件導向中,我們在物件上建立函式,函式回傳的物件讓你可以將函式鏈結在一起。 ```js function Person(name) { this.name = name; this.sayName = function() { console.log("Hello my name is: ", this.name); return this; }; this.changeName = function(name) { this.name = name; return this; }; } var person = new Person("John"); person.sayName().changeName("Timmy").sayName(); ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-21-shuffle-an-array.md ================================================ --- layout: post title: 將陣列洗牌 tip-number: 21 tip-username: 0xmtn tip-username-profile: https://github.com/0xmtn/ tip-tldr: Fisher-Yates Shuffling 是一個將陣列洗牌的演算法。 redirect_from: - /zh_tw/shuffle-an-array/ categories: - zh_TW - javascript --- 這段程式碼使用了 [Fisher-Yates Shuffling](https://www.wikiwand.com/en/Fisher%E2%80%93Yates_shuffle) 演算法將給定的陣列洗牌。 ```javascript function shuffle(arr) { var i, j, temp; for (i = arr.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; }; ``` 範例: ```javascript var a = [1, 2, 3, 4, 5, 6, 7, 8]; var b = shuffle(a); console.log(b); // [2, 7, 8, 6, 5, 3, 1, 4] ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-22-two-ways-to-empty-an-array.md ================================================ --- layout: post title: 將陣列清空的兩種方法 tip-number: 22 tip-username: microlv tip-username-profile: https://github.com/microlv tip-tldr: 在 JavaScript 中當你相要清空陣列,有許多方法,但這是最具效能的方法。 redirect_from: - /zh_tw/two-ways-to-empty-an-array/ categories: - zh_TW - javascript --- 你定義了一個陣列然後你想清空陣列內的內容。 通常你會這麼做: ```javascript // 定義陣列 var list = [1, 2, 3, 4]; function empty() { // 清空陣列 list = []; } empty(); ``` 但是這裡有另一個更能具效能的清空陣列的方式。 你應該像這樣使用程式碼: ```javascript var list = [1, 2, 3, 4]; function empty() { // 清空陣列 list.length = 0; } empty(); ``` * `list = []` 將分配一個新的參考陣列給變數,而其他的參考則不受影響。 這個意思是說,前面參考的陣列內容還存在記憶體中,導致記憶體洩漏。 * `list.length = 0` 刪除陣列內所有的內容,也會影響到其他的參考。 換句話說,假設你有兩個變數且參考到同一個陣列(`a = [1,2,3]; a2 = a;`),而你使用 `list.length = 0` 刪除陣列的內容,兩個參考(a 和 a2)將指向相同的空陣列。(如果你不想讓 a2 內容變成空的陣列,請不要使用這個方法!) 思考一下這些會輸出什麼: ```js var foo = [1,2,3]; var bar = [1,2,3]; var foo2 = foo; var bar2 = bar; foo = []; bar.length = 0; console.log(foo, bar, foo2, bar2); // [] [] [1, 2, 3] [] ``` 在 Stackoverflow 了解更多的細節: [difference-between-array-length-0-and-array](http://stackoverflow.com/questions/4804235/difference-between-array-length-0-and-array) ================================================ FILE: _posts/zh_TW/javascript/2016-01-23-converting-to-number-fast-way.md ================================================ --- layout: post title: 轉換為數字更快的方式 tip-number: 23 tip-username: sonnyt tip-username-profile: http://twitter.com/sonnyt tip-tldr: 轉換字串為數字是相當常見的。最簡單和快速的方式是使用 + 運算子來實現。 redirect_from: - /zh_tw/converting-to-number-fast-way/ categories: - zh_TW - javascript --- 轉換字串為數字是相當常見的。最簡單和快速的方式是使用 `+` (加號)運算子來實現。([jsPerf](https://jsperf.com/number-vs-parseint-vs-plus/29)) ```javascript var one = '1'; var numberOne = +one; // Number 1 ``` 你也可以使用 `-` (減號)運算子將數字類型轉換成負數。 ```javascript var one = '1'; var negativeNumberOne = -one; // Number -1 ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-24-use_===_instead_of_==.md ================================================ --- layout: post title: 使用 === 來替代 == tip-number: 24 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: ==(或 !=)運算子如果在執行上需要的話,會自動將類型轉換。 `===`(或 `!==`)運算子則不會執行轉換。`===`(或 `!==`)用來比較數值和類型,相較於 `==` 更來的快速。 redirect_from: - /zh_tw/use_===_instead_of_==/ categories: - zh_TW - javascript --- `==`(或 `!=`)運算子如果在執行上需要的話,會自動將類型轉換。 `===`(或 `!==`)運算子則不會執行轉換。`===`(或 `!==`)用來比較數值和類型,相較於 `==` 更來的快速。([jsPref](http://jsperf.com/strictcompare)) ```js [10] == 10 // is true [10] === 10 // is false '10' == 10 // is true '10' === 10 // is false [] == 0 // is true [] === 0 // is false '' == false // is true but true == "a" is false '' === false // is false ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-25-Using-immediately-invoked-function-expression.md ================================================ --- layout: post title: 使用立即函式表達式 tip-number: 25 tip-username: rishantagarwal tip-username-profile: https://github.com/rishantagarwal tip-tldr: 稱作「Iffy」(IIFE - immediately invoked function expression)是一個匿名函式表達式,而且可以立即被調用,在 JavaScript 中有一些相當重要的用途。 redirect_from: - /zh_tw/Using-immediately-invoked-function-expression/ categories: - zh_TW - javascript --- 稱作「Iffy」(IIFE - immediately invoked function expression)是一個匿名函式表達式,而且可以立即被調用,在 JavaScript 中有一些相當重要的用途。 ```javascript (function() { // Do something​ } )() ``` 它是一個匿名函式表達式,而且可以立即被調用,在 JavaScript 某些部分中相當重要。 一對括號包著匿名函式,將匿名函式變成函式表達式或變數表達式。我們現在有一個未命名的函式表達式,它不是一個在全域 scope 內的簡單匿名函式或者是其他任何被定義的函式。 同樣的,我們也可以為立即函式表達式命名: ```javascript (someNamedFunction = function(msg) { console.log(msg || "Nothing for today !!") })(); // 輸出 --> Nothing for today !!​ ​ someNamedFunction("Javascript rocks !!"); // 輸出 --> Javascript rocks !! someNamedFunction(); // 輸出 --> Nothing for today !!​ ``` 更多細節,請參考以下網址 - 1. [連結一](https://blog.mariusschulz.com/2016/01/13/disassembling-javascripts-iife-syntax) 2. [連結二](http://javascriptissexy.com/12-simple-yet-powerful-javascript-tips/) 效能: [jsPerf](http://jsperf.com/iife-with-call) ================================================ FILE: _posts/zh_TW/javascript/2016-01-26-filtering-and-sorting-a-list-of-strings.md ================================================ --- layout: post title: 過濾和排序字串清單 tip-number: 26 tip-username: davegomez tip-username-profile: https://github.com/davegomez tip-tldr: 你可能有一份很大的清單,而你需要過濾重複的內容並移除,再依字母排序。 redirect_from: - /zh_tw/filtering-and-sorting-a-list-of-strings/ categories: - zh_TW - javascript --- 你可能有一份很大的清單,而你需要過濾重複的內容並移除,再依字母排序。 在我們的範例,我使用了不同版本的 **JavaScript 保留字元** 的清單,但你要注意到,這裡有一些重複的字元而且他們不是依字母排序的。所以,這裡有一個相當棒的字串[陣列](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)清單來測試這個 JavaScript tip。 ```js var keywords = ['do', 'if', 'in', 'for', 'new', 'try', 'var', 'case', 'else', 'enum', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'delete', 'export', 'import', 'return', 'switch', 'typeof', 'default', 'extends', 'finally', 'continue', 'debugger', 'function', 'do', 'if', 'in', 'for', 'int', 'new', 'try', 'var', 'byte', 'case', 'char', 'else', 'enum', 'goto', 'long', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'final', 'float', 'short', 'super', 'throw', 'while', 'delete', 'double', 'export', 'import', 'native', 'public', 'return', 'static', 'switch', 'throws', 'typeof', 'boolean', 'default', 'extends', 'finally', 'package', 'private', 'abstract', 'continue', 'debugger', 'function', 'volatile', 'interface', 'protected', 'transient', 'implements', 'instanceof', 'synchronized', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof']; ``` 因為我們不想改變我們原有的清單,所以我們使用高階函式叫做 [`filter`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),它會基於我們傳送的陣列(*函式*)並回傳一個過濾後的新陣列。基於目前的清單和新的清單索引 `index` 的比較,會把和 index 相符的元素 push 到新陣列成為新的清單。 最後,我們使用 [`sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 函式來排序過濾後的結果,將比較函式當作唯一的參數,回傳依字母排序後的清單。 ```js var filteredAndSortedKeywords = keywords .filter(function (keyword, index) { return keywords.indexOf(keyword) === index; }) .sort(function (a, b) { if (a < b) return -1; else if (a > b) return 1; return 0; }); ``` 使用 **ES6**(ECMAScript 2015)版本的[箭頭函式](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions)看起來更簡潔: ```js const filteredAndSortedKeywords = keywords .filter((keyword, index) => keywords.indexOf(keyword) === index) .sort((a, b) => { if (a < b) return -1; else if (a > b) return 1; return 0; }); ``` 這是最後過濾以及排序後的 JavaScript 保留字元清單: ```js console.log(filteredAndSortedKeywords); // ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield'] ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-27-short-circiut-evaluation-in-js.md ================================================ --- layout: post title: JavaScript 中的捷徑計算 tip-number: 27 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: 捷徑計算意思是說,假設第一個參數不足以確定表達式的值,第二個參數才會被執行,當 AND 函數的第一個參數計算結果為 false,則所有結果則為 false;當 OR 函數的第一個參數計算結果為 true,則所有結果為 true。 redirect_from: - /zh_tw/short-circiut-evaluation-in-js/ categories: - zh_TW - javascript --- [捷徑計算](https://en.wikipedia.org/wiki/Short-circuit_evaluation)意思是說,假設第一個參數不足以確定表達式的值,第二個參數才會被執行,當 AND 函數的第一個參數計算結果為 false,則所有結果則為 false;當 OR 函數的第一個參數計算結果為 true,則所有結果為 true。 對於以下的 `test` 條件和 `isTrue` 以及 `isFalse` 函式。 ```js var test = true; var isTrue = function(){ console.log('Test is true.'); }; var isFalse = function(){ console.log('Test is false.'); }; ``` 使用 AND 邏輯 - `&&`。 ```js // 一個正常的 if 條件式 if (test){ isTrue(); // Test 是 true } // 上面可以使用 '&&' 作為 - ( test && isTrue() ); // Test 是 true ``` 使用 OR 邏輯 - `||`。 ```js test = false; if (!test){ isFalse(); // Test 是 false. } ( test || isFalse()); // Test 是 false. ``` OR 邏輯可以用在為函式參數設定預設值。 ```js function theSameOldFoo(name){ name = name || 'Bar' ; console.log("My best friend's name is " + name); } theSameOldFoo(); // My best friend's name is Bar theSameOldFoo('Bhaskar'); // My best friend's name is Bhaskar ``` 當屬性尚未被定義的時候,邏輯 AND 可以避免異常情況。 例如: ```js var dog = { bark: function(){ console.log('Woof Woof'); } }; // 呼叫 dog.bark(); dog.bark(); // Woof Woof. // 但是如果 dog 尚未被定義,dog.bark() 將會發生「無法讀取尚未定義的屬性 'bark'」的錯誤。 // 為了防止這個問題,我們使用 &&。 dog && dog.bark(); // 如果 dog 被定義了,那我們就可以呼叫 dog.bark()。 ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-28-curry-vs-partial-application.md ================================================ --- layout: post title: 柯里化(currying)和部分應用程式 tip-number: 28 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: 柯里化(currying)和部分應用程式(partial application)是將函式轉換成一般較小的 arity 和另一個函式的兩種方式。 redirect_from: - /zh_tw/curry-vs-partial-application/ categories: - zh_TW - javascript --- **柯里化** 柯里化是將一個函式 `f: X * Y -> R` 轉換成 `f': X -> (Y -> R)` 而不是帶有兩個參數的 `f'`,我們調用 `f'` 與第一個參數。其結果是一個函式,我們可以呼叫第二個參數來產生結果。 因此,未使用柯里化的函式 `f` 像這樣調用: `f(3, 5)` 如果使用柯里化後的函式 `f'` 像這樣調用: `f(3)(5)` 例如: 尚未柯里化的函式 `add()` ```javascript function add(x, y) { return x + y; } add(3, 5); // returns 8 ``` 柯里化後的函式 `add()` ```javascript function addC(x) { return function (y) { return x + y; } } addC(3)(5); // returns 8 ``` **柯里化的規則** 柯里化將一個二元函式轉換成一個一元函式,這個一元函式再回傳一個一元函式。 柯里化:`(X × Y → R) → (X → (Y → R))` JavaScript 程式碼: ```javascript function curry(f) { return function(x) { return function(y) { return f(x, y); } } } ``` **部分應用程式** 部分應用程式將一個函式的 f:`X * Y -> R` 第一個參數固定來產生新的函式 f':`Y -> R` `f'` 和 `f` 不同,只需要填寫第二個參數,這也是為什麼 `f'` 比 `f` 少一個參數的原因。 例如:綁定函式的第一個參數來產生函式 `plus5`。 ```javascript function plus5(y) { return 5 + y; } plus5(3); // returns 8 ``` **部分應用程式的規則** 部分應用程式將二元函式和數值產生一個一元函式。 部分應用程式: `((X × Y → R) × X) → (Y → R)` Javascript 程式碼: ```javascript function partApply(f, x) { return function(y) { return f(x, y); } } ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-29-speed-up-recursive-functions-with-memoization.md ================================================ --- layout: post title: 使用 memoization 加速遞迴 tip-number: 29 tip-username: hingsir tip-username-profile: https://github.com/hingsir tip-tldr: 大家對費式(Fibonacci)數列都很熟悉。我們可以在 20 秒內寫出以下的函式。它可以執行,但是效率不高。它做了大量的重複計算,我們可以快取先前的計算結果來加快計算速度。 redirect_from: - /zh_tw/speed-up-recursive-functions-with-memoization/ categories: - zh_TW - javascript --- 大家對費式(Fibonacci)數列都很熟悉。我們可以在 20 秒內寫出以下的函式。 ```js var fibonacci = function(n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); } ``` 它可以執行,但是效率不高。它做了大量的重複計算,我們可以快取先前的計算結果來加快計算速度。 ```js const fibonacci = (function() { let cache = [0, 1]; // cache the value at the n index return function(n) { if (cache[n] === undefined) { for (let i = cache.length; i <= n; ++i) { cache[i] = cache[i - 1] + cache[i - 2]; } } return cache[n]; } })() ``` 也許,我們可以定義高階函式,來接受一個函式作為參數,並回傳一個函式回傳的暫存值。 ```js const memoize = function(func) { let cache = {}; return function() { const key = JSON.stringify(Array.prototype.slice.call(arguments)); return key in cache ? cache[key] : (cache[key] = func.apply(this, arguments)); } } fibonacci = memoize(fibonacci); ``` 這裡是 ES6 版本的 memoize 函式。 ```js const memoize = function(func) { const cache = {}; return (...args) => { const key = JSON.stringify(args) return key in cache ? cache[key] : (cache[key] = func(...args)); } } fibonacci = memoize(fibonacci); ``` 我們可以將 `memoize()` 使用在其他地方 * GCD(最大公因數) ```js const gcd = memoize(function(a, b) { let t; if (a < b) t = b, b = a, a = t; while (b != 0) t = b, b = a % b, a = t; return a; }); gcd(27, 183); //=> 3 ``` * 階乘計算 ```js var factorial = memoize(function(n) { return (n <= 1) ? 1 : n * factorial(n - 1); }) factorial(5); //=> 120 ``` 學習更多關於 memoization: - [Memoization - Wikipedia](https://en.wikipedia.org/wiki/Memoization) - [Implementing Memoization in JavaScript](https://www.sitepoint.com/implementing-memoization-in-javascript/) ================================================ FILE: _posts/zh_TW/javascript/2016-01-30-converting-truthy-falsy-values-to-boolean.md ================================================ --- layout: post title: 將 truthy 和 falsy 轉換成布林值 tip-number: 30 tip-username: hakhag tip-username-profile: https://github.com/hakhag tip-tldr: 邏輯運算子是 JavaScript 核心之一 ,在這裡你可以發現這個方式,不管你給他什麼值,都會得到 true 或 false 。 redirect_from: - /zh_tw/converting-truthy-falsy-values-to-boolean/ categories: - zh_TW - javascript --- 你可以使用 `!!` 運算子將 [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) 或 [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) 轉換成布林值。 ```js !!"" // false !!0 // false !!null // false !!undefined // false !!NaN // false !!"hello" // true !!1 // true !!{} // true !![] // true ``` ================================================ FILE: _posts/zh_TW/javascript/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions—it-kills-optimization.md ================================================ --- layout: post title: 避免修改或傳送 arguments 到其他函式 - 它會影響優化 tip-number: 31 tip-username: berkana tip-username-profile: https://github.com/berkana tip-tldr: 在 JavaScript 的函式,變數名稱 `arguments` 讓你可以存取所有傳送到函式的參數。`arguments` 是一個 *類陣列物件*;`arguments` 可以使用陣列表示來存取,而且它有 *length* 屬性,但不具備陣列的 `filter` 和 `map` 以及 `forEach` 的方法。以下程式碼是轉換 `arguments` 到陣列相當普遍的做法。 redirect_from: - /zh_tw/avoid-modifying-or-passing-arguments-into-other-functions-it-kills-optimization/ categories: - zh_TW - javascript --- ### 背景 在 JavaScript 的函式,變數名稱 [`arguments`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) 讓你可以存取所有傳送到函式的參數。`arguments` 是一個 *類陣列物件*;`arguments` 可以使用陣列表示來存取,而且它有 *length* 屬性,但不具備陣列的 `filter` 和 `map` 以及 `forEach` 的方法。以下程式碼是轉換 `arguments` 到陣列相當普遍的做法。 ```js var args = Array.prototype.slice.call(arguments); ``` 將 `arguments` 傳送到 `Array` 原型的上的 `slice` 方法;`slice` 方法回傳淺拷貝的 `arguments` 當作新的陣列物件。一般常見的簡寫方法: ```js var args = [].slice.call(arguments); ``` 在這個情況下,只是呼叫一個空陣列陣列,而不是從 `Array` 原型上呼叫 `slice` 方法。 ### 優化 不幸的是,傳送到任何函式呼叫的 `arguments`,造成在 Chrome 和 Node 跳過 JavaScript V8 引擎的優化功能,這可能會導致性能降低。參考這篇 [optimization killers](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) 文章。傳送 `arguments` 到函式稱為 *leaking `arguments`*。 相反的,假設你需要包含參數的陣列,你可以藉助這個方法: ```js var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } ``` 雖然程式碼更冗長,但是在上線環境增進了效能的優化,所以是值得的。 ================================================ FILE: _posts/zh_TW/javascript/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md ================================================ --- layout: post title: 透過 Map() 在你的物件屬性加入排序 tip-number: 32 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 物件是一個沒有排序的屬性集合...意思說,如果你想嘗試在你的物件儲存已排序的資料,你需要重新檢查,因為屬性在物件不保證是有排序的。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/map-to-the-rescue-adding-order-to-object-properties/ categories: - zh_TW - javascript --- ## 物件屬性的順序 > 一個物件是一個 `Object` 的類型。它是包含原始值、物件、或是函式這些尚未排序屬性的集合。一個物件的屬性儲存了一個函式稱為方法。 [ECMAScript](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf) 實際看一下範例 ```js var myObject = { z: 1, '@': 2, b: 3, 1: 4, 5: 5 }; console.log(myObject) // Object {1: 4, 5: 5, z: 1, @: 2, b: 3} for (item in myObject) {... // 1 // 5 // z // @ // b ``` 因為每個瀏覽器有自己排序物件的規則,所以順序是不確定的。 ## 要如何解決這個問題? ### Map 使用 ES6 的新特性 - Map。[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 物件迭代元素插入的順序。`for...of` 迴圈每次迭代回傳一個 [key, value] 的陣列。 ```js var myObject = new Map(); myObject.set('z', 1); myObject.set('@', 2); myObject.set('b', 3); for (var [key, value] of myObject) { console.log(key, value); ... // z 1 // @ 2 // b 3 ``` ### 對舊的瀏覽器 Hack Mozilla 建議: > 所以,如果你想要在跨瀏覽器的環境模擬一個有排序的關聯陣列,你要麼強制使用兩個分離的陣列(其中一個給 keys,另一個給 values),或者建立一個單一屬性的物件陣列,等等。 ```js // 使用兩個分離的陣列 var objectKeys = [z, @, b, 1, 5]; for (item in objectKeys) { myObject[item] ... // 建立一個單一屬性的物件陣列 var myData = [{z: 1}, {'@': 2}, {b: 3}, {1: 4}, {5: 5}]; ``` ================================================ FILE: _posts/zh_TW/javascript/2016-02-02-create-range-0-n-easily-using-one-line.md ================================================ --- layout: post title: 使用一行程式碼建立一個 `[0, 1, ..., N - 1]` 的陣列 tip-number: 33 tip-username: SarjuHansaliya tip-username-profile: https://github.com/SarjuHansaliya tip-tldr: 使用一行程式碼,來產生有順序性的陣列。 redirect_from: - /zh_tw/create-range-0-n-easily-using-one-line/ categories: - zh_TW - javascript --- 這裡有兩種 compact 程式碼序列方式,來產生一個 `[0, 1, ..., N-1]` 的 `N` 個元素的陣列: ### 方法一(ES5) ```js Array.apply(null, {length: N}).map(Function.call, Number); ``` #### 簡要說明 1. `Array.apply(null, {length: N)` 回傳一個 `N` 個元素的陣列,裡面陣列元素都是為 `undefined`(i.e. `A = [undefined, undefined, ...]`)。 2. `A.map(Function.call, Number)` 回傳一個 `N` 個元素的陣列,索引 `I` 從 `Function.call.call(Number, undefined, I, A)` 取得結果。 3. `Function.call.call(Number, undefined, I, A)` 轉變成 `Number(I)`,這剛好就是 `I`。 4. 結果:`[0, 1, ..., N-1]`。 更深入的解釋,請前往[這裡](https://github.com/gromgit/jstips-xe/blob/master/tips/33.md)。 ### 方法二(ES6) ```js Array.from(new Array(N), (val, index) => index); ``` #### 簡要說明 1. `A = new Array(N)` 回傳一個有 `N` 個 _holes_ 的陣列(i.e. `A = [,,,...]`,但是 `x` 在 `0...N-1` 時 `A[x] = undefined`)。 2. `F = (val, index) => index` 等同於 `function F (val, index) { return index; }`。 3. `Array.from(A, F)` 回傳一個 `N` 個元素的陣列,索引 `I` 取得 `F(A[I], I)` 的結果,也就是 `I`。 4. 結果:`[0, 1, ..., N-1]`。 ### 還有一件事情 如果你真的想要排序 [1, 2, ..., N],**方法一**改為: ```js Array.apply(null, {length: N}).map(function(value, index){ return index + 1; }); ``` 和 **方法二**: ```js Array.from(new Array(N), (val, index) => index + 1); ``` ================================================ FILE: _posts/zh_TW/javascript/2016-02-03-implementing-asynchronous-loops.md ================================================ --- layout: post title: 實作非同步迴圈 tip-number: 34 tip-username: madmantalking tip-username-profile: https://github.com/madmantalking tip-tldr: 你或許可以實作非同步迴圈,但是執行時可能會遇到問題。 redirect_from: - /zh_tw/implementing-asynchronous-loops/ categories: - zh_TW - javascript --- 讓我們嘗試撰寫一個非同步的函式,在每秒列印出迴圈的索引值。 ```js for (var i = 0; i < 5; i++) { setTimeout(function(){ console.log(i); }, 1000 * (i + 1)); } ``` 上面的程式將會輸出以下的結果: ```js > 5 > 5 > 5 > 5 > 5 ``` 所以這肯定是不能執行的。 **原因** 每個 timeout 指向到原來的 `i`,而非拷貝的。所以在迴圈下 `i` 會增加直到 5,然後 timeout 執行並使用目前的 `i` 數值(i 是 5)。 當然,這個問題看似簡單。一個直接的解決方法就是將迴圈的索引暫存到變數。 ```js for (var i = 0; i < 5; i++) { var temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i + 1)); } ``` 但以上的程式輸出的結果是: ```js > 4 > 4 > 4 > 4 > 4 ``` 所以,一樣不能執行,因為區塊初始化時沒有建立一個範圍和變數,把它們提升到 scope 的頂部。事實上,在前面的程式碼也是相同的: ```js var temp; for (var i = 0; i < 5; i++) { temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i + 1)); } ``` **解決辦法** 這裡有一些不同的方式來複製 `i`。一般的方式是建立一個 closure,宣告一個函式並將 `i` 作為一個參數傳送。在這裡我們做了一個立即函式。 ```js for (var i = 0; i < 5; i++) { (function(num){ setTimeout(function(){ console.log(num); }, 1000 * (i + 1)); })(i); } ``` 在 JavaScript,參數是透過傳值方式給函數。所以原始的類型像是數字、日期、和字串基本上都是被複製的。如果你想要再函式內改變他們,它是不會影響外部的範圍。物件比較特別:假設內部函數改變屬性,這個改變會影響所有範圍。 其他解決方法可以使用 `let`。它是 `ES6` 其中一種變數的宣告方式,它和 `var` 不一樣,只在區塊內作用。 ```js for (let i = 0; i < 5; i++) { var temp = i; setTimeout(function(){ console.log(temp); }, 1000 * (i + 1)); } ``` ================================================ FILE: _posts/zh_TW/javascript/2016-02-04-assignment-shorthands.md ================================================ --- layout: post title: 賦值運算子 tip-number: 35 tip-username: hsleonis tip-username-profile: https://github.com/hsleonis tip-tldr: 賦值是相當常見的。有時候我們這些「懶惰的開發者」覺得打字是很耗時的。所以我們使用一些技巧來幫助我們讓程式碼更乾淨簡單。 redirect_from: - /zh_tw/assignment-shorthands/ categories: - zh_TW - javascript --- 賦值是相當常見的。有時候我們這些「懶惰的開發者」覺得打字是很耗時的。所以我們使用一些技巧來幫助我們讓程式碼更乾淨簡單。 這是類似的方法 ````javascript x += 23; // x = x + 23; y -= 15; // y = y - 15; z *= 10; // z = z * 10; k /= 7; // k = k / 7; p %= 3; // p = p % 3; d **= 2; // d = d ** 2; m >>= 2; // m = m >> 2; n <<= 2; // n = n << 2; n ++; // n = n + 1; n --; n = n - 1; ```` ### `++` 和 `--` 運算子 這是一個特殊的 `++` 運算子。透過範例來做一個更好的解釋: ````javascript var a = 2; var b = a++; // 現在 a 是 3 然後 b 是 2 ```` `a++` 執行以下的操作: 1. 回傳 `a` 的數值 2. 將 `a` 加 1 如果我們想要先把數值加一呢?很簡單的: ````javascript var a = 2; var b = ++a; // 現在 a 和 b 都是 3 ```` 看見了嗎?我把運算子放在變數_之前_。 `--` 運算子也是類似的,用來遞減數值。 ### If-else (使用三元運算符) 這是我們一般常見的寫法: ````javascript var newValue; if (value > 10) newValue = 5; else newValue = 2; ```` 我們可以使用三元運算符看起來更棒: ````javascript var newValue = (value > 10) ? 5 : 2; ```` ### Null、Undefined、Empty 確認 ````javascript if (variable1 !== null || variable1 !== undefined || variable1 !== '') { var variable2 = variable1; } ```` 簡化後: ````javascript var variable2 = variable1 || ''; ```` P.S.:假設 variable1 是數字,首先會檢查是否為 0 。 ### 物件陣列表示 不是使用: ````javascript var a = new Array(); a[0] = "myString1"; a[1] = "myString2"; ```` 而是這樣使用: ````javascript var a = ["myString1", "myString2"]; ```` ### 關聯陣列 不是使用: ````javascript var skillSet = new Array(); skillSet['Document language'] = 'HTML5'; skillSet['Styling language'] = 'CSS3'; ```` 而是這樣使用: ````javascript var skillSet = { 'Document language' : 'HTML5', 'Styling language' : 'CSS3' }; ```` ================================================ FILE: _posts/zh_TW/javascript/2016-02-05-observe-dom-changes.md ================================================ --- layout: post title: 觀察在擴充中 DOM 的變化 tip-number: 36 tip-username: beyondns tip-username-profile: https://github.com/beyondns tip-tldr: 由於現代動態的 JavaScript,當你在現有的網站開發擴充時,操作 DOM 並不容易。 redirect_from: - /zh_tw/observe-dom-changes/ categories: - zh_TW - javascript --- [MutationObserver](https://developer.mozilla.org/en/docs/Web/API/MutationObserver) 是一個 listen DOM 的改變以及元素變化時的解決方法。在下面的範例是使用計時器模擬了內容動態載入,在第一個元素「target」建立後,接著建立「subTarget」。 在擴充的程式碼中,首先 `rootObserver` 執行直到 `targetElement` 出現,然後 `elementObserver` 才開始執行。這個級聯觀察有助於直到最後發現 `subTargetElement`。 這個在開發擴充複雜的網站與動態內容載入時相當有用。 ```js const observeConfig = { attributes: true, childList: true, characterData: true, subtree: true }; function initExtension(rootElement, targetSelector, subTargetSelector) { var rootObserver = new MutationObserver(function(mutations) { console.log("Inside root observer"); targetElement = rootElement.querySelector(targetSelector); if (targetElement) { rootObserver.disconnect(); var elementObserver = new MutationObserver(function(mutations) { console.log("Inside element observer"); subTargetElement = targetElement.querySelector(subTargetSelector); if (subTargetElement) { elementObserver.disconnect(); console.log("subTargetElement found!"); } }) elementObserver.observe(targetElement, observeConfig); } }) rootObserver.observe(rootElement, observeConfig); } (function() { initExtension(document.body, "div.target", "div.subtarget"); setTimeout(function() { del = document.createElement("div"); del.innerHTML = "
      target
      " document.body.appendChild(del); }, 3000); setTimeout(function() { var el = document.body.querySelector('div.target'); if (el) { del = document.createElement("div"); del.innerHTML = "
      subtarget
      " el.appendChild(del); } }, 5000); })(); ``` ================================================ FILE: _posts/zh_TW/javascript/2016-02-06-deduplicate-an-array.md ================================================ --- layout: post title: 移除陣列重複元素 tip-number: 37 tip-username: danillouz tip-username-profile: https://www.twitter.com/danillouz tip-tldr: 如何從陣列中移除不同資料類型重複的元素。 redirect_from: - /zh_tw/deduplicate-an-array/ categories: - zh_TW - javascript --- # 原始函數 如果陣列只有包含原始數值,我們可以透過 [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) 和 [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) 方法來刪除重複的元素。 ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter(function (el, i, arr) { return arr.indexOf(el) === i; }); console.log(deduped); // [ 1, 'a' ] ``` ## ES2015 我們可以使用[箭頭函式](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions)來撰寫讓程式更簡潔。 ```javascript var deduped = [ 1, 1, 'a', 'a' ].filter( (el, i, arr) => arr.indexOf(el) === i); console.log(deduped); // [ 1, 'a' ] ``` 但是根據 [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) 和 [`from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 方法,我們可以用更簡潔的方式得到同樣的結果。 ```javascript var deduped = Array.from( new Set([ 1, 1, 'a', 'a' ]) ); console.log(deduped); // [ 1, 'a' ] ``` # 物件 當元素都是物件時,我們不能使用相同的方法,因為物件儲存的值是參考原始儲存的值。 ```javascript 1 === 1 // true 'a' === 'a' // true { a: 1 } === { a: 1 } // false ``` 因此,我們需要改變我們的方式以及使用雜湊表。 ```javascript function dedup(arr) { var hashTable = {}; return arr.filter(function (el) { var key = JSON.stringify(el); var match = Boolean(hashTable[key]); return (match ? false : hashTable[key] = true); }); } var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ] ]); console.log(deduped); // [ {a: 1}, [1, 2] ] ``` 因為在 JavaScript 雜湊表只是一個 `Object`,它的 key 的類型是 `String`。意思說,在同一個數值下,我們不能區分字串和數字,例如:`1` 和 `'1'`。 ```javascript var hashTable = {}; hashTable[1] = true; hashTable['1'] = true; console.log(hashTable); // { '1': true } ``` 然而,因為我們使用了 [`JSON.stringify`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), keys 是 `String` 的類型,會被轉成字串儲存,這樣 `hashTable` key 就是唯一的。 ```javascript var hashTable = {}; hashTable[JSON.stringify(1)] = true; hashTable[JSON.stringify('1')] = true; console.log(hashTable); // { '1': true, '\'1\'': true } ``` 意思說重複相同的元素數值,但為不同類型的,可以被保留,而重複的元素仍然會被刪除。 ```javascript var deduped = dedup([ { a: 1 }, { a: 1 }, [ 1, 2 ], [ 1, 2 ], 1, 1, '1', '1' ]); console.log(deduped); // [ {a: 1}, [1, 2], 1, '1' ] ``` # 資源 ## 方法 * [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) * [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) * [`from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) * [`JSON.stringify`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) ## ES2015 * [箭頭函式](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) * [Sets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) ## Stack overflow * [將陣列重複的元素移除](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array/9229821#9229821) ================================================ FILE: _posts/zh_TW/javascript/2016-02-07-flattening-multidimensional-arrays-in-javascript.md ================================================ --- layout: post title: 在 JavaScript 中將多維陣列扁平化 tip-number: 38 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 三種不同的解決方法,將多維陣列合併為單一的陣列。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - zh_tw/flattening-multidimensional-arrays-in-javascript/ categories: - zh_TW - javascript --- 三種不同的解決方法,將多維陣列合併為單一的陣列。 給定一個陣列: ```js var myArray = [[1, 2],[3, 4, 5], [6, 7, 8, 9]]; ``` 我們想到得到這個結果: ```js [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### 方案一: 使用 [`concat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) 和 [`apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) ```js var myNewArray = [].concat.apply([], myArray); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### 方案二:使用 [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Flatten_an_array_of_arrays) ```js var myNewArray = myArray.reduce(function(prev, curr) { return prev.concat(curr); }); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` ### 方案三: ```js var myNewArray3 = []; for (var i = 0; i < myArray.length; ++i) { for (var j = 0; j < myArray[i].length; ++j) myNewArray3.push(myArray[i][j]); } console.log(myNewArray3); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` 在[這裡](https://jsbin.com/qeqicu/edit?js,console)觀察三種方式的實際應用。 ### 方案四:使用 ES6 的[展開運算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) ```js var myNewArray4 = [].concat(...myArray); console.log(myNewArray4); // [1, 2, 3, 4, 5, 6, 7, 8, 9] ``` 在[這裡](https://jsbin.com/janana/edit?js,console) 查看這四個方法的 如果需要無限的巢狀陣列嘗試使用 Lodash 的 [flattenDeep()](https://lodash.com/docs#flattenDeep)。 如果你擔心效能問題的話,[這裡](http://jsperf.com/flatten-an-array-loop-vs-reduce/6) 有一個測試讓你來確認它們是如何執行的。 對於較大的巢狀陣列可以嘗試使用 Underscore 的 [flatten()](https://github.com/jashkenas/underscore/blob/master/underscore.js#L501)。 如果你好奇效能方面的表現, [這裡](http://jsperf.com/flatten-an-array-loop-vs-reduce/6)有相關的測試。 ================================================ FILE: _posts/zh_TW/javascript/2016-02-08-advanced-properties.md ================================================ --- layout: post title: 進階 JavaScript 屬性 tip-number: 39 tip-username: mallowigi tip-username-profile: https://github.com/mallowigi tip-tldr: 如何加入私有屬性、getters 和 setters 到物件。 redirect_from: - /zh_tw/advanced-properties/ categories: - zh_TW - javascript --- 在 JavaScript 可以設定物件屬性,例如:將屬性設定成偽私有或是唯讀。這是 ECMAScript 5.1 可用的功能,因此支援所有現代的瀏覽器。 如果需要這麼做,你需要使用 `Object` 的 `defineProperty` 方法,像是: ```js var a = {}; Object.defineProperty(a, 'readonly', { value: 15, writable: false }); a.readonly = 20; console.log(a.readonly); // 15 ``` 語法如下: ```js Object.defineProperty(dest, propName, options) ``` 或是多個定義: ```js Object.defineProperties(dest, { propA: optionsA, propB: optionsB, //... }) ``` 其中,包括以下可選的屬性: - *value*:如果屬性不是 getter(如下),數值是必要的屬性。`{a: 12}` === `Object.defineProperty(obj, 'a', {value: 12})` - *writable*:將屬性設定為唯讀。請注意,如果該屬性是巢狀的物件,該屬性是仍然可以編輯。 - *enumerable*:將屬性設定為隱藏。這意思說,在 `for ... of` 迴圈和 `stringify` 的結果不包含屬性,但屬性仍然存在。注意:這不代表屬性為私有的!它依然可以從外部存取,它只是不會列印出來。 - *configurable*:將屬性設定為不可修改。防止刪除或重新定義。再者,如果該屬性是一個巢狀的物件,其屬性仍然可以設定。 因此,為了建立一個私有的常數屬性,你像可以這樣定義: ```js Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false}); ``` 除了設定屬性,由於第二個參數是一個字串,`defineProperty` 允許我們定義*動態屬性*。舉個例子,比如說我想要根據一些外部設定來建立屬性: ```js var obj = { getTypeFromExternal(): true // 違反 ES5.1 規則 } Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok // 因為範例緣故, ES6 引入了一個新語法: var obj = { [getTypeFromExternal()]: true } ``` 但是這還不是全部!進階屬性允許我們建立 **getters** 和 **setters**,就像其他物件導向語言一樣!在這個情況下,我們不能使用 `writable`、`enumerable` 和 `configurable` 屬性,而是: ```js function Foobar () { var _foo; // true 私有屬性 Object.defineProperty(obj, 'foo', { get: function () { return _foo; } set: function (value) { _foo = value } }); } var foobar = new Foobar(); foobar.foo; // 15 foobar.foo = 20; // _foo = 20 ``` 除了有明顯的封裝和存取的優點,你可以注意到我們沒有「呼叫」getter,相反的,我們只是「取得」不帶括號的屬性!這太棒了!舉個例子,讓我們想像我們有一個物件具有多層巢狀的屬性,像是: ```js var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } }; ``` 現在我們不需要做 `a.b.c[0].d`(其中某個屬性可以解析成 `undefined` 然後拋出錯誤),我們可以建立另一個別名: ```js Object.defineProperty(obj, 'firstD', { get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d } }) console.log(obj.firstD) // 10 ``` ### 注意 如果你定義一個 getter 而沒有一個 setter 然後你想要嘗試設定數值,你會得到一個錯誤!當使用輔助函式像是 `$.extend` 或 `_.merge`,這是相當重要的一部份。請小心使用! ### 連結 - [defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) - [在 JavaScript 中定義屬性](http://bdadam.com/blog/defining-properties-in-javascript.html) ================================================ FILE: _posts/zh_TW/javascript/2016-02-09-using-json-stringify.md ================================================ --- layout: post title: 使用 JSON.Stringify tip-number: 40 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: 從 JSON 物件中,將被選到的屬性建立成字串。 redirect_from: - /zh_tw/using-json-stringify/ categories: - zh_TW - javascript --- 假設有一個物件有「prop1」、「prop2」、「prop3」屬性。 我們傳送__額外的參數__到 __JSON.stringify__ 將物件的屬性變成字串,像是: ```javascript var obj = { 'prop1': 'value1', 'prop2': 'value2', 'prop3': 'value3' }; var selectedProperties = ['prop1', 'prop2']; var str = JSON.stringify(obj, selectedProperties); // str // {"prop1":"value1","prop2":"value2"} ``` __"str"__ 只包含被選擇到的屬性的資訊。 除了傳送陣列,我們也可以傳送函式。 ```javascript function selectedProperties(key, val) { // 第一個數值是整個物件,key 是空的字串 if (!key) { return val; } if (key === 'prop1' || key === 'prop2') { return val; } return; } ``` 最後一個可選參數式是修改物件寫入字串的方式。 ```javascript var str = JSON.stringify(obj, selectedProperties, '\t\t'); /* str 每個輸出會有 doube tabs。 { "prop1": "value1", "prop2": "value2" } */ ``` ================================================ FILE: _posts/zh_TW/javascript/2016-02-10-array-average-and-median.md ================================================ --- layout: post title: 陣列平均值和中間值 tip-number: 41 tip-username: soyuka tip-username-profile: https://github.com/soyuka tip-tldr: 計算陣列的平均值和中間值。 redirect_from: - /zh_tw/array-average-and-median/ categories: - zh_TW - javascript --- 以下範例基於一個陣列: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; ``` 如果要取得平均,我們必須加總所有數字和再除以數字的個數。步驟是: - 取得陣列長度 - 加總數值 - 取得平均(`數值總和 / 陣列長度`) ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let sum = values.reduce((previous, current) => current += previous); let avg = sum / values.length; // avg = 28 ``` 或: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; let count = values.length; values = values.reduce((previous, current) => current += previous); values /= count; // avg = 28 ``` 現在,如果要取得中間值的步驟: - 將陣列排序 - 取得中間值的算術平均值 ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let lowMiddle = Math.floor((values.length - 1) / 2); let highMiddle = Math.ceil((values.length - 1) / 2); let median = (values[lowMiddle] + values[highMiddle]) / 2; // median = 13,5 ``` 隨著位元運算符: ```javascript let values = [2, 56, 3, 41, 0, 4, 100, 23]; values.sort((a, b) => a - b); let median = (values[(values.length - 1) >> 1] + values[values.length >> 1]) / 2 // median = 13,5 ``` ================================================ FILE: _posts/zh_TW/javascript/2016-02-11-preventing-unapply-attacks.md ================================================ --- layout: post title: 防止 Unapply 攻擊 tip-number: 42 tip-username: emars tip-username-profile: https://twitter.com/marseltov tip-tldr: 凍結內建的屬性。 redirect_from: - /zh_tw/preventing-unapply-attacks/ categories: - zh_TW - javascript --- 透過覆寫內建的原型,攻擊者可以重寫程式碼來暴露或更改綁定的參數。這是一個嚴重的安全漏洞,利用 es5 polyfill 的方法。 ```js // 綁定 polyfill 範例 function bind(fn) { var prev = Array.prototype.slice.call(arguments, 1); return function bound() { var curr = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.concat.apply(prev, curr); return fn.apply(null, args); }; } // unapply 攻擊 function unapplyAttack() { var concat = Array.prototype.concat; Array.prototype.concat = function replaceAll() { Array.prototype.concat = concat; // 恢復正確的版本 var curr = Array.prototype.slice.call(arguments, 0); var result = concat.apply([], curr); return result; }; } ``` 以上的函式拋棄了綁定的 `prev` 陣列,任何 `.concat` 在第一個 `concat` 被呼叫之後使用 unapply 攻擊會拋出錯誤。 使用 [Object.freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) 讓物件保持不變,你可以防止任何內建物件原型被覆寫。 ```js (function freezePrototypes() { if (typeof Object.freeze !== 'function') { throw new Error('Missing Object.freeze'); } Object.freeze(Object.prototype); Object.freeze(Array.prototype); Object.freeze(Function.prototype); }()); ``` 你可以在[這裡](https://glebbahmutov.com/blog/unapply-attack/)閱讀更多關於 unapply 攻擊。 ================================================ FILE: _posts/zh_TW/javascript/2016-02-12-use-destructuring-in-function-parameters.md ================================================ --- layout: post title: 在函式參數裡使用解構子 tip-number: 43 tip-username: dislick tip-username-profile: https://github.com/dislick tip-tldr: 你知道可以在函式的參數裡使用解構子嗎? redirect_from: - /zh_tw/use-destructuring-in-function-parameters/ categories: - zh_TW - javascript --- 我相信很多人都已經很熟悉 [ES6 解構賦值](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)。你知道也可以用在函式的參數裡嗎? ```js var sayHello = function({ name, surname }) { console.log(`Hello ${name} ${surname}! How are you?`); }; sayHello({ name: 'John', surname: 'Smith' }) // -> Hello John Smith! How are you? ``` 對函式來說可以接受一個可選擇的物件是非常有用的。對於這個使用方法,你可以加入[預設參數](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters)來使用,不管是 caller 忘記賦值,或者是 caller 根本忘記傳送參數: ```js var sayHello2 = function({ name = "Anony", surname = "Moose" } = {}) { console.log(`Hello ${name} ${surname}! How are you?`); }; ``` `= {}` 意思說預設物件可以被解構成這個 `{}` 參數,萬一 caller 忘記傳送參數,或傳送一個錯誤的類型(更多關於在下面)。 ```js sayHello2() // -> Hello Anony Moose! How are you? sayHello2({ name: "Bull" }) // -> Hello Bull Moose! How are you? ``` ##### 參數處理 使用解構賦值,如果輸入參數不符合函式指定的物件參數,所有不符合的參數都是 `undefined`,所以你需要加入程式碼來正確的處理這些不符合的參數: ```js var sayHelloTimes = function({ name, surname }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); } sayHelloTimes({ name: "Pam" }, 5678) // -> Hello Pam undefined! I've seen you 5678 times before. sayHelloTimes(5678) // -> Hello undefined undefined! I've seen you undefined times before. ``` 假設參數被解構時不存在的話,會拋出例外,可能會造成你的應用程式停止: ```js sayHelloTimes() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'... ``` 它的概念上類似於存取未定義對象的屬性,只是用不同的例外類型。 解構賦值某些程度上會隱藏所有預設參數: ```js var sayHelloTimes2 = function({ name = "Anony", surname = "Moose" } = {}, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2() // -> Hello Anony Moose! I've seen you undefined times before. ``` `= {}` 掩蓋了 _object_ 屬性不存在的情況;但對於個別屬性預設值的情形下則是會拋出例外: ```js var sayHelloTimes2a = function({ name = "Anony", surname = "Moose" }, times) { console.log(`Hello ${name} ${surname}! I've seen you ${times} times before.`); }; sayHelloTimes2a({ name: "Pam" }, 5678) // -> Hello Pam Moose! I've seen you 5678 times before. sayHelloTimes2a(5678) // -> Hello Anony Moose! I've seen you undefined times before. sayHelloTimes2a() // -> Uncaught TypeError: Cannot match against 'undefined' or 'null'. ``` ##### 可用性 > 請注意解構賦值現在 Node.js 還有其他瀏覽器還無法使用。如果你想要試試看的話,你可以在 Node.js 使用 `--harmony-destructuring`。 ================================================ FILE: _posts/zh_TW/javascript/2016-02-13-know-the-passing-mechanism.md ================================================ --- layout: post title: 了解傳送機制 tip-number: 44 tip-username: bmkmanoj tip-username-profile: https://github.com/bmkmanoj tip-tldr: JavaScript 技術上只傳送原始和物件類型的值(或參考)。在參考的類型下,參考值本身是 passed by value。 redirect_from: - /zh_tw/know-the-passing-mechanism/ categories: - zh_TW - javascript --- JavaScript 技術上是透過 pass-by-value。它既不是傳值(pass-by-value)也不是傳參考(pass-by-reference),要理解這些術語的意義,你要了它們傳送的機制,透過以下的範例程式碼和解釋了解它們的意義。 ### 範例一 ```js var me = { // 1 'partOf' : 'A Team' }; function myTeam(me) { // 2 me = { // 3 'belongsTo' : 'A Group' }; } myTeam(me); console.log(me); // 4 : {'partOf' : 'A Team'} ``` 在上方的範例,當 `myTeam` 函式被調用,JavaScript *將 `me` 物件當作 passing the reference 作為值*,調用本身建立了兩個獨立的參考在相同的物件,(雖然在這邊名稱相同,例如 `me`,這是一個誤導讓我們以為它是單一參考物件的印象。),因此,參考變數它們都是獨立的。 當我們在 #`3` 給了一個新的物件,我們完全改變 `myTeam` 函式內的參考值,在函式之外的原始物件不會受到影響,從這裡物件參考到外部保留了原始物件的值,並傳入函式內,因此可以從 #`4` 看到輸出值。 ### 範例二 ```js var me = { // 1 'partOf' : 'A Team' }; function myGroup(me) { // 2 me.partOf = 'A Group'; // 3 } myGroup(me); console.log(me); // 4 : {'partOf' : 'A Group'} ``` 在這個例子 `myGroup` 被調用,我們傳送了物件 `me`。但不像範例一的程式碼,我們沒有給予 `me` 變數到任何的新物件,當我們在 `myGroup` 函式內修改物件的屬性,物件參考值一直是參考到原始的值,這是可以有效的改變物件的屬性。因此你可以從 #`7` 看到輸出值。 所以最後一個情況也不能證明 JavaScript 是 pass-by-reference 嗎?不,它不是的。記住,*如果是物件的情況下,JavaScript 將 passes the reference 作為值*。這裡會產生困惑,所以我們往往不能完全理解什麼是傳參考。這是明確的原因,有些人喜歡稱它們為 *call-by-sharing*。 *本文最早被作者發表於 [js-by-examples](https://github.com/bmkmanoj/js-by-examples/blob/master/examples/js_pass_by_value_or_reference.md)* ================================================ FILE: _posts/zh_TW/javascript/2016-02-14-calculate-the-max-min-value-from-an-array.md ================================================ --- layout: post title: 從陣列計算最大和最小值 tip-number: 45 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 在數字陣列使用內建的 Max.max() 和 Max.min() 函式的方式。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/calculate-the-max-min-value-from-an-array/ categories: - zh_TW - javascript --- 內建函式 [Math.max()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max) 和 [Math.min()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min) 分別可以從參數中找到最大值和最小值。 ```js Math.max(1, 2, 3, 4); // 4 Math.min(1, 2, 3, 4); // 1 ``` 這些函式對於數字陣列是無法作用的。然而,這裡有些解決辦法。 [`Function.prototype.apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) 允許你呼叫函式並給定一個 `this` 和一個_陣列_的參數。 ```js var numbers = [1, 2, 3, 4]; Math.max.apply(null, numbers) // 4 Math.min.apply(null, numbers) // 1 ``` 傳送 `numbers` 陣列當作 `apply()` 的第二個參數,函式會呼叫陣列內所有的值當作函式的參數。 更簡單的方式,透過 ES2015 的[展開運算子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator)來完成。 ```js var numbers = [1, 2, 3, 4]; Math.max(...numbers) // 4 Math.min(...numbers) // 1 ``` 這個運算子可以在函式的參數中把陣列內的數值「展開」。 ================================================ FILE: _posts/zh_TW/javascript/2016-02-15-detect-document-ready-in-pure-js.md ================================================ --- layout: post title: 使用 pure JavaScript 檢查文件是否準備 tip-number: 46 tip-username: loverajoel tip-username-profile: https://www.twitter.com/loverajoel tip-tldr: 可以跨瀏覽器,而且使用 pure JavaScript 來確認文件是否已經載入完成。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/detect-document-ready-in-pure-js/ categories: - zh_TW - javascript --- 可以跨瀏覽器,而且使用 pure JavaScript 來確認文件是否已經載入完成的方式是使用 [`readyState`](https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState)。 ```js if (document.readyState === 'complete') { // 網頁已經完全載入 } ``` 當文件載入後,你可以檢查文件是否載入完成... ```js let stateCheck = setInterval(() => { if (document.readyState === 'complete') { clearInterval(stateCheck); // 文件載入 } }, 100); ``` 或使用 [onreadystatechange](https://developer.mozilla.org/en-US/docs/Web/Events/readystatechange)... ```js document.onreadystatechange = () => { if (document.readyState === 'complete') { // 文件載入 } }; ``` 當 DOM 載入後,使用 `document.readyState === 'interactive'` 來檢查是否載入完成。 ================================================ FILE: _posts/zh_TW/javascript/2016-02-16-basics-declarations.md ================================================ --- layout: post title: 宣告的基本知識 tip-number: 47 tip-username: adaniloff tip-username-profile: https://github.com/adaniloff tip-tldr: 了解並處理宣告。 redirect_from: - /zh_tw/basics-declarations/ categories: - zh_TW - javascript --- 以下有不同的 JavaScript 變數宣告方式。 註解和 `console.log` 應該可以足夠解釋在這裡發生了什麼: ```js var y, x = y = 1 //== var x; var y; x = y = 1 console.log('--> 1:', `x = ${x}, y = ${y}`) // 將會印出 //--> 1: x = 1, y = 1 ``` 首先,我們只是設定兩個變數,沒有其他的變數。 ```js ;(() => { var x = y = 2 // == var x; x = y = 2; console.log('2.0:', `x = ${x}, y = ${y}`) })() console.log('--> 2.1:', `x = ${x}, y = ${y}`) // 將會印出 //2.0: x = 2, y = 2 //--> 2.1: x = 1, y = 2 ``` 你可以看到,程式碼只改變了全域的 y,因為我們還沒有宣告變數在 closure。 ```js ;(() => { var x, y = 3 // == var x; var y = 3; console.log('3.0:', `x = ${x}, y = ${y}`) })() console.log('--> 3.1:', `x = ${x}, y = ${y}`) // 將會印出 //3.0: x = undefined, y = 3 //--> 3.1: x = 1, y = 2 ``` 現在我們透過 var 來宣告兩個變數。意思說它們只存在 closure 的 context。 ```js ;(() => { var y, x = y = 4 // == var x; var y; x = y = 4 console.log('4.0:', `x = ${x}, y = ${y}`) })() console.log('--> 4.1:', `x = ${x}, y = ${y}`) // 將會印出 //4.0: x = 4, y = 4 //--> 4.1: x = 1, y = 2 ``` 兩個變數已經使用 var 宣告,也設定了他們的值。既然 `local > global`,x 和 y 在 closure 內,意思說全域的 x 和 y 是不會改變的。 ```js x = 5 // == x = 5 console.log('--> 5:', `x = ${x}, y = ${y}`) // 將會印出 //--> 5: x = 5, y = 2 ``` 最後的結果是很明顯的。 你可以測試這些程式碼並觀看結果,[感謝 babel](https://babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&code=var%20y%2C%20x%20%3D%20y%20%3D%201%20%2F%2F%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%201%0Aconsole.log('--%3E%201%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%201%3A%20x%20%3D%201%2C%20y%20%3D%201%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%20%3D%20y%20%3D%202%20%2F%2F%20%3D%3D%20var%20x%3B%20y%20%3D%202%3B%0A%20%20console.log('2.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%202.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F2.0%3A%20x%20%3D%202%2C%20y%20%3D%202%0A%2F%2F--%3E%202.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20x%2C%20y%20%3D%203%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%20%3D%203%3B%0A%20%20console.log('3.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%203.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F3.0%3A%20x%20%3D%20undefined%2C%20y%20%3D%203%0A%2F%2F--%3E%203.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0A%3B(()%20%3D%3E%20%7B%20%0A%20%20var%20y%2C%20x%20%3D%20y%20%3D%204%20%2F%2F%20%3D%3D%20var%20x%3B%20var%20y%3B%20x%20%3D%20y%20%3D%203%0A%20%20console.log('4.0%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%7D)()%0Aconsole.log('--%3E%204.1%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F4.0%3A%20x%20%3D%204%2C%20y%20%3D%204%0A%2F%2F--%3E%204.1%3A%20x%20%3D%201%2C%20y%20%3D%202%0A%0Ax%20%3D%205%20%2F%2F%20%3D%3D%20x%20%3D%205%0Aconsole.log('--%3E%205%3A'%2C%20%60x%20%3D%20%24%7Bx%7D%2C%20y%20%3D%20%24%7By%7D%60)%0A%0A%2F%2F%20Will%20print%0A%2F%2F--%3E%205%3A%20x%20%3D%205%2C%20y%20%3D%202)。 更多可用的資訊在 [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)。 特別感謝 @kurtextrem 的合作 :)! ================================================ FILE: _posts/zh_TW/javascript/2016-02-17-reminders-about-reduce-function-usage.md ================================================ --- layout: post title: 如何 reduce 陣列 tip-number: 48 tip-username: darul75 tip-username-profile: https://twitter.com/darul75 tip-tldr: 有關使用 `reduce` 函式的一些提醒。 redirect_from: - /zh_tw/reminders-about-reduce-function-usage/ categories: - zh_TW - javascript --- 正如官方文件所寫的,`reduce()` 方法接收一個函式做為累加器(accumulator),陣列中的每個值(由左到右)將會合併,最後只得到一個值。 ### 署名 [reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) 函式接受 2 個參數(M:強制的,O:可選的): - (M) 一個 callback **reducer 函式** 可以處理先前的計算結果,到下一個元素直到最後。 - (O) 一個**初始值**可以被當作第一個呼叫 callback 的第一個參數。 所以讓我們來看一下普遍的用法,之後再看更多的複雜的用法。 ### 普遍用法(累加、關聯) 我們在逛 Amazon 網站(價格為美元),但是我們的 caddy 太滿了,所以讓我們計算總價。 ```javascript // 我目前在 amazon 購買的 caddy var items = [{price: 10}, {price: 120}, {price: 1000}]; // 我們的 reducer 函式 var reducer = function add(sumSoFar, item) { return sumSoFar + item.price; }; // 計算結果 var total = items.reduce(reducer, 0); console.log(total); // 1130 ``` 我們在第一個事件,我們提供了 `reduce` 可選函式參數為一個整數 0,可以是一個物件、或是一個陣列,而不是原始的類型,之後我們將會看到。 現在,太酷了我得到一個 20$ 的折價券。 ```javascript var total = items.reduce(reducer, -20); console.log(total); // 1110 ``` ### 進階用法(組合) 第二個使用範例的靈感是來自 Redux 的 [combineReducers](http://redux.js.org/docs/api/combineReducers.html) 函式,[原始碼](https://github.com/reactjs/redux/blob/master/src/combineReducers.js#L93)。 背後的想法是將 reducer 函式拆成獨立的函式,然後在最後計算出一個新的*單一 reducer 函式*。 如果要說明這些,讓我們建立一個單一物件和一些 reducers 函式可以計算不同的貨幣($、€...)的總價。 ```javascript var reducers = { totalInDollar: function(state, item) { // 具體宣告... return state.dollars += item.price; }, totalInEuros : function(state, item) { state.euros += item.price * 0.897424392; return state; }, totalInPounds : function(state, item) { state.pounds += item.price * 0.692688671; return state; }, totalInYen : function(state, item) { state.yens += item.price * 113.852; return state; } // 更多... }; ``` 然後,我們建立一個新的多功能函式 - 負責應用每個部分的 reduce 函式。 - 回傳一個新的 callback reducer 函式。 ```javascript var combineTotalPriceReducers = function(reducers) { return function(state, item) { return Object.keys(reducers).reduce( function(nextState, key) { reducers[key](state, item); return state; }, {} ); } }; ``` 現在讓我們看看如何使用它。 ```javascript var bigTotalPriceReducer = combineTotalPriceReducers(reducers); var initialState = {dollars: 0, euros:0, yens: 0, pounds: 0}; var totals = items.reduce(bigTotalPriceReducer, initialState); console.log(totals); /* Object {dollars: 1130, euros: 1015.11531904, yens: 127524.24, pounds: 785.81131152} */ ``` 我希望這種方法可以給你使用 reduce() 函數的時候提供另一個想法。 你的 reduce 函式可以處理每個計算的歷史記錄。這個在 Ramdajs 的 [scan](http://ramdajs.com/docs/#scan) 函式已經實現了。 [在 JSFiddle 試試看](https://jsfiddle.net/darul75/81tgt0cd/) ================================================ FILE: _posts/zh_TW/javascript/2016-02-26-extract-unix-timestamp-easily.md ================================================ --- layout: post title: 在 JavaScript 簡單取得 unix timestamp tip-number: 49 tip-username: nmrony tip-username-profile: https://github.com/nmrony tip-tldr: 在 JavaScript 你可以簡單取得 unix timestamp redirect_from: - /zh_tw/extract-unix-timestamp-easily/ categories: - zh_TW - javascript --- 我們經常需要計算 unix 的 timestamp。有許多方式可以取得 timestamp。目前取得 unix timestamp 最簡單和快速的方式是: ```js const dateTime = Date.now(); const timestamp = Math.floor(dateTime / 1000); ``` 或 ```js const dateTime = new Date().getTime(); const timestamp = Math.floor(dateTime / 1000); ``` 如果要取得特定的 unix timestamp 傳送一個 `YYYY-MM-DD` 或 `YYYY-MM-DDT00:00:00Z` 參數到 `Date` 的建構子。例如: ```js const timestamp = new Date('2012-06-08').getTime() ``` 當你宣告一個 `Date` 物件,也可以加上 `+` 符號: ```js const dateTime = +new Date(); const timestamp = Math.floor(dateTime / 1000); ``` 或指定日期 ```js const dateTime = +new Date('2012-06-08'); const timestamp = Math.floor(dateTime / 1000); ``` 在執行時呼叫了 `Date` 物件的 `valueOf` 方法。`+` 運算子呼叫了 `toNumber()` 和回傳的值。更多細節的說明請參考以下的連結。 * [Date.prototype.valueOf](http://es5.github.io/#x15.9.5.8) * [Unary + operator](http://es5.github.io/#x11.4.6) * [toNumber()](http://es5.github.io/#x9.3) * [Date Javascript MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) ================================================ FILE: _posts/zh_TW/javascript/2016-03-03-helpful-console-log-hacks.md ================================================ --- layout: post title: 實用的 Console Logging 技巧 tip-number: 50 tip-username: zackhall tip-username-profile: https://twitter.com/zthall tip-tldr: 使用條件中斷點的 logging 實用技巧。 redirect_from: - /zh_tw/helpful-console-log-hacks/ categories: - zh_TW - javascript --- ## 使用條件中斷點來記錄資料 當函式被呼叫時,如果你想要在 console 顯示每次記錄 的值,你可以透過條件中斷點來記錄。打開你的開發工具,找到你要 console 出來資料的函式,並設定以下的條件中斷點: ```js console.log(data.value) && false ``` 如果條件中斷點為 true 才會停止頁面。所以,使用條件像是 console.log('foo') && false 可以保證為 false,因為在 AND 條件下有一個 false。所以這不會停止你的頁面,但是還是會 console 你記錄的資料。這也可以使用在記錄你的函式或 callback 被呼叫了多少次。 這裡有一些各個瀏覽器如何設定條件中斷點:[Edge](https://dev.windows.com/en-us/microsoft-edge/platform/documentation/f12-devtools-guide/debugger/#setting-and-managing-breakpoints "Managing Breakpoints in Edge")、[Chrome](https://developer.chrome.com/devtools/docs/javascript-debugging#breakpoints "Managing Breakpoints in Chrome") 和 [Firefox](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Set_a_conditional_breakpoint "Managing Breakpoints in Firefox") 和 [Safari](https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/Debugger/Debugger.html "Managing Breakpoints in Safari")。 ## 在 console 顯示函式的變數 你曾經想要 console 顯示函式的變數,但是卻無法看到函式的程式碼嗎?最快速看到函式的程式碼的方式是將函式的變數串接一個空字串強制把它轉成字串來顯示。 ```js console.log(funcVariable + ''); ``` ================================================ FILE: _posts/zh_TW/javascript/2016-03-16-DOM-event-listening-made-easy.md ================================================ --- layout: post title: 簡單的監聽 DOM 事件 tip-number: 51 tip-username: octopitus tip-username-profile: https://github.com/octopitus tip-tldr: 一個優雅和簡單的方式來處理 DOM 事件。 redirect_from: - /zh_tw/DOM-event-listening-made-easy/ categories: - zh_TW - javascript --- 很多人仍然透過以下方法來處理 DOM 事件︰ - `element.addEventListener('type', obj.method.bind(obj))` - `element.addEventListener('type', function (event) {})` - `element.addEventListener('type', (event) => {})` 當你不需要這些處理函式時,使用者可能因為交互事件或[冒泡事件](http://www.javascripter.net/faq/eventbubbling.htm)而不小心觸發意外。 安全的事件處理模式包含以下這些: 使用一個參考: ```js const handler = function () { console.log("Tada!") } element.addEventListener("click", handler) // 之後 element.removeEventListener("click", handler) ``` 命名函式刪除本身: ```js element.addEventListener('click', function click(e) { if (someCondition) { return e.currentTarget.removeEventListener('click', click); } }); ``` 更好的方式: ```js function handleEvent (eventName, {onElement, withCallback, useCapture = false} = {}, thisArg) { const element = onElement || document.documentElement function handler (event) { if (typeof withCallback === 'function') { withCallback.call(thisArg, event) } } handler.destroy = function () { return element.removeEventListener(eventName, handler, useCapture) } element.addEventListener(eventName, handler, useCapture) return handler } // 任何你需要的時候 const handleClick = handleEvent('click', { onElement: element, withCallback: (event) => { console.log('Tada!') } }) // 任何時候你想要將它移除 handleClick.destroy() ``` ================================================ FILE: _posts/zh_TW/javascript/2016-04-05-return-values-with-the-new-operator.md ================================================ --- layout: post title: 使用「new」運算符的回傳值 tip-number: 52 tip-username: Morklympious tip-username-profile: https://github.com/morklympious tip-tldr: 當使用 new 和不使用 new 時,了解他們所回傳的值。 redirect_from: - /zh_tw/return-values-with-the-new-operator/ categories: - zh_TW - javascript --- 在 JavaScript 你會碰到一些情況,使用 `new` 來分配新的物件。這會打斷你的思維,除非你閱讀這篇 tip 來了解到底在背後發生了哪些事。 在合理情況下,JavaScript 中的 `new` 是一個運算符,回傳一個新的物件實例,假設我們有一個建構函式: ````js function Thing() { this.one = 1; this.two = 2; } var myThing = new Thing(); myThing.one // 1 myThing.two // 2 ```` __注意__:除此之外,如果 `Thing()` 沒有透過 `new` 來呼叫的話,_沒有物件會被建立_,`this` 會指向到全域物件 window。這個意思就是: 1. 你突然會有兩個全域變數叫作 `one` 和 `two`。 2. `myThing` 現在是 undefined,因為在 `Thing()` 沒有任何的回傳。 現在這個範例中,有些東西有點令人疑惑。比方我說加入建構函式: ````js function Thing() { this.one = 1; this.two = 2; return 5; } var myThing = new Thing(); ```` 現在 myThing 等於多少?是 5 嗎?它不是一個物件嗎?還是自我的缺少感?可能永遠都不知道! 除了我們可知道的是: ````js myThing.one // 1 myThing.two // 2 ```` 更有趣的是,我們從來都沒看到從我們建構子中「回傳」的 5 。真是奇怪,到底怎麼了?function 你到底在做什麼呀 ?我的 5 呢?讓我來嘗試一些東西。 讓我們回傳一個非原始類型來做取代,像是一個物件。 ````js function Thing() { this.one = 1; this.two = 2; return { three: 3, four: 4 }; } var myThing = new Thing(); ```` 讓我們來確認一下,快速的 console.log 透露了所有訊息: ````js console.log(myThing); /* Object {three: 3, four: 4} this.one and this.two 發生了什麼事!? 我的朋友呀,他們被蓋掉了。 */ ```` __在這裡我們可以學到:__ 當你調用一個有 `new` keyword 的函式,你可以使用 `this` 來設定屬性(但是你可能已經知道這些了)。從 `new` keyword 你呼叫了函式,所回傳的值不是你所指定的值,而是將函式的 `this` 實例回傳(你所放的屬性,像是 `this.one = 1`;)。 然而,回傳一個非原始值,像是 `object`、`array` 或者是 `function` 會覆蓋在 `this` 這個實例,將會破壞原本你所分配給 `this` 的值。 ================================================ FILE: _posts/zh_TW/javascript/2016-04-21-get-file-extension.md ================================================ --- layout: post title: 取得檔案的副檔名 tip-number: 53 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: 如何更有效的取得檔案的附檔名? redirect_from: - /zh_tw/get-file-extension/ categories: - zh_TW - javascript --- ### 問題:如何取得檔案的副檔名? ```javascript var file1 = "50.xsl"; var file2 = "30.doc"; getFileExtension(file1); //returs xsl getFileExtension(file2); //returs doc function getFileExtension(filename) { /*TODO*/ } ``` ### 解決方案一:正規表達式 ```js function getFileExtension1(filename) { return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined; } ``` ### 解決方案二:String 的 `split` 方法 ```js function getFileExtension2(filename) { return filename.split('.').pop(); } ``` 那些兩個解決方案不能處理一些邊緣情況,這裡是另一個更好的解決方案。 ### 解決方案三:String 的 `slice`、`lastIndexOf` 方法 ```js function getFileExtension3(filename) { return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2); } console.log(getFileExtension3('')); // '' console.log(getFileExtension3('filename')); // '' console.log(getFileExtension3('filename.txt')); // 'txt' console.log(getFileExtension3('.hiddenfile')); // '' console.log(getFileExtension3('filename.with.many.dots.ext')); // 'ext' ``` _它是如何處理的呢?_ - [String.lastIndexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf) 方法回傳最後一個符合指定的值(在範例中的 `'.'`)。如果找不到值則回傳 `-1`。 - 對於參數 `'filename'` 和 `'.hidden'` 的 `lastIndexOf` 的回傳值,分別為 `0` 和 `1`。[無符號右移運算子(>>>)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3E%3E%3E_%28Zero-fill_right_shift%29) 會將 `-1` 轉換成 `4294967295` 和將 `-2` 轉換成 `4294967294`,這裡是一個小技巧來確保在邊緣情況下檔案名稱不會改變。 - [String.prototype.slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice) 從上面計算的索引中提取檔副檔名。如果索引超過檔案的長度,那結果會是 `""`。 ### 比較 | 解決方案 | 參數 | 結果 | | ----------------------------------------- |:-------------------:|:--------:| | 解決方案一:正規表達式 | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | undefined
      undefined
      'txt'
      'hiddenfile'
      'ext'
      | | 解決方案二:String 的 `split` 方法 | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | ''
      'filename'
      'txt'
      'hiddenfile'
      'ext'
      | | 解決方案三:String 的 `slice`、`lastIndexOf` 方法 | ''
      'filename'
      'filename.txt'
      '.hiddenfile'
      'filename.with.many.dots.ext' | ''
      ''
      'txt'
      ''
      'ext'
      | ### 實際範例和效能 [這裡](https://jsbin.com/tipofu/edit?js,console)是上方程式碼的實際範例。 [這裡](http://jsperf.com/extract-file-extension)是三個解決方案的效能測試。 ### 來源 [如何使用 JavaScript 來取得檔案副檔名](http://stackoverflow.com/questions/190852/how-can-i-get-file-extensions-with-javascript) ================================================ FILE: _posts/zh_TW/javascript/2016-05-06-use-optional-arguments.md ================================================ --- layout: post title: 如何使用在函式中的可選參數(包含 callback) tip-number: 54 tip-username: alphashuro tip-username-profile: https://github.com/alphashuro tip-tldr: 你的 function 參數和 callback 是可選參數。 redirect_from: - /zh-tw/use-optional-arguments/ categories: - javascript - zh-TW --- Example function 中第二個和第三個為可選的參數: ```javascript function example( err, optionalA, optionalB, callback ) { // 以陣列形式取得參數。 var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; }; // 第一個參數是一個 error 物件, // 從陣列移除第一個參數,並將它回傳。 err = args.shift(); // 如果最後一個參數是 function,那它則為 callback function。 // 移除在陣列中最後一個參數,並將它回傳。 if (typeof args[args.length-1] === 'function') { callback = args.pop(); } // 如果還有其他參數, // 那些都是可選參數,你可以像這樣一個一個將他取出: if (args.length > 0) optionalA = args.shift(); else optionalA = null; if (args.length > 0) optionalB = args.shift(); else optionalB = null; // 像往常一樣︰檢查是否有錯誤。 if (err) { return callback && callback(err); } // 為了這個教學,log 是可選的參數。 console.log('optionalA:', optionalA); console.log('optionalB:', optionalB); console.log('callback:', callback); /* 你任何想做的邏輯。 */ } // ES6 程式碼更簡短、更簡潔。 function example(...args) { // 第一個參數是一個 error 物件 const err = args.shift(); // 如果最後一個參數是 function,那它則為 callback function。 const callback = (typeof args[args.length-1] === 'function') ? args.pop() : null; // 如果還有其他參數,那些都是可選參數,你可以像這樣一個一個將他取出: const optionalA = (args.length > 0) ? args.shift() : null; const optionalB = (args.length > 0) ? args.shift() : null; // ... repeat for more items if (err && callback) return callback(err); /* 你任何想做的邏輯。 */ } // 調用 example function 和沒有可選的參數。 example(null, 'AA'); example(null, function (err) { /* 你任何想做的邏輯。 */ }); example(null, 'AA', function (err) {}); example(null, 'AAAA', 'BBBB', function (err) {}); ``` ### 我要如何確認 optionalA 或 optionalB 是預期的? 設計你的 function,為了接受 optionalB 而要求 optionalA。 ================================================ FILE: _posts/zh_TW/javascript/2016-05-12-make-easy-loop-on-array.md ================================================ --- layout: post title: 使用一個陣列建立一個簡單的迴圈 tip-number: 55 tip-username: jamet-julien tip-username-profile: https://github.com/jamet-julien tip-tldr: 有時候我們需要不停的 loop 我們陣列的項目,像是旋轉木馬的圖片或是播放清單。這裡是如何把一個陣列並給他「looping powers」。 redirect_from: - /zh_tw/make-easy-loop-on-array/ categories: - zh_TW - javascript --- 有時候我們需要不停的 loop 我們陣列的項目,像是旋轉木馬的圖片或是播放清單。這裡是如何把一個陣列並給他「looping powers」: ```js var aList = ['A', 'B', 'C', 'D', 'E']; function make_looper( arr ){ arr.loop_idx = 0; // 回傳目前項目 arr.current = function(){ if( this.loop_idx < 0 ){ // 第一次檢查 this.loop_idx = this.length - 1; // 更新 loop_idx } if( this.loop_idx >= this.length ){// 第二次檢查 this.loop_idx = 0;// 更新 loop_idx } return arr[ this.loop_idx ]; // 回傳項目 }; // 增加 loop_idx 並回傳 new current arr.next = function(){ this.loop_idx++; return this.current(); }; // 減少 loop_idx 並回傳 new current arr.prev = function(){ this.loop_idx--; return this.current(); }; } make_looper( aList); aList.current(); // -> A aList.next(); // -> B aList.next(); // -> C aList.next(); // -> D aList.next(); // -> E aList.next(); // -> A aList.pop() ; // -> E aList.prev(); // -> D aList.prev(); // -> C aList.prev(); // -> B aList.prev(); // -> A aList.prev(); // -> D ``` 使用 ```%```(模數)運算符更好看一些。模數運算回傳餘數(``` 2 % 5 = 1``` 和 ``` 5 % 5 = 0```): ```js var aList = ['A', 'B', 'C', 'D', 'E']; function make_looper( arr ){ arr.loop_idx = 0; // 回傳目前項目 arr.current = function(){ this.loop_idx = ( this.loop_idx ) % this.length; // 沒有驗證! return arr[ this.loop_idx ]; }; // 增加 loop_idx 並回傳 new current arr.next = function(){ this.loop_idx++; return this.current(); }; // 減少 loop_idx 並回傳 new current arr.prev = function(){ this.loop_idx += this.length - 1; return this.current(); }; } make_looper( aList); aList.current(); // -> A aList.next(); // -> B aList.next(); // -> C aList.next(); // -> D aList.next(); // -> E aList.next(); // -> A aList.pop() ; // -> E aList.prev(); // -> D aList.prev(); // -> C aList.prev(); // -> B aList.prev(); // -> A aList.prev(); // -> D ``` ================================================ FILE: _posts/zh_TW/javascript/2016-08-02-copy-to-clipboard.md ================================================ --- layout: post title: 複製到剪貼版 tip-number: 56 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 本週我建立了一個常見的「複製到剪貼版」按鈕,我以前不曾做過這樣的功能,我想分享我如何實作它。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/copy-to-clipboard/ categories: - zh_TW - javascript --- 這是一個簡單的 tip,本週我建立了一個常見的「複製到剪貼版」按鈕,我以前不曾做過這樣的功能,我想分享我如何實作它。 它很簡單,比較麻煩的是我們必須加入一個 `` 與文字複製到 DOM。我們選擇內容並執行 [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) 複製指令。 `execCommand('copy')` 將複製時間被選擇的內容 這個指令目前[支援](http://caniuse.com/#search=execCommand)所有最新的瀏覽器,允許我們執行其他系統指令,像是:`copy`、`cut`、`paste` 和改變字體顏色、大小等等。 ```js document.querySelector('#input').select(); document.execCommand('copy'); ``` 具體呈現請看參考[這裡](https://jsbin.com/huhozu/edit?html,js,output) ================================================ FILE: _posts/zh_TW/javascript/2016-08-10-comma-operaton-in-js.md ================================================ --- layout: post title: JavaScript 的逗號操作符 tip-number: 57 tip-username: bhaskarmelkani tip-username-profile: https://www.twitter.com/bhaskarmelkani tip-tldr: 在一個表達式中,由左到右計算每個表達式並回傳最後的一個。 redirect_from: - /zh_tw/comma-operaton-in-js/ categories: - zh_TW - javascript --- 除了分號之外,逗號允許你在同一個地方放多個語句。 例如: ```js for(var i=0, j=0; i<5; i++, j++, j++){ console.log("i:"+i+", j:"+j); } ``` 輸出: ```js i:0, j:0 i:1, j:2 i:2, j:4 i:3, j:6 i:4, j:8 ``` 當放置一個表達式時,它由左到右計算每個表達式,並回傳最右邊的表達式。 例如: ```js function a(){console.log('a'); return 'a';} function b(){console.log('b'); return 'b';} function c(){console.log('c'); return 'c';} var x = (a(), b(), c()); console.log(x); // 輸出「c」 ``` 輸出: ```js "a" "b" "c" "c" ``` * 注意:逗號(`,`)操作符在 JavaScript 所有 的操作符是最低的優先順序,所以如果沒有括號表達式將變成:`(x = a()), b(), c();`。 ##### Playground ================================================ FILE: _posts/zh_TW/javascript/2016-08-17-break-continue-loop-functional.md ================================================ --- layout: post title: 在 functional programming break 或 continue 迴圈 tip-number: 58 tip-username: vamshisuram tip-username-profile: https://github.com/vamshisuram tip-tldr: 迭代一個陣列來尋找值是很平常的事,如果我要搜尋的值是在陣列第一個,也不能直接從迴圈內部回傳,我們需要迭代整個陣列。在這個 tip 我們將看到如何使用 `.some` 和 `.every` 快速完成迭代。 redirect_from: - /zh_tw/break-continue-loop-functional/ categories: - zh_TW - javascript --- 取消迭代是常見的需求。使用 `for` 迴圈我們可以 `break` 讓迭代提早結束。 ```javascript const a = [0, 1, 2, 3, 4]; for (var i = 0; i < a.length; i++) { if (a[i] === 2) { break; // stop the loop } console.log(a[i]); } //> 0, 1 ``` 另一個常見的需求是結束我們的變數。 一個快速的方式是使用 `.forEach`,但是我們沒有辦法使用 `break`。在這個情況我們要讓 forEach 透過 `return` 來更貼近 `continue` 的功能。 ```javascript [0, 1, 2, 3, 4].forEach(function(val, i) { if (val === 2) { // 我們要如何停止? return true; } console.log(val); // 你的程式碼 }); //> 0, 1, 3, 4 ``` `.some` 是 Array 的原型(prototype)。它透過提供的 function 測試陣列內的元素是否滿足,如果任何值回傳 true,它將停止執行。這裡是 [MDN 連結](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some) 有更多關於 `some` 的細節。 引用以上連結內的範例: ```javascript const isBiggerThan10 = numb => numb > 10; [2, 5, 8, 1, 4].some(isBiggerThan10); // false [12, 5, 8, 1, 4].some(isBiggerThan10); // true ``` 使用 `.some` 我們取得像是 `.forEach` 迭代的功能和透過 `return` 來替代 `break` 的方式。 ```javascript [0, 1, 2, 3, 4].some(function(val, i) { if (val === 2) { return true; } console.log(val); // 你的程式碼 }); //> 0, 1 ``` 保持回傳 `false` 來確保`持續`迭代到下一個元素。當你回傳 `true` 時,迴圈將 `break` 且 `a.some(..)` 將 `return` `true`。 ```javascript // Array contains 2 const isTwoPresent = [0, 1, 2, 3, 4].some(function(val, i) { if (val === 2) { return true; // break } }); console.log(isTwoPresent); //> true ``` 還有 `.every` 也可以使用。我們要回傳和 `.some` 相反的 boolean 值。 ##### Playground ================================================ FILE: _posts/zh_TW/javascript/2016-08-25-keyword-var-vs-let.md ================================================ --- layout: post title: ES6 的 var 和 let tip-number: 59 tip-username: richzw tip-username-profile: https://github.com/richzw tip-tldr: 在這個 tip,我將介紹 var 和 let 他們之間不同的作用域。我應該使用 let 替代 var 嗎?讓我們來看一下吧! redirect_from: - /zh_tw/keyword-var-vs-let/ categories: - zh_TW - javascript --- ### 概觀 - 透過 `var` 被定義的變數,它的作用域是在 function 或任何外部已經被宣告的 function,是全域的 。 - 透過 `let` 被定義的變數,它的作用域是在一個區塊(block)。 ```js function varvslet() { console.log(i); // i 是 undefined 的,因為 hositing // console.log(j); // ReferenceError: j 沒有被定義 for( var i = 0; i < 3; i++ ) { console.log(i); // 0, 1, 2 }; console.log(i); // 3 // console.log(j); // ReferenceError: j 沒有被定義 for( let j = 0; j < 3; j++ ) { console.log(j); }; console.log(i); // 3 // console.log(j); // ReferenceError: j 沒有被定義 } ``` ### 詳細的差別 - 變數 Hoisting `let` 不會被 hoist 到整個區塊的作用域。相較之下,`var` 可以被 hoist。 ```js { console.log(c); // undefined。因為 hoisting var c = 2; } { console.log(b); // ReferenceError: b 沒有被定義 let b = 3; } ``` - 在迴圈的 Closure `let` 在每次迴圈可以重新被 bind,確保它從先前結束的迴圈被重新賦值,所以在 closure 它被用來避免一些問題。 ```js for (var i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); // 輸出 '5' 五次 }, 100); } ``` fter replacing `var` with `let`使用 `let` 替代 `var` ```js // print 1, 2, 3, 4, 5 for (let i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); // 輸出 0, 1, 2, 3, 4 }, 100); } ``` ### 我應該使用 `let` 替代 `var` 嗎? > 不是的,`let` 是新的區塊作用域。語法強調在當 `var` 已經信號區塊作用域時,`let` 應該替代 `var` ,否則請不要單獨使用 `var`。`let` 改善了在 JS 作用域的選項,而不是取代。`var` 對於變數依然是有用的,可被用在整個 function 之中。 ### `let` 兼容性 - 在 server 端,像是 Node.js,你現在可以安心的使用 `let`。 - 在 client 端,透過 transpiler(像是 [Traceur](https://github.com/google/traceur-compiler)),你可以安心的使用 `let` 語法。否則請在[這裡](http://caniuse.com/#search=let)確認你的瀏覽器是否支援。 ### Playground ### 更多資訊 - [Let keyword vs var keyword](http://stackoverflow.com/questions/762011/let-keyword-vs-var-keyword) - [For and against let](https://davidwalsh.name/for-and-against-let) - [Explanation of `let` and block scoping with for loops](http://stackoverflow.com/questions/30899612/explanation-of-let-and-block-scoping-with-for-loops/30900289#30900289). ================================================ FILE: _posts/zh_TW/javascript/2016-10-28-three-useful-hacks.md ================================================ --- layout: post title: 三個有用的技巧 tip-number: 60 tip-username: leandrosimoes tip-username-profile: https://github.com/leandrosimoes tip-tldr: 三個非常有用的技巧來加速你的開發速度。 redirect_from: - /zh_tw/three-useful-hacks/ categories: - zh_TW - javascript --- #### 由後往前取得陣列數值: 如果你想要由後往前取得陣列的數值,可以這麼做: ```javascript var newArray = [1, 2, 3, 4]; console.log(newArray.slice(-1)); // [4] console.log(newArray.slice(-2)); // [3, 4] console.log(newArray.slice(-3)); // [2, 3, 4] console.log(newArray.slice(-4)); // [1, 2, 3, 4] ``` #### Short-circuits 狀態 如果你有一個 function,它的狀態為 `true`,像是這樣: ```javascript if (condition){ dosomething(); } ``` 你可以像這樣使用 short-circuit: ```javascript condition && dosomething(); ``` #### 使用「||」設定變數的預設值 如果你要在變數設定一個預設值,你可以這麼做: ```javascript var a; console.log(a); //undefined a = a || 'default value'; console.log(a); //default value a = a || 'new value'; console.log(a); //default value ``` ================================================ FILE: _posts/zh_TW/javascript/2017-01-19-binding-objects-to-functions.md ================================================ --- layout: post title: Bind 物件到 function tip-number: 61 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 了解在 JavaScript 中如何使用 `Bind` 方法和物件以及 function redirect_from: - /zh_tw/binding-objects-to-functions/ categories: - zh_TW - javascript --- 很多時候,我們需要 bind 一個物件到一個 function 的 this 物件。當 this 明確的被指定在 JS 的 bind 方法且我們需要調用所需的方法。 ### Bind 語法 ```js fun.bind(thisArg[, arg1[, arg2[, ...]]]) ``` ## 參數 **thisArg** `this` 參數值可以被傳送到目標的 function 同時呼叫 被 `bind` 的 function。 **arg1, arg2, ...** 前置參數被傳送到被 `bind` 的 function 同時調用目標 function。 **回傳值** 一個給定 function 的副本以及指定的 `this` 值和初始參數。 ### 在 JS 的 action 中 Bind 方法 ```js const myCar = { brand: 'Ford', type: 'Sedan', color: 'Red' }; const getBrand = function () { console.log(this.brand); }; const getType = function () { console.log(this.type); }; const getColor = function () { console.log(this.color); }; getBrand(); // object not bind,undefined getBrand(myCar); // object not bind,undefined getType.bind(myCar)(); // Sedan let boundGetColor = getColor.bind(myCar); boundGetColor(); // Red ``` ================================================ FILE: _posts/zh_TW/javascript/2017-03-09-working-with-websocket-timeout.md ================================================ --- layout: post title: 處理 Websocket 超時 tip-number: 63 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 一個控制 timeout 的技巧 categories: - zh_TW - javascript --- 在 websocket 連接被建立的情況下,如果一段時間處於非活動狀態,伺服器或防火牆可能超時或終止連接。為了要處理這個情況,我們向伺服器週期性的傳送訊息。為了控制超時,我們將新增兩個 function 再我們的程式碼:一是確認連接持久連線(keep alive),另一個則是取消持久連線。我們也還需要一個共同的 `timerID` 變數。 讓我們來實作看看: ```js var timerID = 0; function keepAlive() { var timeout = 20000; if (webSocket.readyState == webSocket.OPEN) { webSocket.send(''); } timerId = setTimeout(keepAlive, timeout); } function cancelKeepAlive() { if (timerId) { cancelTimeout(timerId); } } ``` 現在我們在 task 有兩個我們需要的 function,我們將放置 ```keepAlive()``` function 在 ```onOpen()``` 方法的 websocket 連接之後以及 ```cancelKeepAlive()``` function 在 ```onClose()``` 方法的 websocket 連接後面。 沒錯!我們完美實作了 websocket 超時問題的方法。 ================================================ FILE: _posts/zh_TW/javascript/2017-03-12-3-array-hacks.md ================================================ --- layout: post title: 使用 Array 的三個技巧 tip-number: 64 tip-username: hassanhelfi tip-username-profile: https://twitter.com/hassanhelfi tip-tldr: 在 JavaScript 中隨處都可以看見陣列,ECMAScript 6 介紹了展開運算符(spread operator),你可以透過它來處理陣列。在這篇 tip,我將示範三個有用的技巧,當你在寫程式時可以使用。 categories: - zh_TW - javascript --- 在 JavaScript 中隨處都可以看見陣列,ECMAScript 6 中介紹到了[展開運算符](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator),你可以透過它來處理陣列。在這篇 tip,我將示範三個有用的技巧,當你在寫程式時可以使用。 ### 1. 迭代一個空的陣列 JavaScript 陣列特性是鬆散的,所以在這裡有許多坑。嘗試使用陣列的建構子建立一個陣列,你會明白我的意思。 ```javascript > const arr = new Array(4); [undefined, undefined, undefined, undefined] ``` 你可能發現到,迭代一個鬆散的陣列並應用在某些轉換是相當困難的。 ```javascript > const arr = new Array(4); > arr.map((elem, index) => index); [undefined, undefined, undefined, undefined] ``` 為了解決這個問題,當你在建立陣列時你可以使用 `Array.apply`。 ```javascript > const arr = Array.apply(null, new Array(4)); > arr.map((elem, index) => index); [0, 1, 2, 3] ``` ### 2. 傳送一個空的參數到方法 如果你想要呼叫一個方法並忽略其中一個參數,如果你將參數為空的話,JavaScript 會告訴你發生錯誤。 ```javascript > method('parameter1', , 'parameter3'); Uncaught SyntaxError: Unexpected token , ``` 通常我們會採取傳送一個 `null` 或 `undefined` 的處理方式。 ```javascript > method('parameter1', null, 'parameter3') // 或者 > method('parameter1', undefined, 'parameter3'); ``` 我個人不喜歡使用 `null`,由於 JavaScript 會它當作一個 object,那是很奇怪的。隨著採用 ES6 的展開運算符,有一個更簡潔的方法將空參數傳送給方法。如我們先前所提到的,陣列的特性是鬆散的而且傳送空的值是可行的。我們將利用這個優勢來使用。 ```javascript > method(...['parameter1', , 'parameter3']); // 成功! ``` ### 唯一的陣列值 我總是在想為什麼陣列建構子沒辦法指定的方法,方便的使用唯一的陣列值。展開運算符在這裡解救了我。使用展開運算符與 `Set` 建構子來產生唯一的陣列值。 ```javascript > const arr = [...new Set([1, 2, 3, 3])]; [1, 2, 3] ``` ================================================ FILE: _posts/zh_TW/javascript/2017-03-16-tapping-for-quick-debugging.md ================================================ --- layout: post title: 使用監聽(tap)來快速 debug tip-number: 65 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 一個相當有用 function 用來快速 debung 一連串的 function 呼叫、匿名函式,實際上你可能只是想要列印而已。 categories: - zh_TW - javascript --- 一個相當有用 function 用來快速 debung 一連串的 function 呼叫、匿名函式,實際上你可能只是想要列印而已。 ``` javascript function tap(x) { console.log(x); return x; } ``` 為什麼你不使用 `console.log` 的老方式呢?讓我示範一個範例: ``` javascript bank_totals_by_client(bank_info(1, banks), table) .filter(c => c.balance > 25000) .sort((c1, c2) => c1.balance <= c2.balance ? 1 : -1 ) .map(c => console.log(`${c.id} | ${c.tax_number} (${c.name}) => ${c.balance}`)); ``` 現在,假設你從一連串的 function 呼叫確沒有得到任何東西(可能是一個錯誤)。 哪裡失敗了?或許是 `bank_info` 沒有回傳任何東西,所以我們將監聽它: ``` javascript bank_totals_by_client(tap(bank_info(1, banks)), table) ``` 根據我們部份的實作,它可能會列印出一些東西或者什麼都沒有。 我假設我們監聽得到的資訊是正確的,因此 bank_info 並不會產生任何結果。 我們必須移到下一個 filter function。 ``` javascript .filter(c => tap(c).balance > 25000) ``` 我們實際上有接收到任何的 client 資訊嗎?如果有,bank_totals_by_client 正常的工作。或許它是 filter 內的條件? ``` javascript .filter(c => tap(c.balance > 25000)) ``` 啊!我們沒看到其他東西,但是 `false` 被列印出來,所以沒有 client 是大於 25000,這也是為什麼 function 沒有回傳任何東西。 ## 更進階的監聽 ``` javascript function tap(x, fn = x => x) { console.log(fn(x)); return x; } ``` 現在我們討論一個更進階的,如果我們想要執行某個運算符*事先*到 tap(監聽)?例如,我需要存取目前 object 的屬性,執行一個邏輯運算等等。與被我們監聽的 object?然後我們呼叫 tap 和一個額外的參數,在監聽的時候應用這個 function。 ``` javascript tap(3, x => x + 2) === 3; // 列印 5,但是表達是計算為 true,為什麼 :-)? ``` ================================================ FILE: _posts/zh_TW/javascript/2017-03-29-recursion-iteration-and-tail-calls-in-js.md ================================================ --- layout: post title: 在 JavaScript 遞歸、反覆運算並尾呼叫 tip-number: 67 tip-username: loverajoel tip-username-profile: https://twitter.com/loverajoel tip-tldr: 如果你已經有一段實務經驗了,你最有可能會遇到遞迴,給定一個數字來定義階乘(factorial),`n! = n * n - 1 * ... * 1` 就是一個標準的例子... tip-md-link: https://github.com/loverajoel/jstips/blob/master/_posts/en/javascript/2017-03-29-recursion-iteration-and-tail-calls-in-js.md categories: - zh_TW - javascript --- 如果你已經有一段實務經驗了,你最有可能會遇到遞迴,給定一個數字來定義階乘(factorial),`n! = n * n - 1 * ... * 1` 就是一個標準的例子... ``` javascript function factorial(n) { if (n === 0) { return 1; } return n * factorial(n - 1); } ``` 上面的範例是最常見的階乘 function。 為了更完整理解,讓我們來看 `n = 6` 是如何執行的: - factorial(6) - 6 * factorial(5) - 5 * factorial (4) - 4 * factorial(3) - 3 * factorial(2) - 2 * factorial(1) - 1 * factorial(0) - 1 - (恢復先前的執行) 1 * 1 = 1 - (恢復...) 2 * 1 = 2 - (...) 3 * 2 = 6 - ... 4 * 6 = 24 - 5 * 24 = 120 - 6 * 120 = 720 - factorial(6) = 120 現在,我們必須非常謹慎的知道發生了什麼事,我們會在下個部分理解到。 當我們在調用一個 function 時,會發生許多事。根據目前的資訊(例如: n 的值),我們在呼叫下一個 function 時必須儲存目前位置。然後空間被分配到新的 function 並產生一個新的 frame。 它仍然繼續執行,我們一直堆積這些 frame 然後並放開,透過它們取代 function 呼叫和被回傳的值。 另一件要注意的事情是,透過我們的 function 處理所產生的 shape。 如果我呼叫這些*遞歸*的 shape 你應該不會很驚訝。因為我們有一個*遞歸的過程*。 讓我們看一下第二種方式實作的 function: ``` javascript function factorial(n, res) { if (n === 0) { return res; } return factorial(n - 1, res * n); } ``` 我們可以透過定義一個內建 function 更進一步封裝功能。 ``` javascript function factorial(n) { function inner_factorial(n, res) { if (n === 0) { return res; } return inner_factorial(n - 1, res * n); } return inner_factorial(n, 1); } ``` 讓我們看一下它是如何執行的: - factorial(6) - 內部匿名 function(iaf)和(n = 6, res = 1)被呼叫 - iaf(5, 1 * 6) - iaf(4, 6 * 5) - iaf(3, 30 * 4) - iaf(2, 120 * 3) - iaf(1, 360 * 2) - iaf(0, 720) - 720 - 720 - 720 - 720 - 720 - 720 - 720 - iaf (6, 1) = 720 - factorial(6) = 720 你可能會注意到,我們不需要在回傳 stack 時執行任何計算。我們只是回傳值。但是根據我們的規則,我們儲存 state 最為一個 stack frame,即使它在這個中 chain 最後沒有被任何的使用。 然而根據規則,不適用於每一個語言。事實上,在 Schema 中,對於這樣的 chain 是必須優化的,在尾呼叫時被優化。這是確保我們的 stack 不會有不必要的 frame。 因此,我們在先前的計算會看到這種方式︰ - factorial(6) - iaf(6, 1) - iaf(5, 6) - iaf(4, 30) - iaf(3, 120) - iaf(2, 360) - iaf(1, 720) - iaf(0, 720) - 720 事實上,看起來實在很像: ``` javascript res = 1; n = 6; while(n > 1) { res = res * n; n--; } ``` 意思是,我們確實有一個*反覆運算的處理*,即使我們使用遞歸。這是多麽酷啊? 好消息是,這是 ES6 的 feature。只要你在尾部遞歸呼叫而且你的 function 是 strict 模式,尾呼叫優化將從 `超過 stack 最大的大小` 踢除錯誤並儲存。 ================================================ FILE: _posts/zh_TW/javascript/2017-04-03-why-you-should-use-Object.is()-in-equality-comparison.md ================================================ --- layout: post title: 為什麼在做相等比較的時候,你應該使用 Object.is() tip-number: 68 tip-username: TarekAlQaddy tip-username-profile: https://github.com/TarekAlQaddy tip-tldr: 在 JavaScript 中對於較鬆散的相等比較,有更好的解決方式 categories: - zh_TW - javascript --- 我們都知道 JavaScript 是一個較鬆散的型別,在某些情況它特別是在 `==` 的情況,當你透過 `==` 來做比較時,常常會得到意外的結果,基於這個原因我們會強制轉換或建立新的變數來「將兩個操作變數中的其中一個轉換為其他類型,然後進行比較」。 ``` javascript 0 == ' ' //true null == undefined //true [1] == true //true ``` 所以他們提供我們三等號 `===` 的相等操作符,它更嚴格而且不強制去轉換變數,然而 `===` 比較不是一個比較好的解決方式,你可以得到這樣的結果: ``` javascript NaN === NaN //false ``` 好消息是在 ES6 有一個新的 `Object.is()`,它是更精確而且和 `===` 有相同的功能,在某些特殊情況下也運作的很好: ``` javascript Object.is(0 , ' '); //false Object.is(null, undefined); //false Object.is([1], true); //false Object.is(NaN, NaN); //true ``` Mozilla 團隊 不認為 `Object.is` 比 `===` 更嚴格,他們宣稱我們應該思考這些方法如何處理 `NaN`、`-0` 和 `+0`,但是整體來說我認為它在實際的應用程式中是一個好的解決方式。 現在我們來看看這個比較表: ![differences of operators in equality comparisons javascript](http://i.imgur.com/pCyqkLc.png) ## 參考: [Equality comparisons and sameness](http://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness) ================================================ FILE: _posts/zh_TW/javascript/2017-04-05-picking-and-rejecting-object-properties.md ================================================ --- layout: post title: 選擇或是拋棄物件屬性 tip-number: 70 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 有時候我們需要列出物件中某些屬性,假設我們有一個陣列來表達一個資料表,我們需要有些 function 來 `select` 欄位。 categories: - zh_TW - javascript --- 有時候我們需要列出物件中某些屬性,假設我們有一個陣列來表達一個資料表,我們需要有些 function 來 `select` 欄位: ``` javascript function pick(obj, keys) { return keys.map(k => k in obj ? {[k]: obj[k]} : {}) .reduce((res, o) => Object.assign(res, o), {}); } const row = { 'accounts.id': 1, 'client.name': 'John Doe', 'bank.code': 'MDAKW213' }; const table = [ row, {'accounts.id': 3, 'client.name': 'Steve Doe', 'bank.code': 'STV12JB'} ]; pick(row, ['client.name']); // 取得 Client 名稱 table.map(row => pick(row, ['client.name'])); // 取得一些 client 名稱的列表 ``` 在 pick 我們透過一個小手段,首先,我們 `map` 一個 function,每次透過目前的 key 只有該屬性的物件被回傳(或者,如果在物件內沒有任何的屬性會回傳一個空的物件)。然後透過 merge 物件,我們 `reduce` 這些單一屬性的集合物件。 但是如果我們想要 `reject` 屬性呢?好吧,function 需要一些改變: ``` javascript function reject(obj, keys) { return Object.keys(obj) .filter(k => !keys.includes(k)) .map(k => Object.assign({}, {[k]: obj[k]})) .reduce((res, o) => Object.assign(res, o), {}); } // 或者恢復 pick function reject(obj, keys) { const vkeys = Object.keys(obj) .filter(k => !keys.includes(k)); return pick(obj, vkeys); } reject({a: 2, b: 3, c: 4}, ['a', 'b']); // => {c: 4} ``` ================================================ FILE: _posts/zh_TW/javascript/2017-06-15-looping-over-arrays.md ================================================ --- layout: post title: 循環陣列 tip-number: 79 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 這裡有一些方法在 JavaScript 中可以循環陣列。我們將從典型例子並且補充標準規範。 categories: - zh_TW - javascript --- 這裡有一些方法在 JavaScript 中可以循環陣列。我們將從典型例子並且補充標準規範。 ## while ```javascript let index = 0; const array = [1,2,3,4,5,6]; while (index < array.length) { console.log(array[index]); index++; } ``` ## for(典型) ```javascript const array = [1,2,3,4,5,6]; for (let index = 0; index < array.length, index++) { console.log(array[index]); } ``` ## forEach ```javascript const array = [1,2,3,4,5,6]; array.forEach(function(current_value, index, array) { console.log(`At index ${index} in array ${array} the value is ${current_value}`); }); // => undefined ``` ## map 最後的 construct 很有用,然而,它不會回傳一個新的陣列,這可能對你指定的情況是不合乎的。`map` 透過對每個元素應用一個函式,並回傳新的陣列來解決問題。 ```javascript const array = [1,2,3,4,5,6]; const square = x => Math.pow(x, 2); const squares = array.map(square); console.log(`Original array: ${array}`); console.log(`Squared array: ${array}`); ``` `map` 完整個宣告方式:`.map(current_value, index, array)`。 ## reduce 根據 MDN: > reduce() 方法對累加器和數組中的每個元素(從左到右)應用一個函式,將其減少為一個值。 ```javascript const array = [1,2,3,4,5,6]; const sum = (x, y) => x + y; const array_sum = array.reduce(sum, 0); console.log(`The sum of array: ${array} is ${array_sum}`); ``` ## filter 基於一個布林函式對陣列進行過濾。 ```javascript const array = [1,2,3,4,5,6]; const even = x => x % 2 === 0; const even_array = array.filter(even); console.log(`Even numbers in array ${array}: ${even_array}`); ``` ## every 取得一個陣列,並且想要測試每個元素中是否滿足給定的條件? ```javascript const array = [1,2,3,4,5,6]; const under_seven = x => x < 7; if (array.every(under_seven)) { console.log('Every element in the array is less than 7'); } else { console.log('At least one element in the array was bigger than 7'); } ``` ## some 測試至少一個元素是否 match 我們的布林函式。 ```javascript const array = [1,2,3,9,5,6,4]; const over_seven = x => x > 7; if (array.some(over_seven)) { console.log('At least one element bigger than 7 was found'); } else { console.log('No element bigger than 7 was found'); } ``` ================================================ FILE: _posts/zh_TW/more/2017-04-06-vuejs-how-vuejs-makes-a-copy-update-replace-inside-the-data-binding.md ================================================ --- layout: post title: Vuejs 如何在資料綁定時,拷貝、更新並取代原來的資料 tip-number: 71 tip-username: pansila tip-username-profile: https://github.com/pansil tip-tldr: 在這篇 Tip,我將介紹一個範例來示範 Vuejs 可能和其他框架產生的衝突。 categories: - zh_TW - more --- ### 概觀 Vuejs 是一個相當優雅的軟體,相對於其他流行的框架像是 Angularjs 和 Reactjs,它讓你保持簡潔而且強大。 你以前可能因為害怕而放棄那些複雜的框架,但是 Vuejs 不像上面那些框架這麼複雜,你可以很快速的上手。 如果你不知道它是如何運作的,你可能會時常在踩地雷。這裡是另一個簡單而且受歡迎的 Framework7 UI 框架衝突的範例。 ```html

      {% raw %}{{content}}{% endraw %}

      ``` ```js var myApp = new Framework7(); myApp.onPageInit('test', function (page) { new Vue({ el: '#test', data: { content: 'hello world' } }); }); ``` 你可能會很驚訝它不能正常的運作,在新的頁面載入後沒有任何的改變。事實上,Vue 內部拷貝了目標 HTML template 元素,在透過綁定後,新的拷貝資料會取代原先的舊資料。當頁面載入時,Framework7 調用 `PageInit` callback,接著 Vue 在 `` 元素進行更新。現在 DOM Tree 有了新的 `` 拷貝元素,而 Framework7 在不知情的情況下持續執行剩餘的初始化工作在舊的 `` 元素,最後顯示出來這就是根本的原因。 為了要解決它,別讓 Vue selector 目標在 `` 元素,而是在它的子元素。這樣 Vuejs 在做資料綁定時不會影響整個頁面了。 ```js var myApp = new Framework7(); myApp.onPageInit('test', function (page) { new Vue({ el: '#test1', data: { content: 'hello world' } }); }); ``` ### 更多資訊 - [Vue](https://github.com/Vuejs/Vue) - [Framework7](https://framework7.io/) ================================================ FILE: _posts/zh_TW/react/2016-01-02-keys-in-children-components-are-important.md ================================================ --- layout: post title: 在子元件 Keys 是很重要的 tip-number: 02 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 你可以從陣列動態建立 key 屬性並傳送到所有的元件(component)。它是一個唯一以及固定的 id,React 用來識別 DOM 裡面的每個元件,並區別它是否為同一個元件。使用 keys 可以確保子元件被保護而不會被重覆建立,也可以防止奇怪的事件發生。 tip-writer-support: https://www.coinbase.com/loverajoel redirect_from: - /zh_tw/keys-in-children-components-are-important/ categories: - zh_TW - react --- 你可以從陣列動態建立 [key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children) 屬性並傳送到所有的元件(component)。它是一個唯一以及固定的 id,React 用來識別 DOM 裡面的每個元件,並區別它是否為同一個元件。使用 keys 可以確保子元件被保護而不會被重覆建立,也可以防止奇怪的事件發生。 > Key 跟效能不太相關,它跟元件識別較有關係(反之,它間接提升了效能)。隨機賦值和改變數值將無法識別。[Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939) - 使用物件內存在的唯一值。 - 在你的父元件定義 keys,而不是子元件。 ```javascript //bad ... render() {
      {% raw %}{{item.name}}{% endraw %}
      } ... //good ``` - [使用陣列索引是不好的習慣。](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318#.76co046o9) - `random()` 將無法使用。 ```javascript //bad ``` - 你可以建立你自己唯一的 id。確認這個方法夠快速可以附加到你的物件上。 - 當你的子元件數量很多或者含有龐大的元件,使用 keys 可以提高效能。 - [你必須提供 key 屬性給 ReactCSSTransitionGroup 的所有子元件。](http://docs.reactjs-china.com/react/docs/animation.html) ================================================ FILE: _posts/zh_TW/react/2017-03-27-state-to-props-maps-with-memory.md ================================================ --- layout: post title: 從 State 到 Prop 的映射 tip-number: 66 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 你可能在你建立的 React Apps 使用 Redux Store 有段時間了,當 component 經常更新時,你可能會感到很不便。 你已經徹底建立你的 state,而且架構你的每個 component 所需要的 state。然而在這些更新 state 的背後,總是在做 state 的 map 和計算。 tip-writer-support: https://www.coinbase.com/loverajoel categories: - zh_TW - react --- 你可能在你建立的 React Apps 使用 Redux Store 有段時間了,當 component 經常更新時,你可能會感到很不便。 你已經徹底建立你的 state,而且架構你的每個 component 所需要的 state。然而在這些更新 state 的背後,總是在做 state 的 map 和計算。 ## 使用 Reselector 來拯救 如何只計算我們所需要的 state?如果這個 state tree 的部分改變了,沒錯!請更新 state。 讓我看一下簡單的 TODOs List,特別是在我們的 container 負責取得 visible TODOs 的 visibility filter 程式碼: ```javascript const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } } ``` 在每次我們的 component 被更新時,它們都需要被重新計算一遍。事實上,Reselector 允許我們取得我們的 `getVisibleTodos` function 的 memory。 所以它知道我們部分的 state 是否需要改變,如果它們需要改變,它將繼續處理,如果沒有的話,它將回傳最後的結果。 讓我們使用 selectors! ```javascript const getVisibilityFilter = (state) => state.visibilityFilter const getTodos = (state) => state.todos ``` 現在我們可以 *select* 我們想要繼續追蹤的部分 state,我們準備把 memory 給 getVisibleTodos: ```javascript import {createSelector} from 'reselect' const getVisibleTodos = createSelector( [getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filtler(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } ) ``` 並改變我們的 map 給它完整的 state: ```javascript const mapStateToProps = (state) => { return { todos: getVisibleTodos(state) } } ``` 就是這樣!我們給定 memory 到我們的 function。如果涉及的 state 沒有改變的話,不會有額外的去計算 state。 ================================================ FILE: _posts/zh_TW/react/2017-04-04-enhancing-react-components-composition.md ================================================ --- layout: post title: 增強你的 React component 的組合 tip-number: 69 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: React 對於你如何組織你的 component 是相當靈活的,但這個模式讓你的 app component 可以更有效的利用。 tip-writer-support: https://www.coinbase.com/loverajoel categories: - zh_TW - react --- > 在 Facebook 上,我們使用上千個 React 的 component, > 我們找不到任何推薦你使用 component 建立繼承的情況。 *Long live to composition, inheritance is dead.* 所以,你如何在 React *繼承*一個 component? 好吧,相當明顯的,@Facebook 的人認為從父 component 繼承是不恰當的,讓我們看看其他方法: ## 提前規劃 在某些情況,你有一個 component 但是不知道它會不會有 children(大多數人都是這樣)。關於這點,React 給了我們 `props.children`: ``` javascript function List(props) { return (
        {props.children}
      ); } function DownloadMenu() { return (
    • Download SBCL
    • Download Racket
    • Download Haskell
    • ); } ``` 我們可以看到,`props.children` 接收從 component 開始到結束 tag 內所有的元素。 此外,我們可以利用 `props` 來填補: ``` javascript function ListWithHeader(props) { return (
      • {props.header}
      • {props.children}
      ); } function DownloadMenuWithHeader() { return ( }>
    • Download SBCL
    • ...
      ); } ``` ## 通用 component 以及客製化 所以,我們有了一個很棒的 *FolderView* component ``` javascript function FolderView(props) { return (

      {props.folderName}

        {props.availableActions}
        {props.files}
      ); } ``` 這可以表達我們檔案系統內任何的資料夾,然而我們只想要區別它,所以它顯示圖片和桌面資料夾。 ``` javascript function DesktopFolder(props) { return ( Create Folder
    • Create Document
    • } files={props.files} /> ); } function PicturesFolder(props) { return ( New Picture } files={props.files} /> ); } ``` 就是這樣,我們*客製化*我們的 component,沒有建立任何的繼承結構。 ================================================ FILE: _posts/zh_TW/react/2017-04-10-adventurers-guide-to-react.md ================================================ --- layout: post title: React 冒險者指南(Part I) tip-number: 72 tip-username: loverajoel tip-username-profile: https://github.com/loverajoel tip-tldr: 所以你聽說過 *React* 這件事,實際上或許你通過了一些網頁看過了文件,然後你突然碰到一些莫名其妙的與 Webpack、Browserify、Yarn、Babel、NPM 等等更多相關的東西。 categories: - zh_TW - react --- > I used to be an adventurer like you, but then I took a RTFM in the knee... > > Guard outside of Castle Reducer in the Land of React 所以你聽說過 *React* 這件事,實際上或許你通過了一些網頁看過了文件,然後你突然碰到一些莫名其妙的與 Webpack、Browserify、Yarn、Babel、NPM 等等更多相關的東西。 因此,你到了它們的官方網站透過文件,只會發現自己為了使用 JavaScript 建立網頁應用程式,而在不斷增加的現代工具集合中所迷失了。 你不一定會害怕。 ## Yarn、NPM 它們只是套件依賴管理工具,沒什麼好害怕的。事實上,它們是比較沒這麼恐怖的部分。如果你使用發行版的 GNU/Linux、FreeBSD、在 MacOSX 的 Homebrew、Ruby 和 Bundler、Lisp 和 Quicklisp 只是一些名詞而已,你會熟悉這些概念。 React 只是 JavaScript 或稱 ECMAScript 的一部份。 假設現在你真的想要使用 React 來建立應用程式,去面對這些工具吧,它們是很受歡迎的。傳統的方式是,我們到了 React 這個領域時,去詢問來如何操作這些工具。問題是這非常浪費時間,你必須與他們保持聯繫,如果這些工具都無法使用了,你需要取得新的。 然而,隨著一些 drone 的到來,一些聰明的傢伙開始展了不同城鎮,省市的登記(註:作者使用一些描述的手法來說明)。此外,他們建立特別的 drone 可以將這些工具交給你。 現在,為了要在你的專案使用 React,你需要: ```bash $ npm init . # 建立你的城鎮 $ npm install --save react # 在商店取得 react 並放入材料 ``` 很方便不是嗎?而且他是免費的。 ## Webpack、Browserify 以及他的朋友們 在過去的時間內,你不得不透過自己來建立你自己小鎮所有的事務。如果你想要一個 John McCarthy 的雕像,你需要取得大理石和一些所需要的 parentheses。隨著*套件依賴管理工具*的出現,得到了一些改善,它可以讓你取得大理石和一些材料。當然,你必須自己組合們。 然而從一些 Copy & Paste(公司)來的人們,開發一些工具,允許你可以按這特定順序來 copy & paste 你的材料。 這是相當酷的,你只要放置所有的材料,並指定他們要如何建造,這段時間都是相當和平的。 但是這個方法可能有些問題,我敢肯定有些人會想嘗試避開。也就是說,你想要透過車子來取代這些材料,你不得不重新建立整個城鎮。對於小型的城鎮沒問題,但是對於大型的城市透過這樣的方式就會遇到問題。 然後這靈感來自強大的主啊,接著 build 和 module bundler 就誕生了。 Module bundler 允許你畫出藍圖,並明確的指示城鎮如何被建造。此外,它們成長得相當快速,只支援重建城鎮的一部份,剩下是其餘的部分。 ## Babel Babel 是一個時空旅行者,它帶來了未來的材料讓你使用,像是 ES6/7/8 的特性。 ## 如何將他們混合在一起 通常,你將在你的專案內建立資料夾,取得 dependencies,設定你的 module bundler,它可以知道去哪裡尋找你的程式碼並輸出。此外,你可以能想要連接到開發伺服器,在你 building 時可以即時的得到 feedback。 然而,module bundler 的文件很龐大。對於新手冒險者有需多選擇和選項,可能會讓他失去動力。 ## create-react-app 和他的朋友們 因此另一個工具被建立了: ```bash $ npm install -g create-react-app # 安裝全域的 create-react-app $ create-react-app my-shiny-new-app ``` 這是這樣。它預先幫我們設定好了,所以我們不需要通過 Webpack/Browserify 來測試 React。它幫你帶了測試腳本,和一個開發伺服器等等更多其他東西。 然而,這不是我們歷史的結束。這裡有無數不同的 React builder 和 tool,可以參考這裡:[Complementary-Tools](https://github.com/facebook/react/wiki/Complementary-Tools)。