# JavaScriptè¯è¨€é€šç”¨ç¼–程规范 ## 目的 è§„åˆ™å¹¶ä¸æ˜¯å®Œç¾Žçš„ï¼Œé€šè¿‡ç¦æ¢åœ¨ç‰¹å®šæƒ…况下有用的特性,å¯èƒ½ä¼šå¯¹ä»£ç å®žçŽ°é€ æˆå½±å“。但是我们制定规则的目的“为了大多数程åºå‘˜å¯ä»¥å¾—到更多的好处â€ï¼Œ 如果在团队è¿ä½œä¸è®¤ä¸ºæŸä¸ªè§„åˆ™æ— æ³•éµå¾ªï¼Œå¸Œæœ›å¯ä»¥å…±åŒæ”¹è¿›è¯¥è§„则。 å‚考该规范之å‰ï¼Œå¸Œæœ›æ‚¨å…·æœ‰ç›¸åº”çš„JavaScriptè¯è¨€åŸºç¡€èƒ½åŠ›ï¼Œè€Œä¸æ˜¯é€šè¿‡è¯¥æ–‡æ¡£æ¥å¦ä¹ JavaScriptè¯è¨€ã€‚ ## 总体原则 代ç 需è¦åœ¨ä¿è¯åŠŸèƒ½æ£ç¡®çš„å‰æä¸‹ï¼Œæ»¡è¶³**å¯è¯»ã€å¯ç»´æŠ¤ã€å®‰å…¨ã€å¯é ã€å¯æµ‹è¯•ã€é«˜æ•ˆã€å¯ç§»æ¤**的特å¾è¦æ±‚。 ## 约定 **规则**:编程时必须éµå®ˆçš„约定 **建议**ï¼šç¼–ç¨‹æ—¶å¿…é¡»åŠ ä»¥è€ƒè™‘çš„çº¦å®š æ— è®ºæ˜¯â€œè§„åˆ™â€è¿˜æ˜¯â€œå»ºè®®â€ï¼Œéƒ½å¿…é¡»ç†è§£è¯¥æ¡ç›®è¿™ä¹ˆè§„å®šçš„åŽŸå› ï¼Œå¹¶åŠªåŠ›éµå®ˆã€‚ ## 例外 在ä¸è¿èƒŒæ€»ä½“原则,ç»è¿‡å……分考虑,有充足的ç†ç”±çš„å‰æä¸‹ï¼Œå¯ä»¥é€‚当è¿èƒŒè§„范ä¸çº¦å®šã€‚ ä¾‹å¤–ç ´å了代ç 的一致性,请尽é‡é¿å…。“规则â€çš„例外应该是æžå°‘的。 ä¸‹åˆ—æƒ…å†µï¼Œåº”é£Žæ ¼ä¸€è‡´æ€§åŽŸåˆ™ä¼˜å…ˆï¼š **修改外部开æºä»£ç ã€ç¬¬ä¸‰æ–¹ä»£ç 时,应该éµå®ˆå¼€æºä»£ç ã€ç¬¬ä¸‰æ–¹ä»£ç å·²æœ‰è§„èŒƒï¼Œä¿æŒé£Žæ ¼ç»Ÿä¸€ã€‚** ## 编程规范 ### 命å规范 #### 规则1.1 须使用æ£ç¡®çš„英文拼写进行命åï¼Œç¦æ¢ä½¿ç”¨æ‹¼éŸ³æ‹¼å†™ã€‚ **å例:**`xingming`ã€`zhanghao` **æ£ä¾‹ï¼š**`username`ã€`account` #### 规则1.2 命åå°½é‡å°‘ç”¨ç¼©å†™ï¼Œé™¤éžæ˜¯å¸¸è§è¯æˆ–è€…ä¸šåŠ¡çº¿çš„é¢†åŸŸè¯æ±‡ã€‚比如:`context`å¯ä»¥ç®€å†™æˆ`ctx`,`request`å¯ç®€å†™æˆ`req`,`response`å¯ç®€å†™æˆ`resp`。 **说明:** 完整的å•è¯æ‹¼å†™å¯ä»¥é¿å…ä¸å¿…è¦çš„阅读障ç¢ã€‚ **例外:** 循环è¯ç§ä¸å¯ä»¥ä½¿ç”¨`i`ã€`j`循环æ¡ä»¶å˜é‡å。 #### 规则1.3 ç±»åã€æžšä¸¾åã€å‘½å空间å采用`UpperCamelCase`é£Žæ ¼ã€‚ **æ£ä¾‹ï¼š** ```javascript // ç±»å class User { constructor(username) { this.username = username; } sayHi() { console.log(`hi, ${this.username}`); } } // 枚举å const UserType = { TEACHER: 0, STUDENT: 1 }; // 命å空间 const Base64Utils = { encrypt: function(text) { // todo encrypt }, decrypt: function(text) { // todo decrypt } }; ``` #### 规则1.4 å˜é‡åã€æ–¹æ³•åã€å‚æ•°å采用`lowerCamelCase`é£Žæ ¼ã€‚ **æ£ä¾‹ï¼š** ```javascript let msg = 'Hello world'; function sendMsg(msg) { // todo send message } function findUser(userID) { // todo find user by user ID } ``` #### 规则1.5 陿€å¸¸é‡åã€æžšä¸¾å€¼å采用全部大写,å•è¯é—´ä½¿ç”¨ä¸‹åˆ’线隔开。 **æ£ä¾‹ï¼š** ```javascript const MAX_USER_SIZE = 10000; const UserType = { TEACHER: 0, STUDENT: 1 }; ``` #### 建议1.6 é¿å…使用å¦å®šçš„布尔å˜é‡å,布尔型的局部å˜é‡æˆ–æ–¹æ³•é¡»åŠ ä¸Šè¡¨è¾¾æ˜¯éžæ„义的å‰ç¼€ã€‚ **å例:** ```javascript let isNoError = true; let isNotFound = false; function empty() {} function next() {} ``` **æ£ä¾‹ï¼š** ```javascript let isError = false; let isFound = true; function isEmpty() {} function hasNext() {} ``` ### ä»£ç æ ¼å¼ #### 规则2.1 采用2ä¸ªç©ºæ ¼ç¼©è¿›ï¼Œç¦æ¢ä½¿ç”¨`tab`å—符 **说明:** åªå…è®¸ä½¿ç”¨ç©ºæ ¼ï¼ˆspaceï¼‰è¿›è¡Œç¼©è¿›ï¼Œæ¯æ¬¡ç¼©è¿›ä¸º2ä¸ªç©ºæ ¼ã€‚ä¸å…许使用Tab 符进行缩进。 **æ£ä¾‹ï¼š** ```javascript const dataSource = [ { id: 1, title: 'Title 1', content: 'Content 1' }, { id: 2, title: 'Title 2', content: 'Content 2' } ]; function render(container, dataSource) { if (!container || !dataSource || !dataSource.length) { return void 0; } const fragment = document.createDocumentFragment(); for (let data of dataSource) { if (!data || !data.id || !data.title || !data.content) { continue; } const element = document.createElement("div"); const textNode = document.createTextNode(`${data.title}: ${data.content}`); element.appendChild(textNode); element.setAttribute("id", data.id); fragment.appendChild(element); } container.appendChild(fragment); } ``` #### 规则2.2 行宽ä¸è¶…过`120`个å—符 **说明:** 建议æ¯è¡Œå—符数ä¸è¦è¶…过 120 个。如果超过120个å—符,请选择åˆç†çš„æ–¹å¼è¿›è¡Œæ¢è¡Œã€‚ **例外:** 如果一行注释包å«äº†è¶…过120 个å—符的命令或URL,则å¯ä»¥ä¿æŒä¸€è¡Œï¼Œä»¥æ–¹ä¾¿å¤åˆ¶ã€ç²˜è´´å’Œé€šè¿‡grep查找; 预处ç†çš„ error ä¿¡æ¯åœ¨ä¸€è¡Œä¾¿äºŽé˜…读和ç†è§£ï¼Œå³ä½¿è¶…过 120 个å—符。 #### 规则2.3 大括å·çš„使用须éµå¾ªçº¦å®šï¼š 1. 如果大括å·å†…为空,则å…许简写æˆ`{}`ï¼Œä¸”æ— éœ€æ¢è¡Œï¼› 2. 左大括å·å‰ä¸æ¢è¡Œï¼Œæ‹¬å·åŽæ¢è¡Œï¼› 3. å³å¤§æ‹¬å·å‰æ¢è¡Œï¼Œæ‹¬å·åŽè¿˜æœ‰`else`ã€`catch`ç‰æƒ…况䏋䏿¢è¡Œï¼Œå…¶ä»–情况都æ¢è¡Œã€‚ #### 规则2.4 æ¡ä»¶è¯å¥å’Œå¾ªçޝè¯å¥çš„实现必须使用大括å·åŒ…裹,å³ä½¿åªæœ‰ä¸€æ¡è¯å¥ã€‚ **å例:** ```javascript if (condition) console.log('success'); for(let idx = 0; idx < 5; ++idx) console.log(idx); ``` **æ£ä¾‹ï¼š** ```javascript if (condition) { console.log('success'); } for(let idx = 0; idx < 5; ++idx) { console.log(idx); } ``` #### 规则2.5 æ¡ä»¶è¯å¥å’Œå¾ªçޝè¯å¥éƒ½ä¸å…许写在åŒä¸€è¡Œã€‚ **å例:** ```javascript if (condition) { /* todo something */ } else { /* todo other */ } let idx = 0; while(idx < 10) console.log(idx); ``` **æ£ä¾‹ï¼š** ```javascript if (condition) { /* todo something */ } else { /* todo other */ } let idx = 0; while(idx < 10) { console.log(idx); } ``` #### 规则2.6 `switch`è¯å¥çš„`case`å’Œ`default` 须缩进一层。 **æ£ä¾‹ï¼š** ```javascript switch(condition) { case 0: doSomething(); break; case 1: { // å¯ä»¥å¸¦æ‹¬å·ä¹Ÿå¯ä»¥ä¸å¸¦ doOtherthing(); break; } default: break; } ``` #### 规则2.7 è¡¨è¾¾å¼æ¢è¡Œé¡»ä¿æŒä¸€è‡´æ€§ï¼Œè¿ç®—符放行末。 **说明:** 较长的表达å¼ï¼Œä¸æ»¡è¶³è¡Œå®½è¦æ±‚时,需è¦åœ¨é€‚当的ä½ç½®è¿›è¡Œæ¢è¡Œã€‚一般在较低优先级è¿ç®—符或连接符åŽé¢é˜¶æ®µï¼Œè¿ç®—符或连接符放行末。è¿ç®—符ã€è¿žæŽ¥ç¬¦æ”¾åœ¨è¡Œæœ«ï¼Œè¡¨ç¤ºâ€œæœªç»“æŸï¼ŒåŽç»è¿˜æœ‰â€ã€‚ **æ£ä¾‹ï¼š** ```javascript // å‡è®¾æ¡ä»¶è¯å¥è¶…出行宽 if (userCount > MAX_USER_COUNT || userCount < MIN_USER_COUNT) { doSomething(); } const sum = number1 + number2 + number3 + number4 + number5 + number6 + number7 + number8 + number9; ``` #### 规则2.8 多个å˜é‡å®šä¹‰å’Œèµ‹å€¼è¯å¥ä¸å…许写在一行。 **å例:** ```javascript let maxCount = 10, isCompleted = false; let pointX, pointY; pointX = 10; pointY = 0; ``` **æ£ä¾‹ï¼š** ```javascript let maxCount = 10; let isCompleted = false; let pointX = 0; let pointY = 0; ``` #### 规则2.9 ç©ºæ ¼åº”è¯¥çªå‡ºå…³é”®å—å’Œé‡è¦ä¿¡æ¯ï¼Œé¿å…ä¸å¿…è¦çš„ç©ºæ ¼ã€‚ **说明:** ç©ºæ ¼å¯ä»¥å‡ä½Žä»£ç å¯†åº¦ï¼Œå¢žåŠ ä»£ç çš„å¯è¯»æ€§ã€‚总体规则如下: 1. `if`ã€`elseif`ã€`else`ã€`switch`ã€`while`ã€`for`ç‰å…³é”®å—之åŽåŠ ç©ºæ ¼ï¼› 2. å°æ‹¬å·å†…部两侧ä¸åŠ ç©ºæ ¼ï¼› 3. 大括å·å†…éƒ¨ä¸¤ä¾§é¡»åŠ ç©ºæ ¼ï¼Œä½†`{}`ç‰ç®€å•场景例外; 4. å¤šé‡æ‹¬å·ä¹‹é—´ä¸åŠ ç©ºæ ¼ï¼› 5. 一元æ“作符(`&`ã€`*`ã€`+`ã€`-`ã€`!`ç‰ï¼‰ä¹‹åŽä¸åŠ ç©ºæ ¼ï¼› 6. 二元æ“作符(`=`ã€`+`ã€`-`ã€`*`ã€`/`ã€`%`ã€`|`ã€`&`ã€`||`ã€`&&`ã€`<`ã€`>`ã€`<=`ã€`>=`ã€`==`ã€`!=`ã€`===`ã€`!==`ç‰ï¼‰å·¦å³ä¸¤ä¾§åŠ ç©ºæ ¼ï¼› 7. 三元è¿ç®—符(`?: `ï¼‰çš„ä¸¤ä¾§æ·»åŠ ç©ºæ ¼ï¼› 8. å‰ç½®æˆ–åŽç½®çš„自增ã€è‡ªå‡ï¼ˆ`++`ã€`--`)和å˜é‡ä¹‹é—´ä¸åŠ ç©ºæ ¼ï¼› 9. 逗å·ï¼ˆ`,`)å‰é¢ä¸åŠ ç©ºæ ¼ï¼ŒåŽé¢åŠ ç©ºæ ¼ï¼› 10. å•行注释`//`åŽåŠ ç©ºæ ¼ï¼› 11. 行尾ä¸åŠ ç©ºæ ¼ã€‚ #### 规则2.10 表达å¼è¯å¥é¡»ä»¥åˆ†å·ç»“尾。 **å例:** ```javascript let username = 'jack' let birthday = '1997-09-01' console.log(`${username}'s birthday is ${birthday}`) ``` **æ£ä¾‹ï¼š** ```javascript let username = 'jack'; let birthday = '1997-09-01'; console.log(`${username}'s birthday is ${birthday}`); ``` #### 建议2.11 优先使用å•引å·åŒ…裹å—符串。 **æ£ä¾‹ï¼š** ```javascript let message = 'wolrd'; console.log(message); ``` ### 代ç 规范 #### 规则3.1 声明å˜é‡æ—¶é¡»ä½¿ç”¨`var`ã€`let`或`const`关键å—进行声明,é¿å…å˜é‡æš´éœ²åˆ°å…¨å±€ä½œç”¨åŸŸä¸Šã€‚ **说明:** ä¸ä½¿ç”¨`var`ã€`let`或`const`关键å—声明å˜é‡ï¼Œä¼šå¯¼è‡´å°†å˜é‡æš´éœ²åˆ°å…¨å±€ä½œç”¨åŸŸä¸Šï¼Œè¿™æ ·å¯èƒ½ä¼šè¦†ç›–全局作用域上的åŒåå˜é‡ï¼Œè¿›è€Œå¼•å‘GCæ— æ³•æœ‰æ•ˆå›žæ”¶å†…å˜çš„问题;å¦å¤–,当å˜é‡ä¸åŒ…嫿•æ„Ÿä¿¡æ¯æ—¶ï¼Œæš´éœ²åˆ°å…¨å±€ä½œç”¨åŸŸå¯èƒ½ä¼šå¯¼è‡´ä¿¡æ¯æ³„露问题。å¦å¤–,**å°½é‡å¯¹æ‰€æœ‰çš„引用使用`const`,ä¸è¦ä½¿ç”¨ `var`ï¼›å¦‚æžœä½ ä¸€å®šéœ€è¦å¯å˜åŠ¨çš„å¼•ç”¨ï¼Œä½¿ç”¨ `let` 代替 `var`**ã€‚å› ä¸º`const`å’Œ`let`的作用域更å°ï¼Œå†™ä»£ç 更容易控制;constå¯ç¡®ä¿æ— æ³•å¯¹å¼•ç”¨é‡æ–°èµ‹å€¼ï¼Œconst引用的指针ä¸å¯å˜ï¼Œé‡æ–°èµ‹å€¼ä¼šæŠ¥é”™ï¼Œé¿å…了ä¸å°å¿ƒçš„釿–°èµ‹å€¼ç»™è¦†ç›–了。 **å例:** ```javascript function open() { url = 'http://127.0.0.1:8080'; //url会暴露到全局作用域上 //todo something } open(); console.log(url); //urlå¯ä»¥è¢«è®¿é—®åˆ°ï¼Œè¾“出内容:http://127.0.0.1:8080 ``` **æ£ä¾‹ï¼š** ```javascript function open() { let url = 'http://127.0.0.1:8080'; // todo something } open(); console.log(url); //报错:Uncaught ReferenceError: url is not defined ``` ```javascript function open() { const url = 'http://127.0.0.1:8080'; //todo something } open(); console.log(url); //报错:Uncaught ReferenceError: url is not defined ``` #### 规则3.2 函数å—内须使用函数表达å¼å£°æ˜Žå‡½æ•° **说明:** 虽然很多 JS 引擎都支æŒå—内声明函数,但它ä¸å±žäºŽ ECMAScript规范,å„个æµè§ˆå™¨ç³Ÿç³•的实现相互ä¸å…¼å®¹ï¼Œæœ‰äº›ä¹Ÿä¸Žæœªæ¥ ECMAScriptè‰æ¡ˆç›¸è¿èƒŒã€‚å¦å¤–,ECMAScript5䏿”¯æŒå—作用域,对于所有的控制æµéƒ½ä¸æ˜¯ç‹¬ç«‹çš„作用域,其内部声明的å˜é‡æˆ–者函数都是在其父函数或者脚本的作用域ä¸ï¼Œå¯¼è‡´å—内函数或者å˜é‡çš„声明会å˜åœ¨è¦†ç›–现象。如果确实需è¦åœ¨å—ä¸å®šä¹‰å‡½æ•°,åº”è¯¥ä½¿ç”¨å‡½æ•°è¡¨è¾¾å¼æ¥åˆå§‹åŒ–。 **å例:** ```javascript function bar(name) { if (name === "hotel") { // 1ã€å®šä¹‰ä¸€ä¸ªfooå‡½æ•°ï¼Œå…¶ä½œç”¨åŸŸä¸æ˜¯if代ç å—,而是bar函数作用域。 function foo() { console.log("hotel foo A"); } } else { // 2ã€å†é‡å¤å®šä¹‰ä¸€æ¬¡foo函数,覆盖上é¢ifæ¡ä»¶åˆ†æ”¯ä¸‹çš„foo函数定义。 function foo() { console.log("hotel foo 2"); } } foo && foo(); } bar("hotel"); // 输出结果总是显示"hotel foo 2" ``` **æ£ä¾‹ï¼š** ```javascript function bar(name) { var foo; if (name == "hotel") { foo = function () { console.log("hotel foo 1"); }; } else { foo = function () { console.log("hotel foo 2"); } } foo && foo(); } bar("hotel"); // æ£ç¡®è¾“出"hotel foo 1" ``` #### 规则3.3 ç¦æ¢å°è£…基本类型 **说明:** JavaScript䏿œ‰äº”ç§åŸºæœ¬æ•°æ®ç±»åž‹ï¼šUndefinedã€Nullã€Booleanã€Numberå’ŒString。基本数æ®ç±»åž‹çš„值是ä¸å¯æ›´æ”¹çš„。JavaScriptä¸ä½¿ç”¨åŸºæœ¬æ•°æ®ç±»åž‹å¯¹è±¡åªæ˜¯å€¼ï¼Œä¸åŒ…å«å™¨å°è£…对象的方法和属性,在ä¸éœ€è¦ä½¿ç”¨å±žæ€§å’Œæ–¹æ³•的时候,ä¸éœ€è¦ä½¿ç”¨å…¶å°è£…类型。 **å例:** ```javascript var isShow = new Boolean(false); if (isShow) { alert('hi'); //被执行,界é¢å¼¹å‡ºæ˜¾ç¤ºï¼šhi } ``` **æ£ä¾‹ï¼š** ```javascript var isShow = false; if (isShow) { alert('hi'); } ``` #### 规则3.4 ç¦æ¢ä½¿ç”¨`with` **说明:** 使用 withè®©ä½ çš„ä»£ç 在è¯ä¹‰ä¸Šå˜å¾—䏿¸…æ™°ï¼Œå› ä¸ºwith的对象,å¯èƒ½ä¼šä¸Žå±€éƒ¨å˜é‡äº§ç”Ÿå†²çªï¼Œä»Žè€Œæ”¹å˜ç¨‹åºåŽŸæœ¬çš„ç”¨ä¹‰ã€‚ **å例:** ```javascript var foo = { x: 5 }; with(foo) { var x = 3; console.log(x); //输出:5 } ``` #### 规则3.5 `this`ä»…å¯åœ¨å¯¹è±¡æž„é€ å‡½æ•°ã€æ–¹æ³•ã€é—包ä¸ä½¿ç”¨ **说明:** 在JavaScript里é¢ï¼Œthis指针代表的是执行当å‰ä»£ç 的对象的所有者。this具有特殊的è¯ä¹‰ï¼š + 全局对象(大多数情况下) + 调用者的作用域(使用evalæ—¶) + DOM æ ‘ä¸çš„节点(æ·»åŠ äº‹ä»¶å¤„ç†å‡½æ•°æ—¶) + 新创建的对象(ä½¿ç”¨ä¸€ä¸ªæž„é€ å™¨) + 其他对象(如果函数被 call() 或 apply()) ```javascript var User = function(username) { this.username = username; }; var user = new User('John'); console.log(user.username); // 输出:John var ClickCounter = { value: 0, click: function() { ++this.value; }, getValue() { return this.value; } }; console.log(Counter.getValue()); //输出:0 Counter.click(); console.log(Counter.getValue()); //输出:1 ``` #### 规则3.6 ç¦æ¢ä½¿ç”¨IE下的æ¡ä»¶æ³¨é‡Š **说明:** 在IE下使用`\@cc_on`è¯å¥æˆ–使用`\@if`或`\@set`è¯å¥å¯ä»¥æ¿€æ´»æ¡ä»¶ç¼–译。尽管å¯ä»¥é€šè¿‡æ³¨é‡Šæ¥å…¼å®¹IE以外的æµè§ˆå™¨ï¼Œä½†å®ƒå¦¨ç¢è‡ªåŠ¨åŒ–å·¥å…·çš„æ‰§è¡Œï¼Œå› ä¸ºåœ¨è¿è¡Œæ—¶ï¼Œå®ƒä»¬ä¼šæ”¹å˜JavaScript è¯æ³•æ ‘ã€‚ **å例:** ```javascript var f = function () { /*@cc_on @*/ /*@if (@_jscript_version >= 4) alert("JavaScript version 4 or better"); @else @*/ alert("Conditional compilation not supported by this scripting engine."); /*@end @*/ }; ``` #### 规则3.7 ç¦æ¢ä¿®æ”¹å†…置对象的原型 **说明:** 内置对象作为一套公共接å£ï¼Œå…·æœ‰çº¦å®šä¿—æˆçš„行为方å¼ï¼Œä¿®æ”¹å…¶åŽŸåž‹ï¼Œå¯èƒ½ç ´å接å£è¯ä¹‰ï¼Œæˆ–导致调试时的诡异现象。 **å例:** ```javascript Array.prototype.indexOf = function () { return -1 } var arr = [1, 1, 1, 1, 1, 2, 1, 1, 1]; console.log(aar.indexOf(2)); // 输出:-1 ``` #### 规则3.8 ç¦æ¢ç›´æŽ¥ä½¿ç”¨`Object.prototype`的内置属性 **说明:** ECMAScript5.1新增了`Object.create`ï¼Œå®ƒåˆ›å»ºä¸€ä¸ªæ–°å¯¹è±¡ï¼Œä½¿ç”¨çŽ°æœ‰çš„å¯¹è±¡æ¥æä¾›æ–°åˆ›å»ºçš„å¯¹è±¡çš„proto。`Object.create(null)`是用于创建用作map的对象的常用模å¼ã€‚当该对象具有`Object.prototype`åŒå的属性时,å¯èƒ½ä¼šå¯¼è‡´æ„å¤–è¡Œä¸ºæˆ–æ¼æ´žã€‚例如,webæœåŠ¡å™¨è§£æžæ¥è‡ªå®¢æˆ·ç«¯çš„JSON输入并且使用`hasOwnProperty`直接调用生æˆçš„对象是ä¸å®‰å…¨çš„ï¼Œå› ä¸ºæ¶æ„客户端å¯èƒ½ä¼šå‘é€ç±»ä¼¼çš„JSON值`'{ "hasOwnProperty": 1 }'` 并导致æœåŠ¡å™¨å´©æºƒã€‚ **å例:** ```javascript var hasBarProperty = foo.hasOwnProperty("bar"); var isPrototypeOfBar = foo.isPrototypeOf(bar); var barIsEnumerable = foo.propertyIsEnumerable("bar"); ``` **æ£ä¾‹ï¼š** ```javascript var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar"); var isPrototypeOfBar = Object.prototype.isPrototypeOf.call(foo, bar); var barIsEnumerable = {}.propertyIsEnumerable.call(foo, "bar"); ``` #### 规则3.9 使用`Object.getPrototypeOf`函数而ä¸è¦ä½¿ç”¨`_proto_` 说明:ES5引入`Object.getPrototypeOf`函数作为获å–å¯¹è±¡åŽŸåž‹çš„æ ‡å‡†API,但在这之å‰å¤§é‡çš„JavaScript引擎早就使用一个特殊的`proto`属性æ¥è¾¾åˆ°ç›¸åŒçš„目的。然而,`proto`å®ƒæœ¬è´¨ä¸Šæ˜¯ä¸€ä¸ªå†…éƒ¨å±žæ€§ï¼Œè€Œä¸æ˜¯ä¸€ä¸ªæ£å¼çš„对外的APIï¼Œç›®å‰æµè§ˆå™¨å¿…须部署这个属性,但其他è¿è¡ŒçŽ¯å¢ƒä¸ä¸€å®šéƒ¨ç½²ï¼Œå› æ¤ï¼Œè¯¥å±žæ€§å¹¶ä¸æ˜¯å®Œå…¨å…¼å®¹çš„。例如,对于拥有null原型的对象,ä¸åŒçš„环境处ç†çš„ä¸ä¸€æ ·ã€‚ ```javascript var empty = Object.create(null); "_proto_" in empty; //有些环境返回false,有些环境返回true ``` æ‰€ä»¥æ— è®ºä»Žè¯ä¹‰çš„角度,还是从兼容性的角度,都ä¸è¦ä½¿ç”¨proto这个属性,而是使用`Object.getPrototypeOf()`æ¥ä»£æ›¿ã€‚æ— è®ºåœ¨ä»€ä¹ˆçŽ¯å¢ƒä¸ï¼Œ`Object.getPrototypeOf`函数都是有效的,而且也是æå–å¯¹è±¡åŽŸåž‹æ›´åŠ æ ‡å‡†ã€å¯ç§»æ¤çš„æ–¹æ³•。 #### 规则3.10 ä¸è¦ä½¿ç”¨å‡½æ•°æž„é€ å™¨åˆ›å»ºå‡½æ•° 说明:定义函数的方法包括3ç§ï¼šå‡½æ•°å£°æ˜Žã€Functionæž„é€ å‡½æ•°å’Œå‡½æ•°è¡¨è¾¾å¼ã€‚ä¸ç®¡ç”¨å“ªç§æ–¹æ³•定义函数,它们都是Function对象的实例,并将继承Functionå¯¹è±¡æ‰€æœ‰é»˜è®¤æˆ–è‡ªå®šä¹‰çš„æ–¹æ³•å’Œå±žæ€§ã€‚ä»¥å‡½æ•°æž„é€ å™¨åˆ›å»ºå‡½æ•°çš„æ–¹å¼ç±»ä¼¼äºŽå—符串`eval()`,å¯ä»¥æŽ¥å—任何å—符串形å¼ä½œä¸ºå®ƒçš„å‡½æ•°ä½“ï¼Œè¿™å°±ä¼šæœ‰å®‰å…¨æ¼æ´žçš„风险。 **å例:** ```javascript var func = new Function('a', 'b', 'return a + b'); var func2 = new Function('alert("Hello")'); ``` **æ£ä¾‹ï¼š** ```javascript function func(a, b) { return a + b; } var func2 = function(a, b) { return a + b; } ``` #### 建议3.11 在使用原型`prototype`实现继承时,尽é‡ä½¿ç”¨çŽ°æœ‰ç¨³å®šçš„åº“æ–¹æ³•è€Œéžè‡ªè¡Œå®žçް **说明:** 多级原型结构是指JavaScriptä¸çš„继承关系。当定义一个D类,且把B类作为其原型,那么就获得了一个多级原型结构。这些原型结构会å˜å¾—å¾ˆå¤æ‚。使用现有的稳定的库方法如`the Closure`库的`goog.inherits()`或其他类似的函数,å¯é¿å…ä¸å¿…è¦çš„ç¼–ç 失误,将是更好的选择。 #### 建议3.12 å®šä¹‰ç±»æ—¶ï¼Œåº”åœ¨åŽŸåž‹ä¸‹å®šä¹‰æ–¹æ³•ï¼Œåœ¨æž„é€ æ–¹æ³•å†…å®šä¹‰å±žæ€§ **说明:** JavaScript䏿œ‰å¤šç§æ–¹æ³•å¯ä»¥ç»™æž„é€ å‡½æ•°æ·»åŠ æ–¹æ³•æˆ–æˆå‘˜ï¼Œä½†æ˜¯ä½¿ç”¨åŽŸåž‹å®šä¹‰æ–¹æ³•ï¼Œå¯ä»¥é™ä½Žå†…å˜å 用,æé«˜è¿è¡Œæ•ˆçŽ‡ã€‚ **å例:** ```javascript function Animals() { this.walk = function() {}; // è¿™æ ·ä¼šå¯¼è‡´æ¯ä¸ªå®žä¾‹ä¸Šéƒ½åˆ›å»ºä¸€ä¸ªwalk方法 } ``` **æ£ä¾‹ï¼š** ```javascript function Animals() {} Animals.prototype.walk = function() {}; ``` #### 建议3.13 使用é—包时,应é¿å…æž„æˆå¾ªçŽ¯å¼•ç”¨ï¼Œå¯¼è‡´å†…å˜æ³„露 **说明:** JavaScript是一ç§åžƒåœ¾æ”¶é›†å¼è¯è¨€ï¼Œå…¶å¯¹è±¡çš„å†…å˜æ˜¯æ ¹æ®å¯¹è±¡çš„创建分é…给该对象的,并且会在没有对该对象的引用时由æµè§ˆå™¨æ”¶å›žã€‚JavaScript的垃圾收集机制本身并没有问题,但æµè§ˆå™¨åœ¨ä¸ºDOM对象分é…å’Œæ¢å¤å†…å˜çš„æ–¹å¼ä¸Šæœ‰äº›å‡ºå…¥ã€‚IEå’ŒFirefoxå‡ä½¿ç”¨å¼•用计数æ¥ä¸ºDOM对象处ç†å†…å˜ã€‚在引用计数系统ä¸ï¼Œæ¯ä¸ªæ‰€å¼•用的对象都会ä¿ç•™ä¸€ä¸ªè®¡æ•°ï¼Œä»¥èŽ·æ‚‰æœ‰å¤šå°‘å¯¹è±¡æ£åœ¨å¼•用它。如果计数为零,那么该对象就会被销æ¯ï¼Œå…¶å 用的内å˜ä¹Ÿä¼šè¿”å›žç»™å †ã€‚è™½ç„¶è¿™ç§è§£å†³æ–¹æ¡ˆæ€»çš„æ¥è¯´è¿˜ç®—æœ‰æ•ˆï¼Œä½†æ˜¯åœ¨å¾ªçŽ¯å¼•ç”¨æ–¹é¢å´å˜åœ¨ä¸€äº›ç›²ç‚¹ã€‚ 当两个对象互相引用时,就构æˆäº†å¾ªçŽ¯å¼•ç”¨ï¼Œå…¶ä¸å¯¹è±¡çš„引用计数值都被赋为1。在纯垃圾收集系统ä¸ï¼Œå¾ªçŽ¯å¼•ç”¨é—®é¢˜ä¸å¤§ï¼šå¦‚果涉åŠçš„ä¸¤ä¸ªå¯¹è±¡ä¸æœ‰ä¸€ä¸ªå¯¹è±¡è¢«ä»»ä½•其他对象引用,那么这两个对象都将被垃圾收集。而在引用计数系统ä¸ï¼Œè¿™ä¸¤ä¸ªå¯¹è±¡éƒ½ä¸èƒ½è¢«é”€æ¯ï¼ŒåŽŸå› æ˜¯å¼•ç”¨è®¡æ•°æ°¸è¿œä¸èƒ½ä¸º0ã€‚åœ¨åŒæ—¶ä½¿ç”¨äº†åžƒåœ¾æ”¶é›†å’Œå¼•用计数的混åˆç³»ç»Ÿä¸ï¼Œå°†ä¼šå‘生泄æ¼ï¼Œå› 为系统ä¸èƒ½æ£ç¡®è¯†åˆ«å¾ªçŽ¯å¼•ç”¨ã€‚åœ¨è¿™ç§æƒ…况下,DOM对象和JavaScript对象å‡ä¸èƒ½è¢«é”€æ¯ã€‚ 循环引用很容易创建。在JavaScript最为方便的编程结构之一——é—包ä¸ï¼Œå¾ªçŽ¯å¼•ç”¨å°¤å…¶çªå‡ºã€‚é—åŒ…ä¼šæŒæœ‰å…¶å¤–部作用域(包括局部å˜é‡ã€å‚æ•°åŠæ–¹æ³•)的引用,当é—包本身åˆè¢«ä½œç”¨åŸŸæˆå‘˜ï¼ˆå¸¸è§äºŽDOMå¯¹è±¡ï¼‰æŒæœ‰æ—¶ä¾¿æž„æˆå¾ªçŽ¯å¼•ç”¨ï¼Œè¿›ä¸€æ¥å¯¼è‡´å†…å˜æ³„露。 **å例:** ```javascript function setClickListener(element, a, b) { element.onclick = function() { // 在这里用到aå’Œb }; }; ``` 在上述代ç ä¸ï¼Œå³ä½¿æ²¡æœ‰ä½¿ç”¨element,é—包也ä¿ç•™äº†elementã€aå’Œb的引用。由于element也ä¿ç•™äº†å¯¹é—åŒ…çš„å¼•ç”¨ï¼Œå› æ¤äº§ç”Ÿäº†å¾ªçŽ¯å¼•ç”¨ï¼Œä¸èƒ½è¢«GC回收。 **æ£ä¾‹ï¼š** ```javascript function setClickListener(element, a, b) { element.onclick = createHandler(a, b); } function createHandler(a, b) { // é€šè¿‡æ·»åŠ å¦å¤–一个函数æ¥é¿å…é—åŒ…æœ¬èº«ï¼Œè¿›è€Œç»„ç»‡å†…å˜æ³„露 return function() { // 在这里用到aå’Œb } } ``` #### 建议3.14 è¦æƒ•JavaScript的浮点数 **说明:** JavaScript具有å•一数å—类型:`IEEE 754`åŒç²¾åº¦æµ®ç‚¹æ•°ã€‚拥有å•一数å—类型是JavaScriptçš„æœ€ä½³åŠŸèƒ½ä¹‹ä¸€ã€‚å¤šç§æ•°å—类型å¯èƒ½æ˜¯å¤æ‚æ€§ï¼Œæ··æ·†å’Œé”™è¯¯çš„æ ¹æºã€‚但是,二进制浮点类型有一个最大的缺点是,它ä¸èƒ½å‡†ç¡®åœ°è¡¨ç¤ºå°æ•°éƒ¨åˆ†ï¼Œä¼šå¯¼è‡´è®©äººæ„外的精度问题,è§ä¸‹é¢ç¤ºä¾‹ã€‚ 示例代ç 1: ```javascript console.log(0.1 + 0.2 === 0.3); // 输出:falseã€‚æ‰€ä»¥é€šå¸¸ç¦æ¢ç›´æŽ¥ä½¿ç”¨==或===æ¥æ¯”较浮点数。 ``` 示例代ç 2: ```javascript var sum1 = (0.1 + 0.2) + 0.3; console.log(sum1); // 输出:0.6000000000000001 var sum2 = 0.1 + (0.2 + 0.3); console.log(sum2); // 输出:0.6。所以对于二进制浮点数,(a + b) + c ä¸èƒ½ä¿è¯äº§ç”ŸäºŽa + (b + c)相åŒçš„结果。 ``` æœ‰æ•ˆçš„è§£å†³æ–¹æ³•æœ‰ä»¥ä¸‹å‡ ç§ï¼š 1. å°½å¯èƒ½çš„采用整数值è¿ç®—ï¼Œå› ä¸ºæ•´æ•°åœ¨è¡¨ç¤ºæ˜¯ä¸éœ€è¦èˆå…¥ï¼› 2. 使用JavaScript的原生方法`Number.prototype.toFixed(digits)`,`digits`傿•°è¡¨ç¤ºå°æ•°ç‚¹åŽæ•°å—的个数,ä¸ä½¿ç”¨æŒ‡æ•°æ³•ï¼Œåœ¨å¿…è¦æ—¶ä¼šè¿›è¡Œå››èˆäº”å…¥ã€‚ä½¿ç”¨è¯¥æ–¹æ³•ï¼Œåœ¨åˆ¤æ–æµ®ç‚¹æ•°è¿ç®—结果å‰å¯¹è®¡ç®—结果进行精度缩å°ã€‚示例代ç 如下所示: ```javascript parseFloat(0.1 + 0.2).toFixed(1); //0.3 ``` 3. ES6 新增了一个æžå°çš„常é‡`Number.EPSILON =.220446049250313e-16 `,约ç‰äºŽ`0.00000000000000022204`。`Number.EPSILON`的出现是用æ¥åˆ¤æ–浮点数的计算误差,如果浮点数计算得到的误差ä¸è¶…过`Number.EPSILON`的值,就表示å¯ä»¥æŽ¥å—è¿™æ ·çš„è¯¯å·®ã€‚ç¤ºä¾‹ä»£ç 如下所示: ```javascript function isNumberEquals(one, other) { return Math.abs(one - other) < Number.EPSILON; } var one = 0.1 + 0.2; var other = 0.3; console.log(isNumberEquals(one, other)); // 输出:true ``` 4. 使用一些支æŒç²¾ç¡®è¿ç®—的类库方法,如`math.js` ```html <!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.0.0/math.js"></script> <script type="text/javascript"> function fn_click() { math.config({ number: "BigNumber", }); var result = math.add(math.bignumber(0.1), math.bignumber(0.2)); alert(result); } </script> </head> <body> <input type="button" value="mathjs(0.1+0.2)" onclick="fn_click();" /> </body> </html> ``` #### 建议3.15 ä¸è¦ä½¿ç”¨å¯å˜å‚æ•°çš„Arrayæž„é€ å‡½æ•° **说明:** 通常ä¸é¼“åŠ±ä½¿ç”¨æž„é€ å‡½æ•°`new Array`çš„æ–¹æ³•æ¥æž„é€ æ–°æ•°ç»„ï¼Œå› ä¸ºå½“æž„é€ å‡½æ•°åªæœ‰ä¸€ä¸ªå‚数的时候,å¯èƒ½ä¼šå¯¼è‡´æ„外情况,å¦å¤–Array的全局定义也å¯èƒ½è¢«é‡æ–°ä¿®æ”¹ï¼Œæ‰€ä»¥æå€¡ä½¿ç”¨æ•°ç»„æ–‡å—表示法æ¥åˆ›å»ºæ•°ç»„,也就是`[]`表示法。 **å例:** ```javascript const arr1 = new Array(x1, x2, x3); const arr2 = new Array(x1, x2); const arr3 = new Array(x1); const arr4 = new Array(); ``` é™¤äº†ç¬¬ä¸‰ç§æƒ…况之外,其他都å¯ä»¥æ£å¸¸å·¥ä½œï¼Œå¦‚æžœ`x1`是个整数,那么`arr3`就是长度为`x1`,值都为`undefined`的数组。如果`x1`是其他任何数å—,那么将抛出异常,如果它是其他任何东西,那么它将是å•元数组。 **æ£ä¾‹ï¼š** ```javascript const arr1 = [x1, x2, x3]; const arr2 = [x1, x2]; const arr3 = [x1]; const arr4 = []; ``` è¿™ç§å†™æ³•,就会çœåŽ»å¾ˆå¤šéº»çƒ¦ã€‚ åŒç†ï¼Œå¯¹äºŽå¯¹è±¡ï¼ŒåŒæ ·ä¸è¦ä½¿ç”¨`new Object()`,而是使用`{}`æ¥åˆ›å»ºã€‚ #### 规则3.16 构建å—符串时,优先使用å—符串模æ¿è€Œä¸æ˜¯å—符串链接。 **说明:** 模æ¿å—符串表达更简æ´ï¼Œæ›´å…·å¯è¯»æ€§ã€‚ **å例:** ```javascript function sayHi(name) { console.log('hi, ' + name); } ``` **æ£ä¾‹ï¼š** ```javascript function sayHi(name) { console.log(`hi, ${name}`); } ``` #### 规则3.17 数组é历采用`for...of`,对象é历采用`for...in`。 **å例:** ```javascript let numbers = [1, 2, 3, 4]; let sum = 0; for (let number in numbers) { sum += number; } // sum === 00123; ``` **æ£ä¾‹ï¼š** ```javascript let numbers = [1, 2, 3, 4]; let sum = 0; for (let number of numbers) { sum += number; } // sum === 10 ```