這段時間在整理前端代碼規範,現將JS部分的內容整理,都是基本基礎的內容。請各位大神斧正!!
目前已經整理以下的代碼規範:HTML編碼規範 、 CSSS編碼規範 、 CSS規範--BEM入門javascript
示例:html
// good switch (variable) { case '1': // do... break; case '2': // do... break; default: // do... } ------------ // bad switch (variable) { case '1': // do... break; case '2': // do... break; default: // do... }
示例:前端
var a = !arr.length; a++; a = b + c;
示例:java
// good var obj = { a: 1, b: 2, c: 3 }; ------------ // bad var obj = { a : 1, b:2, c :3 };
,
和 ;
前不容許有空格。若是不位於行尾,
和 ;
後必須跟一個空格。示例:node
// good callFunc(a, b); ------------ // bad callFunc(a , b) ;
示例:
解釋:
聲明包含元素的數組與對象,只有當內部元素的形式較爲簡單時,才容許寫在一行。元素複雜的狀況,仍是應該換行書寫。
示例:ajax
// good var arr1 = []; var arr2 = [1, 2, 3]; var obj1 = {}; var obj2 = {name: 'obj'}; var obj3 = { name: 'obj', age: 20, sex: 1 }; ------------ // bad var arr1 = [ ]; var arr2 = [ 1, 2, 3 ]; var obj1 = { }; var obj2 = { name: 'obj' }; var obj3 = {name: 'obj', age: 20, sex: 1};
{
前必須有一個空格。示例:正則表達式
// good if (condition) { } while (condition) { } function funcName() { } ------------ // bad if (condition){ } while (condition){ } function funcName(){ }
if
/ else
/ for
/ while
/ function
/ switch
/ do
/ try
/ catch
/ finally
關鍵字後,必須有一個空格。示例:算法
// good if (condition) { } while (condition) { } (function () { })(); ------------ // bad if(condition) { } while(condition) { } (function() { })();
(
之間不容許有空格。示例:編程
// good function funcName() { } var funcName = function funcName() { }; funcName(); ------------ // bad function funcName () { } var funcName = function funcName () { }; funcName ();
解釋:
超長的不可分割的代碼容許例外,好比複雜的正則表達式。長字符串不在例外之列。segmentfault
示例:
// good if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') || user.hasAuthority('delete-admin') ) { // Code } var result = number1 + number2 + number3 + number4 + number5; ------------ // bad if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') || user.hasAuthority('delete-admin')) { // Code } var result = number1 + number2 + number3 + number4 + number5;
,
或 ;
前換行。示例:
// good var obj = { a: 1, b: 2, c: 3 }; foo( aVeryVeryLongArgument, anotherVeryLongArgument, callback ); ------------ // bad var obj = { a: 1 , b: 2 , c: 3 }; foo( aVeryVeryLongArgument , anotherVeryLongArgument , callback );
示例:
// 僅爲按邏輯換行的示例,不表明setStyle的最優實現 function setStyle(element, property, value) { if (element == null) { return; } element.style[property] = value; }
示例:
// 較複雜的邏輯條件組合,將每一個條件獨立一行,邏輯運算符放置在行首進行分隔,或將部分邏輯按邏輯組合進行分隔。 // 建議最終將右括號 ) 與左大括號 { 放在獨立一行,保證與 `if` 內語句塊能容易視覺辨識。 if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') || user.hasAuthority('delete-admin') ) { // Code } // 按必定長度截斷字符串,並使用 + 運算符進行鏈接。 // 分隔字符串儘可能按語義進行,如不要在一個完整的名詞中間斷開。 // 特別的,對於 HTML 片斷的拼接,經過縮進,保持和 HTML 相同的結構。 var html = '' // 此處用一個空字符串,以便整個 HTML 片斷都在新行嚴格對齊 + '<article>' + '<h1>Title here</h1>' + '<p>This is a paragraph</p>' + '<footer>Complete</footer>' + '</article>'; // 也可以使用數組來進行拼接,相對 `+` 更容易調整縮進。 var html = [ '<article>', '<h1>Title here</h1>', '<p>This is a paragraph</p>', '<footer>Complete</footer>', '</article>' ]; html = html.join(''); // 當參數過多時,將每一個參數獨立寫在一行上,並將結束的右括號 ) 獨立一行。 // 全部參數必須增長一個縮進。 foo( aVeryVeryLongArgument, anotherVeryLongArgument, callback ); // 也能夠按邏輯對參數進行組合。 // 最經典的是 baidu.format 函數,調用時將參數分爲「模板」和「數據」兩塊 baidu.format( dateFormatTemplate, year, month, date, hour, minute, second ); // 當函數調用時,若是有一個或以上參數跨越多行,應當每個參數獨立一行。 // 這一般出如今匿名函數或者對象初始化等做爲參數時,如 `setTimeout` 函數等。 setTimeout( function () { alert('hello'); }, 200 ); order.data.read( 'id=' + me.model.id, function (data) { me.attchToModel(data.result); callback(); }, 300 ); // 鏈式調用較長時採用縮進進行調整。 $('#items') .find('.selected') .highlight() .end(); // 三元運算符由3部分組成,所以其換行應當根據每一個部分的長度不一樣,造成不一樣的狀況。 var result = thisIsAVeryVeryLongCondition ? resultA : resultB; var result = condition ? thisIsAVeryVeryLongResult : resultB; // 數組和對象初始化的混用,嚴格按照每一個對象的 `{` 和結束 `}` 在獨立一行的風格書寫。 var array = [ { // ... }, { // ... } ];
if...else...
、try...catch...finally
等語句,推薦使用在 }
號後添加一個換行 的風格,使代碼層次結構更清晰,閱讀性更好。示例:
if (condition) { // some statements; } else { // some statements; } try { // some statements; } catch (ex) { // some statements; }
示例:
// good if (condition) { callFunc(); } ------------ // bad if (condition) callFunc(); if (condition) callFunc();
示例:
// good function funcName() { } ------------ // bad function funcName() { }; // 若是是函數表達式,分號是不容許省略的。 var funcName = function () { };
(
,非 IIFE 不得在函數表達式外添加 (
。解釋:
IIFE = Immediately-Invoked Function Expression.
額外的 (
可以讓代碼在閱讀的一開始就能判斷函數是否當即被調用,進而明白接下來代碼的用途。而不是一直拖到底部才恍然大悟。
示例:
// good var task = (function () { // Code return result; })(); var func = function () { }; ------------ // bad var task = function () { // Code return result; }(); var func = (function () { });
示例:
var loadingModules = {};
示例:
// good var $sidebar = $('.sidebar'); ------------ // bad var sidebar = $('.sidebar');
示例:
var HTML_ENTITY = {};
示例:
function getStyle(element) { }
示例:
function getStyle(element) { }
示例:
function TextNode(options) { }
示例:
function Engine(options) { }
示例:
function TextNode(value, engine) { this.value = value; this.engine = engine; } TextNode.prototype.clone = function () { return this; };
示例:
var TargetState = { READING: 1, READED: 2, APPLIED: 3, READY: 4 };
示例:
function XMLParser() { } function insertHTML(element, html) { } var httpRequest = new HTTPRequest();
示例:
var isReady = false; var hasMoreCommands = false;
示例:
var loadingData = ajax.get('url'); loadingData.then(callback);
示例:
// good function () { return function () { console.log(this); }.bind(this); } // bad function () { var self = this; return function () { console.log(self); }; } // bad function () { var that = this; return function () { console.log(that); }; } // bad function () { var _this = this; return function () { console.log(_this); }; }
解釋:
文件
namespace
類
函數或方法
類屬性
事件
全局變量
常量
{
開始, 以 }
結束。解釋:
經常使用類型如:{string}
, {number}
, {boolean}
, {Object}
, {Function}
, {RegExp}
, {Array}
, {Date}
。
類型不只侷限於內置的類型,也能夠是自定義的類型。好比定義了一個類 Developer,就可使用它來定義一個參數和返回值的類型。
示例:
示例:
/** * @file Describe the file */
解釋:
生成的文檔中將有可訪問性的標記,避免用戶直接使用非 public 的屬性或方法。
示例:
/** * 類描述 * * @class * @extends Developer */ var Fronteer = function () { Developer.call(this); /** * 屬性描述 * * @type {string} * @private */ this.level = 'T12'; // constructor body }; util.inherits(Fronteer, Developer); /** * 方法描述 * * @private * @return {string} 返回值描述 */ Fronteer.prototype.getLevel = function () { };
示例:
/** * 描述 * * @class */ function Developer() { // constructor body }
示例:
/** * 描述 * * @class * @extends Developer */ function Fronteer() { Developer.call(this); // constructor body } util.inherits(Fronteer, Developer);
解釋:
當 return
關鍵字僅做退出函數/方法使用時,無須對返回值做註釋標識。
示例:
/** * 函數描述 * * @param {string} p1 參數1的說明 * @param {string} p2 參數2的說明,比較長 * 那就換行了. * @param {number=} p3 參數3的說明(可選) * @return {Object} 返回值描述 */ function foo(p1, p2, p3) { var p3 = p3 || 10; return { p1: p1, p2: p2, p3: p3 }; }
示例:
/** * 函數描述 * * @param {Object} option 參數描述 * @param {string} option.url option項描述 * @param {string=} option.method option項描述,可選參數 */ function foo(option) { // TODO }
示例:
/** * 值變動時觸發 * * @event Select#change * @param {Object} e e描述 * @param {string} e.before before描述 * @param {string} e.after after描述 */ this.fire( 'change', { before: 'foo', after: 'bar' } );
示例:
/** * 點擊處理 * * @fires Select#change * @private */ Select.prototype.clickHandler = function () { /** * 值變動時觸發 * * @event Select#change * @param {Object} e e描述 * @param {string} e.before before描述 * @param {string} e.after after描述 */ this.fire( 'change', { before: 'foo', after: 'bar' } ); };
示例:
/** * 常量說明 * * @const * @type {string} */ var REQUEST_URL = 'myurl.do';
解釋:
TODO: 有功能待實現。此時須要對將要實現的功能進行簡單說明。
FIXME: 該處代碼運行沒問題,但可能因爲時間趕或者其餘緣由,須要修正。此時須要對如何修正進行簡單說明。
HACK: 爲修正某些問題而寫的不太好或者使用了某些詭異手段的代碼。此時須要對思路或詭異手段進行描述。
XXX: 該處存在陷阱。此時須要對陷阱進行描述。
解釋:
不經過 var 定義變量將致使變量污染全局環境。
示例:
// good var name = 'MyName'; ------------ // bad name = 'MyName';
原則上不建議使用全局變量,對於已有的全局變量或第三方框架引入的全局變量,須要根據檢查工具的語法標識。
示例:
/* globals jQuery */ var element = jQuery('#element-id');
[建議] 最後再聲明未賦值的變量。當你須要引用前面的變量賦值時這將變的頗有用。
示例:
// good var items = getItems(); var goSportsTeam = true; var dragonball; var length; var i; ------------ // bad var i, len, dragonball, items = getItems(), goSportsTeam = true; // bad var i; var items = getItems(); var dragonball; var goSportsTeam = true; var len;
解釋:
變量聲明與使用的距離越遠,出現的跨度越大,代碼的閱讀與維護成本越高。雖然JavaScript的變量是函數做用域,仍是應該根據編程中的意圖,縮小變量出現的距離空間。
示例:
// good function kv2List(source) { var list = []; for (var key in source) { if (source.hasOwnProperty(key)) { var item = { k: key, v: source[key] }; list.push(item); } } return list; } ------------ // bad function kv2List(source) { var list = []; var key; var item; for (key in source) { if (source.hasOwnProperty(key)) { item = { k: key, v: source[key] }; list.push(item); } } return list; }
解釋:
使用 === 能夠避免等於判斷中隱式的類型轉換。
示例:
// good if (age === 30) { // ...... } ------------ // bad if (age == 30) { // ...... }
示例:
// 字符串爲空 // good if (!name) { // ...... } ------------ // bad if (name === '') { // ...... }
// 字符串非空 // good if (name) { // ...... } ------------ // bad if (name !== '') { // ...... }
// 數組非空 // good if (collection.length) { // ...... } ------------ // bad if (collection.length > 0) { // ...... }
// 布爾不成立 // good if (!notTrue) { // ...... } ------------ // bad if (notTrue === false) { // ...... }
// null 或 undefined // good if (noValue == null) { // ...... } ------------ // bad if (noValue === null || typeof noValue === 'undefined') { // ...... }
解釋:
按執行頻率排列分支的順序好處是:
閱讀的人容易找到最多見的狀況,增長可讀性。
提升執行效率。
示例:
// good switch (typeof variable) { case 'object': // ...... break; case 'number': case 'boolean': case 'string': // ...... break; } ------------ // bad var type = typeof variable; if (type === 'object') { // ...... } else if (type === 'number' || type === 'boolean' || type === 'string') { // ...... }
解釋:
循環體中的函數表達式,運行過程當中會生成循環次數個函數對象。
示例:
// good function clicker() { // ...... } for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; addListener(element, 'click', clicker); } // bad for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; addListener(element, 'click', function () {}); }
示例:
// good var width = wrap.offsetWidth + 'px'; for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; element.style.width = width; // ...... } // bad for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; element.style.width = wrap.offsetWidth + 'px'; // ...... }
解釋:
雖然現代瀏覽器都對數組長度進行了緩存,但對於一些宿主對象和老舊瀏覽器的數組對象,在每次 length 訪問時會動態計算元素個數,此時緩存 length 能有效提升程序性能。
示例:
for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; // ...... }
解釋:
逆序遍歷能夠節省變量,代碼比較優化。
示例:
var len = elements.length; while (len--) { var element = elements[len]; // ...... }
示例:
// string typeof variable === 'string' // number typeof variable === 'number' // boolean typeof variable === 'boolean' // Function typeof variable === 'function' // Object typeof variable === 'object' // RegExp variable instanceof RegExp // Array variable instanceof Array // null variable === null // null or undefined variable == null // undefined typeof variable === 'undefined'
示例:
// good num + ''; // bad new String(num); num.toString(); String(num);
示例:
// good +str; // bad Number(str);
示例:
var width = '200px'; parseInt(width, 10);
示例:
// good parseInt(str, 10); // bad parseInt(str);
示例:
var num = 3.14; !!num;
示例:
// good var num = 3.14; Math.ceil(num); // bad var num = 3.14; parseInt(num, 10);
解釋:
輸入單引號不須要按住 shift,方便輸入。
實際使用中,字符串常常用來拼接 HTML。爲方便 HTML 中包含雙引號而不須要轉義寫法。
示例:
var str = '我是一個字符串'; var html = '<div class="cls">拼接HTML能夠省去雙引號轉義</div>';
[建議] 使用 數組 或 + 拼接字符串。
解釋:
使用 + 拼接字符串,若是拼接的所有是 StringLiteral,壓縮工具能夠對其進行自動合併的優化。因此,靜態字符串建議使用 + 拼接。
在現代瀏覽器下,使用 + 拼接字符串,性能較數組的方式要高。
如須要兼顧老舊瀏覽器,應儘可能使用數組拼接字符串。
示例:
// 使用數組拼接字符串 var str = [ // 推薦換行開始並縮進開始第一個字符串, 對齊代碼, 方便閱讀. '<ul>', '<li>第一項</li>', '<li>第二項</li>', '</ul>' ].join(''); // 使用 `+` 拼接字符串 var str2 = '' // 建議第一個爲空字符串, 第二個換行開始並縮進開始, 對齊代碼, 方便閱讀 + '<ul>', + '<li>第一項</li>', + '<li>第二項</li>', + '</ul>';
示例:
var items; var messages; var length; var i; messages = [{ state: 'success', message: 'This one worked.' }, { state: 'success', message: 'This one worked as well.' }, { state: 'error', message: 'This one did not work.' }]; length = messages.length; // good function inbox(messages) { items = []; for (i = 0; i < length; i++) { // use direct assignment in this case because we're micro-optimizing. items[i] = '<li>' + messages[i].message + '</li>'; } return '<ul>' + items.join('') + '</ul>'; } // bad function inbox(messages) { items = '<ul>'; for (i = 0; i < length; i++) { items += '<li>' + messages[i].message + '</li>'; } return items + '</ul>'; }
解釋:
在 JavaScript 中拼接,而且最終將輸出到頁面中的字符串,須要進行合理轉義,以防止安全漏洞。下面的示例代碼爲場景說明,不能直接運行。
示例:
// HTML 轉義 var str = '<p>' + htmlEncode(content) + '</p>'; // HTML 轉義 var str = '<input type="text" value="' + htmlEncode(value) + '">'; // URL 轉義 var str = '<a href="/?key=' + htmlEncode(urlEncode(value)) + '">link</a>'; // JavaScript字符串 轉義 + HTML 轉義 var str = '<button onclick="check(\'' + htmlEncode(strLiteral(name)) + '\')">提交</button>';
解釋:
使用模板引擎有以下好處:
在開發過程當中專一於數據,將視圖生成的過程由另一個層級維護,使程序邏輯結構更清晰。
優秀的模板引擎,經過模板編譯技術和高質量的編譯產物,能得到比手工拼接字符串更高的性能。
模板引擎能方便的對動態數據進行相應的轉義,部分模板引擎默認進行HTML轉義,安全性更好。
介紹幾款模板插件:
artTemplate: 體積較小,在全部環境下性能高,語法靈活。
dot.js: 體積小,在現代瀏覽器下性能高,語法靈活。
etpl: 體積較小,在全部環境下性能高,模板複用性高,語法靈活。
handlebars: 體積大,在全部環境下性能高,擴展性高。
hogon: 體積小,在現代瀏覽器下性能高。
nunjucks: 體積較大,性能通常,模板複用性高。
示例:
// 如下行爲絕對禁止 String.prototype.trim = function () { };
// good var obj = {}; // bad var obj = new Object();
解釋:
若是屬性不符合 Identifier 和 NumberLiteral 的形式,就須要以 StringLiteral 的形式提供。
示例:
// good var info = { 'name': 'someone', 'age': 28, 'more-info': '...' }; // bad var info = { name: 'someone', age: 28, 'more-info': '...' };
.
。解釋:
屬性名符合 Identifier 的要求,就能夠經過 . 來訪問,不然就只能經過 [expr] 方式訪問。
一般在 JavaScript 中聲明的對象,屬性命名是使用 Camel 命名法,用 . 來訪問更清晰簡潔。部分特殊的屬性(好比來自後端的 JSON ),可能採用不尋常的命名方式,能夠經過 [expr] 方式訪問。
示例:
info.age; info['more-info'];
示例:
var newInfo = {}; for (var key in info) { if (info.hasOwnProperty(key)) { newInfo[key] = info[key]; } }
示例:
// good var arr = []; // bad var arr = new Array();
解釋:
數組對象可能存在數字之外的屬性, 這種狀況下 for in 不會獲得正確結果。
示例:
var arr = ['a', 'b', 'c']; // 這裏僅做演示, 實際中應使用 Object 類型 arr.other = 'other things'; // 正確的遍歷方式 for (var i = 0, len = arr.length; i < len; i++) { console.log(i); } // 錯誤的遍歷方式 for (var i in arr) { console.log(i); }
解釋:
將過多的邏輯單元混在一個大函數中,易致使難以維護。一個清晰易懂的函數應該完成單一的邏輯單元。複雜的操做應進一步抽取,經過函數的調用來體現流程。
特定算法等不可分割的邏輯容許例外。
示例:
function syncViewStateOnUserAction() { if (x.checked) { y.checked = true; z.value = ''; } else { y.checked = false; } if (a.value) { warning.innerText = ''; submitButton.disabled = false; } else { warning.innerText = 'Please enter it'; submitButton.disabled = true; } } // 直接閱讀該函數會難以明確其主線邏輯,所以下方是一種更合理的表達方式: function syncViewStateOnUserAction() { syncXStateToView(); checkAAvailability(); } function syncXStateToView() { y.checked = x.checked; if (x.checked) { z.value = ''; } } function checkAAvailability() { if (a.value) { clearWarnignForA(); } else { displayWarningForAMissing(); } }
解釋:
除去不定長參數之外,函數具有不一樣邏輯意義的參數建議控制在 6 個之內,過多參數會致使維護難度增大。
某些狀況下,如使用 AMD Loader 的 require 加載多個模塊時,其 callback 可能會存在較多參數,所以對函數參數的個數不作強制限制。
解釋:
有些函數的參數並非做爲函數邏輯的輸入,而是對函數邏輯的某些分支條件判斷之用,此類參數建議經過一個 options 參數傳遞。
以下函數:
/** * 移除某個元素 * * @param {Node} element 須要移除的元素 * @param {boolean} removeEventListeners 是否同時將全部註冊在元素上的事件移除 */ function removeElement(element, removeEventListeners) { element.parent.removeChild(element); if (removeEventListeners) { element.clearEventListeners(); } }
能夠轉換爲下面的簽名:
/** * 移除某個元素 * * @param {Node} element 須要移除的元素 * @param {Object} options 相關的邏輯配置 * @param {boolean} options.removeEventListeners 是否同時將全部註冊在元素上的事件移除 */ function removeElement(element, options) { element.parent.removeChild(element); if (options.removeEventListeners) { element.clearEventListeners(); } }
這種模式有幾個顯著的優點:
boolean 型的配置項具有名稱,從調用的代碼上更易理解其表達的邏輯意義。
當配置項有增加時,無需無休止地增長參數個數,不會出現 removeElement(element, true, false, false, 3) 這樣難以理解的調用代碼。
當部分配置參數可選時,多個參數的形式很是難處理重載邏輯,而使用一個 options 對象只需判斷屬性是否存在,實現得以簡化。
示例:
// good function yup(name, options, args) { // ...stuff... } // bad function nope(name, options, arguments) { // ...stuff... }
解釋:
一般使用其餘 library 的類繼承方案都會進行 constructor
修正。若是是本身實現的類繼承方案,須要進行 constructor 修正。
示例:
/** * 構建類之間的繼承關係 * * @param {Function} subClass 子類函數 * @param {Function} superClass 父類函數 */ function inherits(subClass, superClass) { var F = new Function(); F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; }
示例:
function Animal(name) { this.name = name; } // 直接prototype等於對象時,須要修正constructor Animal.prototype = { constructor: Animal, jump: function () { alert('animal ' + this.name + ' jump'); } }; // 這種方式擴展prototype則無需理會constructor Animal.prototype.jump = function () { alert('animal ' + this.name + ' jump'); };
解釋:
原型對象的成員被全部實例共享,能節約內存佔用。因此編碼時咱們應該遵照這樣的原則:原型對象包含程序不會修改的成員,如方法函數或配置項。
function TextNode(value, engine) { this.value = value; this.engine = engine; } TextNode.prototype.clone = function () { return this; };
解釋:
在 JavaScript 普遍應用的瀏覽器環境,絕大多數 DOM 事件名稱都是全小寫的。爲了遵循大多數 JavaScript 開發者的習慣,在設計自定義事件時,事件名也應該全小寫。
解釋:
一個事件對象的好處有:
順序無關,避免事件監聽者須要記憶參數順序。
每一個事件信息均可以根據須要提供或者不提供,更自由。
擴展方便,將來添加事件信息時,無需考慮會破壞監聽器參數形式而沒法向後兼容。
解釋:
常見禁止默認行爲的方式有兩種:
事件監聽函數中 return false。
事件對象中包含禁止默認行爲的方法,如 preventDefault。
解釋:
直接 eval,指的是以函數方式調用 eval 的調用方法。直接 eval 調用執行代碼的做用域爲本地做用域,應當避免。
若是有特殊狀況須要使用直接 eval,需在代碼中用詳細的註釋說明爲什麼必須使用直接 eval,不能使用其它動態執行代碼的方式,同時須要其餘資深工程師進行 Code Review。
解釋:
使用 with 可能會增長代碼的複雜度,不利於閱讀和管理;也會對性能有影響。大多數使用 with 的場景都能使用其餘方式較好的替代。因此,儘可能不要使用 with。
解釋:
對於有被遍歷需求,且值 null 被認爲具備業務邏輯意義的值的對象,移除某個屬性必須使用 delete 操做。
在嚴格模式或 IE 下使用 delete 時,不能被刪除的屬性會拋出異常,所以在不肯定屬性是否能夠刪除的狀況下,建議添加 try-catch 塊。
示例:
try { delete o.x; } catch (deleteError) { o.x = null; }
解釋:
JavaScript 因其腳本語言的動態特性,當一個對象未被 seal 或 freeze 時,能夠任意添加、刪除、修改屬性值。
可是隨意地對 非自身控制的對象 進行修改,很容易形成代碼在不可預知的狀況下出現問題。所以,設計良好的組件、函數應該避免對外部傳入的對象的修改。
下面代碼的 selectNode 方法修改了由外部傳入的 datasource 對象。若是 datasource 用在其它場合(如另外一個 Tree 實例)下,會形成狀態的混亂。
function Tree(datasource) { this.datasource = datasource; } Tree.prototype.selectNode = function (id) { // 從datasource中找出節點對象 var node = this.findNode(id); if (node) { node.selected = true; this.flushView(); } };
對於此類場景,須要使用額外的對象來維護,使用由自身控制,不與外部產生任何交互的 selectedNodeIndex 對象來維護節點的選中狀態,不對 datasource 做任何修改。
function Tree(datasource) { this.datasource = datasource; this.selectedNodeIndex = {}; } Tree.prototype.selectNode = function (id) { // 從datasource中找出節點對象 var node = this.findNode(id); if (node) { this.selectedNodeIndex[id] = true; this.flushView(); } };
除此以外,也能夠經過 deepClone 等手段將自身維護的對象與外部傳入的分離,保證不會相互影響。
解釋:
若是一個屬性被設計爲 boolean 類型,則不要使用 1 或 0 做爲其值。對於標識性的屬性,如對代碼體積有嚴格要求,能夠從一開始就設計爲 number 類型且將 0 做爲否認值。
從 DOM 中取出的值一般爲 string 類型,若是有對象或函數的接收類型爲 number 類型,提早做好轉換,而不是指望對象、函數能夠處理多類型的值。