这段时间在整理前端代码规范,现将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 类型,提早做好转换,而不是指望对象、函数能够处理多类型的值。