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