-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocalStorage_zh_CN.json
1 lines (1 loc) · 165 KB
/
localStorage_zh_CN.json
1
{"2016-01-03-improve-nested-conditionals.md":{"name":"2016-01-03-improve-nested-conditionals.md","sha":"0ff034e3c69d47f53dc73c66ec6f62ea0624e239","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 - /zh_cn/improve-nested-conditionals/\n\ncategories:\n - zh_CN\n - javascript\n---\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\n一种方法来提高嵌套的`if`语句是用`switch`语句。虽然它不那么啰嗦而且排列整齐,但是并不建议使用它,因为这对于调试错误很困难。这告诉你[为什么](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如果可以重构的话,我们可以试着简化函数。比如不需要为每个颜色写一个函数,而是将颜色作为函数的参数。\n\n```javascript\nfunction printBackground(color) {\n if (!color || typeof color !== 'string') {\n return; // Invalid color, return immediately\n }\n}\n```\n\n但是如果不能重构的话,我们必须避免过多的条件检查,避免过多使用`switch`。我们必须考虑最有效率的方法,使用`object`。\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但是我们应该时刻注意避免太多判断在一个条件里,尽量少的使用`switch`,考虑最有效率的方法:借助`object`。\n\n```javascript\nvar colorObj = {\n 'black': printBlackBackground,\n 'red': printRedBackground,\n 'blue': printBlueBackground,\n 'green': printGreenBackground,\n 'yellow': printYellowBackground\n};\n\n\nif (color in colorObj) {\n colorObj[color]();\n}\n```\n\n\n这里有更多相关的[内容](http://www.nicoespeon.com/en/2015/01/oop-revisited-switch-in-js/).\n\n"},"2016-01-01-angularjs-digest-vs-apply.md":{"name":"2016-01-01-angularjs-digest-vs-apply.md","sha":"a468281c1e3f2eb9f651116a15d9dc3cd2bc3e19","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_cn/angularjs-digest-vs-apply/\n\ncategories:\n - zh_CN\n - angular\n---\n\nAngularJs最令人欣赏的特性之一就是双向数据绑定。AngularJs通过循环(`$digest`)检查model和view的变化实现此功能。想要理解框架底层的运行机制你需要理解这个概念。\n\n当一个事件被触发时,Angular触发每个watcher. 这是我们已知的`$digest`循环。有时你需要强制手动运行一个新的循环,而且因为这是最影响性能的一方面,你必须选择一个正确的选项。\n\n### `$apply`\n这个核心方法可以让你显式启动`digest`循环。这意味着所有的watcher将会被检测;整个应用启动`$digest loop`。在内部在会执行一个可选的方法之后,会调用`$rootScope.$digest();`.\n\n### `$digest`\n这种情况下`$digest`方法在当前作用域和它的子作用域启动`$digest`循环。你需要注意他的父作用域将不会被检测也不会被影响。\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- 如果你只需要更新当前的作用域或者它的子作用域的话,使用`$digest`,而且要防止在整个应用里运行新的`digest`循环。这在性能上的好处是显而易见的。\n- `$apply()`对机器来说是一个困难的处理过程,在绑定过多的时候可能会引发性能问题。\n- 如果你正使用`>AngularJS 1.2.X`版本,使用`$evalAsync`, 这个方法将在当前循环或下一个循环执行表达式,这能提高你的应用的性能。\n"},"2016-01-05-differences-between-undefined-and-null.md":{"name":"2016-01-05-differences-between-undefined-and-null.md","sha":"e311502b22237631dc38170458b099bd13e0d9c6","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_cn/differences-between-undefined-and-null/\n\ncategories:\n - zh_CN\n - javascript\n---\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- 它们都是基本类型\n- 他们都是[falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)\n (`Boolean(undefined) // false`, `Boolean(null) // false`)\n- 你可以这样判断一个变量是否是[undefined](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined)\n\n```javascript\n typeof variable === \"undefined\"\n```\n\n- 你可以这样判断一个变量是否是[null](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/null)\n\n```javascript\n variable === null\n```\n\n- **双等号**比较时它们相等,但**三等号**比较时不相等\n\n```javascript\n null == undefined // true\n\n null === undefined // false\n```\n"},"2015-12-29-insert-item-inside-an-array.md":{"name":"2015-12-29-insert-item-inside-an-array.md","sha":"c7696b046f3fc544cc1ef6eef612c0442837e7ec","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\nredirect_from:\n - /zh_cn/insert-item-inside-an-array/\n\ncategories:\n - zh_CN\n - javascript\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\n两种方法都是修改原始数组。不信?看看[jsperf](http://jsperf.com/push-item-inside-an-array)\n\n### 手机上的效率\n\n#### Android (v4.2.2)\n\n1. _arr.push(6);_ and _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### 桌面上的效率\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\n[0].concat(arr);\n```\n\n这里有一些小区别,unshift操作的是原始数组,concat返回一个新数组,参考[jsperf](http://jsperf.com/unshift-item-inside-an-array)\n\n\n### 手机上的效率 :\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### 桌面上的效率\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\n我在许多浏览器和系统中进行了测试,结果都是相似的。希望这条小知识可以帮到你,也欢迎大家自行测试。\n"},"2016-01-04-sorting-strings-with-accented-characters.md":{"name":"2016-01-04-sorting-strings-with-accented-characters.md","sha":"57914f66dd91aa0c4ec472c906489ffa5adcc688","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_cn/sorting-strings-with-accented-characters/\n\ncategories:\n - zh_CN\n - javascript\n---\n\nJavascript有一个原生方法**[sort](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)**可以排列数组。一次简单的`array.sort()`将每一个数组元素视为字符串并按照字母表排列。你也可以提供[自定义排列方法](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters)。\n\n```javascript\n['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort();\n// [\"Buenos Aires\", \"Mumbai\", \"New York\", \"Shanghai\"]\n```\n\n\n但是当你试图整理一个如`['é', 'a', 'ú', 'c']`这样的非ASCII元素的数组时,你可能会得到一个奇怪的结果`['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\n幸运的是,有两种方法可以解决这个问题,由ECMAScript国际化API提供的[localeCompare](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare)和[Intl.Collator](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Collator)。\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\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- 两个方法都可以自定义区域位置。\n- 根据[Firefox](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Performance),当比较大数量的字符串时,使用`Intl.Collator`更快。\n\n所以当你处理一个由非英语组成的字符串数组时,记得使用此方法来避免排序出现意外。\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":"445835ad64c0e97366780d829329b2fffb833336","content":"---\nlayout: post\n\ntitle: 可以接受单参数与数组的方法\ntip-number: 06\ntip-username: mattfxyz\ntip-username-profile: https://twitter.com/mattfxyz\ntip-tldr: 写一个方法可以接受单个参数也可以接受一个数组,而不是分开写两个方法。这和jQuery的一些方法的工作原理很像(`css` 可以修改任何匹配到的选择器).\n\nredirect_from:\n - /zh_cn/writing-a-single-method-for-arrays-and-a-single-element/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n写一个方法可以接受单个参数也可以接受一个数组,而不是分开写两个方法。这和jQuery的一些方法的工作原理很像(`css` 可以修改任何匹配到的选择器).\n\n你只要把任何东西连接到一个数组. `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\n`printUpperCase`现在可以接受单个单词或多个单词的数组作为它的参数。同时也可以避免在不传递参数时抛出的`TypeError`错误的隐患。\n\n```javascript\nprintUpperCase(\"cactus\");\n// => CACTUS\nprintUpperCase([\"cactus\", \"bear\", \"potato\"]);\n// => CACTUS\n// BEAR\n// POTATO\n```\n\n"},"2016-01-02-keys-in-children-components-are-important.md":{"name":"2016-01-02-keys-in-children-components-are-important.md","sha":"46de5ce58d120b6d191a691410b709875ae53101","content":"---\nlayout: post\n\ntitle: 子容器的Key是很重要的\ntip-number: 02\ntip-username: loverajoel \ntip-username-profile: https://github.com/loverajoel\ntip-tldr: key是必须传递给从数组中动态创建的所有组件的一个值。它是一个唯一且固定的id,用来识别DOM中的每个组件,也可以让我们区别它是否是同一个组件。使用key可以确保子容器是可保存而且不需要重复创建的,还可以防止奇怪的事情发生。\ntip-writer-support: https://www.coinbase.com/loverajoel\n\nredirect_from:\n - /zh_cn/keys-in-children-components-are-important/\n\ncategories:\n - zh_CN\n - react\n---\n\n[key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children)必须传递给从数组中动态创建的所有组件的一个值。它是一个唯一且固定的id,用来识别DOM中的每个组件,也可以让我们区别它是否是同一个组件。使用key可以确保子容器是可保存而且不需要重复创建的,还可以防止奇怪的事情发生。\n\n> key跟效率不是很相关,它更与身份有关系(这间接的使效率更好)。随机的赋值或改变值将不能识别身份[Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939)\n\n- 使用对象存在的的唯一值。\n- 在父组件定义key,而不是子组件。\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\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- 你可以创建以自己的唯一id。确定这个方法运行速度够快,把它附着到你的对象上。\n- 当子组件的数量很大或者包含重量级的组件时,使用key来提高性能。\n- [你必须提供key值给ReactCSSTransitionGroup的每个子组件](http://docs.reactjs-china.com/react/docs/animation.html)"},"2016-01-07-use-strict-and-get-lazy.md":{"name":"2016-01-07-use-strict-and-get-lazy.md","sha":"f18984280206c0a212170ebb6fe89100c5164fba","content":"---\nlayout: post\n\ntitle: 使用\"use strict\" 变得懒惰\ntip-number: 07\ntip-username: nainslie\ntip-username-profile: https://twitter.com/nat5an\ntip-tldr: JavaScript的严格模式使开发者更容易写出“安全”的代码。\n\nredirect_from:\n - /zh_cn/use-strict-and-get-lazy/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n(译者注:此片翻译较渣,欢迎指正,建议大家[阅读原文](http://www.jstips.co/en/use-strict-and-get-lazy/)或直接阅读[MDN对严格模式的中文介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode) 并欢迎PR)\n\nJavaScript的严格模式使开发者更容易写出“安全”的代码。\n\n通常情况下,JavaScript允许程序员相当粗心,比如你可以引用一个从未用\"var\"声明的变量。或许对于一个刚入门的开发者来说这看起来很方便,但这也是变量拼写错误或者从作用域外引用变量时引发的一系列错误的原因。\n\n程序员喜欢电脑帮我们做一些无聊的工作,喜欢它自动的检查我们工作上的错误。这就是\"use strict\"帮我们做的,它把我们的错误转变为了JavaScript错误。\n\n我们把这个指令放在js文件顶端来使用它:\n\n```javascript\n// 全脚本严格模式\n\"use strict\";\nvar v = \"Hi! I'm a strict mode script!\";\n```\n\n\n或者放在一个方法内:\n\n```javascript\nfunction f()\n{\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\n通过在JavaScript文件或方法内引入此指令,使JavaScript引擎运行在严格模式下,这直接禁止了许多大项目中不受欢迎的操作。另外,严格模式也改变了以下行为:\n* 只有被\"var\"声明过的变量才可以引用\n* 试图写只读变量时将会报错\n* 只能通过\"new\"关键字调用构造方法\n* \"this\"不再隐式的指向全局变量\n* 对eval()有更严格的限制\n* 防止你使用预保留关键字命名变量\n\n严格模式对于新项目来说是很棒的,但对于一些并没有使用它的老项目来说,引入它也是很有挑战性的。如果你把所有js文件都连接到一个大文件中的话,可能导致所有文件都运行在严格模式下,这可能也会有一些问题。\n\n它不是一个声明,而是一个表达式,被低版本的JavaScript忽略。\n严格模式的支持情况:\n* Internet Explorer 10+\n* Firefox 4+\n* Chrome 13+\n* Safari 5.1+\n* Opera 12+\n\n\n[MDN对严格模式的全面介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)\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":"32600f136d4752d53e4956235409211c6b6f1e4b","content":"---\nlayout: post\n\ntitle: 将Node List转换为数组(Array)\ntip-number: 08\ntip-username: Tevko\ntip-username-profile: https://twitter.com/tevko\ntip-tldr: 这是一个快速、安全、可重用的方法将node list转换为DOM元素的数组。\n\nredirect_from:\n - /zh_cn/converting-a-node-list-to-an-array/\n\ncategories:\n - zh_CN\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//之后 ..\n\nnodelistToArray.forEach(...);\nnodelistToArray.map(...);\nnodelistToArray.slice(...);\n\n\n//等...\n```\n\n`apply`方法可以在指定`this`时以数组形式向方法传递参数。[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)规定`apply`可以接受类数组对象,恰巧就是`querySelectorAll`方法所返回的内容。如果我们不需要指定方法内`this`的值时传`null`或`0`即可。返回的结果即包含所有数组方法的DOM元素数组。\n\n另外你可以使用`Array.prototype.slice`结合`Function.prototype.call`或`Function.prototype.apply`, 将类数组对象当做`this`传入:\n\n```javascript\nconst nodelist = document.querySelectorAll('div');\nconst nodelistToArray = Array.prototype.slice.call(nodelist); // or equivalently Array.prototype.slice.apply(nodelist);\n\n//之后 ..\n\nnodelistToArray.forEach(...);\nnodelistToArray.map(...);\nnodelistToArray.slice(...);\n\n//等...\n```\n\n\n如果你正在用ES2015你可以使用[展开运算符 `...`](https://developer.mozilla.org/zh-CN/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```\n"},"2016-01-11-hoisting.md":{"name":"2016-01-11-hoisting.md","sha":"7065cd7861bcda33e89f52d0afe3394130af7423","content":"---\nlayout: post\n\ntitle: 变量提升\ntip-number: 11\ntip-username: squizzleflip\ntip-username-profile: https://twitter.com/squizzleflip\ntip-tldr: 理解变量提升有助于管理函数作用域\n\nredirect_from:\n - /zh_cn/hoisting/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n理解[变量提升](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var#var_hoisting)可以帮助你组织方法作用域。只要记住变量声明和方法声明都会被提升到顶部。变量的定义不会提升,即使你在同一行声明和定义一个变量。变量**声明**是让系统知道有这个变量存在而**定义**是给其赋值。\n\n```javascript\nfunction doTheThing() {\n // ReferenceError: notDeclared is not defined\n console.log(notDeclared);\n\n // Outputs: undefined\n console.log(definedLater);\n var definedLater;\n\n definedLater = 'I am defined!'\n // Outputs: 'I am defined!'\n console.log(definedLater)\n\n // Outputs: undefined\n console.log(definedSimulateneously);\n var definedSimulateneously = 'I am defined!'\n // Outputs: 'I am defined!'\n console.log(definedSimulateneously)\n\n // Outputs: 'I did it!'\n doSomethingElse();\n\n function doSomethingElse(){\n console.log('I did it!');\n }\n\n // TypeError: undefined is not a function\n functionVar();\n\n var functionVar = function(){\n console.log('I did it!');\n }\n}\n```\n\n\n为了让你的代码更易读,将所有的变量声明在函数的顶端,这样可以更清楚的知道变量来自哪个作用域。在使用变量之前声明变量。将方法定义在函数的底部。\n"},"2016-01-09-template-strings.md":{"name":"2016-01-09-template-strings.md","sha":"0e6a2a0b6e54eb262f58cfe206f30b6a544b4979","content":"---\nlayout: post\n\ntitle: 模板字符串\ntip-number: 09\ntip-username: JakeRawr\ntip-username-profile: https://github.com/JakeRawr\ntip-tldr: ES6中,JS现在有了引号拼接字符串的替代品,模板字符串。\n\nredirect_from:\n - /zh_cn/template-strings/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n\nES6中,JS现在有了引号拼接字符串的替代品,模板字符串。\n\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```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)甚至使用[逻辑运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)。\n\n```javascript\nvar val1 = 1, val2 = 2;\nconsole.log(`${val1} is ${val1 < val2 ? 'less than': 'greater than'} ${val2}`)\n// 1 is less than 2\n```\n\n你也可以使用函数修改末班字符串的输出内容;这被称为[带标签的模板字符串](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings#带标签的模板字符串),其中包含了带标签的模板字符串的示例.\n\n\n或许你还想[阅读更多内容](https://hacks.mozilla.org/2015/05/es6-in-depth-template-strings-2)来了解模板字符串。\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":"7046aa978a42ba8b0eaea93212f4852e9a177b85","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_cn/filtering-and-sorting-a-list-of-strings/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n你可能有一个很多名字组成的列表,需要过滤掉重复的名字并按字母表将其排序。\n\n在我们的例子里准备用不同版本语言的**JavaScript 保留字**的列表,但是你能发现,有很多重复的关键字而且它们并没有按字母表顺序排列。所以这是一个完美的字符串列表([数组](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array))来测试我们的JavaScript小知识。\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/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),它将基于我们传递的回调方法返回一个新的过滤后的数组。回调方法将比较当前关键字在原始列表里的索引和新列表中的索引,仅当索引匹配时将当前关键字push到新数组。\n\n最后我们准备使用[`sort`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)方法排序过滤后的列表,`sort`只接受一个比较方法作为参数,并返回按字母表排序后的列表。\n\n```js\nvar filteredAndSortedKeywords = keywords\n .filter(function (keyword, index) {\n return keywords.lastIndexOf(keyword) === index;\n })\n .sort(function (a, b) {\n return a < b ? -1 : 1;\n });\n```\n\n在**ES6** (ECMAScript 2015)版本下使用[箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions)看起来更简单:\n\n```js\nconst filteredAndSortedKeywords = keywords\n .filter((keyword, index) => keywords.lastIndexOf(keyword) === index)\n .sort((a, b) => a < b ? -1 : 1);\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\n*感谢[@nikshulipa](https://github.com/nikshulipa)、[@kirilloid](https://twitter.com/kirilloid)、[@lesterzone](https://twitter.com/lesterzone)、[@tracker1](https://twitter.com/tracker1)、[@manuel_del_pozo](https://twitter.com/manuel_del_pozo)所有的回复与建议!*\n"},"2016-01-23-converting-to-number-fast-way.md":{"name":"2016-01-23-converting-to-number-fast-way.md","sha":"1b6913890b994c138292e2d3a81ad51a99963b98","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_cn/converting-to-number-fast-way/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n将字符串转换为数字是极为常见的。最简单和快速的方法([jsPref](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-13-tip-to-measure-performance-of-a-javascript-block.md":{"name":"2016-01-13-tip-to-measure-performance-of-a-javascript-block.md","sha":"8bf32febed4c0b05ca514efe81c37b2119b44739","content":"---\nlayout: post\n\ntitle: 测量javascript代码块性能的小知识\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_cn/fat-arrow-functions/\n\ncategories:\n - zh_CN\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\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\"); // Outputs: Array initialize: 0.711ms\n```\n\n\n更多内容:\n[Console object](https://github.com/DeveloperToolsWG/console-object),\n[Javascript benchmarking](https://mathiasbynens.be/notes/javascript-benchmarking)\n\nDemo: [jsfiddle](https://jsfiddle.net/meottb62/) - [codepen](http://codepen.io/anon/pen/JGJPoa) (在浏览器控制台输出)\n\n> 注意:由于[Mozilla](https://developer.mozilla.org/zh-CN/docs/Web/API/Console/time)不建议将其使用在线上项目中,建议仅在开发中使用。"},"2016-01-20-return-objects-to-enable-chaining-of-functions.md":{"name":"2016-01-20-return-objects-to-enable-chaining-of-functions.md","sha":"8c45960461baf14f049f3194904c8bcea03cd29c","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_cn/return-objects-to-enable-chaining-of-functions/\n\ncategories:\n - zh_CN\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-01-14-fat-arrow-functions.md":{"name":"2016-01-14-fat-arrow-functions.md","sha":"0eb952e33aa81ba30055778022f35f03adb8ba37","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_cn/fat-arrow-functions/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n\n介绍一个ES6的新特性,箭头函数或许一个让你用更少行写更多代码的方便工具。它的名字(fat arrow functions)来自于它的语法`=>`是一个比瘦箭头`->`要'胖的箭头'(译者注:但是国内貌似不分胖瘦就叫箭头函数)。Some programmers might already know this type of functions from different languages such as Haskell as 'lambda expressions' respectively 'anonymous functions'. It is called anonymous, as these arrow functions do not have a descriptive function name.(译者注:一些其他语言中的箭头函数,避免不准确就不翻译了 欢迎PR)\n\n### 有什么益处呢?\n* 语法: 更少的代码行; 不再需要一遍一遍的打`function`了\n* 语义: 从上下文中捕获`this`关键字\n\n### 简单的语法示例\n观察一下这两个功能完全相同的代码片段。你将很快明白箭头函数做了什么。\n\n```javascript\n// 箭头函数的一般语法\nparam => expression\n\n// 也可以用用小括号\n// 多参数时小括号是必须的\n(param1 [, param2]) => expression\n\n\n// 使用functions\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正如你所看到的,箭头函数在这种情况下省去了写小括号,function以及return的时间。我建议你总是使用小括号,因为对于像`(x,y) => x+y`这样多参数函数,小括号总是需要的。这仅是以防在不同使用场景下忘记小括号的一种方法。但是上面的代码和`x => x*x`是一样的。至此仅是语法上的提升,减少了代码行数并提高了可读性。\n\n### Lexically binding `this`\n\n这是另一个使用箭头函数的好原因。这是一个关于`this`上下文的问题。使用箭头函数,你不需要再担心`.bind(this)`也不用再设置`that = this`了,因为箭头函数继承了外围作用域的`this`值。看一下下面的[示例](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// 不好的例子\nfunction CounterA() {\n // CounterA的`this`实例 (!!调用时忽略了此实例)\n this.i = 0;\n setInterval(function () {\n // `this` 指向全局(global)对象,而不是CounterA的`this`\n // 所以从100开始计数,而不是0 (本地的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/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions)。更多语法选项请看[这里](http://jsrocks.org/2014/10/arrow-functions-and-their-scope/).\n\n"},"2016-01-12-pseudomandatory-parameters-in-es6-functions.md":{"name":"2016-01-12-pseudomandatory-parameters-in-es6-functions.md","sha":"17c3b7e4ee82e1b0862768dbd163275ce4cd3ada","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_cn/tip-to-measure-performance-of-a-javascript-block/\n\ncategories:\n - zh_CN\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\n throw new Error( message );\n\n}\n\nconst getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b\n\ngetSum( 10 ) // throws Error, b is not defined\n\ngetSum( undefined, 10 ) // throws Error, a is not defined\n```\n\n `_err` 是一个即时抛出错误的方法。如果参数中的任何一个没有值,参数默认的值将会被使用, `_err`方法将被调用,并且会抛出一个错误。你可以从[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Default_parameters)看到更多关于**默认参数特性**的例子。 "},"2016-01-18-rounding-the-fast-way.md":{"name":"2016-01-18-rounding-the-fast-way.md","sha":"1d572b3f5e85fdb94a76d30f941fa3dfdb3d1f18","content":"---\nlayout: post\n\ntitle: 快速(但危险)的取整方法\ntip-number: 18\ntip-username: pklinger\ntip-username-profile: https://github.com/pklinger\ntip-tldr: 通常情况下`~~X`比`Math.trunc(X)`要快,但同时也会使你的代码做一些讨厌的事情。\n\nredirect_from:\n - /zh_cn/rounding-the-fast-way/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n本条小知识关于性能...\n\n你曾遇到过[双波浪线`~~`操作符](http://stackoverflow.com/questions/5971645/what-is-the-double-tilde-operator-in-javascript)吗?它也被称为“双按位非”操作符。你通常可以使用它作为代替`Math.trunc()`的更快的方法。为什么呢?\n\n一个按位非操作符`~`首先将输入`input`截取为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如果你想迷惑其他人,或者想在`minifier/uglifier`时取得更大功效,这是一种相对廉价的方式。\n\n### 禁用的情况\n\n##### 当你的代码需要维护时\n代码可读性始终是最重要的。无论你工作在一个团队,或是贡献给开源仓库,或是单飞。正如[名言所说](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\nFor a solo programmer, that psychopath is inevitably \"you in six months\".(这句不会翻译……)\n\n##### 当你忘记`~~`永远趋向于0时\n新手程序员或许更关注`~~`的聪明之处,却忘记了“只去掉小数部分”的意义。这在将浮点数转换为数组索引或关联有序的值时很容易导致**差一错误** ,这时明显需要一个不同的取整方法。 (代码可读性不高往往会导致此问题)\n\n打个比方,如果你想得到离一个数“最近的整数”,你应该用`Math.round()`而不是`~~`,但是由于程序员的惰性和**_每次使用需要敲10个键_**的事实,人类的手指往往会战胜冷冷的逻辑,导致错误的结果。\n\n相比之下,`Math.xyz()`(举例)函数的名字清楚的传达了它们的作用,减少了可能出现的意外的错误。\n\n##### 当处理大数时\n因为`~`首先将数组转换为32位,`~~`的结果伪值在 ±2.15*10^12左右。如果你没有明确的检查输入值的范围,当转换的值最终与原始值有很大差距时,用户就可能触发未知的行为:\n\n```js\na = 2147483647.123 // 比32位最大正数,再多一点\nconsole.log(~~a) // -> 2147483647 (ok)\na += 10000 // -> 2147493647.123 (ok)\nconsole.log(~~a) // -> -2147483648 (huh?)\n```\n\n一个特别容易中招的地方是在处理Unix时间戳时(从1970年1月1日 00:00:00 UTC开始以秒测量)。一个快速获取的方法:\n\n```js\nepoch_int = ~~(+new Date() / 1000) // Date() 以毫秒计量,所以我们缩小它\n```\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\n一些程序员将其看作适当输入验证的替代品。然而,这将导致奇怪的逻辑问题,因此你不能辨别违法输入还是真正的`0`。因此这_并不_推荐。\n\n##### 当很多人认为`~~X == Math.floor(X)`时\n很多人由于很多原因错误的把\"双按位非\"等同于`Math.floor()`。如果你不能准确地使用它,最终你很有可能会滥用它。\n\n另一些人很细心的注意正数使用`Math.floor()`而负数使用`Math.ceil()`,但这又强制你在处理它的时候需要停下来想一想你处理的数是什么值。这又违背了使用`~~`快捷无陷阱的目的。\n\n### 结论\n尽量避免,并有节制的使用。\n\n### 使用\n1. 谨慎使用。\n2. 在应用前检查值。\n3. 仔细记录被转化值的相关假设。\n4. 审查代码至少处理:\n * 逻辑错误,不合法的输入作为合法的`0`传入其他代码模块\n * 输入转换后范围错误\n * 错误的舍入方向导致差一错误\n"},"2016-01-19-safe-string-concatenation.md":{"name":"2016-01-19-safe-string-concatenation.md","sha":"541d18bd0cc4c725dea87a6f3c68de7f92ec6c86","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_cn/safe-string-concatenation/\n\ncategories:\n - zh_CN\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\" instead of \"123\"\n```\n\n关于性能,与用[```join```](http://www.sitepoint.com/javascript-fast-string-concatenation/)来拼接字符串相比 ```concat```的效率是几乎一样的。\n\n你可以在[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)了解更多关于```concat```方法的内容。\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":"5e103fa3e03c4d8375646998e895e93dd88ab66a","content":"---\nlayout: post\n\ntitle: Node.js - 运行未被引用的模块\ntip-number: 17\ntip-username: odsdq\ntip-username-profile: https://twitter.com/odsdq\ntip-tldr: 在Node里,你可以让你的程序根据其运行自`require('./something.js')`或者`node something.js`而做不同的处理。如果你想与你的一个独立的模块进行交互,这是非常有用的。\n\nredirect_from:\n - /zh_cn/nodejs-run-a-module-if-it-is-not-required/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n在Node里,你可以让你的程序根据其运行自`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更多内容请看 [modules的文档](https://nodejs.org/api/modules.html#modules_module_parent)\n"},"2016-01-21-shuffle-an-array.md":{"name":"2016-01-21-shuffle-an-array.md","sha":"c79dc8fb0f49ad01f16cf0f6db2224aa659b042f","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_cn/shuffle-an-array/\n\ncategories:\n - zh_CN\n - javascript\n---\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\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-01-24-use_===_instead_of_==.md":{"name":"2016-01-24-use_===_instead_of_==.md","sha":"4a2c6206349e539804ab5c722dd14cbd14581bf4","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_cn/use_===_instead_of_==/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n`==` (或者 `!=`) 操作在需要的情况下自动进行了类型转换。`===` (或 `!==`)操作不会执行任何转换。`===`在比较值和类型时,可以说比`==`更快([jsPref](http://jsperf.com/strictcompare))。 \n\n```js\n[10] == 10 // 为 true\n[10] === 10 // 为 false\n\n'10' == 10 // 为 true\n'10' === 10 // 为 false\n\n [] == 0 // 为 true\n [] === 0 // 为 false\n\n '' == false // 为 true 但 true == \"a\" 为false\n '' === false // 为 false \n\n```\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":"248e83a284c46f5b0b691f762bfdd75cef6e168a","content":"---\nlayout: post\n\ntitle: 更简单的使用indexOf实现contains功能\ntip-number: 15\ntip-username: jhogoforbroke\ntip-username-profile: https://twitter.com/jhogoforbroke\ntip-tldr: JavaScript并未提供contains方法。检测子字符串是否存在于字符串或者变量是否存在于数组你可能会这样做。\n\nredirect_from:\n - /zh_cn/even-simpler-way-of-using-indexof-as-a-contains-clause/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n\nJavaScript并未提供contains方法。检测子字符串是否存在于字符串或者变量是否存在于数组你可能会这样做:\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```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```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```javascript\n// key is invalid\nif (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));\n```\n\n难点是 [位操作符](https://developer.mozilla.org/zh-CN/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 contains \"tex\" - true\n!~someText.indexOf('tex'); // someText NOT contains \"tex\" - false\n~someText.indexOf('asd'); // someText doesn't contain \"asd\" - false\n~someText.indexOf('ext'); // someText contains \"ext\" - true\n```\n\n### String.prototype.includes()\n\n在ES6中提供了[includes() 方法](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/includes)供我们判断一个字符串是否包含了另一个字符串:\n\n```javascript\n'something'.includes('thing'); // true\n```\n\n在ECMAScript 2016 (ES7)甚至可能将其应用于数组,像indexOf一样:\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-01-27-short-circiut-evaluation-in-js.md":{"name":"2016-01-27-short-circiut-evaluation-in-js.md","sha":"83df232918d90df250764a4c10093d1a4e14fb97","content":"---\nlayout: post\n\ntitle: JS中的短路求值\ntip-number: 27\ntip-username: bhaskarmelkani\ntip-username-profile: https://www.twitter.com/bhaskarmelkani\ntip-tldr: 短路求值是说, 只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值:当AND(`&&`)的第一个运算数的值为false时,其结果必定为false;当OR(`||`)的第一个运算数为true时,最后结果必定为true。\n\ncategories:\n - zh_CN\n---\n\n[短路求值](https://zh.wikipedia.org/wiki/%E7%9F%AD%E8%B7%AF%E6%B1%82%E5%80%BC)是说, 只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值:当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使用逻辑与 - `&&`.\n\n```js\n// 普通的if语句\nif(test){\n isTrue(); // Test is true\n}\n\n// 上面的语句可以使用 '&&' 写为:\n\n( test && isTrue() ); // Test is true\n```\n使用逻辑或 - `||`.\n\n```js\ntest = false;\nif(!test){\n isFalse(); // Test is false.\n}\n\n( test || isFalse()); // Test is false.\n```\n逻辑或可以用来给参数设置默认值。\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逻辑与可以用来避免调用undefined参数的属性时报错\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() 将会抛出\"Cannot read property 'bark' of undefined.\" 错误\n// 防止这种情况,我们可以使用 &&.\n\ndog&&dog.bark(); // This will only call dog.bark(), if dog is defined.\n\n```\n"},"2016-01-16-passing-arguments-to-callback-functions.md":{"name":"2016-01-16-passing-arguments-to-callback-functions.md","sha":"b401153b78e2b0c9f4b7394d3fb720e64e6dabeb","content":"---\nlayout: post\n\ntitle: 向回调方法传递参数\ntip-number: 16\ntip-username: minhazav\ntip-username-profile: https://twitter.com/minhazav\ntip-tldr: 通常下,你并不能给回调函数传递参数,但是你可以借助Javascript闭包的优势来传递参数给回调函数。\n\nredirect_from:\n - /zh_cn/passing-arguments-to-callback-functions/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n通常下,你并不能给回调函数传递参数。 比如:\n\n```js\nfunction callback() {\n console.log('Hi human');\n}\n\ndocument.getElementById('someelem').addEventListener('click', callback);\n```\n\n你可以借助Javascript闭包的优势来传递参数给回调函数。看这个例子:\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闭包是指函数有自由独立的变量。换句话说,定义在闭包中的函数可以“记忆”它创建时候的环境。想了解更多请[参考MDN的文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures)。\n\n这种方法使参数`x`和`y`在回调方法被调用时处于其作用域内。\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\n两种方法之间有着微小的性能差异,请看[jsperf](http://jsperf.com/bind-vs-closure-23).\n"},"2016-02-04-assignment-shorthands.md":{"name":"2016-02-04-assignment-shorthands.md","sha":"062d5de5908a218c0a1a18ba41f96cc149c3a1c5","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_cn/assignment-shorthands/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n赋值是很常见的。有时候打字对于我们这些“懒惰的程序员”来说是很费时间的。\n所以,我们可以使用一些小把戏来使我们的代码更清楚更简单。\n\n这类似于使用:\n\n```js\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```js\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```js\nvar a = 2;\nvar b = ++a;\n// 现在a和b都是3\n```\n\n看明白了吗?我将操作符放在了参数_前面_。\n\n`--`操作符除了使值减小外,其他功能是类似的。\n\n### If-else (使用三元运算符)\n\n我们平时会这样写:\n\n```js\nvar newValue;\nif(value > 10) \n newValue = 5;\nelse\n newValue = 2;\n```\n\n我们可以使用三元运算符是它更简便:\n\n```js\nvar newValue = (value > 10) ? 5 : 2;\n```\n\n### 检测Null、Undefined、空\n\n```js\nif (variable1 !== null || variable1 !== undefined || variable1 !== '') {\n var variable2 = variable1;\n}\n```\n\n简便写法:\n\n```js\nvar variable2 = variable1 || '';\n```\n\nP.S.:如果`variable1`是一个数字,则先检查他是否为0。\n\n### 对象数组表示法\n\n不要用:\n\n```js\nvar a = new Array();\na[0] = \"myString1\";\na[1] = \"myString2\";\n```\n\n使用:\n\n```js\nvar a = [\"myString1\", \"myString2\"];\n```\n\n### 关联数组\n\n不要用:\n\n```js\nvar skillSet = new Array();\nskillSet['Document language'] = 'HTML5';\nskillSet['Styling language'] = 'CSS3';\n```\n\n使用:\n\n```js\nvar skillSet = {\n 'Document language' : 'HTML5', \n 'Styling language' : 'CSS3'\n};\n```\n"},"2016-02-12-use-destructuring-in-function-parameters.md":{"name":"2016-02-12-use-destructuring-in-function-parameters.md","sha":"7b0e70f25178554c2259e5fdcf777ba5f627911a","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_cn/use-destructuring-in-function-parameters/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n大家一定对[ES6解构赋值](https://developer.mozilla.org/zh-CN/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/zh-CN/docs/Web/JavaScript/Reference/Functions/Default_parameters)来填充调用者没有传递或忘记传递的参数值:\n\n```js\nvar sayHello2 = function({ name = \"Anony\", surname = \"Moose\" } = {}) {\n console.log(`Hello ${name} ${surname}! How are you?`);\n};\n```\n\n`= {}`表示此参数需要解构的默认对象是一个`{}`,以防调用者忘记传值,或传递了一个错误类型(大多情况为后者)。\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-01-25-Using-immediately-invoked-function-expression.md":{"name":"2016-01-25-Using-immediately-invoked-function-expression.md","sha":"ef9587a6da83a55b6600822da80d754531a63123","content":"---\nlayout: post\n\ntitle: 使用立即执行函数表达式\ntip-number: 25\ntip-username: rishantagarwal \ntip-username-profile: https://github.com/rishantagarwal\ntip-tldr: 立即执行函数表达式( IIFE - immediately invoked function expression)是一个立即执行的匿名函数表达式,它在Javascript中有一些很重要的用途。\n\n\nredirect_from:\n - /zh_cn/Using-immediately-invoked-function-expression/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n立即执行函数表达式( IIFE - immediately invoked function expression)是一个立即执行的匿名函数表达式,它在Javascript中有一些很重要的用途。\n\n```javascript\n\n(function() {\n // Do something\n }\n)()\n\n```\n\n这是一个立即执行的匿名函数表达式,它在有JavaScript一些特别重要的用途。\n\n两对括号包裹着一个匿名函数,使匿名函数变成了一个函数表达式。于是,我们现在拥有了一个未命名的函数表达式,而不是一个全局作用域下或在任何地方定义的的简单函数。\n\n类似地,我们也可以创建一个命名过的立即执行函数表达式:\n\n```javascript\n(someNamedFunction = function(msg) {\n\tconsole.log(msg || \"Nothing for today !!\")\n\t}) (); // 输出 --> Nothing for today !!\n\nsomeNamedFunction(\"Javascript rocks !!\"); // 输出 --> Javascript rocks !!\nsomeNamedFunction(); // 输出 --> Nothing for today !!\n```\n\n更多内容, 请参考下面链接 - \n1. [链接 1](https://blog.mariusschulz.com/2016/01/13/disassembling-javascripts-iife-syntax) \n2. [链接 2](http://javascriptissexy.com/12-simple-yet-powerful-javascript-tips/) \n\n效率:\n[jsPerf](http://jsperf.com/iife-with-call)"},"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":"83f6bbef64e96d9d3e4f345077baac81425b3bee","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_cn/avoid-modifying-or-passing-arguments-into-other-functions-it-kills-optimization/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n### 背景\n\n在JavaScript的方法里,[`arguments`](https://developer.mozilla.org/zh-CN/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\n将`arguments`传递给`Array`原型(prototype)上的`slice`方法;`slice`方法返回一个对`arguments`浅复制后的数组对象。更短的写法:\n\n```js\nvar args = [].slice.call(arguments);\n```\n\n在这里,简单的调用了空数组的`slice`方法,而没有从`Array`的原型(prototype)上调用。\n\n### 系统优化\n\n不幸的是,传递`arguments`给任何参数,将导致Chrome和Node中使用的V8引擎跳过对其的优化,这也将使性能相当慢。看一下这篇文章[optimization killers](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers)。传递`arguments`给任何方法被称为*leaking `arguments`*。\n\n如果你想用一个包含参数(arguments)的数组,你需要求助于这个办法:\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没错,这很啰嗦,但是在生产环境中的代码里,为了系统性能优化,这是值得的。"},"2016-02-03-implementing-asynchronous-loops.md":{"name":"2016-02-03-implementing-asynchronous-loops.md","sha":"35e9d1b4c69cea8f26df3abbef3eed3f4782bc8a","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_cn/implementing-asynchronous-loops/\n\ncategories:\n - zh_CN\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\n```js\n> 5\n> 5\n> 5\n> 5\n> 5\n```\n\n这明显是有问题的。\n\n**原因**\n\n每次时间结束(timeout)都指向原始的`i`,而并非它的拷贝。所以,for循环使`i`增长到5,之后`timeout`运行并调用了当前`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\n```js\n> 4\n> 4\n> 4\n> 4\n> 4\n```\n\n这仍然有问题,这是因为并不存在块作用域,而且变量的声明被提升到了作用域顶端。实际上,如上代码和下面是一样的:\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\n有几个不同的方式可以拷贝`i`。最普通且常用方法是通过声明函数来建立一个闭包,并将`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\n在JavaScript里,参数是按值传递给函数的。像`Number`、`Date`和`String`这些原始类型为基本复制。当你们在一个函数内改变它的值,并不影响外面的作用域。但`Object`类型不一样:如果你在函数内部修改了它的参数,将会影响到所有包含该`Object`的作用域内它的参数。\n\n另一种方法是使用`let`。在ES6中的`let`关键字是可以实现的,它和`var`不一样,因为它支持块作用域的。\n\n```js\nfor (let i=0; i<5; i++) {\n\tvar temp = i;\n \tsetTimeout(function(){\n\t\tconsole.log(i); \n\t}, 1000 * (i+1));\n} \n```"},"2016-01-30-converting-truthy-falsy-values-to-boolean.md":{"name":"2016-01-30-converting-truthy-falsy-values-to-boolean.md","sha":"852c56f0f5a574c9d73fe923e7ac3a1d17914da3","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_cn/converting-truthy-falsy-values-to-boolean/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n你可以使用`!!`操作符将[truthy](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy)或[falsy](https://developer.mozilla.org/zh-CN/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\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":"26de96d053abcce287d59444bb07b26ff2777aa4","content":"---\nlayout: post\n\ntitle: 仅用一行生成`[0, 1, ..., N-1]`数列\ntip-number: 33\ntip-username: SarjuHansaliya\ntip-username-profile: https://github.com/SarjuHansaliya\ntip-tldr: 我们可以创建一个函数,它可以仅用一行代码生成0...(N-1)数列。\n\n\nredirect_from:\n - /zh_cn/create-range-0...n-easily-using-one-line/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n使用下面一行代码,我们就可以生成0...(N-1)数列。\n\n### 方法1 (需要 ES5)\n\n```js\nArray.apply(null, {length: N}).map(Function.call, Number);\n```\n\n#### 简要说明\n\n1. `Array.apply(null, {length: N})` 返回一个由`undefined`填充的长度为`N`的数组(例如 `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### 方法2 (需要 ES6)\n\n这里用到了`Array.from` [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from)\n\n```js\n Array.from(new Array(N),(val,index)=>index);\n```\n\n#### 简要说明\n\n1. `A = new Array(N)` 返回一个有`N`个_小孔_的数组 (例如 `A = [,,,...]`, 但是对于`x` in `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### One More Thing\n\n如果你需要[1, 2, ..., N]序列, **方法1** 可改为:\n\n```js\nArray.apply(null, {length: N}).map(function(value, index){\n return index + 1;\n});\n```\n\n**方法2**可改为:\n\n```js\nArray.from(new Array(N),(val,index)=>index+1);\n```"},"2016-02-11-preventing-unapply-attacks.md":{"name":"2016-02-11-preventing-unapply-attacks.md","sha":"0c51c2404dd1685cc4955045eebb9ccf263c017e","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_cn/preventing-unapply-attacks/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n重写内置对象的原型方法,外部代码可以通过重写代码达到暴漏和修改已绑定参数的函数。这在es5的方法下使用`polyfill`时是一个严重的安全问题。\n\n```js\n// bind 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; // restore the correct version\n var curr = Array.prototype.slice.call(arguments, 0);\n var result = concat.apply([], curr);\n return result;\n };\n}\n```\n\n上面的函数声明忽略了函数bind的`prev`参数,意味着调用`unapplyAttack`之后首次调用`.concat`将会抛出错误。\n\n使用[Object.freeze](https://developer.mozilla.org/zh-CN/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攻击。"},"2016-01-28-curry-vs-partial-application.md":{"name":"2016-01-28-curry-vs-partial-application.md","sha":"b02209e0c729737fe8396ad3754edb811f73bded","content":"---\nlayout: post\n\ntitle: 柯里化(currying)与部分应用(partial application)\ntip-number: 28\ntip-username: bhaskarmelkani\ntip-username-profile: https://www.twitter.com/bhaskarmelkani\ntip-tldr: 柯里化(currying)与部分应用(partial application)是两种将一个函数转换为另一个有着较小普通参数个数的函数的方法。\n\nredirect_from:\n - /zh_cn/curry-vs-partial-application/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n**柯里化(currying)**\n\n柯里化是使一个函数\n\nf: X * Y -> R\n\n转变为\n\nf': X -> (Y -> R)\n\n与用两个参数调用f不同,我们用一个参数运行f'。返回的结果是一个函数,然后用第二个参数调用此函数,得到结果。\n\n如此,如果未柯里化的函数f这样调用\n\nf(3,5)\n\n柯里化后的函数f'是这样调用的\n\nf(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\ncurry: (X × Y → R) → (X → (Y → R))\n\nJavascript Code:\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**部分应用(partial application)**\n\n部分应用将一个函数\n\nf: X * Y -> R\n\n的第一个参数固定而产生一个新的函数\n\nf`: Y -> R\n\nf'与f不同,只需要填写第二个参数,这也是f'比f少一个参数的原因。\n\n比如:将函数add的第一个参数绑定为5来产生函数plus5。\n\n```javascript\nfunction plus5(y) {\n return 5 + y;\n}\n\nplus5(3); // returns 8\n```\n\n**部分应用的规则** \n\n部分应用使用一个二元函数和一个值产生了一个一元函数。\n\npartApply : ((X × Y → R) × X) → (Y → R)\n\nJavascript Code:\n\n```javascript\nfunction partApply(f, x) {\n return function(y) {\n return f(x, y);\n }\n}\n```\n"},"2016-02-07-flattening-multidimensional-arrays-in-javascript.md":{"name":"2016-02-07-flattening-multidimensional-arrays-in-javascript.md","sha":"3f7df22f76c2f3e82fa16c00960c10ad362c42e1","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\n\nredirect_from:\n - /zh_cn/flattening-multidimensional-arrays-in-javascript/\n\ncategories:\n - zh_CN\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### 解决方案1:使用[`concat()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)和[`apply()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)\n\n```js\nvar myNewArray = [].concat.apply([], myArray);\n// [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n### 解决方案2:使用[`reduce()`](https://developer.mozilla.org/zh-CN/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### 解决方案3:\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/zh-CN/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)有一个测试。"},"2016-02-05-observe-dom-changes.md":{"name":"2016-02-05-observe-dom-changes.md","sha":"bc67fe5bdde7750c2075a24e792204b6a3d2996a","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_cn/observe-dom-changes/\n\ncategories:\n - zh_CN\n - javascript\n---\n[MutationObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver)是监听DOM变化与当元素变化时做适当操作的一个解决方法。在下面的例子中我们使用计时器模拟了内容的动态加载,第一个元素\"target\"创建后,创建\"subTarget\"。\n在扩展中的代码,`rootObserver`首先开始工作,直到`targetElement`被创建后`rootObserver`停止,然后`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\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":"46beffd933fe611f84fbc073a2018bc57b7d2323","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_cn/map-to-the-rescue-adding-order-to-object-properties/\n\ncategories:\n - zh_CN\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\n### Map\n\n使用ES6的新特性Map。[Map](https://developer.mozilla.org/zh-CN/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### 攻克老浏览器\n\nMozilla 建议:\n> 所以,如果过你想在跨浏览器环境中模拟一个有序的关联数组,你要么使用两个分开的数组(一个保存key,另一个保存value),要么构建一个单属性对象(single-property objects)的数组。\n\n```js\n// 使用分开的数组\nvar objectKeys = [z, @, b, 1, 5];\nfor (item in objectKeys) {\n\tmyObject[item]\n...\n\n// 构建一个单属性对象(single-property objects)的数组\nvar myData = [{z: 1}, {'@': 2}, {b: 3}, {1: 4}, {5: 5}];\n```"},"2016-02-08-advanced-properties.md":{"name":"2016-02-08-advanced-properties.md","sha":"fa7a9db226448f1fc616f28fe496f83f38fecbe4","content":"---\nlayout: post\n\ntitle: Javascript高级特性\ntip-number: 39\ntip-username: mallowigi\ntip-username-profile: https://github.com/mallowigi\ntip-tldr: 怎样给一个对象添加私有参数、`getter`或`setter`。\n\n\nredirect_from:\n - /zh_cn/advanced-properties/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n在Javascript里配置对象属性是可以实现的,比如将一个参数设为伪私有或者只读。这个特性从ECMAScript 5.1开始就可以使用了,因此近来的浏览器都是支持的。\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\n```js\nObject.defineProperty(dest, propName, options);\n```\n\n或者定义多个:\n\n```js\nObject.defineProperties(dest, {\n propA: optionsA,\n propB: optionsB, //...\n});\n```\n\n`options`包含如下的属性:\n- *value*: 如果参数不是`getter`(请看下文),`value`是必须的。`{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`第二个参数是字符串,所以允许我们定义*动态变量(defineProperty)*。例如,我们可以说我们要根据一些外部配置创建一个属性:\n\n```js\n\nvar obj = {\n getTypeFromExternal(): true // illegal in ES5.1\n};\n\nObject.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok\n\n// For the example sake, ES6 introduced a new syntax:\nvar obj = {\n [getTypeFromExternal()]: true\n};\n```\n\n还没有结束!高级特性允许我们创建**getter**和**setter**,就像其他面向对象(OOP)语言!这种情况下,我们不能使用`writable`、`enumerable`和`configurable`参数,而是:\n\n```js\nfunction Foobar () {\n var _foo; // true private property\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\n如果你定义了`getter`而没有定义`setter`却仍要给它赋值,你将会得到一个错误。这在使用像`$.extend`或`_.merge`这样的辅助方法时是尤为重要的。要小心!\n\n### 链接\n\n- [defineProperty](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)\n- [Defining properties in JavaScript](http://bdadam.com/blog/defining-properties-in-javascript.html)\n"},"2016-02-15-detect-document-ready-in-pure-js.md":{"name":"2016-02-15-detect-document-ready-in-pure-js.md","sha":"682e29b3e424eadbb169dcdb9fe57c088094ebbd","content":"---\nlayout: post\n\ntitle: 纯JS监听document是否加载完成\ntip-number: 46\ntip-username: loverajoel\ntip-username-profile: https://www.twitter.com/loverajoel\ntip-tldr: 跨浏览器且纯JavaScript检测document是否加载完成。\ntip-writer-support: https://www.coinbase.com/loverajoel\n\nredirect_from:\n - /zh_cn/detect-document-ready-in-pure-js/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n跨浏览器且纯JavaScript检测document是否加载完成的方法是使用[`readyState`](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/readyState).\n\n```js\nif (document.readyState === 'complete') {\n // 页面已完全加载\n}\n```\n\n这样可以在document完全加载时监测到……\n\n\n```js\nlet stateCheck = setInterval(() => {\n if (document.readyState === 'complete') {\n\tclearInterval(stateCheck);\n\t// document ready\n }\n}, 100);\n```\n\n或者使用[onreadystatechange](https://developer.mozilla.org/zh-CN/docs/Web/Events/readystatechange)\n\n\n```js\ndocument.onreadystatechange = () => {\n if (document.readyState === 'complete') {\n\t// document ready\n }\n};\n```\n\n使用`document.readyState === 'interactive'`监听DOM是否加载完成。\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":"2c0ecd9ce421e17c7821e7c6dca9b3d08e9839a9","content":"---\nlayout: post\n\ntitle: 计算数组中的最大值/最小值\ntip-number: 45\ntip-username: loverajoel\ntip-username-profile: https://www.twitter.com/loverajoel\ntip-tldr: 对于纯数字数组,使用内置函数Math.max()和Math.min()的方法。\ntip-writer-support: https://www.coinbase.com/loverajoel\n\nredirect_from:\n - /zh_cn/calculate-the-max-min-value-from-an-array/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n内置函数[Math.max()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/max)和[Math.min()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/min)可以分别找出参数中的最大值和最小值。\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/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)让你可以使用提供的`this`与参数组成的_数组(array)_来调用函数。\n\n```js\nvar numbers = [1, 2, 3, 4];\nMath.max.apply(null, numbers) // 4\nMath.min.apply(null, numbers) // 1\n```\n\n给`apply()`第二个参数传递`numbers`数组,等于使用数组中的所有值作为函数的参数。\n\n一个更简单的,基于ES2015的方法来实现此功能,是使用[展开运算符](https://developer.mozilla.org/zh-CN/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-02-13-know-the-passing-mechanism.md":{"name":"2016-02-13-know-the-passing-mechanism.md","sha":"ddd09cea4ab1e889a72c829ba9a868ead55f4936","content":"---\nlayout: post\n\ntitle: 了解传值机制\ntip-number: 44\ntip-username: bmkmanoj\ntip-username-profile: https://github.com/bmkmanoj\ntip-tldr: JavaScript理论上说只传递原始类型和对象(或引用)类型的值。在引用类型的情况下引用值本身通过值传递。\n\nredirect_from:\n - /zh_cn/know-the-passing-mechanism/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n理论上,JavaScript通过值传递。它既不是值传递也不是引用传递,具体取决于它的真实场景。要理解传值机制,看一下下面两个实例代码和解释。\n\n### 实例 1\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} \t\n\nmyTeam(me);\t\t\nconsole.log(me);\t\t\t// 4 : {'partOf' : 'A Team'}\n\n```\n\n在上面的实例里`myTeam`被调用的时候,JavaScript *传递*`me`*对象的引用值,因为它是一个对象*。而且调用本身建立了同一个对象的两个独立的引用,(虽然在这里的的命名都是相同的,比如`me`, 这有些无调行,而且给我们一个这是单个引用的印象)因此,引用变量本身是独立的。\n\n当我们在#`3`定义了一个新的对象,我们完全改变了`myTeam`函数内的引用值,这对此函数作用域外的原始对象是没有任何影响的,外作用域的引用仍保留在原始对象上,因此从#`4`输出去了。 \n\n\n### 实例 2\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\t\n```\n\n当`myGroup`调用时,我们将对象`me`传给函数。但是与实例1的情况不同,我们没有指派`me`变量到任何新对象,有效的说明了`myGroup`函数作用域内的对象引用值依旧是原始对象的引用值,而且我们在作用域内修改对象的参数值同样有效的修改了原始对象的参数。因此你得到了#`7`的输出结果。\n\n所以后面的例子是否说明javascript是引用传递呢?不,并没有。请记住,*如果是对象的话,JavaScript将引用按值传递*。这种混乱往往发生在我们没有完全理解什么通过引用传递的情况下。这就是确切的原因,有些人更愿意称它为*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)*"},"2016-02-10-array-average-and-median.md":{"name":"2016-02-10-array-average-and-median.md","sha":"41168404898af2c5f388dc1c9a1a2f80670d6457","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_cn/array-average-and-median/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n下面的例子都基于如下数组:\n\n```javascript\nlet values = [2, 56, 3, 41, 0, 4, 100, 23];\n```\n\n要取得平均值,我们需要将数字求和,然后除以`values`的数目,步骤如下:\n- 取得数组长度(length)\n- 求和(sum)\n- 取得平均值(`sum/length`)\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或者使用[无符号右移](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift)操作符:\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 = 23\n```\n"},"2016-01-29-speed-up-recursive-functions-with-memoization.md":{"name":"2016-01-29-speed-up-recursive-functions-with-memoization.md","sha":"d31eec361931c6766a749c4ed1f55edd9ea2b06a","content":"---\nlayout: post\n\ntitle: 运用存储加速递归 Speed up recursive functions with memoization\ntip-number: 29\ntip-username: hingsir\ntip-username-profile: https://github.com/hingsir\ntip-tldr: 大家对斐波那契(Fibonacci)数列都很熟悉。我们可以再20秒内写出下面这样一个方法,它可以运行,但并不高效。它做了太多重复的运算,我们可以通过存储这些运算结果来使其加速。\n\n\nredirect_from:\n - /zh_cn/speed-up-recursive-functions-with-memoization/\n\ncategories:\n - zh_CN\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\n```js\nvar fibonacci = (function() {\n var cache = [0, 1]; // cache the value at the n index\n return function(n) {\n if (cache[n] === undefined) {\n for (var 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\n```js\nvar memoize = function(func){\n var cache = {};\n return function(){\n var key = Array.prototype.slice.call(arguments).toString();\n return key in cache ? cache[key] : (cache[key] = func.apply(this,arguments));\n }\n}\nfibonacci = memoize(fibonacci);\n```\n\nES6版本的memoize函数如下:\n\n```js\nvar memoize = function(func){\n const cache = {};\n return (...args) => {\n const key = [...args].toString();\n return key in cache ? cache[key] : (cache[key] = func(...args));\n }\n}\nfibonacci = memoize(fibonacci);\n```\n\n我们可以将`memoize()`用在很多其他地方\n\n* GCD(最大公约数)\n\n```js\nvar gcd = memoize(function(a,b){\n var 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\n```js\nvar factorial = memoize(function(n) {\n return (n <= 1) ? 1 : n * factorial(n-1);\n})\nfactorial(5); //=> 120\n```\n"},"2016-01-22-two-ways-to-empty-an-array.md":{"name":"2016-01-22-two-ways-to-empty-an-array.md","sha":"24872dadf605742cc481e2cc87b5e89e16d3715d","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_cn/two-ways-to-empty-an-array/\n\ncategories:\n - zh_CN\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 //empty your array\n list.length = 0;\n}\nempty();\n```\n\n* `list = []` 将一个新的数组的引用赋值给变量,其他引用并不受影响。\n这意味着以前数组的内容被引用的话将依旧存在于内存中,这将导致内存泄漏。\n\n* `list.length = 0` 删除数组里的所有内容,也将影响到其他引用。\n\n然而,如果你复制了一个数组(A 和 Copy-A),如果你用`list.length = 0`清空了它的内容,复制的数组也会清空它的内容。\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\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":"1a574abd239826ef0524215f7f108ca8ec69cd99","content":"---\nlayout: post\n\ntitle: 检查某对象是否有某属性\ntip-number: 10\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_cn/check-if-a-property-is-in-a-object/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n当你需要检查某属性是否存在于一个[对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects),你可能会这样做:\n\n```javascript\nvar myObject = {\n name: '@tips_js'\n};\n\n\nif (myObject.name) { ... }\n\n```\n\n这是可以的,但是你需要知道有两种原生方法可以解决此类问题。[`in` 操作符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in) 和 [`Object.hasOwnProperty`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty),任何继承自`Object`的对象都可以使用这两种方法。\n\n### 看一下较大的区别\n\n```javascript\nvar myObject = {\n name: '@tips_js'\n};\n\nmyObject.hasOwnProperty('name'); // true\n'name' in myObject; // true\n\n\nmyObject.hasOwnProperty('valueOf'); // false, valueOf 继承自原型链\n'valueOf' in myObject; // true\n\n```\n\n\n两者检查属性的深度不同,换言之`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\n\nuser.hasOwnProperty('age'); // false, 因为age来自于原型链\n```\n\n[在线示例](https://jsbin.com/tecoqa/edit?js,console)!\n\n同样建议阅读关于检查对象是否包含属性时常见错误的[讨论](https://github.com/loverajoel/jstips/issues/62)。\n"},"2016-02-06-deduplicate-an-array.md":{"name":"2016-02-06-deduplicate-an-array.md","sha":"059905fb4d84206b2869fe8de1bca79847e01393","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_cn/deduplicate-an-array/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n# 原始变量\n如果一个数组只包含原始变量,我们可以使用[`filter`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)和[`indexOf`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)方法将其去重:\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/zh-CN/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/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set)和[`from`](https://developer.mozilla.org/zh-CN/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# Objects\n当元素为对象(Object)时,我们就不能用这种办法了,\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/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify),`String`类型的`key`\n将会被存储为一个字符串值,这样`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/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)\n* [`indexOf`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)\n* [`from`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from)\n* [`JSON.stringify`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)\n\n## ES2015\n* [箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions)\n* [Set](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set)\n\n## Stack overflow\n* [remove duplicates from array](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array/9229821#9229821)\n"},"2016-02-09-using-json-stringify.md":{"name":"2016-02-09-using-json-stringify.md","sha":"3c60a0cfc8410ac1ab9c0682c8a0f7cc32285e8c","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_cn/using-json-stringify/\n\ncategories:\n - zh_CN\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 // the first val will be the entire object, key is empty string\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 output with double tabs in every line.\n{\n \"prop1\": \"value1\",\n \"prop2\": \"value2\"\n}\n*/\n\n```\n\n"},"2016-02-16-basics-declarations.md":{"name":"2016-02-16-basics-declarations.md","sha":"9379ebf8ab5413e095a2cfc73c4734b4c43d2c21","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_cn/basics-declarations/\n\ncategories:\n - zh_CN\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; 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`,因为我们在闭包里并没有声明此变量。\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`声明了两个变量。意味着他们仅在闭包内有作用。\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`,意味着闭包内是无法访问全局的`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/zh-CN/docs/Web/JavaScript/Reference/Statements/var).\n\n特别感谢@kurtextrem的合作 :)!\n"},"2016-02-17-reminders-about-reduce-function-usage.md":{"name":"2016-02-17-reminders-about-reduce-function-usage.md","sha":"88e6bd89d90697ccb2ae62cfe08df203d8504719","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_cn/reminders-about-reduce-function-usage/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n文档里说`reduce()`方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值。\n\n### `reduce()`\n\n[reduce()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) 函数接收2个参数(M: 必填, O: 可选):\n\n- (M) 回调**reducer 函数** 处理先前的结算结果和下一个元素直到序列结束。\n- (O) **初值** 作为第一次调用回调时的第一个参数。\n\n所以,让我们先看一个普通用法,之后再看一个复杂用法。\n\n### 普通用法 (累加,关联)\n\n我们正在逛亚马逊(单价为美元$) 我们的购物车实在太满了,我们来计算一下总价吧:\n\n```javascript\n// 当前的购物清单\nvar items = [{price: 10}, {price: 120}, {price: 1000}];\n\n// reducer函数\nvar reducer = function add(sumSoFar, nextPrice) { return sumSoFar + nextPrice.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为了说明,我们创建一个单一的对象,包含一些可以计算不同货币($, €...)的总价值的`reducer`函数。\n\n```javascript\nvar reducers = {\n totalInDollar: function(state, item) {\n state.dollars += item.price;\n return state;\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 // more...\n};\n```\n\n然后我们建立一个瑞士军刀函数 \n\n- 能够调用每一部分的`reduce`函数\n- 返回一个新的`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我希望这种方法可以使你在自己的需求内使用`reduce()`函数时有新的想法。\n\n使用`reduce`函数也可以实现保存每一次计算结果的功能。这在`Ramdajs`里的[scan](http://ramdajs.com/docs/#scan)函数已经实现了。\n\n[在JSFiddle里运行](https://jsfiddle.net/darul75/81tgt0cd/)\n"},"2016-01-27-short-circuit-evaluation-in-js.md":{"name":"2016-01-27-short-circuit-evaluation-in-js.md","sha":"77478357abdd73eb0f3116427b769e9799e6eb83","content":"---\nlayout: post\n\ntitle: JS中的短路求值\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_cn/short-circuit-evaluation-in-js/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n[短路求值](https://zh.wikipedia.org/wiki/%E7%9F%AD%E8%B7%AF%E6%B1%82%E5%80%BC)是说, 只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值:当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\n使用逻辑与 - `&&`.\n\n```js\n// 普通的if语句\nif(test){\n isTrue(); // Test is true\n}\n\n// 上面的语句可以使用 '&&' 写为:\n\n( test && isTrue() ); // Test is true\n```\n\n使用逻辑或 - `||`.\n\n```js\ntest = false;\nif(!test){\n isFalse(); // Test is false.\n}\n\n( test || isFalse()); // Test is false.\n```\n\n逻辑或可以用来给参数设置默认值。\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\n逻辑与可以用来避免调用undefined参数的属性时报错\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() 将会抛出\"Cannot read property 'bark' of undefined.\" 错误\n// 防止这种情况,我们可以使用 &&.\n\ndog&&dog.bark(); // This will only call dog.bark(), if dog is defined.\n\n```\n"},"2016-02-26-extract-unix-timestamp-easily.md":{"name":"2016-02-26-extract-unix-timestamp-easily.md","sha":"e4e84b7fd160f3675242d60bbf97d3a7be1ec571","content":"---\nlayout: post\n\ntitle: 简单获取unix时间戳\ntip-number: 49\ntip-username: nmrony\ntip-username-profile: https://github.com/nmrony\ntip-tldr: 在Javascript里,你可以简单的取得unix时间戳\n\nredirect_from:\n - /zh_cn/extract-unix-timestamp-easily/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n\n我们经常需要使用unix时间戳计算。有很多方法可以取得unix时间戳。目前取得unix时间戳最简单最快的方法是:\n\n```js\nconst dateTime = Date.now();\nconst timestamp = Math.floor(dateTime / 1000);\n```\n或\n\n```js\nconst dateTime = new Date().getTime();\nconst timestamp = Math.floor(dateTime / 1000);\n```\n\n要取得一个具体时间的unix时间戳,将`yyyy-mm-dd`或`YYYY-MM-DDT00:00:00Z`作为参数传递给`Date`构造函数。例如\n\n```js\nconst dateTime = new Date('2012-06-08').getTime();\nconst timestamp = Math.floor(dateTime / 1000);\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\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-03-03-helpful-console-log-hacks.md":{"name":"2016-03-03-helpful-console-log-hacks.md","sha":"246b1bd4639001a0d4f933ea059ef977dc7c526c","content":"---\nlayout: post\n\ntitle: 实用的`log`技巧\ntip-number: 50\ntip-username: zackhall\ntip-username-profile: https://twitter.com/zthall\ntip-tldr: 运用`&&`与条件断点的实用的`log`技巧\n\nredirect_from:\n - /zh_cn/helpful-console-log-hacks/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n## 使用条件断点输出`log`\n\n如果你想当函数每次被调用时都在控制台打印一个值,你可以应用条件断点来实现。打开你的开发工具,找到你准备打印的值所在的函数然后使用如下条件设置一个条件断点:\n\n```js\nconsole.log(data.value) && false\n```\n\n条件断点只有在条件运行的结果为`true`时才会中断页面。所以使用`console.log('foo') && false`这样的条件,由于你把`false`放在了`AND`条件中,所以结果肯定是`false`。因此这并不会中断页面但是会打印`log`到控制台。这也可以应用在计算某个函数或回调被调用了多少次上面。\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## 打印函数到控制台\n\n你曾经有过打算打印函数到控制台却不能看到函数的代码的情况吗?最快的方法查看函数的代码是将其与空字符串连接,从而将其强制转换为字符串。\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":"d576481d6da0c891898e8e3684a436536649fe7a","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_cn/DOM-event-listening-made-easy/\n\ncategories:\n - zh_CN\n - javascript\n---\n很多人还在这样做:\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":"c5a71286e55300c26a8492b8a210ceedf33c5a2d","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_cn/return-values-with-the-new-operator/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n你将会遇到在JavaScript中使用`new`来分配新对象的一些情况。这将会扰乱你的思绪,除非你阅读了这篇文章并理解在内部发生了什么。\n\nJavaScript中的`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__提示__: `this`指向`new`产生的新对象。否则如果`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怎么找不到了?这很奇怪不是吗?函数都做了什么?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 和 this.two发生了什么!?\n 他们被覆盖了,朋友。\n*/\n````\n\n__我们了解到:__ 当你使用`new`关键字调用一个函数的时候,你可以使用`this`关键字给其设置参数(但这些你应该已经知道了)。使用`new`关键字调用一个返回原始变量的函数将不会返回你指定的值,而是返回函数的实例`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":"1b189780f67ceb6b799263c98e7ee219608a1630","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_cn/get-file-extension/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n### 问题 1: 怎样取得文件扩展名?\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### 解决方法 1: 正则表达式\n\n```js\nfunction getFileExtension1(filename) {\n return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined;\n}\n```\n\n### 解决方法 2: String的`split`方法\n\n```js\nfunction getFileExtension2(filename) {\n return filename.split('.').pop();\n}\n```\n\n这两种解决方法不能解决一些边缘情况,这有另一个更加强大的解决方法。\n\n### 解决方法 3: 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/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf) 方法返回指定值(本例中的`'.'`)在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。\n- 对于`'filename'`和`'.hiddenfile'`,`lastIndexOf`的返回值分别为`0`和`-1`[无符号右移操作符(>>>)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3E%3E%3E_%28Zero-fill_right_shift%29) 将`-1`转换为`4294967295`,将`-2`转换为`4294967294`,这个方法可以保证边缘情况时文件名不变。\n- [String.prototype.slice()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/slice) 从上面计算的索引处提取文件的扩展名。如果索引比文件名的长度大,结果为`\"\"`。\n\n### 对比\n\n| 解决方法 | 参数 | 结果 |\n| ----------------------------------------- |:-------------------:|:--------:|\n| 解决方法 1: Regular Expression | ''<br> 'filename' <br> 'filename.txt' <br> '.hiddenfile' <br> 'filename.with.many.dots.ext' | undefined <br> undefined <br> 'txt' <br> 'hiddenfile' <br> 'ext' <br> |\n| 解决方法 2: 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| 解决方法 3: 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[How can I get file extensions with 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":"a40ca53540c7049cd28b0842d6e07da68ff7b3f9","content":"---\nlayout: post\n\ntitle: 函数中如何使用可选参数(包括可选回调函数)\ntip-number: 54\ntip-username: alphashuro\ntip-username-profile: https://github.com/alphashuro\ntip-tldr: 使函数的参数与回调函数为可选参数。\n\nredirect_from:\n - /zh_cn/use-optional-arguments/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n实例函数中第2个与第3个参数为可选参数\n\n```javascript\n function example( err, optionalA, optionalB, callback ) {\n // 使用数组取出arguments\n var args = new Array(arguments.length);\n for(var i = 0; i < args.length; ++i) {\n args[i] = arguments[i];\n };\n \n // 第一个参数为错误参数\n // shift() 移除数组中第一个参数并将其返回\n err = args.shift();\n\n // 如果最后一个参数是函数,则它为回调函数\n // pop() 移除数组中最后一个参数并将其返回\n if (typeof args[args.length-1] === 'function') { \n callback = args.pop();\n }\n \n // 如果args中仍有元素,那就是你需要的可选参数\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 // 为了教程目的,打印可选参数\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 // 第一个参数为错误参数\n const err = args.shift();\n // 如果最后一个参数是函数,则它为回调函数\n const callback = (typeof args[args.length-1] === 'function') ? args.pop() : null;\n\n // 如果args中仍有元素,那就是你需要的可选参数你可以像这样一个一个的将其取出:\n const optionalA = (args.length > 0) ? args.shift() : null;\n const optionalB = (args.length > 0) ? args.shift() : null;\n // ... 重复取更多参数\n\n if (err && callback) return callback(err);\n\n /* 你想做的逻辑 */\n }\n\n // 使用或不适用可选参数调用实例函数\n \n example(null, 'AA');\n\n example(null, function (err) { /* do something */ });\n\n example(null, 'AA', function (err) {});\n\n example(null, 'AAAA', 'BBBB', function (err) {});\n```\n\n### 如何保证optionalA和optionalB是预期的值?\n\n设计你的函数,使其在接收optionalB时optionalA为必选参数。\n"},"2016-05-12-make-easy-loop-on-array.md":{"name":"2016-05-12-make-easy-loop-on-array.md","sha":"98f7235d63506cfa4db2dc56de993ad85f35a812","content":"---\nlayout: post\n\ntitle: 用数组建立一个简单的循环\ntip-number: 55\ntip-username: jamet-julien\ntip-username-profile: https://github.com/jamet-julien\ntip-tldr: 有时我们需要不停的循环数组的元素,就像一组旋转的图片,或者音乐的播放列表。这里告诉你如何使一个数组拥有循环的能力。\n\nredirect_from:\n - /zh_cn/make-easy-loop-on-array/\n\ncategories:\n - zh_CN\n - javascript\n---\n有时我们需要不停的循环数组的元素,就像一组旋转的图片,或者音乐的播放列表。这里告诉你如何使一个数组拥有循环的能力:\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 然后返回新的当前元素\n arr.next = function(){\n this.loop_idx++;\n return this.current();\n };\n // 减少 loop_idx 然后返回新的当前元素\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``` and ``` 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 // return current item\n arr.current = function(){\n this.loop_idx = ( this.loop_idx ) % this.length;// 无需检查 !!\n return arr[ this.loop_idx ];\n };\n\n // 增加 loop_idx 然后返回新的当前元素\n arr.next = function(){\n this.loop_idx++;\n return this.current();\n };\n \n // 减少 loop_idx 然后返回新的当前元素\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":"a82d87086b64dd10501c6149448aa85324d71f4d","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_cn/copy-to-clipboard/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n这是一个简单的小知识,本周我做了一个简单的“复制到剪贴板”按钮,这是我第一次做这种功能,向大家分享一下我的实现方法。\n\n这很简单,比较麻烦的是我们必须为需要复制的文本增加`<input/>`标签。之后我们选择要复制的内容然后调用复制命令[execCommand](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand).\n`execCommand('copy')` 将会复制被选择的内容。\n\n此方法目前被所有最新版本的浏览器[支持](http://caniuse.com/#search=execCommand),它可以让我们执行如`复制`、`剪切`、`粘贴`等命令,还可以改变字体颜色、大小等。\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":"e18c8552621024e4fc96f9382f7387d59f674cac","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_cn/comma-operaton-in-js/\n\ncategories:\n - zh_CN\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##### 实验\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":"4ee9544f8e1632ba7f17424a4437a38c215b4660","content":"---\nlayout: post\n\ntitle: break 或 continue 循环函数\ntip-number: 58\ntip-username: vamshisuram\ntip-username-profile: https://github.com/vamshisuram\ntip-tldr: 循环一个`list`从中寻找一个或一些值,是一个很常见的需求。但是即使我们要找的元素就是数组里的第一个,我们不也能从循环中直接`return`,只能遍历整个数组。本文教你如何使用`.some`和`.every`快速结束循环。\n\n\nredirect_from:\n - /zh_cn/break-continue-loop-functional/\n\ncategories:\n - zh_CN\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`的能力。这种情况下,最接近的方式是使用`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); // your code\n});\n//> 0, 1, 3, 4\n```\n\n`.some`是一个原型方法。他用来检测是否某些元素满足所提供的函数。如果任何元素最终返回`true`,它就会停止运行。更多解释请看[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/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); // your code\n});\n//> 0, 1\n```\n\n\n你可以返回`false`使循环`continue`到下一个元素。当你返回`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`相反的布尔值。\n\n##### 示例\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":"6a4c294a9a9cc2da924907e3a5741367fbe4107a","content":"---\nlayout: post\n\ntitle: var和ES6的let\ntip-number: 59\ntip-username: richzw\ntip-username-profile: https://github.com/richzw\ntip-tldr: 在这个 tip,我将介绍 var 和 let 之间不同的作用域。我应该使用 let 替代 var 吗?让我们来看一下吧!\nredirect_from:\n - /zh_cn/keyword-var-vs-let/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n### 概述\n\n- 通过 `var` 定义的变量,它的作用域是在 function 或任何外部已经被声明的 function,是全域的 。\n- 透過 `let` 定义的变量,它的作用域是在一個块(block)。\n\n```js\nfunction varvslet() {\n console.log(i); // i 是 undefined 的,因为变量提升\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- 变量提升\n\n `let` 不會被提升到整个块的作用域。相比之下,`var` 可以被提升。\n\n```js\n{\n console.log(c); // undefined。因为变量提升\n var c = 2;\n}\n\n{\n console.log(b); // ReferenceError: b 没有被定义\n let b = 3;\n}\n```\n\n- 循环中的闭包\n\n `let` 在每次循环可以重新被 bind,确保在它之前结束的循环被重新赋值,所以在闭包中它被用來避免一些问题。\n\n```js\nfor (var i = 0; i < 5; ++i) {\n setTimeout(function () {\n console.log(i); // 输出 '5' 五次\n }, 100); \n}\n```\n\n 使用 `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":"ba1723a169634571976f2c79b7d87a599e1bafb5","content":"---\nlayout: post\n\ntitle: 三个实用的javascript小技巧\ntip-number: 60\ntip-username: leandrosimoes\ntip-username-profile: https://github.com/leandrosimoes\ntip-tldr: 分享三个让开发变得更高效的实用语法糖\n\n\nredirect_from:\n - /zh_cn/three-useful-hacks/\n\ncategories:\n - zh_CN\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#### 短路条件句\n\n如果你想在某个条件逻辑值为`true`时,执行某个函数,就像这样:\n\n```javascript\nif (condition) {\n dosomething()\n}\n```\n\n这时,你可以这样子运用短路:\n\n```javascript\ncondition && dosomething()\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":"bd16c6fae358ecbd8c135ee1b7894cd9ded0f41b","content":"---\nlayout: post\n\ntitle: 给函数 Bind 对象\ntip-number: 61\ntip-username: loverajoel\ntip-username-profile: https://github.com/loverajoel\ntip-tldr: 理解在 JavaScript 中如何使用 `Bind` 方法绑定对象和函数\n\n\nredirect_from:\n - /zh_cn/binding-objects-to-functions/\n\ncategories:\n - zh_CN\n - javascript\n---\n\n我们常常需要将一个对象绑定到一个方法的 `this` 上。在 JS 中,如果你想要调用一个函数并指定它的 `this` 时可以使用 `bind` 方法。\n\n### Bind 语法\n\n```js\nfun.bind(thisArg[, arg1[, arg2[, ...]]])\n```\n\n## 参数\n**thisArg**\n\n当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。\n\n**arg1, arg2, ...**\n\n当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。\n\n**返回值**\n\n返回由指定的this值和初始化参数改造的原函数拷贝\n\n### JS 中的实例\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```"},"2017-03-12-3-array-hacks.md":{"name":"2017-03-12-3-array-hacks.md","sha":"24d0b3554a4a8739f8ba3683514f04da9cd5939c","content":"---\nlayout: post\n\ntitle: Array 的三个技巧\ntip-number: 64\ntip-username: hassanhelfi\ntip-username-profile: https://twitter.com/hassanhelfi\ntip-tldr: 在 JavaScript 中 数组(Array)随处可见,使用ECMAScript 6 中的新特性 扩展运算符 你可以做很多很棒事情。在这边文章中,我将为你介绍在编码中有用的3个技巧。\n\n\ncategories:\n - zh_CN\n - javascript\n---\n\n在 JavaScript 中 数组(Array)随处可见,使用ECMAScript 6 中的新特性 [扩展运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_operator) 你可以做很多很棒事情。在这边文章中,我将为你介绍在编码中有用的3个技巧。\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') // or\n> method('parameter1', undefined, 'parameter3');\n```\n\n自从 JavaScript 把 `null` 当做一个 object 的时候, 我个人就不太喜欢使用它了。根据 ES6 中对扩展运算符的介绍,有一个更简洁的方法可以将空参数传递给一个方法。正如前文所提到的,数组是松散的,所以给它传空值是可以的,我们正式用到了这个优点。\n\n```javascript\n> method(...['parameter1', , 'parameter3']); // works!\n```\n\n### 数组去重\n\n我一直不明白为什么数组不提供一个内置函数可以让我们方便的取到去重以后的值。扩展运算符帮到了我们,使用扩展运算符配合 `Set` Spread operators are here for the rescue. Use spread operators with the `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":"1b60afc959f51e4f34f79bbdae0ff91d1d844d3c","content":"---\nlayout: post\n\ntitle: 使用 tap 来快速 debug\ntip-number: 65\ntip-username: loverajoel\ntip-username-profile: https://twitter.com/loverajoel\ntip-tldr: 在这里 tap 是一个小怪物。一个可以用来快速调试、链式调用、匿名函数,还可以打印任何你想打印的东西的函数。\ntip-md-link: https://github.com/loverajoel/jstips/blob/master/_posts/en/javascript/2017-03-16-tapping-for-quick-debugging.md\n\ncategories:\n - zh_CN\n - javascript\n---\n\n在这里 tap 是一个小怪物。一个可以用来快速调试、链式调用、匿名函数,还可以打印任何你想打印的东西的函数。\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现在,加入你从这个链式调用中没有得到任何返回。\n在哪里除了问题呢?或许 `bank_info` 没有返回东西,我们需要监听(tap)它:\n\n``` javascript\nbank_totals_by_client(tap(bank_info(1, banks)), table)\n```\n\n基于我们特殊的实现,它可能会打印一些东西,也可能什么也不打印。\n我们假设,打印出来的东西是正确的,因此, `bank_info` 没有问题。\n\n我们需要继续调试下一个函数, filter.\n\n``` javascript\n .filter(c => tap(c).balance > 25000)\n```\n\n我们可以得到 `c` 吗?如果可以,说明 `bank_totals_by_client` 运行正常。\n可能是 filter 内的条件有问题?\n\n``` javascript\n .filter(c => tap(c.balance > 25000))\n```\n\n啊哈!我们发现除了 `false` 没有打印其他东西,所以说明没有一个 client >25000,\n这就是为什么方法什么也没返回的原因。\n\n## (附) 更先进的 tap\n\n``` javascript\nfunction tap(x, fn = x => x) {\n console.log(fn(x));\n return x;\n}\n```\n\n让我们来看一下一个更强大的怪物,如果我们想在监听(tap)之前*事先*做一些操作应该怎么办?比如,我们只想方位某个对象特定的参数,位于一个逻辑运算,等等。使用上面的方法,在调用的时候增加一个额外参数,这个函数在被监听(tap)的时候就会被执行。\n\n``` javascript\ntap(3, x => x + 2) === 3; // prints 5, but expression evaluates to true, why :-)?\n```\n"},"2017-03-09-working-with-websocket-timeout.md":{"name":"2017-03-09-working-with-websocket-timeout.md","sha":"f78da88af7645e38f627d3bb1884734baf2a3a8b","content":"---\nlayout: post\n\ntitle: 处理 Websocket 超时问题\ntip-number: 63\ntip-username: loverajoel \ntip-username-profile: https://github.com/loverajoel\ntip-tldr: 一个控制超时的技巧\n\ncategories:\n - zh_CN\n - javascript\n---\n\n在 websocket 连接被建立后,如果一段时间未活动,服务器或防火墙可能会超时或终止连接。想要解决这个问题, 我们可以周期性地给服务器发消息。我们需要两个方法实现:一个来确保连接不会中断,,另一个用来取消此设定。同我们也需要一个 ```timerID``` 变量.\n\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 clearTimeout(timerId); \n } \n}\n```\n\n现在我们实现了我们需要的两个方法,我们可以在 ```onOpen()``` 的最后面调用 ```keepAlive()``` ,在```onClose()``` 的组后面调用 ```cancelKeepAlive()```。\n\n好了!我们我们完美的解决了 websocket 超时的问题。\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":"004d8f32fdcb06a453aff247673af2d97c4b8425","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_CN\n - javascript\n---\n\n我们都知道 JavasSript 是弱类型的,并且当我们使用 `==` 作比较时,在一些情况下由于类型转换或者说“把两个操作数中的一个转换成另一个,然后在比较”,会出现意想不到的结果。\n\n``` javascript\n0 == ' ' //true\nnull == undefined //true\n[1] == true //true\n```\n\n因此 JavaScript 中给我们提供了全等操作符 `===`, 它比不全等操作符更加严格并且不会发生类型转换。但是用 `===` 来进行比较并不是最好的解决方案。你可能会得到:\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## References:\n[Equality comparisons and sameness](http://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness)\n\n"},"2017-04-05-picking-and-rejecting-object-properties.md":{"name":"2017-04-05-picking-and-rejecting-object-properties.md","sha":"83ce9f4dc140c0dee96f990abb20f6885a953f30","content":"---\nlayout: post\n\ntitle: 选择(picking)和反选(rejecting)对象的属性 \ntip-number: 70\ntip-username: loverajoel\ntip-username-profile: https://github.com/loverajoel\ntip-tldr: 有时候我们需要将一个对象的某些属性放到白名单里,这样来说,我们有一个数组代表了一张数据库表,并且为了一些功能我们需要从中选出(`select`)一些字段。\n\ncategories:\n - zh_CN\n - javascript\n---\n\n\n有时候我们需要将一个对象的某些属性放到白名单里,这样来说,我们有一个数组代表了一张数据库表,并且为了一些功能我们需要从中选出(`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 name\n\ntable.map(row => pick(row, ['client.name'])); // 取到了一系列 client name\n```\n\n在 pick 函数中用到了一点‘诡计’。首先,我们用 `map` 遍历了键名数组(keys), 每次都会返回一个包含当前键名(key)的对象(如果在目标对象(obj)中没有当前键名,就会返回空对象)。然后我们用 `reduce` 把返回的所有单个键-值对象和合并到一个对象中。\n\n但是,如果我们想反选(`reject`)属性/键名呢?改造一下我们的函数就好了:\n\n``` javascript\nfunction reject(obj, keys) {\n return Object.keys(obj)\n .filter(k => !keys.includes(k))\n .map(k => ({[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```"},"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":"f35a6e77b51e98869c9303a414558d731d517278","content":"---\nlayout: post\n\ntitle: Vuejs在資料綁定時會複製更新並替換目標元素\ntip-number: 71\ntip-username: pansila\ntip-username-profile: https://github.com/pansila\ntip-tldr: 在這個提示中,我會通過一個例子向您展示Vue會如何與其它軟體衝突如果你不知道這一點。\n\ncategories:\n - zh_CN\n---\n\n### 概述\n\nVuejs是一款簡單而強大的軟體傑作,類似其它流行的UI框架,Angularjs和Reactjs,但不像這兩者令人生畏的複雜性,Vue非常簡單,在從入門到放棄之前,你能很快掌握它的全部知識並投入生產。\n\n但是如果你不知道它怎麼工作的,有時候它也會難為你。這裡是一個和其它UI框架(Framework7)衝突的例子。\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你可能會驚訝它竟然無法工作,新的page點擊後並沒有顯示出來。事實上,Vue內部會複製目標HTML元素,然後根據綁定的資料更新並替換原來的元素。當Framework7載入新的頁面時,它會調用`PageInit`回呼函數,這裡我們又調用了Vue在`<page>`元素上資料綁定,這之後DOM樹裡面包含的已經是新的`<page>`元素,但Framework7對此並不知情又接著在舊的`<page>`元素上完成剩下的初始化工作,比如最終顯示這個新的頁面,這就是根本原因。\n\n為了繞過這個問題,不要讓Vue的元素選擇器錨定在`<page>`元素上,而是定在它的子元素,這樣Vue做資料綁定時就不會影響到整個頁面顯示。\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- [Vue教程] (https://cn.vuejs.org/)\n- [Framework7](https://framework7.io/)"}}