全面介绍javascript实用技巧及单竖杠
JavaScript本身可以算是一门简单的语言,但我们也不断用智慧和灵活的模式来改进它。昨天我们将这些模式应用到了JavaScript框架中,今天这些框架又驱动了我们的Web应用程序。很多新手开发者被各种强大的JavaScript框架吸引进来,但他们却忽略了框架身后浩如星海的JavaScript实用技巧。本文将为你全面的介绍其中的知识点。
一、js整数的操作
使用|0和~~可以将浮点转成整型且效率方面要比同类的parseInt,Math.round要快,在处理像素及动画位移等效果的时候会很有用。性能比较见此。
varfoo=(12.4/4.13)|0;//结果为3 varbar=~~(12.4/4.13);//结果为3
还有一个小技巧,就是!!2个叹号,可以讲一个值,快速转化为布尔值。你可以测试一下!
vareee="eee"; alert(!!eee)
返回的是true,也就是说任何一个值前面加!!都可以恒等于true。除非这个值本来就是布尔值,或者为undefined,null,0,false,NaN,'',因为我提到的undefined,null,0,false,NaN,'',这些,本来就是false,所以加了两个!!之后,还是fasle。
二、重写原生alert,记录弹框次数
(function(){ varoldAlert=window.alert, count=0; window.alert=function(a){ count++; oldAlert(a+"\nYou'vecalledalert"+count+"timesnow.Stop,it'sevil!"); }; })(); alert("HelloHaorooms");
三、数字交换不声明中间变量的方法
两个数字之间做交换,我们的一般做法是声明一个中间变量,但是今天的做法比较奇葩,不用声明中间变量,看看是如何实现的!
vara=1,b=2;a=[b,b=a][0]; console.log('a:'+a+',b:'+b); //输出a:2,b:1
怎么样,这个方法是不是有一种焕然一新的感觉?
四、万物皆对象
在JavaScript的世界,万物皆对象。除了null和undefined,其他基本类型数字,字符串和布尔值都有对应有包装对象。对象的一个特征是你可以在它身上直接调用方法。
对于数字基本类型,当试图在其身上调用toString方法会失败,但用括号括起来后再调用就不会失败了,内部实现是用相应的包装对象将基本类型转为对象。所以(1).toString()相当于newNumber(1).toString()。因此,你的确可以把基本类型数字,字符串,布尔等当对象使用的,只是注意语法要得体。
同时我们注意到,JavaScript中数字是不分浮点和整形的,所有数字其实均是浮点类型,只是把小数点省略了而以,比如你看到的1可以写成1.,这也就是为什么当你试图1.toString()时会报错,所以正确的写法应该是这样:1..toString(),或者如上面所述加上括号,这里括号的作用是纠正JS解析器,不要把1后面的点当成小数点。内部实现如上面所述,是将1.用包装对象转成对象再调用方法。
五、If语句的变形
当你需要写一个if语句的时候,不妨尝试另一种更简便的方法,用JavaScript中的逻辑操作符来代替。
varday=(newDate).getDay()===0; //传统if语句 if(day){ alert('TodayisSunday!'); }; //运用逻辑与代替if day&&alert('TodayisSunday!');
比如上面的代码,首先得到今天的日期,如果是星期天,则弹窗,否则什么也不做。我们知道逻辑操作存在短路的情况,对于逻辑与表达式,只有两者都真才结果才为真,如果前面的day变量被判断为假了,那么对于整个与表达式来说结果就是假,所以就不会继续去执行后面的alert了,如果前面day为真,则还要继续执行后面的代码来确定整个表达式的真假。利用这点达到了if的效果。
对于传统的if语句,如果执行体代码超过了1条语句,则需要加花括号,而利用逗号表达式,可以执行任意条代码而不用加花括号。
if(conditoin)alert(1),alert(2),console.log(3);
六、使用===,而不是==
==(或!=)操作符在需要的时候会自动执行类型转换。===(或!==)操作不会执行任何转换。它将比较值和类型,而且在速度上也被认为优于==。
[10]===10//isfalse [10]==10//istrue '10'==10//istrue '10'===10//isfalse []==0//istrue []===0//isfalse ''==false//istruebuttrue=="a"isfalse ''===false//isfalse
七、使用闭包实现私有变量
functionPerson(name,age){ this.getName=function(){returnname;}; this.setName=function(newName){name=newName;}; this.getAge=function(){returnage;}; this.setAge=function(newAge){age=newAge;}; //未在构造函数中初始化的属性 varoccupation; this.getOccupation=function(){returnoccupation;}; this.setOccupation=function(newOcc){occupation= newOcc;}; }
八、创建对象的构造函数
functionPerson(firstName,lastName){ this.firstName=firstName; this.lastName=lastName; } varSaad=newPerson("Saad","Mousliki");
九、小心使用typeof、instanceof和constructor
vararr=["a","b","c"]; typeofarr;//return"object" arrinstanceofArray//true arr.constructor();//[]
十、创建一个自调用函数(Self-callingFuntion)
这个经常被称为自调用匿名函数(Self-InvokedAnonymousFunction)或者即时调用函数表达式(IIFE-ImmediatelyInvokedFunctionExpression)。这是一个在创建后立即自动执行的函数,通常如下:
(function(){ //someprivatecodethatwillbeexecutedautomatically })(); (function(a,b){ varresult=a+b; returnresult; })(10,20)
十一、从数组中获取一个随机项
varitems=[12,548,'a',2,5478,'foo',8852,,'Doe',2145,119]; varrandomItem=items[Math.floor(Math.random()*items.length)];
十二、在特定范围内获取一个随机数
这个代码片段在你想要生成测试数据的时候非常有用,比如一个在最小最大值之间的一个随机薪水值。
varx=Math.floor(Math.random()*(max-min+1))+min;
十三、在0和设定的最大值之间生成一个数字数组
varnumbersArray=[],max=100; for(vari=1;numbersArray.push(i++)<max;);//numbers=[0,1,2,3...100]
十四、生成一个随机的数字字母字符串
functiongenerateRandomAlphaNum(len){ varrdmstring=""; for(;rdmString.length<len;rdmString+=Math.random().toString(36).substr(2)); returnrdmString.substr(0,len); } //调用方法generateRandomAlphaNum(15);
十五、打乱一个数字数组
varnumbers=[5,458,120,-215,228,400,122205,-85411]; numbers=numbers.sort(function(){returnMath.random()-0.5}); /*thearraynumberswillbeequalforexampleto[120,5,228,-215,400,458,-85411,122205]*/
十六、String的trim函数
String.prototype.trim=function(){returnthis.replace(/^\s+|\s+$/g,"");};
十七、附加(append)一个数组到另一个数组上
vararray1=[12,"foo",{name:"Joe"},-2458]; vararray2=["Doe",555,100]; Array.prototype.push.apply(array1,array2); /*array1willbeequalto[12,"foo",{name"Joe"},-2458,"Doe",555,100]*/
十八、将arguments对象转换成一个数组
varargArray=Array.prototype.slice.call(arguments); 【译者注:arguments对象是一个类数组对象,但不是一个真正的数组】
十九、验证参数是否是数字(number)
functionisNumber(n){ return!isNaN(parseFloat(n))&&isFinite(n); }
二十、验证参数是否是数组
functionisArray(obj){ returnObject.prototype.toString.call(obj)==='[objectArray]'; }
注意:如果toString()方法被重写了(overridden),你使用这个技巧就不能得到想要的结果了。或者你可以使用:
Array.isArray(obj);//这是一个新的array的方法
如果你不在使用多重frames的情况下,你还可以使用instanceof方法。但如果你有多个上下文,你就会得到错误的结果。
varmyFrame=document.createElement('iframe'); document.body.appendChild(myFrame); varmyArray=window.frames[window.frames.length-1].Array; vararr=newmyArray(a,b,10);//[a,b,10] //instanceofwillnotworkcorrectly,myArrayloseshisconstructor //constructorisnotsharedbetweenframes arrinstanceofArray;//false
【译者注:关于如何判断数组网上有不少讨论,大家可以google一下。这篇就写的挺详细的。】
二十一、获取一个数字数组中的最大值或最小值
var numbers=[5,458,120,-215,228,400,122205,-85411];
varmaxInNumbers=Math.max.apply(Math,numbers);
varminInNumbers=Math.min.apply(Math,numbers);
【译者注:这里使用了Function.prototype.apply方法传递参数的技巧】
二十二、清空一个数组
varmyArray=[12,222,1000]; myArray.length=0;//myArraywillbeequalto[].
二十三、不要使用delete来删除一个数组中的项。
使用splice而不要使用delete来删除数组中的某个项。使用delete只是用undefined来替换掉原有的项,并不是真正的从数组中删除。
不要使用这种方式:
varitems=[12,548,'a',2,5478,'foo',8852,,'Doe',2154,119]; items.length;//return11 deleteitems[3];//returntrue items.length;//return11 /*itemswillbeequalto[12,548,"a",undefined×1,5478,"foo",8852,undefined×1,"Doe",2154,119]*/
而使用:
varitems=[12,548,'a',2,5478,'foo',8852,,'Doe',2154,119]; items.length;//return11 items.splice(3,1); items.length;//return10 /*itemswillbeequalto[12,548,"a",5478,"foo",8852,undefined×1,"Doe",2154,119]*/
delete方法应该被用来删除一个对象的某个属性。
二十四、使用length来截短一个数组
跟上面的清空数组的方式类似,我们使用length属性来截短一个数组。
varmyArray=[12,222,1000,124,98,10]; myArray.length=4;//myArraywillbeequalto[12,222,1000,124].
此外,如果你将一个数组的length设置成一个比现在大的值,那么这个数组的长度就会被改变,会增加新的undefined的项补上。数组的length不是一个只读属性。
myArray.length=10;//thenewarraylengthis10 myArray[myArray.length-1];//undefined
二十五、使用逻辑AND/OR做条件判断
同(五),if变形语句!
varfoo=10; foo==10&&doSomething();//等价于if(foo==10)doSomething(); foo==5||doSomething();//等价于if(foo!=5)doSomething();
逻辑AND还可以被使用来为函数参数设置默认值
functiondoSomething(arg1){ Arg1=arg1||10;//如果arg1没有被设置的话,Arg1将被默认设成10 }
二十六、使用map()方法来遍历一个数组里的项
varsquares=[1,2,3,4].map(function(val){ returnval*val; }); //squareswillbeequalto[1,4,9,16]
二十七、四舍五入一个数字,保留N位小数
varnum=2.443242342; num=num.toFixed(4);//numwillbeequalto2.4432
二十八、浮点数问题
0.1+0.2===0.3//isfalse 9007199254740992+1//isequalto9007199254740992 9007199254740992+2//isequalto9007199254740994
为什么会这样?0.1+0.2等于0.30000000000000004。你要知道,所有的JavaScript数字在内部都是以64位二进制表示的浮点数,符合IEEE754标准。更多的介绍,可以阅读这篇博文。你可以使用toFixed()和toPrecision()方法解决这个问题。
二十九、使用for-in遍历一个对象内部属性的时候注意检查属性
下面的代码片段能够避免在遍历一个对象属性的时候访问原型的属性
for(varnameinobject){ if(object.hasOwnProperty(name)){ //dosomethingwithname } }
三十、逗号操作符
vara=0; varb=(a++,99); console.log(a);//awillbeequalto1 console.log(b);//bisequalto99
三十一、缓存需要计算和查询(calculationorquerying)的变量
对于jQuery选择器,我们最好缓存这些DOM元素。
varnavright=document.querySelector('#right'); varnavleft=document.querySelector('#left'); varnavup=document.querySelector('#up'); varnavdown=document.querySelector('#down');
三十二、在调用isFinite()之前验证参数
isFinite(0/0);//false isFinite("foo");//false isFinite("10");//true isFinite(10);//true isFinite(undifined);//false isFinite();//false isFinite(null);//true!!!
三十三、避免数组中的负数索引(negativeindexes)
varnumbersArray=[1,2,3,4,5]; varfrom=numbersArray.indexOf("foo");//fromisequalto-1 numbersArray.splice(from,2);//willreturn[5]
确保调用indexOf时的参数不是负数。
三十四、基于JSON的序列化和反序列化(serializationanddeserialization)
varperson={name:'Saad',age:26,department:{ID:15,name:"R&D"}}; varstringFromPerson=JSON.stringify(person); /*stringFromPersonisequalto"{"name":"Saad","age":26,"department":{"ID":15,"name":"R&D"}}"*/ varpersonFromString=JSON.parse(stringFromPerson); /*personFromStringisequaltopersonobject*/
三十五、避免使用eval()和Function构造函数
使用eval和Function构造函数是非常昂贵的操作,因为每次他们都会调用脚本引擎将源代码转换成可执行代码。
varfunc1=newFunction(functionCode); varfunc2=eval(functionCode);
三十六、避免使用with()
使用with()会插入一个全局变量。因此,同名的变量会被覆盖值而引起不必要的麻烦。
三十七、避免使用for-in来遍历一个数组
避免使用这样的方式:
varsum=0; for(variinarrayNumbers){ sum+=arrayNumbers[i]; }
更好的方式是:
varsum=0; for(vari=0,len=arrayNumbers.length;i<len;i++){ sum+=arrayNumbers[i]; }
附加的好处是,i和len两个变量的取值都只执行了一次,会比下面的方式更高效:
for(vari=0;i<arrayNumbers.length;i++)
为什么?因为arrayNumbers.length每次循环的时候都会被计算。
三十八、在调用setTimeout()和setInterval()的时候传入函数,而不是字符串。
如果你将字符串传递给setTimeout()或者setInterval(),这个字符串将被如使用eval一样被解析,这个是非常耗时的。
不要使用:
setInterval('doSomethingPeriodically()',1000); setTimeOut('doSomethingAfterFiveSeconds()',5000)
而用:
setInterval(doSomethingPeriodically,1000); setTimeOut(doSomethingAfterFiveSeconds,5000);
三十九、使用switch/case语句,而不是一长串的if/else
在判断情况大于2种的时候,使用switch/case更高效,而且更优雅(更易于组织代码)。但在判断的情况超过10种的时候不要使用switch/case。
四十、在判断数值范围时使用switch/case
在下面的这种情况,使用switch/case判断数值范围的时候是合理的:
functiongetCategory(age){ varcategory=""; switch(true){ caseisNaN(age): category="notanage"; break; case(age>=50): category="Old"; break; case(age<=20): category="Baby"; break; default: category="Young"; break; }; returncategory; } getCategory(5);//willreturn"Baby"
【译者注:一般对于数值范围的判断,用if/else会比较合适。switch/case更适合对确定数值的判断】
四十一、为创建的对象指定prototype对象
写一个函数来创建一个以指定参数作为prototype的对象是有可能:
functionclone(object){ functionOneShotConstructor(){}; OneShotConstructor.prototype=object; returnnewOneShotConstructor(); } clone(Array).prototype;//[]
四十二、一个HTML转义函数
functionescapeHTML(text){ varreplacements={"<":"<",">":">","&":"&","\"":"""}; returntext.replace(/[<>&"]/g,function(character){ returnreplacements[character]; }); }
四十三、避免在循环内部使用try-catch-finally
在运行时,每次当catch从句被执行的时候,被捕获的异常对象会赋值给一个变量,而在try-catch-finally结构中,每次都会新建这个变量。
避免这样的写法:
varobject=['foo','bar'],i; for(i=0,len=object.length;i<len;i++){ try{ //dosomethingthatthrowsanexception } catch(e){ //handleexception } }
而使用:
varobject=['foo','bar'],i; try{ for(i=0,len=object.length;i<len;i++){ //dosomethingthatthrowsanexception } } catch(e){ //handleexception }
四十四、为XMLHttpRequests设置超时。
在一个XHR请求占用很长时间后(比如由于网络问题),你可能需要中止这次请求,那么你可以对XHR调用配套使用setTimeout()。
varxhr=newXMLHttpRequest(); xhr.onreadystatechange=function(){ if(this.readyState==4){ clearTimeout(timeout); //dosomethingwithresponsedata } } vartimeout=setTimeout(function(){ xhr.abort();//callerrorcallback },60*1000/*timeoutafteraminute*/); xhr.open('GET',url,true); xhr.send();
此外,一般你应该完全避免同步的Ajax请求。
四十五、处理WebSocket超时
通常,在一个WebSocket连接创建之后,如果你没有活动的话,服务器会在30秒之后断开(timeout)你的连接。防火墙也会在一段时间不活动之后断开连接。
为了防止超时的问题,你可能需要间歇性地向服务器端发送空消息。要这样做的话,你可以在你的代码里添加下面的两个函数:一个用来保持连接,另一个用来取消连接的保持。通过这个技巧,你可以控制超时的问题。
使用一个timerID:
vartimerID=0; functionkeepAlive(){ vartimeout=15000; if(webSocket.readyState==webSocket.OPEN){ webSocket.send(''); } timerId=setTimeout(keepAlive,timeout); } functioncancelKeepAlive(){ if(timerId){ cancelTimeout(timerId); } }
keepAlive()方法应该被添加在webSOcket连接的onOpen()方法的最后,而cancelKeepAlive()添加在onClose()方法的最后。
四十六、牢记,原始运算符始终比函数调用要高效。使用VanillaJS。
举例来说,不使用:
varmin=Math.min(a,b); A.push(v);
而用:
varmin=a<b?ab; A[A.length]=v;
四十七、从整数中,随机选取一个数值
有如下公式,非常有用,可以让我们随机显示某些名人名言或者新闻事件!
值=Math.floor(Math.random()*可能值的总数+第一个可能的值)
例如:要想选择一个介于2到10之间的值,我们可以这么写
varnum=Math.floor(Math.random()*9+2)
请记住上面公式!~
js运算符单竖杠“|”的用法和作用及js数据处理
刚才在js整数操作的时候,相当于去除小数点,parseInt。在正数的时候相当于Math.floor(),负数的时候相当于Math.ceil()注:
1.Math.ceil()用作向上取整。 2.Math.floor()用作向下取整。 3.Math.round()我们数学中常用到的四舍五入取整。 console.log(0.6|0)//0 console.log(1.1|0)//1 console.log(3.65555|0)//3 console.log(5.99999|0)//5 console.log(-7.777|0)//-7
注:除了Math的三个方法处理数字,我们还经常用parseInt()、parseFloat()、toFixed()与toPrecision()等等。简单解释:
toFixed方法用法如下:
100.456001.toFixed(2);//100.47 100.456001.toFixed(3);//100.456 Number.prototype.toFixed.call(100.456001,2);//100.47
缺点:用之后就会变成字符串。
toPrecision用法如下:
99.456001.toPrecision(5);//99.456 100.456001.toPrecision(5);//100.46 Number.prototype.toPrecision.call(10.456001,5);//10.456
单竖杠的运算规则
看了上面的例子,大体知道单竖杠可以进行取整运算,就是只保留正数部分,小数部分通过拿掉,但是“|0”,又是如何进行运算的呢,为什么能“|0”能达到取整的目的呢?单竖杠不是0有会是多少呢?
带着这些问题,我们看下面例子:
console.log(3|4);//7 console.log(4|4);//4 console.log(8|3);//11 console.log(5.3|4.1);//5 console.log(9|3455);//3455
好像无规律可以寻找啊?网上搜索吧。http://tools.jb51.net/table/priority
这里面提到了单竖杠“|”但是没有javascript的。
好吧,我在这里公布答案吧。其实单竖杠“|”就是转换为2进制之后相加得到的结果。例如我们拿简单的举例:
3|4 转换为二进制之后011|100相加得到111=7 4|4 转换为二进制之后100|100相加得到100=4 8|3 转换为二进制之后1000|011相加得到1011=11
以此类推,我在这里就不一一列举了,单竖杠“|”运算就是转换为2进制之后相加得到的结果!都学会了吗?