老是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以建立独立隔绝的定义域。这一举措可防止全局命名空间被污染。javascript
IIFE 还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(好比第三方库,window 引用,被覆盖的未定义的关键字等等)。css
不推荐html
var x = 10, y = 100; console.log(window.x + ' ' + window.y);
推荐java
(function(w){ 'use strict'; var x = 10, y = 100; w.console.log((w.x === undefined) + ' ' + (w.y === undefined)); }(window));
不管什么时候,想要建立一个新的封闭的定义域,那就用 IIFE。它不只避免了干扰,也使得内存在执行完后当即释放。git
全部脚本文件建议都从 IIFE 开始。github
当即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内仍是写在外都是有效的,但写在内使得整个表达式看起来更像一个总体,所以推荐这么作。编程
不推荐数组
(function(){})();
推荐安全
(function(){}());
因此用下列写法来格式化你的 IIFE 代码:闭包
(function($, w, d){ 'use strict'; $(function() { w.alert(d.querySelectorAll('div').length); }); }(jQuery, window, document));
ECMAScript5 严格模式可在整个脚本或独个方法内被激活。它对应不一样的 javascript 语境会作更加严格的错误检查。严格模式也确保了 javascript 代码更加的健壮,运行的也更加快速。
严格模式会阻止使用在将来极可能被引入的预留关键字。
你应该在你的脚本中启用严格模式,最好是在独立的 IIFE 中应用它。避免在你的脚本第一行使用它而致使你的全部脚本都启动了严格模式,这有可能会引起一些第三方类库的问题。
不推荐
'use strict'; (function(){ // some code }());
推荐
(function(){ 'use strict'; // some code }());
老是使用 var
来声明变量。如不指定 var
,变量将被隐式地声明为全局变量,这将对变量难以控制。若是没有声明,变量处于什么定义域就变得不清(能够是在 Document 或 Window 中,也能够很容易地进入本地定义域)。因此,请老是使用 var
来声明变量。
采用严格模式带来的好处是,当你手误输入错误的变量名时,它能够经过报错信息来帮助你定位错误出处。
不推荐
x = 10;
y = 100;
推荐
var x = 10, y = 100;
在 javascript 中变量和方法定义会自动提高到执行以前。javascript 只有 function
级的定义域,而无其余不少编程语言中的块定义域,因此使得你在某一 function
内的某语句和循环体中定义了一个变量,此变量可做用于整个 function
内,而不只仅是在此语句或循环体中,由于它们的声明被 javascript 自动提高了。
咱们经过例子来看清楚这究竟是怎么一回事:
原 function
(function(){ 'use strict'; var a = 10; for(var i = 0; i < a; i++) { var b = i * i; console.log(b); } if(a === 10) { var f = function() { console.log(a); }; f(); } function x() { console.log('Mr. X!'); } x(); }());
被 js 提高事后
(function(){ 'use strict'; var a, i, b, f; function x() { console.log('Mr. X!'); } a = 10; for(i = 0; i < a; i++) { b = i * i; console.log(b); } if(a === 10) { f = function() { console.log(a); }; f(); } x(); }());
根据以上提高过程,你是否可理解如下代码?
有效代码
(function(){ 'use strict'; var a = 10; i = 5; x(); for(var i; i < a; i++) { console.log(b); var b = i * i; } if(a === 10) { f = function() { console.log(a); }; f(); var f; } function x() { console.log('Mr. X!'); } }());
正如你所看到的这段使人充满困惑与误解的代码致使了出人意料的结果。只有良好的声明习惯,也就是下一章节咱们要提到的声明规则,才能尽量的避免这类错误风险。
为避免上一章节所述的变量和方法定义被自动提高形成误解,把风险降到最低,咱们应该手动地显示地去声明变量与方法。也就是说,全部的变量以及方法,应当定义在 function
内的首行。
只用一个 var
关键字声明,多个变量用逗号隔开。
不推荐
(function(){ 'use strict'; var a = 10; var b = 10; for(var i = 0; i < 10; i++) { var c = a * b * i; } function f() { } var d = 100; var x = function() { return d * d; }; console.log(x()); }());
推荐
(function(){ 'use strict'; var a = 10, b = 10, i, c, d, x; function f() { } for(i = 0; i < 10; i++) { c = a * b * i; } d = 100; x = function() { return d * d; }; console.log(x()); }());
把赋值尽可能写在变量申明中。
不推荐
var a, b, c; a = 10; b = 10; c = 100;
推荐
var a = 10, b = 10, c = 100;
使用匈牙利命名法来命名变量。
老是使用 ===
精确的比较操做符,避免在判断的过程当中,由 javascript 的强制类型转换所形成的困扰。
若是你使用 ===
操做符,那比较的双方必须是同一类型为前提的条件下才会有效。
在只使用 ==
的状况下,javascript 所带来的强制类型转换使得判断结果跟踪变得复杂,下面的例子能够看出这样的结果有多怪了:
(function(){ 'use strict'; console.log('0' == 0); // true console.log('' == false); // true console.log('1' == true); // true console.log(null == undefined); // true var x = { valueOf: function() { return 'X'; } }; console.log(x == 'X'); }());
当咱们在一个 if 条件语句中使用变量或表达式时,会作真假判断。if(a == true)
是不一样于 if(a)
的。后者的判断比较特殊,咱们称其为真假判断。这种判断会经过特殊的操做将其转换为 true 或 false,下列表达式通通返回 false:false
,0
,undefined
,null
,NaN
,''
(空字符串)。这种真假判断在咱们只求结果而不关心过程的状况下,很是的有帮助。
如下示例展现了真假判断是如何工做的:
(function(){ 'use strict'; function logTruthyFalsy(expr) { if(expr) { console.log('truthy'); } else { console.log('falsy'); } } logTruthyFalsy(true); // truthy logTruthyFalsy(1); // truthy logTruthyFalsy({}); // truthy logTruthyFalsy([]); // truthy logTruthyFalsy('0'); // truthy logTruthyFalsy(false); // falsy logTruthyFalsy(0); // falsy logTruthyFalsy(undefined); // falsy logTruthyFalsy(null); // falsy logTruthyFalsy(NaN); // falsy logTruthyFalsy(''); // falsy }());
逻辑操做符 ||
和 &&
也可被用来返回布尔值。若是操做对象为非布尔对象,那每一个表达式将会被自左向右地作真假判断。基于此操做,最终总有一个表达式被返回回来。这在变量赋值时,是能够用来简化你的代码的。
不推荐
if(!x) { if(!y) { x = 1; } else { x = y; } }
推荐
x = x || y || 1;
这一小技巧常常用来给方法设定默认的参数。
(function(){ 'use strict'; function multiply(a, b) { a = a || 1; b = b || 1; console.log('Result ' + a * b); } multiply(); // Result 1 multiply(10); // Result 10 multiply(3, NaN); // Result 3 multiply(9, 5); // Result 45 }());
老是使用分号,由于隐式的代码嵌套会引起难以察觉的问题。固然咱们更要从根本上来杜绝这些问题。
javascript 中语句要以分号结束,不然它将会继续执行下去,无论换不换行。
澄清:分号与函数
分号须要用在表达式的结尾,而并不是函数声明的结尾。区分它们最好的例子是:
var foo = function() { return true; }; function foo() { return true; }
嵌套函数是很是有用的,好比用在持续建立和隐藏辅助函数的任务中。你能够很是自由随意地使用它们。
切勿在语句块内声明函数,在 ECMAScript5 的严格模式下,这是不合法的。函数声明应该在定义域的顶层。但在语句块内可将函数申明转化为函数表达式赋值给变量。
不推荐
if (x) { function foo() {} }
推荐
if (x) { var foo = function() {}; }
基本上你没法避免出现异常,特别是在作大型开发时(使用应用开发框架等等)。
在没有自定义异常的状况下,从有返回值的函数中返回错误信息必定很是的棘手,更别提多不优雅了。很差的解决方案包括了传第一个引用类型来接纳错误信息,或老是返回一个对象列表,其中包含着可能的错误对象。以上方式基本上是比较简陋的异常处理方式。适时可作自定义异常处理。
在复杂的环境中,你能够考虑抛出对象而不只仅是字符串(默认的抛出值)。
if(name === undefined) { throw { name: 'System Error', message: 'A name should always be specified!' } }
老是优先考虑使用标准特性。为了最大限度地保证扩展性与兼容性,老是首选标准的特性,而不是非标准的特性(例如:首选 string.charAt(3)
而不是 string[3]
;首选 DOM 的操做方法来得到元素引用,而不是某一应用特定的快捷方法)。
若是你想在 javascript 中继承你的对象,请遵循一个简易的模式来建立此继承。若是你预计你会赶上复杂对象的继承,那能够考虑采用一个继承库,好比 Proto.js by Axel Rauschmayer。
简易继承请用如下方式:
(function(){ 'use strict'; // Constructor function function Apple(name) { this.name = name; } // Defining a method of apple Apple.prototype.eat = function() { console.log('Eating ' + this.name); }; // Constructor function function GrannySmithApple() { // Invoking parent constructor Apple.prototype.constructor.call(this, 'Granny Smith'); } // Set parent prototype while creating a copy with Object.create GrannySmithApple.prototype = Object.create(Apple.prototype); // Set constructor to the sub type, otherwise points to Apple GrannySmithApple.prototype.constructor = GrannySmithApple; // Calling a super method GrannySmithApple.prototype.eat = function() { // Be sure to apply it onto our current object with call(this) Apple.prototype.eat.call(this); console.log('Poor Grany Smith'); }; // Instantiation var apple = new Apple('Test Apple'); var grannyApple = new GrannySmithApple(); console.log(apple.name); // Test Apple console.log(grannyApple.name); // Granny Smith // Instance checks console.log(apple instanceof Apple); // true console.log(apple instanceof GrannySmithApple); // false console.log(grannyApple instanceof Apple); // true console.log(grannyApple instanceof GrannySmithApple); // true // Calling method that calls super method grannyApple.eat(); // Eating Granny Smith\nPoor Grany Smith }());
闭包的建立也许是 js 最有用也是最易被忽略的能力了。关于闭包如何工做的合理解释。
在简单的循环语句中加入函数是很是容易造成闭包而带来隐患的。下面的例子就是一个典型的陷阱:
不推荐
(function(w){ 'use strict'; var numbers = [1, 2, 3], i; for(i = 0; i < numbers.length; i++) { w.setTimeout(function() { w.alert('Index ' + i + ' with number ' + numbers[i]); }, 0); } }(window));
接下来的改进虽然已经解决了上述例子中的问题或 bug,但仍是违反了不在循环中建立函数或闭包的原则。
不推荐
(function(w){ 'use strict'; var numbers = [1, 2, 3], i; for(i = 0; i < numbers.length; i++) { (function(index, number){ w.setTimeout(function() { w.alert('Index ' + index + ' with number ' + number); }, 0); }(i, numbers[i])); } }(window));
接下来的改进已解决问题,并且也遵循了规范。但是,你会发现看上去彷佛过于复杂繁冗了,应该会有更好的解决方案吧。
不彻底推荐
(function(w){ 'use strict'; var numbers = [1, 2, 3], i; function alertIndexWithNumber(index, number) { return function() { w.alert('Index ' + index + ' with number ' + number); }; } for(i = 0; i < numbers.length; i++) { w.setTimeout(alertIndexWithNumber(i, numbers[i]), 0); } }(window));
将循环语句转换为函数执行的方式问题能获得立马解决,每一次循环都会对应地建立一次闭包。函数式的风格更加值得推荐,并且看上去也更加地天然和可预料。
推荐
(function(w){ 'use strict'; var numbers = [1, 2, 3]; numbers.forEach(function(number, index) { w.setTimeout(function() { w.alert('Index ' + index + ' with number ' + number); }, 0); }); }(window));
eval()
不但混淆语境还很危险,总会有比这更好、更清晰、更安全的另外一种方案来写你的代码,所以尽可能不要使用 eval()
函数。
只在对象构造器、方法和在设定的闭包中使用 this
关键字。this
的语义在此有些误导。它时而指向全局对象(大多数时),时而指向调用者的定义域(在 eval
中),时而指向 DOM 树中的某一节点(当用事件处理绑定到 html 属性上时),时而指向一个新建立的对象(在构造器中),还时而指向其它的一些对象(若是函数被 call()
和 apply()
执行和调用时)。
正由于它是如此容易地被搞错,请限制它的使用场景:
建议使用 ECMAScript5 中新增的语法和函数。这将简化你的程序,并让你的代码更加灵活和可复用。
用 ECMAScript5 的迭代方法来迭代数组。使用 Array.forEach
或者若是你要在特殊场合下中断迭代,那就用 Array.every
。
(function(){ 'use strict'; [1, 2, 3, 4, 5].every(function(element, index, arr) { console.log(element + ' at index ' + index + ' in array ' + arr); if(index !== 5) { return true; } }); var obj = { a: 'A', b: 'B', 'c-d-e': 'CDE' }; Object.keys(obj).forEach(function(element, index, arr) { console.log('Key ' + element + ' has value ' + obj[element]); }); }());
用数组和对象字面量来代替数组和对象构造器。数组构造器很容易让人在它的参数上犯错。
不推荐
var a1 = new Array(x1, x2, x3); var a2 = new Array(x1, x2); var a3 = new Array(x1); var a4 = new Array();
正因如此,若是将代码传参从两个变为一个,那数组颇有可能发生意料不到的长度变化。为避免此类怪异情况,请老是采用更多可读的数组字面量。
推荐
var a = [x1, x2, x3]; var a2 = [x1, x2]; var a3 = [x1]; var a4 = [];
对象构造器不会有相似的问题,可是为了可读性和统一性,咱们应该使用对象字面量。
不推荐
var o = new Object(); var o2 = new Object(); o2.a = 0; o2.b = 1; o2.c = 2; o2['strange key'] = 3;
推荐
var o = {}; var o2 = { a: 0, b: 1, c: 2, 'strange key': 3 };
修改内建的诸如 Object.prototype
和 Array.prototype
是被严厉禁止的。修改其它的内建对象好比 Function.prototype
,虽危害没那么大,但始终仍是会致使在开发过程当中难以 debug 的问题,应当也要避免。
你能够经过自定义 toString()
来控制对象字符串化。这很好,但你必须保证你的方法老是成功并不会有其它反作用。若是你的方法达不到这样的标准,那将会引起严重的问题。若是 toString()
调用了一个方法,这个方法作了一个断言,当断言失败,它可能会输出它所在对象的名称,固然对象也须要调用 toString()
。
通常在语法和语义上真正须要时才谨慎地使用圆括号。不要用在一元操做符上,例如 delete
,typeof
和 void
,或在关键字以后,例如 return
,throw
,case
,new
等。
统一使用单引号(‘’),不使用双引号(“”)。这在建立 html 字符串很是有好处:
var msg = 'This is some HTML <div class="makes-sense"></div>';
用三元操做符分配或返回语句。在比较简单的状况下使用,避免在复杂的状况下使用。没人愿意用 10 行三元操做符把本身的脑子绕晕。
不推荐
if(x === 10) { return 'valid'; } else { return 'invalid'; }
推荐
return x === 10 ? 'valid' : 'invalid';