45个有用的JavaScript技巧

时间:2023-03-25 17:22:26

众所周知,JavaScript是世界上最流行的变成语言,不管是web网页,手机APP(例如PhoneGap或Appcelerator),还是服务器端(例如NodeJS或Wakanda)还有许多其他的实现。JavaScript也是许多新入门编程的开发人员的首先语言,因为他它不仅可以在web浏览器中显示警告框也能控制一个机器人(使用nodebot或nodruino).那些掌握JavaScript并且能编写有严谨性高性能的代码的开发人员已经成为就业市场最抢手的了。

在这篇文章中,我将分享一组JavaScript提示、技巧和最佳实践,这些应该被所有JavaScript开发人员认识到。不管他们是关于浏览器/引擎或SSJS(服务器端JavaScript)开发人员。

注意,本文中的代码片段在最新的Google Chrome版本测试,它使用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, ''(empty string) 均是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 : 一个JavaScript一元运算符。用于返回一个变量的基本类型。typeof null 将会返回"object", 其他的Object类型(包括Array, Date, 还有其他对象类型)也会返回"object".
  • constructor : 返回内部原型属性的属性(呵呵,翻译的好蹩脚哦,原文是is a property of the internal prototype property),可以通过代码覆盖。
  • instanceof : JavaScript操作符。 它用来判断一个变量是否是某个对象的实例。如果是返回true,否则返回false。
var arr = ["a", "b", "c"];
typeof arr; //object
arr instanceof Array; //true
arr.constructor(); //[]

7. 创建闭包(Self-calling)函数

这经常被称为自我调用匿名函数或者即时执行函数表达式(Immediately Invoked Function Expression(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. 在一个特定的范围内得到随机数

这段代码很有用,比如当你试图根据生成的假数据测试项目时,如工资之间的最大值和最小值。

var x = Math.floor(Math.random()*(max-min+1))+min;

10. 生成一组数字从0到最大值的数组

var numbersArray = [], max = 100;
for(var i = 1;numbersArray.push(i++)<max;); //numbers = [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;})

更好的选择可以通过代码来实现随机排序顺序(例如:Fisher–Yates 洗牌), 然后使用JavaScript原生的sort函数。更多细节可以查看这个讨论

13. 字符串trim函数

在Java,c#, PHP还有其他函数都有经典的trim函数。trim函数时移除字符串的空格,但是JavaScript没有trim函数,所以我们可以把它添加到String对象中。

String.prototype.trim = function(){return this.replace(/^\s+|\s+$/g, "");}; 

原生的trim()函数在最新的JavaScript引擎中已经实现了。

14. 将一个函数附加到另一个函数中。

ar 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] */

15. 将参数对象转换成数组

var argArray = Array.prototype.slice.call(arguments);

16. 验证给定的参数是否是数字

function isNumber(n){
return !isNaN(parseFloat(n)&&isFinite(n));
}

17. 验证给定的参数是否是数组

function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}

记住,如果toString()方法被重写了,你用这段代码将有可能不会得到期望的值。

或者使用。。。

Array.isArray(obj);   // its a new Array method

你也可以使用instanceof,如果你没有使用多个框架。然而如果在多种环境下,会得到错误的值。

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

18. 获取数字数组中最大值或最小值

var  numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];
var maxInNumbers = Math.max.apply(Math, numbers);
var minInNumbers = Math.min.apply(Math, numbers);

19. 置空数组

var myArray = [12, 222, 1000];
myArray.length = 0; // myArray will be equal to []

20. 不要使用删除(delete)或移除(remove)数组中元素

使用splice而不是使用delete来删除数组元素。使用delete将元素替换成undefined而不是从数组中移除元素。

替换。。。

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 变成 [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 变成 [12, 548, "a", 5478, "foo", 8852, undefined × 1, "Doe", 2154, 119] */

delete方法应该用来删除对象属性。

21. 使用数组长度截断数组

和之前置空数组的例子类似,我们使用数组的长度属性来截断数组。

var myArray = [12 , 222 , 1000 , 124 , 98 , 10];
myArray.length = 4; //myArray变成[12, 222, 1000, 124];

作为奖励,如果你设置数组的长度是一个长度大于数组长度的值,那么数组的长度将会改变并且新添加的元素值为undefined。数组长度不是一个可读的属性。

myArray.length = 10; // 新的数组长度是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();

逻辑或也可以用来设置函数参数的默认值。

function doSomething(arg1){
arg1 = arg1 || 10; //arg1 将设为10,如果没有设置的话。
}

23. 使用map()函数来遍历数组元素

var squares = [1,2,3,4].map(function (val) {
return val * val;
});
// squares 变成 [1, 4, 9, 16]

24. 将数组舍入到N个小数的位置

var num =2.443242342;
num = num.toFixed(4); // num 变成 2.4432

记住:toFixed()函数将返回的是一个数组而不是数字。

25. 浮点运算问题

0.1+0.2 === 0.3  // is false
9007199254740992 + 1 // 等于 9007199254740992
9007199254740992 + 2 // 等于 9007199254740994

发生了什么?0.1 +0.2 等于 0.30000000000000004. 你需要知道的是根据IEEE754标准所以JavaScript数值都是内部为64为二进制的浮点数。更多的解释,可以看看这篇博客.

你可以使用toFixed()和 toPrecision()来解决这个问题。

26. 当使用for-in遍历时,检查对象的属性

代码用来避免遍历循环对象原型的属性。

for(var name in obj){
if(object.hasOwnProperty(name)){
// do something with name
}
}

27. 逗号操作符

var a =0;
var b = (a++, 99);
cosole.log(a); // a will be equal to 1
cosole.log(b); // b is equal to 99

28. 缓存变量需要计算或查询

在jQuery选择器中,我们可以缓存DOM元素

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(undefined); // false
isFinite(); // false
isFinite(null); // true !!!

30.  避免负数索引数组

var numbersArray = [1,2,3,4,5];
var from = numbersArray.indexOf("foo") ; // from等于 -1
numbersArray.splice(from,2); // 返回 [5]

确保传递给splice的参数是非负数。

31. 序列化和反序列化(使用JSON)

var person = {name :'Saad', age : 26, department : {ID : 15, name : "R&D"} };
var stringFromPerson = JSON.stringify(person);
/* stringFromPerson 变成 "{"name":"Saad","age":26,"department":{"ID":15,"name":"R&D"}}" */
var personFromString = JSON.parse(stringFromPerson);
/* personFromString 等于 person 对象 */

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];
}

作为奖励,i和len的实例化只执行一次,因为它在for循环中是第一个声明。这个比使用下面的快多了。

for (var i = 0; i < arrayNumbers.length; i++)

为什么呢?数组arrayNumbers的长度在每次循环迭代时都有重新计算。

请注意:在每次迭代时重新计算长度的问题在最新的JavaScript引擎已经解决了。

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种情况就避免使用它。

37. 用数值范围使用switch/case语句。

用数值范围使用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. 创建一个对象的原型是一个给定的对象

创建一个对象,它的原型是下面给定的参数。。。

function clone(object) {
function OneShotConstructor(){};
OneShotConstructor.prototype= object;
return new OneShotConstructor();
}
clone(Array).prototype ; // []

39. 一个HTML escape函数

function escapeHTML(text) {
var replacements= {"<": "&lt;", ">": "&gt;","&": "&amp;", "\"": "&quot;"};
return text.replace(/[<>&"]/g, function(character) {
return replacements[character];
});
}

40. 在一个循环中避免使用try-catch-finally

try-catch-finally构造函数在运行时在当前链中创建一个新的变量,每次捕获异常时catch子句被执行对象赋给一个变量。

不要这样使用。。。

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设置超时

如果XHR花费很长时间,你可以终止连接(例如,由于network问题),通过以XHR调用方式使用setTimeout()。

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 /* 一分钟后超时 */ );
xhr.open('GET', url, true); xhr.send();

作为奖励,通常你应该完全避免XHR同步调用。

42. 处理WebSocket超时

一般在建立WebSocket连接时,服务器会在空闲后30秒暂停你的连接。防火墙也可能在经过一段空闲时间暂停你的连接。

为了处理超时的问题,你可以定期向服务器发送空消息。做到这点,需要把这两个函数添加到你的代码里:一个保持连接,一个取消连接。使用这种方法,你将控制超时。

添加timerID...

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

For example, instead of using…

var min = Math.min(a,b);
A.push(v);
…use… var min = a < b ? a : b;
A[A.length] = v;

44. 不要忘记在编程时使用代码美化器。 在正式使用前使用JSLint 和minification (JSMin, 例如) .

45. JavaScript是极好的:Best Resources To Learn JavaScript

结论

我知道还有很多技巧,窍门和最佳实践,所以如果你有任何添加的或那些我已经共享如果您有任何反馈或修正,请在评论中添加。

参考文献

在本文中,我使用我自己的代码片段,还有一些片段的灵感来自其他文章和论坛。

译文:45 Useful JavaScript Tips, Tricks and Best Practices

翻译不对的地方,欢迎指正。