From 8b53a328216a5d56b1fe33571e4234cc6145d8a8 Mon Sep 17 00:00:00 2001 From: Peng-Jie Date: Sat, 6 Feb 2016 20:07:26 +0800 Subject: [PATCH] translate jstips 2015-12-29 - 2016-02-12 articles --- .../2015-12-29-insert-item-inside-an-array.md | 46 +++++++ .../2016-01-01-angularjs-digest-vs-apply.md | 37 +++++ ...ys-in-children-components-are-important.md | 43 ++++++ .../2016-01-03-improve-nested-conditionals.md | 92 +++++++++++++ ...orting-strings-with-accented-characters.md | 66 +++++++++ ...-differences-between-undefined-and-null.md | 40 ++++++ ...-method-for-arrays-and-a-single-element.md | 36 +++++ .../2016-01-07-use-strict-and-get-lazy.md | 61 +++++++++ ...1-08-converting-a-node-list-to-an-array.md | 43 ++++++ _posts/zh_TW/2016-01-09-template-strings.md | 37 +++++ ...1-10-check-if-a-property-is-in-a-object.md | 59 ++++++++ _posts/zh_TW/2016-01-11-hoisting.md | 51 +++++++ ...domandatory-parameters-in-es6-functions.md | 27 ++++ ...asure-performance-of-a-javascript-block.md | 32 +++++ .../zh_TW/2016-01-14-fat-arrow-functions.md | 102 ++++++++++++++ ...y-of-using-indexof-as-a-contains-clause.md | 84 ++++++++++++ ...passing-arguments-to-callback-functions.md | 54 ++++++++ ...dejs-run-a-module-if-it-is-not-required.md | 28 ++++ .../zh_TW/2016-01-18-rounding-the-fast-way.md | 37 +++++ .../2016-01-19-safe-string-concatenation.md | 36 +++++ ...objects-to-enable-chaining-of-functions.md | 33 +++++ _posts/zh_TW/2016-01-21-shuffle-an-array.md | 37 +++++ .../2016-01-22-two-ways-to-empty-an-array.md | 61 +++++++++ ...016-01-23-converting-to-number-fast-way.md | 28 ++++ .../zh_TW/2016-01-24-use_===_instead_of_==.md | 29 ++++ ...immediately-invoked-function-expression.md | 46 +++++++ ...filtering-and-sorting-a-list-of-strings.md | 56 ++++++++ ...16-01-27-short-circiut-evaluation-in-js.md | 78 +++++++++++ ...2016-01-28-curry-vs-partial-application.md | 113 +++++++++++++++ ...up-recursive-functions-with-memoization.md | 78 +++++++++++ ...nverting-truthy-falsy-values-to-boolean.md | 28 ++++ ...tions\342\200\224it-kills-optimization.md" | 42 ++++++ ...escue-adding-order-to-object-properties.md | 72 ++++++++++ ...reate-range-0...n-easily-using-one-line.md | 55 ++++++++ ...6-02-03-implementing-asynchronous-loops.md | 93 +++++++++++++ .../zh_TW/2016-02-04-assignment-shorthands.md | 97 +++++++++++++ .../zh_TW/2016-02-05-observe-dom-changes.md | 67 +++++++++ .../zh_TW/2016-02-06-deduplicate-an-array.md | 129 ++++++++++++++++++ ...g-multidimensional-arrays-in-javascript.md | 60 ++++++++ .../zh_TW/2016-02-08-advanced-properties.md | 112 +++++++++++++++ .../zh_TW/2016-02-09-using-json-stringify.md | 66 +++++++++ .../2016-02-10-array-average-and-median.md | 63 +++++++++ .../2016-02-11-preventing-unapply-attacks.md | 56 ++++++++ ...se-destructuring-in-function-parameters.md | 28 ++++ 44 files changed, 2538 insertions(+) create mode 100644 _posts/zh_TW/2015-12-29-insert-item-inside-an-array.md create mode 100644 _posts/zh_TW/2016-01-01-angularjs-digest-vs-apply.md create mode 100644 _posts/zh_TW/2016-01-02-keys-in-children-components-are-important.md create mode 100644 _posts/zh_TW/2016-01-03-improve-nested-conditionals.md create mode 100644 _posts/zh_TW/2016-01-04-sorting-strings-with-accented-characters.md create mode 100644 _posts/zh_TW/2016-01-05-differences-between-undefined-and-null.md create mode 100644 _posts/zh_TW/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md create mode 100644 _posts/zh_TW/2016-01-07-use-strict-and-get-lazy.md create mode 100644 _posts/zh_TW/2016-01-08-converting-a-node-list-to-an-array.md create mode 100644 _posts/zh_TW/2016-01-09-template-strings.md create mode 100644 _posts/zh_TW/2016-01-10-check-if-a-property-is-in-a-object.md create mode 100644 _posts/zh_TW/2016-01-11-hoisting.md create mode 100644 _posts/zh_TW/2016-01-12-pseudomandatory-parameters-in-es6-functions.md create mode 100644 _posts/zh_TW/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md create mode 100644 _posts/zh_TW/2016-01-14-fat-arrow-functions.md create mode 100644 _posts/zh_TW/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md create mode 100644 _posts/zh_TW/2016-01-16-passing-arguments-to-callback-functions.md create mode 100644 _posts/zh_TW/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md create mode 100644 _posts/zh_TW/2016-01-18-rounding-the-fast-way.md create mode 100644 _posts/zh_TW/2016-01-19-safe-string-concatenation.md create mode 100644 _posts/zh_TW/2016-01-20-return-objects-to-enable-chaining-of-functions.md create mode 100644 _posts/zh_TW/2016-01-21-shuffle-an-array.md create mode 100644 _posts/zh_TW/2016-01-22-two-ways-to-empty-an-array.md create mode 100644 _posts/zh_TW/2016-01-23-converting-to-number-fast-way.md create mode 100644 _posts/zh_TW/2016-01-24-use_===_instead_of_==.md create mode 100644 _posts/zh_TW/2016-01-25-Using-immediately-invoked-function-expression.md create mode 100644 _posts/zh_TW/2016-01-26-filtering-and-sorting-a-list-of-strings.md create mode 100644 _posts/zh_TW/2016-01-27-short-circiut-evaluation-in-js.md create mode 100644 _posts/zh_TW/2016-01-28-curry-vs-partial-application.md create mode 100644 _posts/zh_TW/2016-01-29-speed-up-recursive-functions-with-memoization.md create mode 100644 _posts/zh_TW/2016-01-30-converting-truthy-falsy-values-to-boolean.md create mode 100644 "_posts/zh_TW/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions\342\200\224it-kills-optimization.md" create mode 100644 _posts/zh_TW/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md create mode 100644 _posts/zh_TW/2016-02-02-create-range-0...n-easily-using-one-line.md create mode 100644 _posts/zh_TW/2016-02-03-implementing-asynchronous-loops.md create mode 100644 _posts/zh_TW/2016-02-04-assignment-shorthands.md create mode 100644 _posts/zh_TW/2016-02-05-observe-dom-changes.md create mode 100644 _posts/zh_TW/2016-02-06-deduplicate-an-array.md create mode 100644 _posts/zh_TW/2016-02-07-flattening-multidimensional-arrays-in-javascript.md create mode 100644 _posts/zh_TW/2016-02-08-advanced-properties.md create mode 100644 _posts/zh_TW/2016-02-09-using-json-stringify.md create mode 100644 _posts/zh_TW/2016-02-10-array-average-and-median.md create mode 100644 _posts/zh_TW/2016-02-11-preventing-unapply-attacks.md create mode 100644 _posts/zh_TW/2016-02-12-use-destructuring-in-function-parameters.md diff --git a/_posts/zh_TW/2015-12-29-insert-item-inside-an-array.md b/_posts/zh_TW/2015-12-29-insert-item-inside-an-array.md new file mode 100644 index 00000000..1388088e --- /dev/null +++ b/_posts/zh_TW/2015-12-29-insert-item-inside-an-array.md @@ -0,0 +1,46 @@ +--- +layout: post + +title: 在陣列中加入元素 +tip-number: 00 +tip-username: loverajoel +tip-username-profile: https://github.com/loverajoel +tip-tldr: 在一個存在的陣列加入新的元素是一件很常見的事情,你可以使用 push 將元素加入到陣列的末端,或是使用 unshift 在陣列起始位置加入元素,也可以使用 slice 在陣列中間的地方加入元素。 + + +categories: + - zh_TW +--- + +在一個存在的陣列加入新的元素是一件很常見的事情,你可以使用 push 將元素加入到陣列的末端,或是使用 unshift 在陣列起始位置加入元素,也可以使用 slice 在陣列中間的地方加入元素。 + +那些都是已知的方法,但這並不代表沒有更好的效能的方法。讓我們開始吧: + +在陣列的末端加入元素我們可以簡單地透過 push() 方式,但以下是更能提升效能方法。 + +```javascript +var arr = [1, 2, 3, 4, 5]; + +arr.push(6); +arr[arr.length] = 6; // 在 Mac OS X 10.11.1 下的 Chrome 47.0.2526.106 加快了 43% +``` +這兩種方法都是修改原始的陣列。不相信嗎?請參考 [jsperf](http://jsperf.com/push-item-inside-an-array)。 + +現在,如果我們要嘗試在陣列的起始位置加入元素: + +```javascript +var arr = [1, 2, 3, 4, 5]; + +arr.unshift(0); +[0].concat(arr); // 在 Mac OS X 10.11.1 下的 Chrome 47.0.2526.106 加快了 98% +``` +這裡有更多的小細節:使用 unshift 修改原始的陣列;concat 返回新的陣列。 [jsperf](http://jsperf.com/unshift-item-inside-an-array) + +使用 splice 陣列的中間加入元素是相當容易的,而且是最高效的方法。 + +```javascript +var items = ['one', 'two', 'three', 'four']; +items.splice(items.length / 2, 0, 'hello'); +``` + +我嘗試在不同的瀏覽器和 OS 執行這些測試,他們的結果是相似的。我希望這些知識對你是有幫助的,也鼓勵你自己進行測試! diff --git a/_posts/zh_TW/2016-01-01-angularjs-digest-vs-apply.md b/_posts/zh_TW/2016-01-01-angularjs-digest-vs-apply.md new file mode 100644 index 00000000..c0957b22 --- /dev/null +++ b/_posts/zh_TW/2016-01-01-angularjs-digest-vs-apply.md @@ -0,0 +1,37 @@ +--- +layout: post + +title: AngularJs - `$digest` vs `$apply` +tip-number: 01 +tip-username: loverajoel +tip-username-profile: https://github.com/loverajoel +tip-tldr: JavaScript 模組以及建構步驟變得更多更複雜,但對於新的框架樣板呢? + +categories: + - zh_TW +--- + +AngularJs 最令人欣賞的特性之一是雙向資料綁定。AngularJS 透過循環方式(`$digest`)來檢查模型和視圖變化來實現這個功能。想要理解框架底層的工作方式,你必須了解這個概念。 + +當一個事件被觸發時,Angular 會檢查每個 watcher 變化,這是我們所知的 `$digest` 循環。 +有時候你需要強迫手動執行一個新的循環,你必須選擇正確的選項,因為這個階段是最影響效能之一。 + +### `$apply` +這個核心方法可以讓你明確地啟動 `$digest` 循環。意思說所有的 watchers 都會被確認;整個應用程式啟動 `$digest loop`。在內部,執行一個可選的功能參數,它會呼叫 `$rootScope.$digest();`。 + +### `$digest` +在這個情況下,`$digest` 方法在目前的作用域和子作用域啟動 `$digeset` 循環。你應該注意到父作用域不會被確認,也不會受影響。 + +### 建議 +- 當瀏覽器 DOM 事件在 AngularJS 之外被觸發使用 `$apply` 或 `$digest`。 +- 傳送一個函數表達式給 `$apply`,這裡有一個錯誤處理機制,並且允許整合在 `$digest` 循環的變化。 + +```javascript +$scope.$apply(() => { + $scope.tip = 'Javascript Tip'; +}); +``` + +- 如果你只要更新目前的作用域和子作用域,使用 `$digest`,以及防止在整個應用程式執行新的 digest 循環。這在性能上的好處是相當明顯的。 +- `$apply()` 對電腦來說是一個相當困難的處理程序,如果過多的綁定會造成性能上的問題。 +- 如果你使用 >AngularJS 1.2.X,使用 `$evalAsync`,這是一個核心方法,可以在目前的循環或下一個循環執行表達式,可以增加你的應用程式效能。 diff --git a/_posts/zh_TW/2016-01-02-keys-in-children-components-are-important.md b/_posts/zh_TW/2016-01-02-keys-in-children-components-are-important.md new file mode 100644 index 00000000..d54f58e5 --- /dev/null +++ b/_posts/zh_TW/2016-01-02-keys-in-children-components-are-important.md @@ -0,0 +1,43 @@ +--- +layout: post + +title: 在子元件 Keys 是很重要的 +tip-number: 02 +tip-username: loverajoel +tip-username-profile: https://github.com/loverajoel +tip-tldr: 從陣列中動態建立所有元件時,你必須傳送 key 屬性。它是一個唯一以及固定的 id,React 用來識別 DOM 裡面的每個元件,並區別它是否為同一個元件。使用 keys 可以確保子元件不會被重覆建立,也可以防止奇怪的事件發生。 + +categories: + - zh_TW +--- + +從陣列中動態建立所有元件時,你必須傳送 [key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children) 屬性。它是一個唯一以及固定的 id,React 用來識別 DOM 裡面的每個元件,並區別它是否為同一個元件。使用 keys 可以確保子元件不會被重覆建立,也可以防止奇怪的事件發生。 + +> Key 跟效能不太相關,它跟元件識別有關係(反之,它間接提升了效能)。隨機賦值和改變數值將無法識別。[Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939) + +- 使用物件內存在的唯一值。 +- 在你的父元件定義 keys,而不是子元件。 + +```javascript +//bad +... +render() { +
{{item.name}}
+} +... + +//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) diff --git a/_posts/zh_TW/2016-01-03-improve-nested-conditionals.md b/_posts/zh_TW/2016-01-03-improve-nested-conditionals.md new file mode 100644 index 00000000..89f62566 --- /dev/null +++ b/_posts/zh_TW/2016-01-03-improve-nested-conditionals.md @@ -0,0 +1,92 @@ +--- +layout: post + +title: 改善嵌套的條件式 +tip-number: 03 +tip-username: AlbertoFuente +tip-username-profile: https://github.com/AlbertoFuente +tip-tldr: 我們要如何在 javascript 改善並更有效的嵌套 `if` 條件式? + +categories: + - zh_TW +--- + +我們要如何在 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; +} +``` + +但是我們必須避免使用過多的條件檢查以及避免使用 `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/。 diff --git a/_posts/zh_TW/2016-01-04-sorting-strings-with-accented-characters.md b/_posts/zh_TW/2016-01-04-sorting-strings-with-accented-characters.md new file mode 100644 index 00000000..e044164a --- /dev/null +++ b/_posts/zh_TW/2016-01-04-sorting-strings-with-accented-characters.md @@ -0,0 +1,66 @@ +--- +layout: post + +title: 將帶有音節字元的字串進行排序 +tip-number: 04 +tip-username: loverajoel +tip-username-profile: https://github.com/loverajoel +tip-tldr: JavaScript 原生的 **sort** 方法讓我們可以排序陣列。做一個簡單的 `array.sort()`, 將每一個陣列元素視為字串並依字母排序。但是當你嘗試排序一個非 ASCII 字元陣列時,你會得到一個奇怪的結果。 + +categories: + - zh_TW +--- + +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 會更加快速。 + +所以,當你在處理非英文語系的字串陣列時,記得使用這個方法來避免排序出現異常。 diff --git a/_posts/zh_TW/2016-01-05-differences-between-undefined-and-null.md b/_posts/zh_TW/2016-01-05-differences-between-undefined-and-null.md new file mode 100644 index 00000000..e5934610 --- /dev/null +++ b/_posts/zh_TW/2016-01-05-differences-between-undefined-and-null.md @@ -0,0 +1,40 @@ +--- +layout: post + +title: `undefined` 和 `null` 的差別 +tip-number: 05 +tip-username: loverajoel +tip-username-profile: https://github.com/loverajoel +tip-tldr: 了解 `undefined` 和 `null` 的差別。 + +categories: + - zh_TW +--- + +- `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/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 +``` diff --git a/_posts/zh_TW/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md b/_posts/zh_TW/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md new file mode 100644 index 00000000..bb9a90f8 --- /dev/null +++ b/_posts/zh_TW/2016-01-06-writing-a-single-method-for-arrays-and-a-single-element.md @@ -0,0 +1,36 @@ +--- +layout: post + +title: 撰寫一個可以接受單一參數和陣列的方法 +tip-number: 06 +tip-username: mattfxyz +tip-username-profile: https://twitter.com/mattfxyz +tip-tldr: 撰寫你的函式可以處理陣列或單一元素參數,而不是透過分開的方法來處理陣列和單一元素參數。這和 jQuery 一些函式工作原理相似(`css` selector 將會修改所有匹配的)。 + +categories: + - zh_TW +--- + +撰寫你的函式可以處理陣列或單一元素參數,而不是透過分開的方法來處理陣列和單一元素參數。這和 jQuery 一些函式工作原理相似(`css` selector 將會修改所有匹配的)。 + +首先,你只要將任何的東西 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` 現在已經可以接收單一文字或是多個文字的陣列當作它的參數了。 + +```javascript +printUpperCase("cactus"); +// => CACTUS +printUpperCase(["cactus", "bear", "potato"]); +// => CACTUS +// BEAR +// POTATO +``` diff --git a/_posts/zh_TW/2016-01-07-use-strict-and-get-lazy.md b/_posts/zh_TW/2016-01-07-use-strict-and-get-lazy.md new file mode 100644 index 00000000..e3fb7264 --- /dev/null +++ b/_posts/zh_TW/2016-01-07-use-strict-and-get-lazy.md @@ -0,0 +1,61 @@ +--- +layout: post + +title: 在 JavaScript 使用嚴格模式 +tip-number: 07 +tip-username: nainslie +tip-username-profile: https://twitter.com/nat5an +tip-tldr: JavaScript 嚴格模式讓開發者可以寫出更「安全」的 JavaScript 程式碼。 + +categories: + - zh_TW +--- + +JavaScript 嚴格模式讓開發者可以寫出更「安全」的 JavaScript 程式碼。 + +預設情況下,JavaScript 允許開發者的粗心行為,例如,當我們引用一個沒有要求我們由「var」宣告的變數。或許這個對於一個剛入門的開發者相當方便,當變數名稱拼寫錯誤或者是不小心參考到其他的範圍(scope),這些都是許多錯誤的來源。 + +開發者喜歡讓電腦幫我們做一些無聊的工作,然後自動地幫我們確認工作上的錯誤。這就是 JavaScript 的 「嚴格模式」(use strict)幫我們做的,將錯誤轉換成 JavaScript 錯誤。 + +我們把這個指令放在 js 檔案的頂端: + +```javascript +// 整個 script 使用嚴格模式 +"use strict"; +var v = "Hi! I'm a strict mode script!"; +``` + +或是在 function 內: + +```javascript +function f() +{ + // Function 內使用嚴格模式 + '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 檔案或 function 內引入這個指令,我們直接讓 JavaScript 引擎執行嚴格模式來禁止一些在大型 JavaScript 專案不良的行為。除了其他之外,嚴格模式改變了以下的行為: + +* 只有被宣告的「var」變數才可以被引用。 +* 嘗試寫入唯讀的變數會造成錯誤。 +* 你只能透過「new」關鍵字才可以呼叫建構子。 +* 「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)。 diff --git a/_posts/zh_TW/2016-01-08-converting-a-node-list-to-an-array.md b/_posts/zh_TW/2016-01-08-converting-a-node-list-to-an-array.md new file mode 100644 index 00000000..b5af224a --- /dev/null +++ b/_posts/zh_TW/2016-01-08-converting-a-node-list-to-an-array.md @@ -0,0 +1,43 @@ +--- +layout: post + +title: 將 Node List 轉換成陣列 +tip-number: 08 +tip-username: Tevko +tip-username-profile: https://twitter.com/tevko +tip-tldr: 這是將 Node List 轉換成 DOM 元素陣列的快速、安全以及可複用的方法。 + +categories: + - zh_TW +--- + +`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(...); + +//等等... +``` diff --git a/_posts/zh_TW/2016-01-09-template-strings.md b/_posts/zh_TW/2016-01-09-template-strings.md new file mode 100644 index 00000000..51e33493 --- /dev/null +++ b/_posts/zh_TW/2016-01-09-template-strings.md @@ -0,0 +1,37 @@ +--- +layout: post + +title: 樣板字串符 +tip-number: 09 +tip-username: JakeRawr +tip-username-profile: https://github.com/JakeRawr +tip-tldr: 在 ES6 中,JS 現在有了樣板字串元來替代原本我們使用的引號字元。 + +categories: + - zh_TW +--- + +在 ES6 中,JS 現在有了樣板字串符來替代原本我們使用的引號字元。 + +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)和了解更多關於樣板字串符。 diff --git a/_posts/zh_TW/2016-01-10-check-if-a-property-is-in-a-object.md b/_posts/zh_TW/2016-01-10-check-if-a-property-is-in-a-object.md new file mode 100644 index 00000000..75c12e1c --- /dev/null +++ b/_posts/zh_TW/2016-01-10-check-if-a-property-is-in-a-object.md @@ -0,0 +1,59 @@ +--- +layout: post + +title: 檢查屬性是否存在物件內 +tip-number: 10 +tip-username: loverajoel +tip-username-profile: https://www.twitter.com/loverajoel +tip-tldr: 這些方法都是檢查屬性是否存在目前的物件內。 + +categories: + - zh_TW +--- + +當你檢查屬性是否存在目前的[物件](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)。 diff --git a/_posts/zh_TW/2016-01-11-hoisting.md b/_posts/zh_TW/2016-01-11-hoisting.md new file mode 100644 index 00000000..63faab3e --- /dev/null +++ b/_posts/zh_TW/2016-01-11-hoisting.md @@ -0,0 +1,51 @@ +--- +layout: post + +title: 提升變數 +tip-number: 11 +tip-username: squizzleflip +tip-username-profile: https://twitter.com/squizzleflip +tip-tldr: JavaScript 模組以及建構步驟變得更多更複雜,但對於新的框架樣板呢? + +categories: + - zh_TW +--- + +了解 [hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) 將會幫助你組織你的函式範圍。只要記得,變數宣告和函式定義都會被提升到頂端。變數定義則不會,即使你宣告和定義一個變數再同一行。變數**宣告**是讓系統知道變數的存在,而**定義**只是分配給它一個值。 + +```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!'); + } +} +``` + +如果要讓你的程式碼更容易閱讀,將你所有的變數宣告在你的函式範圍頂端,這樣可以更清楚知道變數是來自哪個範圍。在你使用變數之前請先定義。在你的範圍底部定義函式,來保持它們的方式。 diff --git a/_posts/zh_TW/2016-01-12-pseudomandatory-parameters-in-es6-functions.md b/_posts/zh_TW/2016-01-12-pseudomandatory-parameters-in-es6-functions.md new file mode 100644 index 00000000..44c7d890 --- /dev/null +++ b/_posts/zh_TW/2016-01-12-pseudomandatory-parameters-in-es6-functions.md @@ -0,0 +1,27 @@ +--- +layout: post + +title: 在 ES6 函式內的預設參數 +tip-number: 12 +tip-username: Avraam Mavridis +tip-username-profile: https://github.com/AvraamMavridis +tip-tldr: 在許多程式設計語言函數的參數預設是強制需要的,而開發者也會明確定義一個可選的參數。 + +categories: + - zh_TW +--- + +在許多程式設計語言函數的參數預設值是強制需要的,而開發者也會明確定義一個可選的參數。在 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) 看更多關於 **預設參數值特性** 的範例。 diff --git a/_posts/zh_TW/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md b/_posts/zh_TW/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md new file mode 100644 index 00000000..f7867ae9 --- /dev/null +++ b/_posts/zh_TW/2016-01-13-tip-to-measure-performance-of-a-javascript-block.md @@ -0,0 +1,32 @@ +--- +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)`。 + +categories: + - zh_TW +--- + +如果要快速的測量 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 下輸出)。 diff --git a/_posts/zh_TW/2016-01-14-fat-arrow-functions.md b/_posts/zh_TW/2016-01-14-fat-arrow-functions.md new file mode 100644 index 00000000..b8dc5e59 --- /dev/null +++ b/_posts/zh_TW/2016-01-14-fat-arrow-functions.md @@ -0,0 +1,102 @@ +--- +layout: post + +title: 箭頭函式 +tip-number: 14 +tip-username: pklinger +tip-username-profile: https://github.com/pklinger/ +tip-tldr: 介紹一個 ES6 的新特性,箭頭函式是一個方便的語法讓你用更少的程式碼做更多事。 + +categories: + - zh_TW +--- + +介紹一個 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` 的上下文。看以下的[範例](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/)。 diff --git a/_posts/zh_TW/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md b/_posts/zh_TW/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md new file mode 100644 index 00000000..cec6c57f --- /dev/null +++ b/_posts/zh_TW/2016-01-15-even-simpler-way-of-using-indexof-as-a-contains-clause.md @@ -0,0 +1,84 @@ +--- +layout: post + +title: 更簡單的方式將 `indexOf` 當作 contains 使用 +tip-number: 15 +tip-username: jhogoforbroke +tip-username-profile: https://twitter.com/jhogoforbroke +tip-tldr: JavaScript 預設沒有 contain 的方法。如果要檢查字串或是陣列內是否有子字串的存在你可以這樣做。 + +categories: + - zh_TW +--- + +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 或更低版本則無法使用。** +**它最好在被控制的環境下使用。** diff --git a/_posts/zh_TW/2016-01-16-passing-arguments-to-callback-functions.md b/_posts/zh_TW/2016-01-16-passing-arguments-to-callback-functions.md new file mode 100644 index 00000000..7c497211 --- /dev/null +++ b/_posts/zh_TW/2016-01-16-passing-arguments-to-callback-functions.md @@ -0,0 +1,54 @@ +--- +layout: post + +title: 將參數傳送到 callback 函式 +tip-number: 16 +tip-username: minhazav +tip-username-profile: https://twitter.com/minhazav +tip-tldr: JavaScript 模組以及建構步驟變得更多更複雜,但對於新的框架樣板呢? + +categories: + - zh_TW +--- + +預設情況下,你不能傳送參數給 callback 函式。例如: + +```js +function callback() { + console.log('Hi human'); +} + +document.getElementById('someelem').addEventListener('click', callback); + +``` + +你在 JavaScript 利用閉包範圍的優點傳送參數給 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)); + +``` + +### 什麼是閉包? + +閉包是指引用獨立(自由)變數的函式。換句話說,在閉包中定義的函式「記得」它被建立時的環境。在 [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)。 diff --git a/_posts/zh_TW/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md b/_posts/zh_TW/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md new file mode 100644 index 00000000..64fbbde8 --- /dev/null +++ b/_posts/zh_TW/2016-01-17-nodejs-run-a-module-if-it-is-not-required.md @@ -0,0 +1,28 @@ +--- +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` 這兩種不同的方式來執行你的程式碼。如果你想要在獨立的模組內使用,這是非常有用的。 + +categories: + - zh_TW +--- + +在 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)。 diff --git a/_posts/zh_TW/2016-01-18-rounding-the-fast-way.md b/_posts/zh_TW/2016-01-18-rounding-the-fast-way.md new file mode 100644 index 00000000..0350eb3d --- /dev/null +++ b/_posts/zh_TW/2016-01-18-rounding-the-fast-way.md @@ -0,0 +1,37 @@ +--- +layout: post + +title: 捨入最快的方法 +tip-number: 18 +tip-username: pklinger +tip-username-profile: https://github.com/pklinger +tip-tldr: 今天的小知識是關於性能的部分。曾經使用過雙波浪 `~~` 運算符嗎?有時候也被稱為 雙 NOT 位元運算符。你可以使用它來替代 `Math.floor()` 更為快速。這是為什麼呢? + +categories: + - zh_TW +--- + +今天的小知識是關於性能的部分。[曾經使用過雙波浪](http://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript) `~~` 運算符嗎?有時候也被稱為 雙 NOT 位元運算符。你可以使用它來替代 `Math.floor()` 更為快速。這是為什麼呢? + +單位元移位運算符 `~` 將輸入的 32 位元轉換成 `-(input+1)`。因此,雙位元移位運算成為更好的工具,將輸入轉換成 `-(-(input + 1)+1)` 更趨近 0。對於數字輸入,負數就像使用 `Math.ceil()` 而正數就像使用 `Math.floor()`。若失敗的話,則回傳 `0`,當 `Math.floor()` 失敗回傳 `NaN` 時,或許此方法可以替代 `Math.floor()`。 + +```javascript +// 單 ~ +console.log(~1337) // -1338 + +// 數字輸入 +console.log(~~47.11) // -> 47 +console.log(~~-12.88) // -> -12 +console.log(~~1.9999) // -> 1 +console.log(~~3) // -> 3 + +// 失敗 +console.log(~~[]) // -> 0 +console.log(~~NaN) // -> 0 +console.log(~~null) // -> 0 + +// 大於 32 位的整數失敗 +console.log(~~(2147483647 + 1) === (2147483647 + 1)) // -> 0 +``` + +雖然 `~~` 可以讓性能更好,但是為了增加程式碼可讀性,請使用 `Math.floor()`。 diff --git a/_posts/zh_TW/2016-01-19-safe-string-concatenation.md b/_posts/zh_TW/2016-01-19-safe-string-concatenation.md new file mode 100644 index 00000000..cb022527 --- /dev/null +++ b/_posts/zh_TW/2016-01-19-safe-string-concatenation.md @@ -0,0 +1,36 @@ +--- +layout: post + +title: 安全的字串串接 +tip-number: 19 +tip-username: gogainda +tip-username-profile: https://twitter.com/gogainda +tip-tldr: 假設你有一些不確定的變數類型,而你想將它們串接成字串。可以肯定的是,算術運算符不是在串接過程的運用,請使用 concat。 + +categories: + - zh_TW +--- + +假設你有一些不確定的變數類型,而你想將它們串接成字串。可以肯定的是,算術運算符不是在串接過程的運用,請使用 `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` 函式的資訊。 diff --git a/_posts/zh_TW/2016-01-20-return-objects-to-enable-chaining-of-functions.md b/_posts/zh_TW/2016-01-20-return-objects-to-enable-chaining-of-functions.md new file mode 100644 index 00000000..321078a4 --- /dev/null +++ b/_posts/zh_TW/2016-01-20-return-objects-to-enable-chaining-of-functions.md @@ -0,0 +1,33 @@ +--- +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: 在 JavaScript 物件導向中,當函數回傳的物件中,讓你可以將函式鏈結在一起。 + +categories: + - zh_TW +--- + +在 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(); +``` diff --git a/_posts/zh_TW/2016-01-21-shuffle-an-array.md b/_posts/zh_TW/2016-01-21-shuffle-an-array.md new file mode 100644 index 00000000..36926c4b --- /dev/null +++ b/_posts/zh_TW/2016-01-21-shuffle-an-array.md @@ -0,0 +1,37 @@ +--- +layout: post + +title: 將陣列洗牌 +tip-number: 21 +tip-username: 0xmtn +tip-username-profile: https://github.com/0xmtn/ +tip-tldr: Fisher-Yates Shuffling 是一個將陣列洗牌的演算法。 + +categories: + - zh_TW +--- + +這段程式碼使用了 [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] +``` diff --git a/_posts/zh_TW/2016-01-22-two-ways-to-empty-an-array.md b/_posts/zh_TW/2016-01-22-two-ways-to-empty-an-array.md new file mode 100644 index 00000000..4b1ad68a --- /dev/null +++ b/_posts/zh_TW/2016-01-22-two-ways-to-empty-an-array.md @@ -0,0 +1,61 @@ +--- +layout: post + +title: 將陣列清空的兩種方法 +tip-number: 22 +tip-username: microlv +tip-username-profile: https://github.com/microlv +tip-tldr: 在 JavaScript 中當你相要清空陣列,有許多方法,但這是最具效能的方法。 + +categories: + - zh_TW +--- + +你定義了一個陣列然後你想清空陣列內的內容。 +通常你會這麼做: + +```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) diff --git a/_posts/zh_TW/2016-01-23-converting-to-number-fast-way.md b/_posts/zh_TW/2016-01-23-converting-to-number-fast-way.md new file mode 100644 index 00000000..6bfe9970 --- /dev/null +++ b/_posts/zh_TW/2016-01-23-converting-to-number-fast-way.md @@ -0,0 +1,28 @@ +--- +layout: post + +title: 轉換為數字更快的方式 +tip-number: 23 +tip-username: sonnyt +tip-username-profile: http://twitter.com/sonnyt +tip-tldr: 轉換字串為數字是相當常見的。最簡單和快速的方式是使用 + 運算符來實現。 + +categories: + - zh_TW +--- + +轉換字串為數字是相當常見的。最簡單和快速的方式是使用 `+` (加號)運算符來實現。([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 +``` diff --git a/_posts/zh_TW/2016-01-24-use_===_instead_of_==.md b/_posts/zh_TW/2016-01-24-use_===_instead_of_==.md new file mode 100644 index 00000000..f4920c01 --- /dev/null +++ b/_posts/zh_TW/2016-01-24-use_===_instead_of_==.md @@ -0,0 +1,29 @@ +--- +layout: post + +title: 使用 === 來替代 == +tip-number: 24 +tip-username: bhaskarmelkani +tip-username-profile: https://www.twitter.com/bhaskarmelkani +tip-tldr: `==`(或 `!=`)運算符如果在執行上需要的話,會自動將類型轉換。 `===`(或 `!==`)運算符則不會執行轉換。`===`(或 `!==`)用來比較數值和類型,相較於 `==` 更來的快速。 + +categories: + - zh_TW +--- + +`==`(或 `!=`)運算符如果在執行上需要的話,會自動將類型轉換。 `===`(或 `!==`)運算符則不會執行轉換。`===`(或 `!==`)用來比較數值和類型,相較於 `==` 更來的快速。([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 + +``` diff --git a/_posts/zh_TW/2016-01-25-Using-immediately-invoked-function-expression.md b/_posts/zh_TW/2016-01-25-Using-immediately-invoked-function-expression.md new file mode 100644 index 00000000..f9c381cd --- /dev/null +++ b/_posts/zh_TW/2016-01-25-Using-immediately-invoked-function-expression.md @@ -0,0 +1,46 @@ +--- +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 中有一些相當重要的用途。 + + +categories: + - zh_TW +--- + +稱作「Iffy」(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. [連結一](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) diff --git a/_posts/zh_TW/2016-01-26-filtering-and-sorting-a-list-of-strings.md b/_posts/zh_TW/2016-01-26-filtering-and-sorting-a-list-of-strings.md new file mode 100644 index 00000000..aa042c1f --- /dev/null +++ b/_posts/zh_TW/2016-01-26-filtering-and-sorting-a-list-of-strings.md @@ -0,0 +1,56 @@ +--- +layout: post + +title: 過濾和排序字串清單 +tip-number: 26 +tip-username: davegomez +tip-username-profile: https://github.com/davegomez +tip-tldr: 你可能有一份很大的清單,而你需要過濾重複的內容並移除,再依字母排序。 + +categories: + - zh_TW +--- + +你可能有一份很大的清單,而你需要過濾重複的內容並移除,再依字母排序。 + +在我們的範例,我使用了不同版本的 **JavaScript 保留字元** 的清單,但你要注意到,這裡有一些重複的字元而且他們不是依字母排序的。所以,這裡有一個相當棒的字串[陣列](https://developer.mozilla.org/en-US/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/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),它會基於我們傳送的陣列(*function*)並回傳一個過濾後的新陣列。基於目前的清單和新的清單索引 `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'] +``` diff --git a/_posts/zh_TW/2016-01-27-short-circiut-evaluation-in-js.md b/_posts/zh_TW/2016-01-27-short-circiut-evaluation-in-js.md new file mode 100644 index 00000000..b171f450 --- /dev/null +++ b/_posts/zh_TW/2016-01-27-short-circiut-evaluation-in-js.md @@ -0,0 +1,78 @@ +--- +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。 + +categories: + - zh_TW +--- + +[短路求值](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()。 + +``` diff --git a/_posts/zh_TW/2016-01-28-curry-vs-partial-application.md b/_posts/zh_TW/2016-01-28-curry-vs-partial-application.md new file mode 100644 index 00000000..95be6107 --- /dev/null +++ b/_posts/zh_TW/2016-01-28-curry-vs-partial-application.md @@ -0,0 +1,113 @@ +--- +layout: post + +title: 柯里化(currying)和部分應用程式 +tip-number: 28 +tip-username: bhaskarmelkani +tip-username-profile: https://www.twitter.com/bhaskarmelkani +tip-tldr: 柯里化(currying)和部分應用程式(partial application)兩種方式是將函式轉換變成其他更小參數的函式。 + + +categories: + - zh_TW +--- + +**柯里化** + +柯里化是將一個函式 + +`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); + } +} +``` diff --git a/_posts/zh_TW/2016-01-29-speed-up-recursive-functions-with-memoization.md b/_posts/zh_TW/2016-01-29-speed-up-recursive-functions-with-memoization.md new file mode 100644 index 00000000..6920f941 --- /dev/null +++ b/_posts/zh_TW/2016-01-29-speed-up-recursive-functions-with-memoization.md @@ -0,0 +1,78 @@ +--- +layout: post + +title: 使用 memoization 加速遞迴 +tip-number: 29 +tip-username: hingsir +tip-username-profile: https://github.com/hingsir +tip-tldr: 大家對費式(Fibonacci)數列都很熟悉。我們可以在 20 秒內寫出以下的函式。它可以執行,但是效率不高。它做了大量的重複計算,我們可以快取先前的計算結果來加快計算速度。 + + +categories: + - zh_TW +--- + +大家對費式(Fibonacci)數列都很熟悉。我們可以在 20 秒內寫出以下的函式。 + +```js +var fibonacci = function(n){ + return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); +} +``` +它可以執行,但是效率不高。它做了大量的重複計算,我們可以快取先前的計算結果來加快計算速度。 + +```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)); + } +})() +``` +也許,我們可以定義高階函式,來接受一個函式作為參數,並回傳一個函式回傳的暫存值。 + +```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 +``` diff --git a/_posts/zh_TW/2016-01-30-converting-truthy-falsy-values-to-boolean.md b/_posts/zh_TW/2016-01-30-converting-truthy-falsy-values-to-boolean.md new file mode 100644 index 00000000..0a6ec5c2 --- /dev/null +++ b/_posts/zh_TW/2016-01-30-converting-truthy-falsy-values-to-boolean.md @@ -0,0 +1,28 @@ +--- +layout: post + +title: 將 truthy 和 falsy 轉換成布林值 +tip-number: 30 +tip-username: hakhag +tip-username-profile: https://github.com/hakhag +tip-tldr: 邏輯運算符是 JavaScript 核心之一 ,在這裡你可以發現這個方式,不管你給他什麼值,都會得到 true 或 false 。 + + +categories: + - zh_TW +--- + +你可以使用 `!!` 運算符將 [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 +``` diff --git "a/_posts/zh_TW/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions\342\200\224it-kills-optimization.md" "b/_posts/zh_TW/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions\342\200\224it-kills-optimization.md" new file mode 100644 index 00000000..6d5fc293 --- /dev/null +++ "b/_posts/zh_TW/2016-01-31-avoid-modifying-or-passing-arguments-into-other-functions\342\200\224it-kills-optimization.md" @@ -0,0 +1,42 @@ +--- +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` 到陣列相當普遍的做法。 + + +categories: + - zh_TW +--- + +### 背景 + +在 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]; +} +``` + +雖然程式碼更冗長,但是在上線環境增進了效能的優化,所以是值得的。 diff --git a/_posts/zh_TW/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md b/_posts/zh_TW/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md new file mode 100644 index 00000000..838848aa --- /dev/null +++ b/_posts/zh_TW/2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md @@ -0,0 +1,72 @@ +--- +layout: post + +title: 透過 Map() 在你的物件屬性加入排序 +tip-number: 32 +tip-username: loverajoel +tip-username-profile: https://twitter.com/loverajoel +tip-tldr: 物件是一個沒有排序的屬性集合...意思說,假設你想嘗試在你的物件內將資料做排序,你需要要重新做排序,因為屬性在物件不保證是有排序的。 + +categories: + - zh_TW +--- + +## 物件屬性的順序 + +> 一個物件是一個 `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}]; +``` diff --git a/_posts/zh_TW/2016-02-02-create-range-0...n-easily-using-one-line.md b/_posts/zh_TW/2016-02-02-create-range-0...n-easily-using-one-line.md new file mode 100644 index 00000000..e6fd9dde --- /dev/null +++ b/_posts/zh_TW/2016-02-02-create-range-0...n-easily-using-one-line.md @@ -0,0 +1,55 @@ +--- +layout: post +使用一行程式碼簡單建立一個 0...(N - 1) 的陣列 +title: Create Range 0...(N-1) easily using one line +tip-number: 33 +tip-username: SarjuHansaliya +tip-username-profile: https://github.com/SarjuHansaliya +tip-tldr: 我們透過一行程式碼簡單建立一個範圍函式,它可以給定一個 0...(N - 1) 的範圍。 + + +categories: + - zh_TW +--- + +使用下方的程式碼,我們可以建立一個 0...(N - 1) 的陣列。 + +```js +Array.apply(null, {length: N}).map(Number.call, Number); +``` + +讓我們來拆解這行程式碼。我們知道 `call` 函式可以在 JavaScript 執行,所以,在 `call` 第一個參數是上下文(context),而第二個參數開始則是呼叫 `call` 函式所需要用到的參數。 + +```js +function add(a, b){ + return (a + b); +} +add.call(null, 5, 6); +``` +回傳 5 和 6 的總和。 + +在 JavaScript 陣列的 `map()` 帶有兩個參數,第一個是 `callback`,第二個則是 `thisArg(context)`。`callback` 中帶有三個參數,`value`、`index` 以及整個要被迭代的陣列。一般語法像是這樣: + +```js +[1, 2, 3].map(function(value, index, arr){ + // 程式碼 +}, this); +``` +以下程式碼建立特定長度的陣列。 + +```js +Array.apply(null, {length: N}) +``` +把所有部分組合一起就是如下的解決方法。 + +```js +Array.apply(null, {length: N}).map(Number.call, Number); +``` + +如果你想要 1...N 的範圍,你可以像這樣。 + +```js +Array.apply(null, {length: N}).map(function(value, index){ + return index + 1; +}); +``` diff --git a/_posts/zh_TW/2016-02-03-implementing-asynchronous-loops.md b/_posts/zh_TW/2016-02-03-implementing-asynchronous-loops.md new file mode 100644 index 00000000..2db04521 --- /dev/null +++ b/_posts/zh_TW/2016-02-03-implementing-asynchronous-loops.md @@ -0,0 +1,93 @@ +--- +layout: post + +title: 實作非同步迴圈 +tip-number: 34 +tip-username: madmantalking +tip-username-profile: https://github.com/madmantalking +tip-tldr: 你或許可以實作非同步迴圈,但是執行時可能會遇到問題。 + +categories: + - zh_TW +--- + +讓我們嘗試撰寫一個非同步的函式,在每秒列印出迴圈的索引值。 + +```js +for (var i = 0; i < 5; i++) { + setTimeout(function(){ + console.log(i); + }, 1000); +} +``` +上面的程式將會輸出以下的結果: + +```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); +} +``` +但以上的程式輸出的結果是: + +```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`。一般的方式是建立一個閉包,宣告一個函式並將 `i` 作為一個參數傳送。在這裡我們做了一個自我呼叫的函式。 + +```js +for (var i = 0; i < 5; i++) { + (function(num){ + setTimeout(function(){ + console.log(num); + }, 1000); + })(i); +} +``` +在 JavaScript,參數是透過傳值方式給函數。所以原始的類型像是數字、日期、和字串基本上都是被複製的。如果你想要再函式內改變他們,它是不會影響外部的範圍。物件比較特別:假設內部函數改變屬性,這個改變會影響所有範圍。 + +其他解決方法可以使用 `let`。它是 `ES6` 其中一種變數的宣告方式,它和 `var` 不一樣,只在區塊內作用。 + +```js +for (let i = 0; i < 5; i++) { + var temp = i; + setTimeout(function(){ + console.log(temp); + }, 1000); +} +``` diff --git a/_posts/zh_TW/2016-02-04-assignment-shorthands.md b/_posts/zh_TW/2016-02-04-assignment-shorthands.md new file mode 100644 index 00000000..e1962f4a --- /dev/null +++ b/_posts/zh_TW/2016-02-04-assignment-shorthands.md @@ -0,0 +1,97 @@ +--- +layout: post + +title: 賦值運算符 +tip-number: 35 +tip-username: hsleonis +tip-username-profile: https://github.com/hsleonis +tip-tldr: 賦值是相當常見的。有時候我們這些「懶惰的開發者」覺得打字是很耗時得。所以我們使用一些技巧來幫助我們讓程式碼更乾淨簡單。 + +categories: + - zh_TW +--- + +賦值是相當常見的。有時候我們這些「懶惰的開發者」覺得打字是很耗時得。所以我們使用一些技巧來幫助我們讓程式碼更乾淨簡單。 + +這是類似的方法 + +````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 (使用三元運算符) + +這是我們一般常見的寫法: + +````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' +}; +```` diff --git a/_posts/zh_TW/2016-02-05-observe-dom-changes.md b/_posts/zh_TW/2016-02-05-observe-dom-changes.md new file mode 100644 index 00000000..4abb0734 --- /dev/null +++ b/_posts/zh_TW/2016-02-05-observe-dom-changes.md @@ -0,0 +1,67 @@ +--- +layout: post + +title: 觀察在擴充中 DOM 的變化 +tip-number: 36 +tip-username: beyondns +tip-username-profile: https://github.com/beyondns +tip-tldr: 由於現代動態的 JavaScript,當你在現有的網站開發擴充時,操作 DOM 並不容易。 + + +categories: + - zh_TW +--- +[MutationObserver](https://developer.mozilla.org/en/docs/Web/API/MutationObserver) 是一個監聽 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); + +})() +``` diff --git a/_posts/zh_TW/2016-02-06-deduplicate-an-array.md b/_posts/zh_TW/2016-02-06-deduplicate-an-array.md new file mode 100644 index 00000000..f526b331 --- /dev/null +++ b/_posts/zh_TW/2016-02-06-deduplicate-an-array.md @@ -0,0 +1,129 @@ +--- +layout: post + +title: 移除陣列重複元素 +tip-number: 37 +tip-username: danillouz +tip-username-profile: https://www.twitter.com/danillouz +tip-tldr: 如何從陣列中移除不同資料類型重複的元素。 + + +categories: + - zh_TW +--- +如果陣列只有包含基本數值,我們可以透過 +# 原始函數 +如果陣列只有包含基本數值,我們可以透過 [`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) diff --git a/_posts/zh_TW/2016-02-07-flattening-multidimensional-arrays-in-javascript.md b/_posts/zh_TW/2016-02-07-flattening-multidimensional-arrays-in-javascript.md new file mode 100644 index 00000000..205de062 --- /dev/null +++ b/_posts/zh_TW/2016-02-07-flattening-multidimensional-arrays-in-javascript.md @@ -0,0 +1,60 @@ +--- +layout: post + +title: 在 JavaScript 中扁平化多維陣列 +tip-number: 38 +tip-username: loverajoel +tip-username-profile: https://www.twitter.com/loverajoel +tip-tldr: 三種不同的解決方法,將多維陣列合併為單一的陣列。 + + +categories: + - zh_TW +--- + +三種不同的解決方法,將多維陣列合併為單一的陣列。 + +給定一個陣列: + +```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)觀察三種方式的實際應用。 + +對於無限嵌套陣列嘗試一下 Underscore 的 [flatten()](https://github.com/jashkenas/underscore/blob/master/underscore.js#L501)。 + +如果你好奇效能方面表現, [這裡](http://jsperf.com/flatten-an-array-loop-vs-reduce/6)有相關的測試。 diff --git a/_posts/zh_TW/2016-02-08-advanced-properties.md b/_posts/zh_TW/2016-02-08-advanced-properties.md new file mode 100644 index 00000000..0869a94e --- /dev/null +++ b/_posts/zh_TW/2016-02-08-advanced-properties.md @@ -0,0 +1,112 @@ +--- +layout: post + +title: 進階 JavaScript 屬性 +tip-number: 39 +tip-username: mallowigi +tip-username-profile: https://github.com/mallowigi +tip-tldr: 如何加入私有屬性、getters 和 setters 到物件。 + + +categories: + - zh_TW +--- + +在 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) diff --git a/_posts/zh_TW/2016-02-09-using-json-stringify.md b/_posts/zh_TW/2016-02-09-using-json-stringify.md new file mode 100644 index 00000000..53f60b3a --- /dev/null +++ b/_posts/zh_TW/2016-02-09-using-json-stringify.md @@ -0,0 +1,66 @@ +--- +layout: post + +title: 使用 JSON.Stringify +tip-number: 40 +tip-username: vamshisuram +tip-username-profile: https://github.com/vamshisuram +tip-tldr: 從 JSON 物件中,將被選到的屬性建立成字串。 + + +categories: + - zh_TW +--- + +假設有一個物件有「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" +} +*/ + +``` diff --git a/_posts/zh_TW/2016-02-10-array-average-and-median.md b/_posts/zh_TW/2016-02-10-array-average-and-median.md new file mode 100644 index 00000000..488def20 --- /dev/null +++ b/_posts/zh_TW/2016-02-10-array-average-and-median.md @@ -0,0 +1,63 @@ +--- +layout: post + +title: 陣列平均值和中間值 +tip-number: 41 +tip-username: soyuka +tip-username-profile: https://github.com/soyuka +tip-tldr: 計算陣列的平均值和中間值。 + + +categories: + - zh_TW +--- + +以下範例基於一個陣列: + +```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 +``` diff --git a/_posts/zh_TW/2016-02-11-preventing-unapply-attacks.md b/_posts/zh_TW/2016-02-11-preventing-unapply-attacks.md new file mode 100644 index 00000000..bbb5d614 --- /dev/null +++ b/_posts/zh_TW/2016-02-11-preventing-unapply-attacks.md @@ -0,0 +1,56 @@ +--- +layout: post + +title: 防止 Unapply 攻擊 +tip-number: 42 +tip-username: emars +tip-username-profile: https://twitter.com/marseltov +tip-tldr: 凍結內建的屬性。 + +categories: + - zh_TW +--- + +透過覆寫內建的原型,攻擊者可以重寫程式碼來暴露或更改綁定的參數。這是一個嚴重的安全漏洞,利用 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 攻擊。 diff --git a/_posts/zh_TW/2016-02-12-use-destructuring-in-function-parameters.md b/_posts/zh_TW/2016-02-12-use-destructuring-in-function-parameters.md new file mode 100644 index 00000000..6571d677 --- /dev/null +++ b/_posts/zh_TW/2016-02-12-use-destructuring-in-function-parameters.md @@ -0,0 +1,28 @@ +--- +layout: post + +title: 在函式參數裡使用解構子 +tip-number: 43 +tip-username: dislick +tip-username-profile: https://github.com/dislick +tip-tldr: 你知道可以在函式的參數裡使用解構子嗎? + +categories: + - zh_TW +--- +我相信很多人都已經很熟悉 [ES6 解構賦值](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)。你知道也可以用在函式的參數裡嗎? + +```javascript +var sayHello = function({ name, surname }) { + console.log(`Hello ${name} ${surname}! How are you?`); +}; + +sayHello({ + name: 'John', + surname: 'Smith' +}); +``` + +這對函式接受一個可選的物件是相當有用的。 + +> 請注意解構賦值現在 Node.js 還有其他瀏覽器還無法使用。如果你想要試試看的話,你可以在 Node.js 使用 `--harmony-destructuring`。