1. 神乎其技的 + 號(hào)
//使用 `+` 運(yùn)算符可以快速將一個(gè)字符串?dāng)?shù)值轉(zhuǎn)化為數(shù)字
console.log(typeof '1'); //string
console.log(typeof +'1'); //number
//Date類型會(huì)轉(zhuǎn)化為number類型的時(shí)間戳小压,精確到ms
console.log(+new Date()) //1484219585488
//相比這種方式轉(zhuǎn)換時(shí)間戳簡(jiǎn)便睁宰,Date.parse()只精確到s澳眷,ms的位置都為0
console.log(Date.parse(new Date())) //'1484219585000'
//無(wú)法轉(zhuǎn)換為有效的數(shù)字一般會(huì)得到NaN
console.log(+'abc') //NaN
console.log(+function(){}) //NaN
//數(shù)組有點(diǎn)奇特
console.log(+[1,2,3]); //NaN
console.log(+[]); //0
console.log(+[5]); //5
//這還就導(dǎo)致了
console.log(++[[]][+[]]+[+[]]) //10
//為什么呢?一臉懵逼的你一定愿意看看推導(dǎo)
+[] = 0
=> ++[[]][+[]]+[+[]] = ++[[]][0]+[0]
[[]][0] = []
=> ++[[]][0] = ++[] = [] + 1 = '' + '1' = '1'
//++運(yùn)算符得到的結(jié)果一定是number甫题,所以有必要用 + 號(hào)再一次轉(zhuǎn)換類型
++[] = +( [] + 1 ) = +'1' = 1
=> 1 + [0] = '1' + '0' = '10'
2. 妙用數(shù)組length屬性
var a = [1, 2, 3, 4];
console.log(a.length) //4
//清空數(shù)組
a.length = 0; //a = []
//截取數(shù)組
a.length = 2; //a = [1, 2]
//擴(kuò)張數(shù)組派敷,用undefined填充
a.length = 5; //a = [1, 2, 3, 4, undefined]
3. 兩個(gè)數(shù)花式交換
//方案一
var a = 1, b =2;
a = [b, b = a][0];
console.log(a, b); //2 1
//方案二
var a = 1, b =2;
a = a ^ b;
b = a ^ b;
a = a ^ b;
console.log(a,b) //2 1
4. 在讀取length的循環(huán)中緩存length
var a = [1, 2, 3];
for(var i = 0; i < a.length; i++){
console.log(a[i])
}
盡管上面的做法沒(méi)有異議,但每次循環(huán)都會(huì)額外做一個(gè)計(jì)算數(shù)組length的操作衙荐,數(shù)組足夠小的時(shí)候這當(dāng)然沒(méi)有任何問(wèn)題,但當(dāng)它足夠大浮创,它就會(huì)成為拖垮性能的一個(gè)元兇忧吟。假如你嘗試過(guò)在java中處理一個(gè)超大文件的每一個(gè)字節(jié)時(shí)使用上面的做法,你可能會(huì)懂得它對(duì)性能的破壞力有多大斩披。
必要的時(shí)候采用下面這種做法吧:
var a = [1, 2, 3];
for(var i = 0, len = a.length; i < len; i++){
console.log(a[i]);
}
5. 可動(dòng)態(tài)指定地訪問(wèn)Object屬性
var o = {
name : 'cmx',
age : 24
}
//可以這么訪問(wèn)一個(gè)key
console.log(o.name);
//還可以這么訪問(wèn)一個(gè)key
console.log(a['name']);
//再直白一點(diǎn)
var key = 'name';
console.log(a[key]);
看完你能知道應(yīng)用場(chǎng)景嗎溜族?
6. 妙用And和Or
//設(shè)置變量的初始默認(rèn)值
function(a){
a = a || '默認(rèn)值';
}
//代替if語(yǔ)句,利用短路特性雏掠,如果a變量是有效值(非undefined斩祭、null劣像、""乡话、false、0等)耳奕,執(zhí)行方法dosomething
a && dosomething(a);
//同理绑青,a為無(wú)效值時(shí)執(zhí)行dosomething函數(shù)
a || dosomething();
7. 數(shù)組拼接
存在兩種方式去拼接兩個(gè)數(shù)組:
var a = [1,2];
var b = [3];
//生成一個(gè)新的數(shù)組,不破壞原數(shù)組
a = a.concat(b) //a=[1,2,3]
//以第一個(gè)參數(shù)數(shù)組作為this屋群,第二個(gè)參數(shù)數(shù)組作為遍歷參數(shù)闸婴,push進(jìn)第一個(gè)數(shù)組,最終第一個(gè)數(shù)組為兩個(gè)數(shù)組的拼接芍躏,第二個(gè)數(shù)組不改變
Array.prototype.push.apply(a, b) //a=[1,2,3] b=[3]
//相當(dāng)于
a.push(b[0], b[1], b[2]..)
什么時(shí)候用哪種呢邪乍?concat
由于生成新的數(shù)組,必然是占內(nèi)存的对竣,但它不限制合并數(shù)組大小庇楞。而第二種方法存在合并數(shù)組個(gè)數(shù)限制(實(shí)際上是函數(shù)的形參個(gè)數(shù)限制,不超過(guò)65536)否纬,內(nèi)存則相對(duì)于concat
減少了一個(gè)合并后數(shù)組的大小吕晌。性能上相差不多。
8. 兩個(gè)操作符typeof跟instanceof
-
typeof
判斷變量的類型临燃,為一元運(yùn)算符 -
instanceof
判斷變量的實(shí)例(原型鏈)睛驳,為二元運(yùn)算符
用法上烙心,
console.log(typeof {name:'chenmuxin'}) //'object'
console.log({name:'chenmuxin'} instanceof Object) //true
typeof
返回的是一個(gè)全小寫字符串,依據(jù)變量的類型可能會(huì)返回:
'object'
乏沸、'number'
淫茵、'string'
、'undefined'
蹬跃、'function'
痘昌、'boolean'
、'symbol'
如你所見炬转,不存在數(shù)組類型辆苔。數(shù)組返回的是'object'
,不僅數(shù)組扼劈,所有諸如Date
驻啤、RegExp
都被作為object
看待
'number'
類型:
typeof 1
typeof(1) //也可以這樣使用,類似java
typeof 3.1415 //js的number類型統(tǒng)一了整型浮點(diǎn)型等
typeof NaN //Not a number表示的是無(wú)窮和非數(shù)值荐吵,它是一個(gè)number類型
typeof Number(1) //Number類直接返回的類型也是number
'undefined'
類型:
typeof undefined
typeof i_am_undeclared //一個(gè)未聲明變量
var i_am_declared_but_undefined_value; //一個(gè)已聲明但未賦值變量
typeof i_am_declared_but_undefined_value
最容易迷惑情形:
typeof new Number(1) //返回'object'骑冗,使用new操作符時(shí),諸如String先煎、Boolean等引用類返回的是object
typeof Number(1) //'number'
typeof null //返回'object'贼涩,js誕生之時(shí)起,null就是跟object同類型薯蝎,這顯然不合適遥倦。但es6的提案typeof null == 'null'被否決,所以目前都還只能這么認(rèn)識(shí)它
typeof /a/ //正則在不同瀏覽器上可能有不同類型占锯,可能是function袒哥,也可能是object,標(biāo)準(zhǔn)是object消略。判斷正則應(yīng)使用instanceof
使用instanceof
時(shí)堡称,首先要保證對(duì)象可以new
,只有允許new
才會(huì)存在所謂實(shí)例艺演。所以想使用instanceof
判斷一個(gè)基本變量的類型是不可行的却紧。
console.log(1 instanceof Number) //false
數(shù)組無(wú)法使用typeof
來(lái)判斷,但可以使用instanceof
(一定場(chǎng)景下會(huì)失敗胎撤,例如多重iframe)
console.log([1] instanceof Array) //true
當(dāng)然也可以使用更穩(wěn)妥的數(shù)組的方法:
console.log(Array.isArray([1])) //true
總結(jié):像數(shù)組晓殊、正則等繼承自Object
但具體的對(duì)象(首先可以new
),我們一般都可以用instanceof
去判斷哩照。像很多基本變量以及它們的引用類型挺物,我們就用typeof
去判斷。
9. in和for遍歷
var a = [10,20,30];
var b = {name : 'cmx'}
//基本數(shù)組遍歷
for(var i=0,length = a.length; i < length; i++){
console.log(a[i]);
}
//of運(yùn)算符每次循環(huán)直接得到數(shù)組值
for(var i of a){
console.log(i);
}
//in運(yùn)算符每次循環(huán)得到當(dāng)前下標(biāo)
for(var i in a){
console.log(a[i]);
}
//in運(yùn)算符遍歷對(duì)象時(shí)飘弧,每次得到對(duì)象的key
for(var i in b){
console.log(i);
console.log(b[i]);
}
存在一些特殊情況识藤,in
遍歷時(shí)砚著,不僅會(huì)把數(shù)組(或?qū)ο螅┑拿恳粋€(gè)下標(biāo)(key)遍歷出來(lái),一旦像下面這樣定義了原型屬性或方法
Array.prototype.what = function(){ }
in
遍歷還會(huì)把它們一起遍歷出來(lái)痴昧,這里得到一個(gè)what
稽穆。如果沒(méi)有作必要的判斷往往會(huì)導(dǎo)致如下:
for(var i in a){
//打印a[what]出錯(cuò)
console.log(a[i]);
}
要么不使用原型直接添加的方式,而使用Object.defineProperty
并設(shè)置可枚舉屬性為false赶撰。要么就in
遍歷數(shù)組時(shí)舌镶,都判斷當(dāng)前i是否為number類型:
var a = [1, 2, 3];
Array.prototype.cmx = 5;
for(var i in a){
//+i是因?yàn)閕遍歷出來(lái)的都是字符串形式。至于為什么不使用 i != NaN 豪娜,因?yàn)槿我鈨蓚€(gè)NaN不相等啊
if(!Number.isNaN(+i)){
console.log(i)
}
}
因?yàn)樵蛿U(kuò)展是不允許添加一個(gè)number類型的餐胀,所以上面的做法成立。
Array.prototype.1 = 1 //error
或者使用通用的方法(同時(shí)適合數(shù)組和對(duì)象)
for(var i in a){
if(a.hasOwnProperty(i)){
console.log(a[i])
}
}
Object.prototype.hasOwnProperty()
會(huì)忽略對(duì)象原型鏈上的屬性和方法瘤载。
10. call和apply
我們先來(lái)看段代碼:
function Chen(){
this.name = '老陳';
this.sayHello = function(hello){
console.log('我是老陳的方法');
console.log(hello + '否灾, I am ' + this.name);
}
}
function Huang(){
this.name = '老黃';
this.sayHello = function(hello){
console.log('我是老黃的方法');
console.log(hello + ', I am ' + this.name);
}
}
var chen = new Chen();
chen.sayHello('你好'); //我是老陳的方法 你好鸣奔, I am 老陳
var huang = new Huang();
chen.sayHello.call(huang,'Hello'); //我是老陳的方法 Hello墨技, I am 老黃
chen.sayHello.apply(huang,['Hi']) //我是老陳的方法 Hi, I am 老黃
call
和apply
非常像挎狸,都是為一個(gè)函數(shù)指定上下文扣汪,并傳入?yún)?shù)。唯一的區(qū)別在于參數(shù)參入的形式锨匆。
Function.prototype.call(thisObj, ...args)
Function.prototype.apply(thisObj[, argArray])
call()
以thisObj
作為上下文帶上若干個(gè)指定的參數(shù)值調(diào)用某個(gè)函數(shù)或方法崭别,參數(shù)必須展開。
apply()
以thisObj
作為上下文帶上一個(gè)數(shù)組或累數(shù)組參數(shù)調(diào)用某個(gè)函數(shù)或方法统刮,參數(shù)必須組合成一個(gè)數(shù)組紊遵。
由于只是形式不同,故以其中一個(gè)作為示例說(shuō)明:
chen.sayHello.call(huang, 'Hello')
以huang
作為上下文侥蒙,調(diào)用chen
的sayHello
方法,由于chen.sayHello()
調(diào)用的this.name
已經(jīng)被更改為huang.name
匀奏,所以得到上面的執(zhí)行結(jié)果鞭衩。
用call
或者apply
來(lái)模擬一下繼承:
function Boy(name, love){
this.name = name;
this.lova = love;
this.say = function(){
console.log('I am '+ name +',I like '+ love +'娃善,what about you?');
}
}
function BadBoy(name, love){
Boy.call(this, name, love);
this.do = function(){
console.log('I made a mistake!');
}
}
var badBoy = new BadBoy('張三','打架');
badBoy.say(); //I am 張三论衍,I like 打架,what about you?
badBoy.do(); //I made a mistake!
var boy = new Boy('李四','學(xué)習(xí)');
boy.say(); //I am 李四聚磺,I like 學(xué)習(xí)坯台,what about you?
boy.do(); //boy.do is not a function
(轉(zhuǎn)載請(qǐng)注明出處,簡(jiǎn)書-沐心chen)