0
0

45个实用的JavaScript技巧、窍门和最佳实践

鸟窝 发表于 2015年07月31日 21:28 | Hits: 2266
Tag: javascript | 前端开发

如你所知,JavaScript是世上编程语言的Number One (编者按: 原文如此), 用来编写Web和移动混合应用(比如PhoneGap或者Appcelerator), 也可以编写服务器端的程序(比如NodeJS或者Wakanda),并且拥有很多其他的实现。 它也是很多新手进入编程世界的启蒙语言,因为它不但可以在浏览器上显示一个简单的alert信息,而且还可以用来控制一个机器人(使用nodebot,或者nodruino)。掌握JavaScript并且能够写出规范并性能高效代码的开发人员,已经成为人才市场上的猎寻目标。

在这篇文章中,Saad Mousliki将分享一组JavaScript的技巧、窍门和最佳实践,这些都是JavaScript程序员应该知晓的,不管他们是使用在浏览器/引擎上,还是服务器端(SSJS Service Side JavaScript)JavaScript解释器上。

需要注意的是,这篇文章中的代码片段都在最新的Google Chrome(版本号30)上测试过,它使用V8 JavaScript引擎(V8 3.20.17.15)

英文原址:45 Useful JavaScript Tips, Tricks and Best Practices

列表

第一次给变量赋值时莫忘使用var关键字.

给一个未声明的变量赋值会自动产生一个全局的变量。 避免全局变量。

使用 === 而不是 ==

操作符 == (或者 !=) 执行自动的类型转换. 操作符 === (或 !==) 不会执行任何转换,它会比较值和类型, 并且被认为比 == 更快.

12345678
[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

undefined, null, 0, false, NaN, '' (empty string) 都是false.

行尾使用分号

行尾使用分号是一个好的习惯。 尽管如果忘了加也不会被警告, 因为大部分情况 JavaScript解析器会自动加上。 这篇文章描述了为什么要加分号的细节:http://davidwalsh.name/javascript-semicolons.

创建对象的构造函数

12345
function Person(firstName, lastName){    this.firstName =  firstName;    this.lastName = lastName;        } var Saad = new Person("Saad", "Mousliki");

使用 typeof, instanceof 和 constructor要万分小心.

  • typeof : 一个JavaScript的一元操作符, 返回代表一个变量的原始primitive类型的字符串。 别忘了typeof null 返回 “object”, 并且大部分的object类型 (Array, Date, and others) 也返回 “object”.
  • constructor : 一个内部prototype属性, 可以被覆盖。
  • instanceof : 另一个JavaScript运算符, 用来在所有的prototype链的constructor。 如果找到返回true,否则false.
1234
var arr = ["a", "b", "c"];typeof arr;   // return "object" arr  instanceof Array // truearr.constructor();  //[]

创建一个自调用的函数 Self-calling Function

常被称作自调用匿名函数Self-Invoked Anonymous Function或者立即调用函数表达式Immediately Invoked Function Expression (IIFE). 它是这样一个函数:当创建时会自动执行。格式如下:

1234567
(function(){    // some private code that will be executed automatically})();  (function(a,b){    var result = a+b;    return result;})(10,20)

从数组中随机选取一个元素

123
var items = [12, 548 , 'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' , 2145 , 119];var  randomItem = items[Math.floor(Math.random() * items.length)];

得到一个特定范围的随机值

This code snippet can be useful when trying to generate fake data for testing purposes, such as a salary between min and max.

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

生成一个[0,max]范围的数组

12
var numbersArray = [] , max = 100;for( var i=1; numbersArray.push(i++) < max;);  // numbers = [1,2,3 ... 100]

javascript

生成随机字符/数字的数组

123456
function generateRandomAlphaNum(len) {    var rdmString = "";    for( ; rdmString.length < len; rdmString  += Math.random().toString(36).substr(2));    return  rdmString.substr(0, len);}

打乱数组

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

A better option could be to implement a random sort order by code (e.g. : Fisher-Yates shuffle), than using the native sort JavaScript function. For more details take a look to this discussion.

字符串的trim函数

The classic trim function of Java, C#, PHP and many other language that remove whitespace from a string doesn’t exist in JavaScript, so we could add it to the String object.

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

A native implementation of the trim() function is available in the recent JavaScript engines.

将一个数组附加到另外一个数组上: append函数

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

将arguments 对象转为数组

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

校验参数是否为数字

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

读者提供了另外一个技巧

123
function isNumber(n) {return n === +n;}

校验参数是否为数组

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

Note that if the toString() method is overridden, you will not get the expected result using this trick.

Or use…

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

You could also use instanceof if you are not working with multiple frames. However, if you have many contexts, you will get a wrong result.

123456789
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 framesarr instanceof Array; // false

得到数组的最大或者最小元素

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

清空数组

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

不要使用delete方法删除数组的元素

Use splice instead of using delete to delete an item from an array. Using delete replaces the item with undefined instead of the removing it from the array.

Instead of…

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

Use…

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

The delete method should be used to delete an object property.

设置length实现截短数组

Like the previous example of emptying an array, we truncate it using the length property.

12
var myArray = [12 , 222 , 1000 , 124 , 98 , 10 ];  myArray.length = 4; // myArray will be equal to [12 , 222 , 1000 , 124].

As a bonus, if you set the array length to a higher value, the length will be changed and new items will be added with undefined as a value. The array length is not a read only property.

12
myArray.length = 10; // the new array length is 10 myArray[myArray.length - 1] ; // undefined

使用逻辑 AND/ OR 作为条件判断

123
var foo = 10;  foo == 10 && doSomething(); // is the same thing as if (foo == 10) doSomething(); foo == 5 || doSomething(); // is the same thing as if (foo != 5) doSomething();

The logical OR could also be used to set a default value for function argument.

123
function doSomething(arg1){     arg1 = arg1 || 10; // arg1 will have 10 as a default value if it’s not already set}

使用map()函数遍历数组

1234
var squares = [1,2,3,4].map(function (val) {      return val * val;  }); // squares will be equal to [1, 4, 9, 16]

四舍五入, 保留 N位小数

12
var num =2.443242342;num = num.toFixed(4);  // num will be equal to 2.4432

NOTE : the toFixed() function returns a string and not a number.

浮点数问题

123
0.1 + 0.2 === 0.3 // is false 9007199254740992 + 1 // is equal to 9007199254740992  9007199254740992 + 2 // is equal to 9007199254740994

Why does this happen? 0.1 +0.2 is equal to 0.30000000000000004. What you need to know is that all JavaScript numbers are floating points represented internally in 64 bit binary according to the IEEE 754 standard. For more explanation, take a look to this blog post.

You can use toFixed() and toPrecision() to resolve this problem.

使用for-in循环检查对象的属性时需要注意

This code snippet could be useful in order to avoid iterating through the properties from the object’s prototype.

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

逗号操作符

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

缓存需要查询或者计算的变量

In the case of a jQuery selector, we could cache the DOM element.

1234
var navright = document.querySelector('#right'); var navleft = document.querySelector('#left'); var navup = document.querySelector('#up'); var navdown = document.querySelector('#down');

传给isFinite()的参数需要校验

1234567
isFinite(0/0) ; // false isFinite("foo"); // false isFinite("10"); // true isFinite(10);   // true isFinite(undefined);  // false isFinite();   // false isFinite(null);  // true  !!!

避免数组的索引为负值

123
var numbersArray = [1,2,3,4,5]; var from = numbersArray.indexOf("foo") ;  // from is equal to -1 numbersArray.splice(from,2);    // will return [5]

Make sure that the arguments passed to splice are not negative.

JSON的序列化和反序列化

12345
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  */

避免使用 eval() 或者Function的构造函数

Use of eval or the Function constructor are expensive operations as each time they are called script engine must convert source code to executable code.

12
var func1 = new Function(functionCode);var func2 = eval(functionCode);

避免使用 with() (The good part)

Using with() inserts a variable at the global scope. Thus, if another variable has the same name it could cause confusion and overwrite the value.

避免使用for-in遍历数组

Instead of using…

1234
var sum = 0;  for (var i in arrayNumbers) {      sum += arrayNumbers[i];  }

…it’s better to use…

1234
var sum = 0;  for (var i = 0, len = arrayNumbers.length; i < len; i++) {      sum += arrayNumbers[i];  }

As a bonus, the instantiation of i and len is executed once because it’s in the first statement of the for loop. Thsi is faster than using…

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

Why? The length of the array arrayNumbers is recalculated every time the loop iterates.

NOTE : the issue of recalculating the length in each iteration was fixed in the latest JavaScript engines.

调用setTimeout() 和 setInterval()时传入函数而不是函数的字符串名字

If you pass a string into setTimeout() or setInterval(), the string will be evaluated the same way as with eval, which is slow. Instead of using…

12
setInterval('doSomethingPeriodically()', 1000);  setTimeout('doSomethingAfterFiveSeconds()', 5000);

…use…

12
setInterval(doSomethingPeriodically, 1000);  setTimeout(doSomethingAfterFiveSeconds, 5000);

使用 switch/case statement 而不是一堆的 if/else

Using switch/case is faster when there are more than 2 cases, and it is more elegant (better organized code). Avoid using it when you have more than 10 cases.

使用数字返回做switch/case 的条件判断

Using a switch/case statement with numeric ranges is possible with this trick.

12345678910111213141516171819
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"

为创建的对象指定prototype

It’s possible to write a function that creates an object whose prototype is the given argument like this…

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

HTML 转义函数

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

不要在循环内部使用try-catch-finally

The try-catch-finally construct creates a new variable in the current scope at runtime each time the catch clause is executed where the caught exception object is assigned to a variable.

Instead of using…

123456789
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      } }

…use…

123456789
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  }

为XMLHttpRequests设置超时

You could abort the connection if an XHR takes a long time (for example, due to a network issue), by using setTimeout() with the XHR call.

12345678910111213
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();

As a bonus, you should generally avoid synchronous XHR calls completely.

处理WebSocket 超时

Generally when a WebSocket connection is established, a server could time out your connection after 30 seconds of inactivity. The firewall could also time out the connection after a period of inactivity.

To deal with the timeout issue you could send an empty message to the server periodically. To do this, add these two functions to your code: one to keep alive the connection and the other one to cancel the keep alive. Using this trick, you’ll control the timeout.

Add a timerID…

12345678910111213
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);      }  }

The keepAlive() function should be added at the end of the onOpen() method of the webSocket connection and the cancelKeepAlive() at the end of the onClose() method.

牢记,原始运算符始终比函数调用要高效。使用VanillaJS

For example, instead of using…

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

编码时不要忘记使用代码美化工具. 发布前使用JSLint 和 minification (如JSMin).

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/

结尾

我(Saad Mousliki)知道还有许许多多的技巧窍门和最佳实践。 所以如果你有更多想增加的,或者针对以上条目的反馈和更正, 请添加注释。

参考

本文大大部分的代码都是我(Saad Mousliki)自己写的。 有部分的代码片段参考了一些其它文章或者论坛的帖子。

附加技巧

除了前面文章中提到的技巧, 我也在这篇文章列出收集的更多的JavaScript技巧

两个感叹号

一个元素转换为真的布尔值,一般用来判断某个元素是否存在,例如:

12345
1.!![] true; ![] false2.!!{} true; !{} false3.!!false false; !false true4.!!true true; !true false5.!!undefined false; !undefined true

双感叹号才能真正的将他转换成对应的Boolean值,第一个感叹号是将其转化成Boolean类型的值,但是这一操作得到的是其取反以后的值,在进行一次取反运算才能得到其对应真正的布尔值

把数字变字符串, 把字符串变数字

把s变数字:s = s - 0;或者s = +s;
把n变字符串:n = n + "";

把数组转换成CSV字符串

12345
var fruits = ['apple', 'peaches', 'oranges', 'mangoes'];  var str = fruits.valueOf();  //print str: apple,peaches,oranges,mangoes

如果不想逗号分隔:

12345
var fruits = ['apple', 'peaches', 'oranges', 'mangoes'];  var str = fruits.join("|");  //print str: apple|peaches|oranges|mangoes

转换CSV字符串为数组

使用split方法:

12345
var str = "apple, peaches, oranges, mangoes";  var fruitsArray = str.split(",");  //print fruitsArray[0]: apple

移除数组指定索引

123456789101112131415
function removeByIndex(arr, index) {    arr.splice(index, 1);} test = new Array();test[0] = 'Apple';test[1] = 'Ball';test[2] = 'Cat';test[3] = 'Dog'; alert("Array before removing elements: "+test); removeByIndex(test, 2); alert("Array after removing elements: "+test);

移除数组特定的值

假定数组的值不会重复。如果有重复且想删除所有的重复的值, 注释掉break.

1234567891011121314
function removeByValue(arr, val) {    for(var i=0; i<arr.length; i++) {        if(arr[i] === val) {            arr.splice(i, 1);            break;        }    }} var somearray = ["mon", "tue", "wed", "thur"] removeByValue(somearray, "tue"); //somearray will now have "mon", "wed", "thur"

下面的方法是为Array类增加removeByValue方法:

123456789101112131415
Array.prototype.removeByValue = function(val) {    for(var i=0; i<this.length; i++) {        if(this[i] == val) {            this.splice(i, 1);            break;        }    }}//.. var somearray = ["mon", "tue", "wed", "thur"] somearray.removeByValue("tue"); //somearray will now have "mon", "wed", "thur"

根据方法名调用方法

12345678
var strFun = "someFunction"; //Name of the function to be calledvar strParam = "this is the parameter"; //Parameters to be passed in function  //Create the functionvar fn = window[strFun];  //Call the functionfn(strParam);

检查Form 是否dirty

12345678910111213141516171819202122232425262728293031323334
/** * Determines if a form is dirty by comparing the current value of each element * with its default value. * * @param {Form} form the form to be checked. * @return {Boolean} <code>true</code> if the form is dirty, <code>false</code> *                   otherwise. */function formIsDirty(form) {  for (var i = 0; i < form.elements.length; i++) {    var element = form.elements[i];    var type = element.type;    if (type == "checkbox" || type == "radio") {      if (element.checked != element.defaultChecked) {        return true;      }    }    else if (type == "hidden" || type == "password" ||             type == "text" || type == "textarea") {      if (element.value != element.defaultValue) {        return true;      }    }    else if (type == "select-one" || type == "select-multiple") {      for (var j = 0; j < element.options.length; j++) {        if (element.options[j].selected !=            element.options[j].defaultSelected) {          return true;        }      }    }  }  return false;}

检查字符串是否包含子串

1234567891011121314
if (!Array.prototype.indexOf) {    Array.prototype.indexOf = function(obj, start) {         for (var i = (start || 0), j = this.length; i < j; i++) {             if (this[i] === obj) { return i; }         }         return -1;    }} if (!String.prototype.contains) {    String.prototype.contains = function (arg) {        return !!~this.indexOf(arg);    };}

移除数组的重复数据

123456789101112131415
function removeDuplicates(arr) {    var temp = {};    for (var i = 0; i < arr.length; i++)        temp[arr[i]] = true;     var r = [];    for (var k in temp)        r.push(k);    return r;} //Usagevar fruits = ['apple', 'orange', 'peach', 'apple', 'strawberry', 'orange'];var uniquefruits = removeDuplicates(fruits);//print uniquefruits ['apple', 'orange', 'peach', 'strawberry'];

记不住apply和call的区别

stackoverflow:
Think of a in apply fora rray of args and c in call forc olumns of args.

双波浪号~~是取整

http://rocha.la/JavaScript-bitwise-operators-in-practice:
单波浪号是按位非,双波浪号是取整。

123
~~2 === Math.floor(2); //true, 2~~2.4 === Math.floor(2); //true, 2~~3.9 === Math.floor(3); //true, 3

参考文档:

原文链接: http://colobu.com/2014/09/23/45-Useful-JavaScript-Tips,-Tricks-and-Best-Practices/

0     0

我要给这篇文章打分:

可以不填写评论, 而只是打分. 如果发表评论, 你可以给的分值是-5到+5, 否则, 你只能评-1, +1两种分数. 你的评论可能需要审核.

评价列表(0)