45个实用的JavaScript黑科技
2016-06-17 00:00
543 查看
众所周知,Javascript是全球最流行的语言之一,它涉足Web开发,移动端开发(PhoneGap 、 Appcelerator ),服务端开发( Nodejs 、 Wakanda ),还有多种第三方实现(CoffeeScript这种)。此外Javascript还是许多开发者进入编程世界所接触的第一门语言。它既可以在浏览器中简单的弹出一个alert窗口,也能达到控制机器人这种复杂的程度(比如 nodebot 、nodruino)。现在那些能够熟练编写结构清晰、性能卓越的Javascript开发者们,已经成为招聘市场上炙手可热的应聘者了。
在这篇文章中,我将向你展示一系列Javascript相关的小技巧和一些最佳实践。除了少数几个示例,大部分示例都可以在浏览器环境或者服务端环境适用。
注意,文章中所有的代码片段都已经在Google Chrome V30(V8 3.20.17.15)测试通过。
1、使用
在Javascript中,如果一个变量没有经过声明就直接进行赋值操作,那么这个变量就会自动转变成全局变量。我们要尽量避免这种情况(全局变量)。
2、使用
4、使用分号
使用分号来结束代码行是一个被Javascript社区推荐的最佳实践。如果你忘了也没有关系,因为现在的Javascript引擎将会自动给你加上分号。至于我们为什么应该使用分号,可以参阅这篇文章 http://davidwalsh.name/javascript-semicolons 。
6、使用
译者注
这里采用了原生的排序函数
不过,在新的Javascript引擎中,已经内置支持了这个功能。
15、将
如果
如果在浏览器中没有使用iframe,还可以用
20、不用使用
应该使用
错误的用法,
正确的用法,
注意,要想移除一个对象的属性,应该采用
21、可以通过操作数组长度
与前面那个使用
除此之外,如果我们使用一个更大的值去重写
22、在条件中使用
23、使用
注意,
为什么呢?因为0.1+0.2等于0.30000000000000004。JavaScript的数字都遵循IEEE 754标准构建,在内部都是64位浮点小数表示,具体可以参阅 这篇文章 。
另外,你也可以使用
译者注
关于这个问题,博主也有一篇相关的文章, Javascript中浮点数的计算精度问题 。
26、使用
下面的代码片段使用
29、提前检查传入
注意传给
31、使用
32、避免使用
33、避免使用
使用
34、避免使用
错误的用法,
更好的做法,
除此之外,
为什么呢?因为数组
注意,这个问题其实在最新的Javascript引擎中已经被修复了。
35、给
如果你给
错误的用法,
正确的用法,
36、使用
当判断有超过两个分支的时候使用
37、在
其实
40、不要在循环中使用
译者注
这里我们可以使用闭包来保存这个运行时的异常变量。
错误的用法
正确的用法
41、使用
如果一个ajax请求长时间没有响应,我们应该中止请求。否则浏览器将会一直等待。我们可以使用
除此之外,我们应该避免同时发送多个 同步的 ajax请求。
为了防止超时中断,你需要每隔一段时间发送一个心跳数据(空字符串)以保持websocket连接。下面的两个方法一个用于周期的发送心跳数据保持连接,一个是取消心跳数据包。
错误的用法,
正确的用法,
在这篇文章中,我将向你展示一系列Javascript相关的小技巧和一些最佳实践。除了少数几个示例,大部分示例都可以在浏览器环境或者服务端环境适用。
注意,文章中所有的代码片段都已经在Google Chrome V30(V8 3.20.17.15)测试通过。
1、使用 var
关键字进行变量赋值
在Javascript中,如果一个变量没有经过声明就直接进行赋值操作,那么这个变量就会自动转变成全局变量。我们要尽量避免这种情况(全局变量)。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
, ''
(空字符串)都为逻辑假值
4、使用分号 ;
来结束一行代码
使用分号来结束代码行是一个被Javascript社区推荐的最佳实践。如果你忘了也没有关系,因为现在的Javascript引擎将会自动给你加上分号。至于我们为什么应该使用分号,可以参阅这篇文章 http://davidwalsh.name/javascript-semicolons 。5、使用对象构造器
functionPerson(firstName, lastName){ this.firstName = firstName; this.lastName = lastName; } var Saad = new Person("Saad", "Mousliki");
6、使用 typeof
、 instanceof
、 constructor
时要谨慎小心
typeof:JavaScript的一元操作符,用于以字符串的形式返回变量的原始类型,注意,
typeof null也会返回
object,大多数的对象类型(数组
Array、时间
Date等)也会返回
object
constructor:对象(函数)的内部原型属性,它是可写的(可以被重写)
instanceof:JavaScript操作符,会在原型链中的构造器中搜索,找到则返回
true,否则返回
false(常用于判断某一个对象是否是某个构造器或者其父类构造器的实例)
var arr = ["a", "b", "c"]; typeof arr; // return "object" arr instanceof Array // true arr.constructor(); // []
7、学会使用自调用函数
函数在创建之后直接自动执行,通常称之为自调用匿名函数(Self-Invoked Anonymous Function)或直接调用函数表达式(Immediately Invoked Function Expression )。比如,(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、生成一个随机的字母数字序列
functiongenerateRandomAlphaNum(len) { var rdmString = ""; for( ;rdmString.length < len; rdmString += Math.random().toString(36).substr(2)); return rdmString.substr(0, len); }
译者注
toString()方法可以接受一个参数表示数字进制。而36进制刚好可以使用a-z和0-9这些字符。所以此方法可以用于生成简单的随机串。
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] */
这里采用了原生的排序函数
sort(),此外我们还可以使用专门的工具库来得到这一目的。
13、字符串去空格
像Java、C#、PHP这些语言都内置了trim()功能函数用于字符串去空格。但是Javascript没有这个内置方法。可以通过下面的方法来得到此目的,
String.prototype.trim = function() { returnthis.replace(/^\s+|\s+$/g, ""); };
不过,在新的Javascript引擎中,已经内置支持了这个功能。
14、将一个数组追加到另一个数组中
var array1 = [12 , "foo" , {name "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
15、将 argruments
转换成数组
var argArray = Array.prototype.slice.call(arguments);
16、检验一个参数是否为数字
functionisNumber(n){ return !isNaN(parseFloat(n)) && isFinite(n); }
17、检验一个参数是否为数组
functionisArray(obj){ return Object.prototype.toString.call(obj) === '[object Array]' ; }
如果
toString()被重写过的话,上面的方法就不行了。此时我们可以使用下面的方法,
Array.isArray(obj); // its a new Array method
如果在浏览器中没有使用iframe,还可以用
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]// myArray 的构造器已经丢失,instanceof 的结果将不正常// 不同iframe中的构造器是不能共享的 arr instanceofArray; // 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
关键字来移除一个数组元素
应该使用 splice方法而不是
delete来移除一个数组元素。对一个数组元素使用
delete会让这个数组元素的值变为
undefined,并没有将这个数组元素给删除掉。
错误的用法,
var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; //return11delete items[3]; //return true items.length; //return11 /* 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; //return11 items.splice(3,1) ; items.length; //return10 /* items 结果为 [12, 548, "a", 5478, "foo", 8852, undefined × 1, "Doe", 2154, 119] */
注意,要想移除一个对象的属性,应该采用
delete方法。
21、可以通过操作数组长度 length
来截断一个数组
与前面那个使用 length清空数组的示例类似,我们可以使用
length来截断一个数组。
var myArray = [12 , 222 , 1000 , 124 , 98 , 10 ]; myArray.length = 4; // myArray will be equal to [12 , 222 , 1000 , 124].
除此之外,如果我们使用一个更大的值去重写
length,那么数组的长度将会改变,同时会用
undefined填充新增的数组元素。
myArray.length = 10; // the new array length is 10 myArray[myArray.length - 1] ; // undefined
22、在条件中使用 &&
及 ||
进行短语判断
var foo = 10; foo == 10 && doSomething(); // is the same thing asif (foo == 10) doSomething(); foo == 5 || doSomething(); // is the same thing asif (foo != 5) doSomething();
||还用于给函数参数设置默认值,比如
functiondoSomething(arg1){ arg1 = arg1 || 10; // arg1 will have 10 as a default value if it’s not already set }
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
注意,
toFixed()方法返回的是字符串而不是一个数字。
25、浮点数计算问题
0.1 + 0.2 === 0.3// is false 9007199254740992 + 1// is equal to 9007199254740992 9007199254740992 + 2// is equal to 9007199254740994
为什么呢?因为0.1+0.2等于0.30000000000000004。JavaScript的数字都遵循IEEE 754标准构建,在内部都是64位浮点小数表示,具体可以参阅 这篇文章 。
另外,你也可以使用
toFixed()或者
toPrecision()来解决这个问题。
译者注
关于这个问题,博主也有一篇相关的文章, Javascript中浮点数的计算精度问题 。
26、使用 for...in
来遍历对象的属性
下面的代码片段使用 for...in来遍历对象属性,可以防止遍历到对象原型链上的属性。
for (var name inobject) { 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、缓存临时变量用于避免再次计算或者查询
在使用jquery时,我们可以临时缓存整个jq对象,比如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]; varfrom = numbersArray.indexOf("foo") ; // from is equal to -1 numbersArray.splice(from,2); // will return [5]
注意传给
splice的索引参数不要是负数,当是负数时,会从数组结尾处删除元素。
31、使用 JSON
来进行序列化和反序列化
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()
和函数构造器
eval()和函数构造器(
Functionconsturctor)的开销都比较大,每次调用JavaScript引擎都要将源代码转换为可执行的代码。
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
当判断有超过两个分支的时候使用 switch/case要更快一些,而且也更优雅,更利于代码的组织,当然,如果有超过10个分支,就不要使用
switch/case了。
37、在 switch/case
中使用数字范围进行分界
其实 switch/case中的
case条件,还可以这样写:
functiongetCategory(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、为创建的对象指定原型
下面的示例演示了可以给定对象作为参数,来创建以此为原型的新对象:functionclone(object) { functionOneShotConstructor(){}; OneShotConstructor.prototype = object; returnnew OneShotConstructor(); } clone(Array).prototype ; // []
39、html转义函数
functionescapeHTML(text) { var replacements= {'<': '<', '>': '>', '&': '&', '"': '"'}; return text.replace(/[<>&"]/g, function(character) { return replacements[character]; }); }
40、不要在循环中使用 try...catch...finally
try...catch...finally在捕获一个异常时,会创建一个运行时环境的子作用域。而异常变量的生命周期仅限在这个运行时的子作用域。
译者注
这里我们可以使用闭包来保存这个运行时的异常变量。
错误的用法
varobject = ['foo', 'bar'], i; for (i = 0, len = object.length; i <len; i++) { try { // do something that throws an exception } catch (e) { // handle exception } }
正确的用法
varobject = ['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
时注意设置超时参数
如果一个ajax请求长时间没有响应,我们应该中止请求。否则浏览器将会一直等待。我们可以使用 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/* timeout after a minute */ ); xhr.open('GET', url, true); xhr.send();
除此之外,我们应该避免同时发送多个 同步的 ajax请求。
42、处理websocket超时
一般地,WebSocket连接创建后,如果30秒内没有任何活动服务器端会对连接进行超时处理,防火墙也可以对单位周期内没有活动的连接进行超时处理。为了防止超时中断,你需要每隔一段时间发送一个心跳数据(空字符串)以保持websocket连接。下面的两个方法一个用于周期的发送心跳数据保持连接,一个是取消心跳数据包。
var timerID = 0; functionkeepAlive() { var timeout = 15000; if (webSocket.readyState == webSocket.OPEN) { webSocket.send(''); } timerId = setTimeout(keepAlive, timeout); } functioncancelKeepAlive() { if (timerId) { cancelTimeout(timerId); } }
keepAlive()方法应该被添加在
webSOcket连接的
onOpen()方法的最后,而
cancelKeepAlive()添加在
onClose()方法的最后。
43、记住:原生操作肯定比函数调用要效率高
比如,错误的用法,
var min = Math.min(a,b); A.push(v);
正确的用法,
var min = a < b ? a:b; A[A.length] = v;
44、编码时注意保持代码的优雅格式,上生产环境前做一些压缩工作。
45、与其他JavaScript类库冲突的解决方案
$(document).ready(function() {
var
$jq = jQuery.noConflict();
$jq('#id').show();
});
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子
- JavaScript 各种遍历方式详解
- call/apply/bind 的理解与实例分享