JavaScript是前端必備,而這其中的精髓也太多太多,最近在溫習的時候發現有些東西比較容易忽略,這裏記錄一下,一方面是但願本身在平時應用的時候可以駕輕就熟,另外一方面也但願能給別人帶來一點點的收穫。前端
1、JavaScript的==和===,即相等運算符和等同運算符。數組
相等運算符,若是操做數有相同的類型,則判斷其等同性,若是兩個操做數的值相等,則返回true(相等),不然返回false(不相等);閉包
若是類型不一樣,則按照這樣的狀況來判斷:ide
null和undefined相等;數字與字符串字符比較,字符串轉化爲數字再比較;其中一個爲true轉化爲1再作比較;若是一個值是對象,另外一個是數字或者字符串,則將對象轉化爲原始值(經過toString()或者valueOf()方法),其餘返回false。函數
等同運算符,若是操做數類型不同,直接返回false,類型相同,以下判斷:學習
一、都是數字,若值相同,則二者等同可是NAN除外,由於NAN與自己也不等,不然不相同;spa
二、都是字符串的狀況:值不等則不等同,不然等同;prototype
三、都是布爾值,均爲true/false則等同不然不等同;3d
四、若是兩個操做數引用同一對象(數組或函數)則等同,不然不等;code
五、均爲null/undefined則等同。
2、函數做用域
做用域在全部語言中都有體現,只是在Javascript腳本里有其特殊性-->Javascript中的做用域爲函數體內有效,而無塊兒做用域。在Java或者C#中,咱們能夠寫出下面的循環:
public void method(string obj1,string obj2){ for(int i=0;i<obj1.length;i++){ //do something } //此時的i爲未定義 for(int i=0;i<obj2.length;i++){ //do another thing } }
而在Javascript中不一樣:
function func(){ for(var i = 0; i < array.length; i++){ //do something here. } //此時i仍然有值,及I == array.length print(i);//i == array.length; }
Javascript的函數是在局部做用域內運行的,在局部做用域內運行的函數體能夠訪問其外層的(多是全局做用域)的變量和函數。JavaScript的做用域爲詞法做用域,所謂詞法做用域是說,其做用域爲在定義時(詞法分析時)就肯定下來的,而並不是在執行時肯定,以下例:
var str = "global"; function scopeTest(){ print(str); var str = "local"; print(str); } scopeTest();
您以爲運行結果是什麼呢?global local或者local local 再或者其餘?而正確的結果倒是 undefined local,沒錯,undefined local!
由於在函數scopeTest的定義中,預先訪問了未聲明的變量str,而後纔對str變量進行初始化,因此第一個print(str)會返回undifined錯誤。那爲何函數這個時候不去訪問外部的str變量呢?這是由於,在詞法分析結束後,構造做用域鏈的時候,會將函數內定義的var變量放入該鏈,所以str在整個函數scopeTest內都是可見的(從函數體的第一行到最後一行),因爲str變量自己是未定義的,程序順序執行,到第一行就會返回未定義,第二行爲str賦值,因此第三行的print(str)將返回」local」。
3、數組操做
經常使用的對數組的操做:
contact() 鏈接兩個或更過的數組,並返回結果
join() 把數組全部元素放入一個字符串,元素經過指定的分隔符進行分隔
pop() 刪除並返回最後一個元素與push()對應,向數組末尾添加一個或更多元素,並返回新長度;相似於壓棧和彈棧
reverse() 顛倒元素的順序
shift() 刪除並返回第一個元素
slice() 從已有數組返回制定數組
sort() 對數組元素排序,默認按字母排序,也可按數字大小排:array.sort(function(a,b){return a-b});
splice()刪除元素,並添加新元素
unshift()向數組開頭添加一個或更多元素,並返回新的長度
valueOf() 返回數組對象的原始值
4、JavaScript閉包特性
咱們來看一個例子,若是不瞭解JavaScript的特性,很難找到緣由:
var outter = []; function clouseTest () { var array = ["one", "two", "three", "four"]; for(var i = 0; i < array.length;i++){ var x = {}; x.no = i; x.text = array[i]; x.invoke = function(){ print(i); } outter.push(x); } } //調用這個函數 clouseTest(); print(outter[0].invoke()); print(outter[1].invoke()); print(outter[2].invoke()); print(outter[3].invoke());
運行的結果如何呢?0 1 2 3?這是不少人指望的答案,但是事實真的是這樣嗎?立刻運行一下吧,是否是驚呆了?結果竟然是4 4 4 4 !
其實,在每次迭代的時候,這樣的語句x.invoke = function(){print(i);}並無被執行,只是構建了一個函數體爲」print(i);」的函數對象,如此而已。而當i=4時,迭代中止,外部函數返回,當再去調用outter[0].invoke()時,i的值依舊爲4,所以outter數組中的每個元素的invoke都返回i的值:4。如何解決呢,咱們能夠聲明一個匿名函數,而後立刻執行它。
function clouseTest2(){ var array = ["one", "two", "three", "four"]; for(var i = 0; i < array.length;i++){ var x = {}; x.no = i; x.text = array[i]; x.invoke = function(no){ return function(){ print(no); } }(i); outter.push(x); } }
這個例子中,咱們爲x.invoke賦值的時候,先運行一個能夠返回一個函數的函數,而後當即執行之,這樣,x.invoke的每一次迭代器時至關與執行這樣的語句:
//x == 0 x.invoke = function(){print(0);} //x == 1 x.invoke = function(){print(1);} //x == 2 x.invoke = function(){print(2);} //x == 3 x.invoke = function(){print(3);}
這樣就能夠獲得正確的結果了。
能夠根據Object.prototype.toString.Call(source)來判斷給定對象的類型。另外還有兩個地方須要注意:
一、若是變量做用域爲函數內部則,則外部沒法訪問,如:
var person=function(){ var name="default"; return { getName:function(){return name;}, setName:function(no){name=no} } }(); print(person.name)//undefined
二、引用
引用也是一個比較有意思的主題,JavaScript中的引用始終指向最終的對象,而並不是引用自己,咱們來看一個例子:
var obj = {};//空對象 var ref = obj;//引用 obj.name = "objectA"; print(ref.name);//ref跟着添加了name屬性 obj = ["one", "two", "three"];//obj指向了另外一個對象(數組對象) print(ref.name);//ref還指向原來的對象 print(obj.length);//3 print(ref.length);//undefined
運行結果:
objectA
objectA
3
undefined
obj只是對一個匿名對象的引用,因此,ref並不是指向它,當obj指向另外一個數組對象時能夠看到,引用ref並未改變,而始終指向那個後來添加了name屬性的"空"對象」{}」。理解這個以後,下面這個例子就不難了:
var obj = {};//新建一個對象,並被obj引用 var ref1 = obj;//ref1引用obj,事實上是引用obj引用的空對象 var ref2 = obj; obj.func = "function"; print(ref1.func); print(ref2.func);
聲明一個對象,而後用兩個引用來引用這個對象,而後修改原始的對象,注意這兩步的順序,運行之:
function
function
根據運行結果咱們能夠看出,在定義了引用以後,修改原始的那個對象會影響到其引用上,這一點也應該注意。
以上是最近發現的一些易錯的地方,也但願您能將您遇到的易錯問題分享出來,一塊兒學習,共同進步!