原文:45 Useful JavaScript Tips, Tricks and Best Practices
译文:45个有用的JavaScript技巧,窍门和最佳实践
译者:dwqs
在这篇文章中,我将分享一些JavaScript常用的技巧,窍门和最佳实践。不管JavaScript开发者是使用在浏览器/引擎上或者服务器端(SSJS——Service Side JavaScript)JavaScript解释器上,这些他们都是应该知晓的。
需要注意的是,文章中的代码片段均是在最新的Google Chrome(版本号30)上测试过,它使用V8 JavaScript引擎(V8 3.20.17.15)。
1、第一个给变量分配值时不要忘了var关键字。
分配值给未定义的变量将是该变量自动成为全局变量,应该避免全局变量。
2、使用===而不是==
==(或!=)操作符在需要时会自动转换变量类型,而===(或!==)则不会执行类型转换。在进行值和类型比较的时候,应首先考虑使用===
[10] === 10 // is false
[10] == 10 // is true
'10' == 10 // is true
'10' === 10 // is false
[] == 0 // is true
[] === 0 // is false
'' == false // is true but true == "a" is false
'' === false // is false
3、undefined、null、0、false、NaN、’’(空字符串)都被视为false。
4、在行尾使用分号
在行尾使用分号是很好的实践。如果你忘了,也不会得到警告,因为大多数情况下JavaScript解释器会插入分号的。更多关于你为什么要使用分号的细节,可以看看此文:http://davidwalsh.name/javascript-semicolons.
5、创建对象构造函数
function Person(firstName, lastName){ this.firstName = firstName; this.lastName = lastName; } var Saad = new Person("Saad", "Mousliki");
6、小心使用typeof、instanceof和constructor
- typeof:不要忘了typeof null返回object,而大多数对象,typeof(Array, Date, and others)也将返回object
- constructor:内部原型属性,可以被覆盖(可以参考:又谈原型对象)
- instanceof:JavaScript的一个用于检查构造函数的原型链的操作符
var arr = ["a", "b", "c"]; typeof arr; // return "object" arr instanceof Array // true arr.constructor(); //[]
7、创建一个自执行函数
这个经常被称为自我调用的匿名函数或者立即调用的函数表达式(IIFE)。当你创建它时,它会自动执行的函数。
(function(){
// some private code that will be executed automatically
})();
(function(a,b){
var result = a+b;
return result;
})(10,20)
8、随机获取一个数组项
var items = [12, 548 , 'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' , 2145 , 119]; var randomItem = items[Math.floor(Math.random() * items.length)];
9、在特定范围内获取一个随机数
x = Math.floor(Math.random() * (max - min + 1)) + min;
10、从数字0到最大值之间获取一个数字数组
var numbersArray = [] , max = 100; for( var i=1; numbersArray.push(i++) < max;); // numbers = [0,1,2,3 ... 100]
11、生成一个随机字符串
function generateRandomAlphaNum(len) { var rdmstring = ""; for( ; rdmString.length < len; rdmString += Math.random().toString(36).substr(2)); return rdmString.substr(0, len); }
12、打乱一个数字数组的元素顺序
var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];
numbers = numbers.sort(function(){ return Math.random() – 0.5});
/* the array numbers will be equal for example to
[120, 5, 228, -215, 400, 458, -85411, 122205] */
String.prototype.trim = function(){return this.replace(/^s+|s+$/g, "");};
var array1 = [12 , "foo" , {name: "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 will be equal to [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
var argArray = Array.prototype.slice.call(arguments);
function isNumber(n){ return !isNaN(parseFloat(n)) && isFinite(n); }
function isArray(obj){ return Object.prototype.toString.call(obj) === '[object Array]' ; }
Array.isArray(obj); // 这是一个新的array的方法
var myFrame = document.createElement('iframe'); document.body.appendChild(myFrame); var myArray = window.frames[window.frames.length-1].Array; var arr = new myArray(a,b,10); // [a,b,10] // instanceof will not work correctly, myArray loses his constructor // constructor is not shared between frames arr instanceof Array; // false
var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411]; var maxInNumbers = Math.max.apply(Math, numbers); var minInNumbers = Math.min.apply(Math, numbers);
var myArray = [12 , 222 , 1000 ]; myArray.length = 0; // myArray will be equal to [].
var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; // return 11 delete items[3]; // return true items.length; // return 11 /* items will be equal to [12, 548, "a", undefined × 1, 5478, "foo", 8852, undefined × 1, "Doe", 2154, 119] */
而是用下列的方式
var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; // return 11 items.splice(3,1) ; items.length; // return 10 /* items will be equal to [12, 548, "a", 5478, "foo", 8852, undefined × 1, "Doe", 2154,119] */
应该用delete来删除一个对象的属性
var myArray = [12 , 222 , 1000 , 124 , 98 , 10 ]; myArray.length = 4; // myArray will be equal to [12 , 222 , 1000 , 124].
此外,如果你将一个数组的 length 设置成一个比现在大的值,那么这个数组的长度就会被改变,会增加新的 undefined 的项补上。 数组的 length 属性是可读写的。
myArray.length = 10; // the new array length is 10
myArray[myArray.length - 1] ; // undefined
22、在条件中使用逻辑运算符:AND、OR
var foo = 10; foo == 10 && doSomething(); // 等价于 if (foo == 10) doSomething(); foo == 5 || doSomething(); // 等价于 if (foo != 5) doSomething();
OR可以用于给函数设置默认参数
function doSomething(arg1){ Arg1 = arg1 || 10; // 如果arg1没有被设置的话,Arg1将被默认设成10 }
23、使用 map() 方法来遍历一个数组里的项
var squares = [1,2,3,4].map(function (val) { return val * val; }); // squares will be equal to [1, 4, 9, 16]
24、保留小数位
var num =2.443242342; num = num.toFixed(4); // num will be equal to 2.4432
25、浮点数问题
0.1 + 0.2 === 0.3 // is false
9007199254740992 + 1 // is equal to 9007199254740992
9007199254740992 + 2 // is equal to 9007199254740994
为什么是false?0.1+0.2等于0.30000000000000004。别忘了,所有的JavaScript数字在内部都是以64位二进制表示的浮点数,符合IEEE 754标准。更多的介绍,可以阅读this blog post.你可以使用 toFixed() 和 toPrecision() 方法解决这个问题。
26、使用for-in遍历一个对象内部属性的时候注意检查
下面的代码片段能够避免在遍历一个对象属性的时候访问原型的属性
for (var name in object) { if (object.hasOwnProperty(name)) { // do something with name } }
27、逗号运算符
var a = 0; var b = ( a++, 99 ); console.log(a); // a will be equal to 1 console.log(b); // b is equal to 99
28、缓存需要查询或计算的变量
var navright = document.querySelector('#right'); var navleft = document.querySelector('#left'); var navup = document.querySelector('#up'); var navdown = document.querySelector('#down');
29、将参数传给isFinite()之前要验证
isFinite(0/0) ; // false
isFinite("foo"); // false
isFinite("10"); // true
isFinite(10); // true
isFinite(undifined); // false
isFinite(); // false
isFinite(null); // true !!!
30、避免数组中的负索引
var numbersArray = [1,2,3,4,5]; var from = numbersArray.indexOf("foo") ; // from is equal to -1 numbersArray.splice(from,2); // will return [5]
var person = {name :'Saad', age : 26, department : {ID : 15, name : "R&D"} }; var stringFromPerson = JSON.stringify(person); /* stringFromPerson is equal to "{"name":"Saad","age":26,"department":{"ID":15,"name":"R&D"}}" */ var personFromString = JSON.parse(stringFromPerson); /* personFromString is equal to person object */
32、避免使用eval()和Function()构造函数
使用 eval 和 Function 构造函数是非常昂贵的操作,因为每次他们都会调用脚本引擎将源代码转换成可执行代码。
var func1 = new Function(functionCode); var func2 = eval(functionCode);
33、避免使用with
使用 with() 会插入一个全局变量。因此,同名的变量会被覆盖值而引起混淆。
34、避免用for-in循环一个数组
不要这么使用
var sum = 0; for (var i in arrayNumbers) { sum += arrayNumbers[i]; }
有更好的方式
var sum = 0; for (var i = 0, len = arrayNumbers.length; i < len; i++) { sum += arrayNumbers[i]; }
35、传函数给setTimeout()和setInterval()而不是字符串
如果你将字符串传递给 setTimeout() 或者 setInterval(),这个字符串将被如使用 eval 一样被解析,这个是非常耗时的。不要使用:
setInterval('doSomethingPeriodically()', 1000);
setTimeOut('doSomethingAfterFiveSeconds()', 5000)
建议使用
setInterval(doSomethingPeriodically, 1000);
setTimeOut(doSomethingAfterFiveSeconds, 5000);
36、使用 switch/case 语句,而不是一系列的 if/else
在2中情况以上的时候,使用 switch/case 更高效,而且更优雅(更易于组织代码)。但在判断的情况超过10种的时候不要使用 switch/case。
37、判断数值范围时使用 switch/case
function getCategory(age) { var category = ""; switch (true) { case isNaN(age): category = "not an age"; break; case (age >= 50): category = "Old"; break; case (age <= 20): category = "Baby"; break; default: category = "Young"; break; }; return category; } getCategory(5); // will return "Baby"
38、为创建的对象指定prototype
写一个函数来创建一个以指定参数作为prototype的对象是有可能:
function clone(object) { function OneShotConstructor(){}; OneShotConstructor.prototype= object; return new OneShotConstructor(); } clone(Array).prototype ; // []
39、HTML转义函数
function escapeHTML(text) { var replacements= {"<": "<", ">": ">","&": "&", """: """}; return text.replace(/[<>&"]/g, function(character) { return replacements[character]; }); }
40、避免在循环内部使用try-catch-finally
在运行时,每次当 catch 从句被执行的时候,被捕获的异常对象会赋值给一个变量,而在 try-catch-finally 结构中,每次都会新建这个变量。不要这样用:
var object = ['foo', 'bar'], i; for (i = 0, len = object.length; i <len; i++) { try { // do something that throws an exception } catch (e) { // handle exception } }
而是这样子用
var object = ['foo', 'bar'], i; try { for (i = 0, len = object.length; i <len; i++) { // do something that throws an exception } } catch (e) { // handle exception }
41、给XMLHttpRequests设置超时
var xhr = new XMLHttpRequest (); xhr.onreadystatechange = function () { if (this.readyState == 4) { clearTimeout(timeout); // do something with response data } } var timeout = setTimeout( function () { xhr.abort(); // call error callback }, 60*1000 /* timeout after a minute */ ); xhr.open('GET', url, true); xhr.send();
42、处理WebSocket超时
通常当WebSocket连接建立的时候,如果30秒内没有活动,服务器将暂停连接,同样的道理,防火墙也会暂停连接。
为了防止超时的问题,你可能需要间隔地向服务器端发送空消息。要这样做的话,你可以在你的代码里添加下面的两个函数:一个用来保持连接,另一个用来取消连接的保持。通过这个技巧,你可以控制超时的问题。
var timerID = 0; function keepAlive() { var timeout = 15000; if (webSocket.readyState == webSocket.OPEN) { webSocket.send(''); } timerId = setTimeout(keepAlive, timeout); } function cancelKeepAlive() { if (timerId) { cancelTimeout(timerId); } }
keepAlive()方法应该被添加在webSOcket连接的 onOpen() 方法的最后,而 cancelKeepAlive() 添加在 onClose() 方法的最后。
43、记住:原始运算符始终比函数调用要搞笑。使用 VanillaJS。
例如,不要使用这个
var min = Math.min(a,b); A.push(v);
而是用这个代替
var min = a < b ? a b; A[A.length] = v;
44、当编码的时候,要保持代码整洁。上线之前对代码进行压缩。
45、JavaScript是极好的:Best Resources To Learn JavaScript
- Code Academy JavaScript tracks: http://www.codecademy.com/tracks/javascript
- Eloquent JavaScript by Marjin Haverbeke: http://eloquentjavascript.net/
- Advanced JavaScript by John Resig: http://ejohn.org/apps/learn/