Javascript是一種動(dòng)態(tài)腳本語(yǔ)言闻蛀,而且還是弱類型的語(yǔ)言。如果大家在之前學(xué)習(xí)過(guò)Java或者c#等面向?qū)ο蟮母呒?jí)語(yǔ)言政溃,然后再來(lái)學(xué)習(xí)Javascript崩掘,那么你們肯定會(huì)覺得很多地方都是與高級(jí)語(yǔ)言一致的,于是覺得只要花個(gè)半天時(shí)間就能“學(xué)完”所謂的Javascript了惩激。但是店煞,這樣也遺留下了很多大家容易忽略的知識(shí)點(diǎn),而這些知識(shí)點(diǎn)风钻,個(gè)人覺得是Javascript的高級(jí)進(jìn)階知識(shí)點(diǎn)顷蟀。本篇文章從小白的角度總結(jié)了一些在Javascript中容易掉下的坑。
進(jìn)階一:能不能實(shí)現(xiàn)同方法名重載骡技?
在高級(jí)語(yǔ)言中鸣个,方法的重載時(shí)很普遍的。那什么是重載布朦?重載就是方法名相同囤萤,返回值和參數(shù)列表不完全相同的多個(gè)方法。以Java為例是趴,如下:
public int demo(int a, int b);
public int demo(int a, int b, int c);
上面這些方法的重載是沒有任何問(wèn)題的涛舍,但是,在Javascript中唆途,是不是也可以呢富雅?比如下面這個(gè)例子:
function demo(a,b){ console.log(a+b);}
function demo(a,b,c){ console.log(a+b+c);}
demo(1,2);
demo(1,2,3);
首先,Javascript的編譯引擎是不會(huì)報(bào)錯(cuò)的肛搬。對(duì)于類似于函數(shù)重載的方法聲明没佑,Javascript會(huì)認(rèn)為所有的方法名都是唯一的,每個(gè)方法名是每個(gè)方法的唯一標(biāo)識(shí)温赔,所以對(duì)于類似上面代碼的這種重載是無(wú)效的蛤奢,但是,編譯器不會(huì)報(bào)錯(cuò),而是會(huì)進(jìn)行覆蓋远剩,先聲明的方法會(huì)被后聲明的同名方法所替代扣溺,也就是說(shuō)骇窍,所有的demo方法都會(huì)指向demo(a,b,c)這個(gè)方法瓜晤。所以輸出結(jié)果,顯而易見:
NaN //demo(1,2)的執(zhí)行結(jié)果腹纳,因?yàn)?c == undefined
6 //demo(1,2,3)的執(zhí)行結(jié)果痢掠,1+2+3 = 6
所以,結(jié)論就是嘲恍,Javascript中是無(wú)法實(shí)現(xiàn)類似于高級(jí)語(yǔ)言的方法重載的足画。但是,可以通過(guò)方法中的屬性
arguments
來(lái)判斷有多少個(gè)參數(shù)佃牛,從而實(shí)現(xiàn)方法的調(diào)用上重載淹辞,形式上非重載,如下:
function demo(){
var length = arguments.length; //判斷參數(shù)個(gè)數(shù)
if(length==2){
console.log(arguments[0] + argument[1]);
return false;
}
if(length==3){
console.log(arguments[0] + argument[1] + arguments[2]);
}
}
demo(1,2);
demo(1,2,3);
則其輸出結(jié)果如下:
3
6
進(jìn)階二: this指向問(wèn)題
在Javascript中俘侠,一個(gè)隱藏著的比較大的坑莫過(guò)于
this
的指向問(wèn)題了象缀。如果沒有仔細(xì)了解其中的原理,那么肯定會(huì)被指來(lái)指去的this所困擾爷速,接下來(lái)主要通過(guò)一些小例子來(lái)闡述this的作用機(jī)制央星。
指向?yàn)g覽器全局對(duì)象:Window
console.log(this);
那么,上述這段代碼的結(jié)果是什么呢?很明顯惫东,這里的this指向的是當(dāng)前的窗體本身莉给,打印出來(lái)的結(jié)果是
object Window
。所以廉沮,在這種情況下指向的就是當(dāng)前瀏覽器窗體本身颓遏。
demo();
function demo(){
console.log(this);
}
上述代碼的結(jié)果,仍然是
object Window
,所以還是指向當(dāng)前窗體本身滞时。所以叁幢,我們可以通過(guò)this關(guān)鍵字來(lái)訪問(wèn)所有的全局變量,如下:
var a = 1;
function demo(){
console.log(this.a);
}
上述代碼的結(jié)果為
1
指向創(chuàng)建的對(duì)象本身:Object
function demo(a,b,c){
this.a = a;
this.b = b;
this.c = c;
}
var obj = new demo(1,2,3);
alert(obj.a+" "+obj.b+" "+obj.c);
在方法demo內(nèi)部的
this
則指向了自己本身漂洋,這里的輸出結(jié)果為1 2 3
,在Javascript中遥皂,一個(gè)方法也是一個(gè)對(duì)象。
所以刽漂,總的來(lái)說(shuō)演训,this
關(guān)鍵字總的來(lái)說(shuō)可以指向以下兩種對(duì)象:
要么指向全局對(duì)象,要么指向?qū)ο笞约罕旧?/strong>贝咙。
進(jìn)階三: 作用域問(wèn)題
a = 0; //a 具有全局作用域
var b = 0; //b 也具有全局作用域
function demo(){
var a = 1; //這里的a和b只能在demo()內(nèi)訪問(wèn)了样悟,具有局域作用域
var b = 1;
alert(a+" "+b);
alert(this.a + " "+ this.b); //this指向的是全局對(duì)象Window
}
demo();
1.未使用var關(guān)鍵字聲明在全局作用域下不具備變量提升的性質(zhì)。本質(zhì)上,沒有使用var聲明的不是一個(gè)變量窟她,只是不使用 var 關(guān)鍵字創(chuàng)建的只是全局對(duì)象的屬性(全局執(zhí)行上下文中的變量對(duì)象使用全局對(duì)象自身實(shí)現(xiàn))陈症,它并不是一個(gè)變量。
alert(a); //undefined
alert(b); //Can't find variable: b
var a = 1;
b = 1;
//此處修改由簡(jiǎn)友ssbunny提出震糖,非常感謝(2016-07-05)
2.在方法中使用var關(guān)鍵字聲明的變量是局部變量
3.在全局區(qū)域使用var關(guān)鍵字聲明的變量是全局變量
4.雖然作用域問(wèn)題談不上高級(jí)知識(shí)录肯,但是很容易被理解錯(cuò),所以讓在這里復(fù)習(xí)以下
進(jìn)階四: 閉包問(wèn)題
在Javascript中吊说,什么是閉包论咏?
通俗一點(diǎn)地講,閉包就是在函數(shù)內(nèi)部聲明函數(shù)颁井,并且該函數(shù)中訪問(wèn)了方法內(nèi)的局部變量的語(yǔ)法結(jié)構(gòu)厅贪,這種語(yǔ)法結(jié)構(gòu)和面向?qū)ο蟮乃枷肫鋵?shí)本質(zhì)上是一致的 (類似于將一個(gè)方法理解成一個(gè)類)
function demo(){
var a = 0;
return function(){
a++;
}
}
這種結(jié)構(gòu)稱之為閉包,那為什么要使用閉包?
- 由于變量的作用域不同雅宾,外部無(wú)法訪問(wèn)內(nèi)部區(qū)域的變量养涮,而內(nèi)部區(qū)域的變量可以訪問(wèn)外部區(qū)域的變量。如果要在方法外部訪問(wèn)方法的內(nèi)部變量眉抬,就必須通過(guò)函數(shù)內(nèi)部定義函數(shù)的方法來(lái)返回函數(shù)內(nèi)部的變量贯吓。
- 理解起來(lái)和OO思想的對(duì)象是一致的。一般吐辙,我們無(wú)法直接訪問(wèn)類中的私有成員宣决,但是,我們可以通過(guò)定義一些成員方法來(lái)訪問(wèn)昏苏。所以個(gè)人覺得閉包的思想其實(shí)和面向?qū)ο蟮乃枷胧遣畈欢嗟摹?/li>
- 那好尊沸,為什么要使用閉包?首先贤惯,使用閉包可以減少不必要的全局變量洼专,比如一個(gè)計(jì)數(shù)器變量,在腳本中有多個(gè)地方需要使用到孵构,那么如果不想使用全局變量進(jìn)行聲明屁商,則可以使用閉包。
- 閉包也是有不好的地方滴颈墅!如果使用了閉包蜡镶,那么該方法內(nèi)的局部變量(閉包中訪問(wèn)到的那些)會(huì)一直駐留在內(nèi)存中,如果閉包使用不當(dāng)恤筛,會(huì)降低整個(gè)應(yīng)用程序的性能官还。
請(qǐng)大家閱讀以下的例子加以理解:
function demo(){
var count = 0;
return function(){
return ++count; // 注意:count 變量一直駐留在內(nèi)存中
}
}
var cal = demo();
var count1 = cal(); //count == 1
var count2 = cal(); //count == 2
那么,如何釋放在閉包中使用到的變量的內(nèi)存呢毒坛?其實(shí)Javascript中支持直接通過(guò)賦空值來(lái)釋放垃圾的望伦,就上述代碼林说,只需要將方法的引用賦值為空就可以了:
cal=null
進(jìn)階五: 如何復(fù)制一個(gè)對(duì)象
在Javascript中,有一種可以遍歷對(duì)象屬性的方法屯伞,那就是
for...in...
結(jié)構(gòu)腿箩,同時(shí)我們可以借助這種結(jié)構(gòu)來(lái)實(shí)現(xiàn)一個(gè)對(duì)象的復(fù)制,這里指的是深度復(fù)制劣摇,不是淺復(fù)制珠移!
function copy(o){
var newObject = {}; //創(chuàng)建一個(gè)新的空對(duì)象
for(var attribute in o){
newObject[attribute] = o[attribute]; //拷貝對(duì)象中的每一個(gè)屬性
}
return newObject;
}
var copyObject = copy(oldObject);
進(jìn)階六: 原型問(wèn)題
在需要訪問(wèn)對(duì)象的內(nèi)部屬性,或者說(shuō)是方法的局部變量的時(shí)候饵撑,我們可以通過(guò)閉包的形式來(lái)訪問(wèn)剑梳。如果我們需要在對(duì)象內(nèi)定義一些方法供外部調(diào)用唆貌,就需要使用到原型和原型鏈滑潘。
在Javascript中,是不支持繼承的锨咙,但是可以通過(guò)原型實(shí)現(xiàn)一些繼承的功能语卤。
function Demo(){}; //聲明一個(gè)對(duì)象類型(類似于高級(jí)語(yǔ)言中的類)
Demo.prototype = { //定義對(duì)象類型中的方法,使用到的是原型模型
gt : function(a,b){ //定義一個(gè)比較a和b的值得方法酪刀,如果a>b返回true
return (a>b);
},
add : function(){ //定義一個(gè)不限定參數(shù)個(gè)數(shù)的加法運(yùn)算
var length = arguments.length;
alert(length);
var result = 0;
for(var i=0;i<length;i++){
result += arguments[i];
}
return result;
}
};
var demo = new Demo(); //聲明一個(gè)對(duì)象
var isGt = demo.gt(3,2); //3>2 true
var add1 = demo.add(1,3,4); //1+3+4 = 8
var add2 = demo.add(1,3,4,5); //1+3+4+5 = 13
個(gè)人感覺這個(gè)原型模式與高級(jí)語(yǔ)言中的類的成員方法是很類似的粹舵。通過(guò)定義Javascript中的原型方法,從而實(shí)現(xiàn)一個(gè)對(duì)象中的多個(gè)方法的效果骂倘。除了上面這種定義方式之外眼滤,還有下面的這種定義方式。
function Demo(){}
Demo.prototype = function(){
gt = function(a,b){
return a + b;
};
add = function(){
var length = arguments.length;
var result = 0;
for(var i=0;i<length;i++){
result += arguments[i];
}
return result;
}
return {
gt : gt, //返回方法的映射
add : add
}
}();
//或者历涝,直接在function中定義原型方法
function Demo(){
this.add = function(a,b){return a+b;};
}
值得注意的是诅需,如果同時(shí)在函數(shù)內(nèi)和方法的prototype屬性同時(shí)聲明了同一個(gè)方法,那么Javascript會(huì)從下往上尋找最頂層的那個(gè)方法作為有效方法荧库,如下:
function Demo(){
add = function(a+b){return a+b;}; //直接在方法內(nèi)定義add方法堰塌,在最頂層定義
}
Demo.prototype = { //通過(guò)prototype屬性定義add方法
add = function(a,b,c){
return a+b+c;
};
}
var demo = new Demo(); //最頂層的方法add(a,b)是有效的,而add(a,b,c)是無(wú)效的
demo.add(1,2); //1+2 = 3
demo.add(1,2,3); //1+2+undefined = NaN
進(jìn)階七: 等于與絕對(duì)等于問(wèn)題
在Javascript中分衫,有兩種數(shù)據(jù)類型场刑,一種是基本數(shù)據(jù)類型,一種是引用數(shù)據(jù)類型蚪战,即常說(shuō)的對(duì)象類型牵现。而基本數(shù)據(jù)類型中又包含有string、boolean邀桑、number瞎疼、null和undefined五種基本數(shù)據(jù)類型。如下:
類型 | 取值范圍 |
---|---|
String | 字符串概漱,具有無(wú)限個(gè)值 |
Boolean | 布爾值丑慎,具有ture或者false |
Number | 數(shù)字,具有無(wú)限個(gè)值 |
Null | 空類型,具有一個(gè)值null |
Undefined | 未定義類型竿裂,具有一個(gè)值undefined |
需要注意的是玉吁,typeof null == object,而typeof undefined == undefined腻异。舉個(gè)例子:
var a = "123";
var b = 123;
console.log(a==b); //返回true
console.log(a===b); //返回false进副,因?yàn)轭愋筒煌?
好了,由于時(shí)間關(guān)系悔常,本篇關(guān)于對(duì)Javascript高級(jí)進(jìn)階的總結(jié)比較倉(cāng)促影斑,還有很多額外的知識(shí)點(diǎn)還沒有添加進(jìn)來(lái)。其他的高階內(nèi)容將會(huì)在日后陸續(xù)更新進(jìn)來(lái)机打,歡迎大家閱讀矫户。
還有,就是残邀,你在這二十分鐘內(nèi)學(xué)會(huì)了嗎皆辽?
附錄 文章更新日志
2016-06-05 首次發(fā)布,初稿內(nèi)容
2016-06-06 添加章節(jié)《原型問(wèn)題》
2016-07-04 修正文章中的一處歧義
2016-07-05 修正解釋錯(cuò)誤:未使用var創(chuàng)建的變量全是全局變量
2016-07-23 添加章節(jié)《等于與絕對(duì)等于問(wèn)題》