接觸JavaScript兩年多遇到過各種錯誤,其中有一些讓人防不勝防,原來對JavaScript的誤會如此之深,僅以此文總結一下常見的各種想當然的誤區
string的replace方法我們經常用,替換string中的某些字符,語法像這樣子
string.replace(subStr/reg,replaceStr/function)
第一個參數是要查找的字符串或者一個正則表達式,第二個參數是想替換成的字符串或一個方法,我們可以這麼使用
"I'm Byron".replace("B","b") // I'm byron
記過和我們想得一樣,但是
"I'm a student, and you?".replace("n","N"); // I'm a studeNt, and you?
和我們預期的不一樣,第二個‘n’沒有被替換。字符串的 replace 方法如果第一個參數傳入字符串,那麼只有第一個匹配項會被替換。如果要替換全部匹配項,需要傳入一個 RegExp 對象並指定其 global 屬性。
"I'm a student, and you?".replace(/n/g,"N"); // I'm a studeNt, aNd you?
這樣才可以達到我們目的,關於string replace方法詳細使用可以看看JavaScript string 的replace
關於typeof最容易讓人誤會的就是typeof是操作符,並不是函數,也就是說我們在判斷一變量類型時沒必要加括號,直接使用即可
typeof 1; //number typeof(1); //number, 沒必要這麼寫,但是也沒錯,等於是給變量或常量包裝了一下
typeof 的返回值是字符串,在大多數情況下返回的和我們預期結果是一樣的,但是有幾點注意的地方,我們知道JavaScript有幾種基本類型,number、string、boolean、null、undefined,再就是對象object了,我們看幾個例子
typeof 123; //number typeof NaN //number typeof "123" //string typeof false; //boolean typeof undefined ; //undefined typeof null //object typeof new Array(); //object typeof (new Array()); //覺得不夠清晰可以這麼用,結果是一樣的 typeof (function(){}); //function typeof a; //undefined
1. 123是個數字返回number
2. 雖然NaN表示不是數字,但是typeof仍然會返回number
3. “123” 變成了字符串,所以返回string
4. false 類型就是boolean
5. undefined的類型就是undefined,這個類型的變量只能有一個字面值”undefined”
6. null的類型並不是null,而是object,所以我們不要寄希望與typeof幫我們判斷null
7. typeof如果判斷是對象只會返回object,而不會返回Array、Date的具體類型
9. function也是object的一種,按說也應該直接返回object。但是typeof對它區別對待了,返回了function,我們可以用此判斷變量是否為函數
10. 沒定義的變量同樣返回undefined
在javascript中if並不僅僅判斷boolean決定真假,0、NaN、””、undefined、null、false都會被認為是假
if (!false) { console.log(1); }; if (!0) { console.log(2); }; if (!"") { console.log(3); }; if (!undefined) { console.log(4); }; if (!null) { console.log(5); };
if (!NaN) {
console.log(6);
};
在console中123456都會被打印出來
但是這並不意味著這些值就==false
0==false; //true "0"==false; //true,這個也是true undefined==false //false null==false //false null==null //true NaN==NaN //false null==undefined //true
納尼!!!
相等運算符 (==、!=)
如果兩表達式的類型不同,則試圖將它們轉換為字符串、數字或 Boolean 量。
NaN 與包括其本身在內的任何值都不相等。
負零等於正零。
null 與 null 和 undefined 相等。
相同的字符串、數值上相等的數字、相同的對象、相同的 Boolean 值或者(當類型不同時)能被強制轉化為上述情況之一,均被認為是相等的。其他比較均被認為是不相等的。
所以說 0==”0”; 返回的也會是true。
那麼應該則麼判斷兩個變量究竟是不是一回事兒呢
恆等運算符 (===、!==)
除了不進行類型轉換,並且類型必須相同以外,這些運算符與相等運算符的作用是一樣的,這下就搞定了。
我們可以這樣構造一個Date對象
new Date() //Date {Fri Aug 02 2013 16:50:33 GMT+0800 (China Standard Time)} new Date(milliseconds) //Date {Fri Aug 02 2013 16:53:26 GMT+0800 (China Standard Time)} new Date("2013/08/02") //Date {Fri Aug 02 2013 00:00:00 GMT+0800 (China Standard Time)} new Date(year,month,day,hours,minutes,seconds,ms)
前三種方式沒有什麼問題,但第四種得到的結果回合我們預期的不一致
new Date(2013,08,02) //Date {Mon Sep 02 2013 00:00:00 GMT+0800 (China Standard Time)}
我們可以看到,傳入的月份是08,返回的結果卻是九月。這是因為Date對象的月份是從0開始計數的(天卻不是),即0代表一月,1代表二月…11代表12月。在調用Date實例的getMonth方法時尤其要注意
var d = new Date(2012, 4, 15); // 2012年5月15日 alert(d.getMonth()); // 結果為4
Date.parse方法可以識別兩種格式的字符串參數(標准的長日期格式,比如帶星期的那種,也可以識別,不過不常用):
1. "M/d/yyyy": 美國的日期顯示格式。如果年傳入2位則作為 19xx 處理
2."yyyy-MM-dd" 或 "yyyy/MM/dd": 注意月和日都必須是兩位
Date.parse 的返回結果不是一個Date對象,而是從1970-01-01午夜(GMT)到給定日期之間的毫秒數。可以用Date的構造函數將其轉換為Date對象。
new Date(Date.parse("8/2/2012")); // 正確識別為2012年8月2日 new Date(Date.parse("2012-08-02")); // 正確識別為2012年8月2日 new Date(Date.parse("2012-8-2")); // 不能識別
for…in用來遍歷一個對象中的成員(屬性,方法),如果用來遍歷數組的到的結果並不是預期中數組每項的值,方法神馬的會被遍歷出來
Array.prototype.contains = function(item) { for (var i = 0; i <= this.length - 1; i++) { if(this[i] == item) return this[i]; } } var staff = [ "Staff A", "Staff B" ]; // Normal Enumeration: Only the 2 items are enumerated for (var i = 0; i <= staff.length - 1; i++) { var singleStaff = staff[i]; alert(singleStaff); } // for...in Enumeration: the method "contains" are enumerated, too for (var singleStaff in staff) { alert(singleStaff); }
事實上很多時候我們都會給數組加上其他屬性。比如 jQuery 對象就是一個數組對象加上一些擴展方法;再比如 String.prototype.match 方法返回值就是一個數組(正則表達式及其子表達式的匹配項)加上 index 和 input 兩個屬性。
語法: parseInt(string, radix)
參數 描述 string 必需。要被解析的字符串。 radix可選。表示要解析的數字的基數。該值介於 2 ~ 36 之間。
如果省略該參數或其值為 0,則數字將以 10 為基礎來解析。如果它以 “0x” 或 “0X” 開頭,將以 16 為基數。
如果該參數小於 2 或者大於 36,則 parseInt() 將返回 NaN。
當參數 radix 的值為 0,或沒有設置該參數時,parseInt() 會根據 string 來判斷數字的基數。
舉例,如果 string(開頭結尾空格自動省略) 以 "0x" 開頭,parseInt() 會把 string 的其余部分解析為十六進制的整數。如果 string 以 0 開頭,那麼 ECMAScript v3 允許 parseInt() 的一個實現把其後的字符解析為八進制或十六進制的數字。如果 string 以 1 ~ 9 的數字開頭,parseInt() 將把它解析為十進制的整數。如果字符串的第一個字符不能被轉換為數字,那麼 parseFloat() 會返回 NaN。
parseInt("10"); //返回 10 parseInt("19",10); //返回 19 (10+9) parseInt("11",2); //返回 3 (2+1) parseInt("17",8); //返回 15 (8+7) parseInt("1f",16); //返回 31 (16+15) parseInt("010"); //未定:返回 10 或 8
setTimeout()和setInterval()經常被用來處理延時和定時任務。setTimeout() 方法用於在指定的毫秒數後調用函數或計算表達式,而setInterval()則可以在每隔指定的毫秒數循環調用函數或表達式,直到clearInterval把它清除。
JavaScript其實是運行在單線程的環境中的,這就意味著定時器僅僅是計劃代碼在未來的某個時間執行,而具體執行時機是不能保證的,因為頁面的生命周期中,不同時間可能有其他代碼在控制JavaScript進程。在頁面下載完成後代碼的運行、事件處理程序、Ajax回調函數都是使用同樣的線程,實際上浏覽器負責進行排序,指派某段程序在某個時間點運行的優先級。
我們可以可以把JavaScript想象成在時間線上運行。當頁面載入的時候首先執行的是頁面生命周期後面要用的方法和變量聲明和數據處理,在這之後JavaScript進程將等待更多代碼執行。當進程空閒的時候,下一段代碼會被觸發
除了主JavaScript進程外,還需要一個在進程下一次空閒時執行的代碼隊列。隨著頁面生命周期推移,代碼會按照執行順序添加入隊列,例如當按鈕被按下的時候他的事件處理程序會被添加到隊列中,並在下一個可能時間內執行。在接到某個Ajax響應時,回調函數的代碼會被添加到隊列。JavaScript中沒有任何代碼是立即執行的,但一旦進程空閒則盡快執行。定時器對隊列的工作方式是當特定時間過去後將代碼插入,這並不意味著它會馬上執行,只能表示它盡快執行。
關於setTimeout/setInterval執行時機詳細說明可以看看 setTimeout()和setInterval() 何時被調用執行
console.log(a); //Error:a is not defined ,直接報錯,下面語句沒法執行,一下結果為注釋該句後結果 console.log(b) //undefined var b="Test"; console.log(b);//Test
很奇怪前兩句變量a,b都沒有聲明,第一句卻報錯,第二句能夠輸出undefined?這是因為JavaScript是解釋型語言,但它並不是直接逐步執行的,JavaScript解析過程分為先後兩個階段,一個是預處理階段,另外一個就是執行階段。在預處理階段JavaScript解釋器將完成把JavaScript腳本代碼轉換到字節碼,然後第二階段JavaScript解釋器借助執行環境把字節碼生成機械碼,並順序執行。
也就說JavaScript值執行第一句語句之前就已經將函數/變量聲明預處理了,var b=”Test” 相當於兩個語句,var b;(undefined結果的來源,在執行第一句語句之前已經解析),b=”Test”(這句是順序執行的,在第二句之後執行)。這也是為什麼我們可以在方法聲明語句之前就調用方法的原因。
showMsg(); // This is message function showMsg() { alert('This is message'); }
JavaScript沒有塊級作用域,只有函數級作用域,這就意味著{}在JavaScript中只能起到語法塊的作用,而不能起到作用域塊作用
if(true){//語法塊,保證{}內代碼if條件成立執行 //... var a=3; } console.log(a); //3
上面例子可以清楚看到屬於window的console.log方法依然可以訪問貌似是局部變量的a
首先從一個經典錯誤談起,頁面上有若干個div, 我們想給它們綁定一個onclick方法,於是有了下面的代碼
<div id="divTest"> <span>0</span> <span>1</span> <span>2</span> <span>3</span> </div> <div id="divTest2"> <span>0</span> <span>1</span> <span>2</span> <span>3</span> </div>
$(document).ready(function() { var spans = $("#divTest span"); for (var i = 0; i < spans.length; i++) { spans[i].onclick = function() { alert(i); } } });
很簡單的功能可是卻偏偏出錯了,每次alert出的值都是4,簡單的修改就好使了
var spans2 = $("#divTest2 span"); $(document).ready(function() { for (var i = 0; i < spans2.length; i++) { (function(num) { spans2[i].onclick = function() { alert(num); } })(i); } });
閉包是指有權限訪問另一個函數作用域的變量的函數,創建閉包的常見方式就是在一個函數內部創建另一個函數,只要存在調用內部函數的可能,JavaScript就需要保留被引用的函數。而且JavaScript運行時需要跟蹤引用這個內部函數的所有變量,直到最後一個變量廢棄,JavaScript的垃圾收集器才能釋放相應的內存空間。
這筆書的很膚淺,想簡單了解閉包可以看看JavaScript 閉包究竟是什麼
關於JavaScript的坑坑窪窪真不少,一邊學習一邊總結吧。