最近在維護代碼的時候,發現一段下面的代碼,發現有幾個不是很熟悉的知識點,先上代碼:javascript
setXy = function(x,y){ return function() { window.midPort = { dataId:"", mode:"view", x:x, y:y }; window.iframe_map.leader(); } }(a,b); function lo() { $(".jsframe").attr("src", 'zzz.html').load(function() { setXy(); }); } setTimeout(lo,400);
代碼中有幾個知識點:html
一、出現了形如 function(x,y){}(a,b)的函數java
二、出現了調用函數是用到的函數名,未加括號jquery
從網上找資料知道,function(x,y){}(a,b)是當即執行的表達式,在解釋這個以前,先了解一下幾張函數的表現形式。瀏覽器
函數聲明:function fnName(){},使用關鍵字function聲明一個函數,並指定一個函數名,叫函數聲明。緩存
函數表達式:var fnName = function(){},使用function關鍵字聲明一個函數,可是未給函數名,最後將匿名函數賦給一個變量,這叫函數表達式。安全
匿名函數:和函數聲明的區別是,沒有給定函數名,它屬於函數表達式。函數
js引擎在解析js代碼的時候,函數聲明會獲得提高,就是說具備優先解析權,在其餘代碼解析以前先解析,一下子示例代碼中,咱們會看到。而函數表達式,就不會具備這種提高,在調用該函數以前,必須先定義這個函數表達式,不然會報錯。spa
下面咱們先在在瀏覽器控制檯裏面寫了一段代碼,先調用tt函數,而後定義一個函數聲明,結果是tt內的函數能被執行到。.net
接下來咱們定義一個函數表達式,在函數表達式定義以前調用(注意,執行以前在瀏覽器控制檯清空緩存一下,不然以前定義的函數聲明會被調用,或者定義的函數表達式和以前定義的函數聲明的名字不要同樣)
結果是,後臺報tt函數未定義,說明該函數表達式未被聲明,驗證了函數表達式在解析的時候是不會被提高的。
咱們再在函數表達式定義以後調用,這是咱們再開發的時候經常使用的套路,結果是能正常調用的:
這種寫法是說調用的時候當即執行,須要注意的是,這種寫法是函數表達式纔有的,沒加括號和加括號到底有什麼區別呢,咱們代碼演示一下:
function t1(){ alert("t1"); t2(); } var t2 = function(){ alert("t2"); }()
上面是說在調用的時候,當即執行,看看結果
t1並無彈出,是由於t1是函數聲明,沒辦法當即執行,只有在調用了(形如t1())的時候纔會彈出,t2彈出,說明在解析t1函數聲明到t2();的時候,t2因爲是當即執行,因此會彈出。
若是如今調用t1(),咱們知道會彈出 t1 ,可是會彈出 t2嗎?咱們就代碼直接試一下吧,結果以下:
能夠得出一個結論,當即執行的函數是一次性的,後續不能夠再調用了。
下面咱們再設置成不是當即執行的函數,效果和咱們預想的是同樣的,即函數只有在調用了纔會彈出,調用t1彈出t1 ,調用t2,彈出t2;
function t1(){ alert("t1"); t2(); } var t2 = function(){ alert("t2"); }
上面咱們的演示代碼是用的普通的函數表達式的當即執行,咱們知道,匿名函數也是一種函數表達式,那換成匿名函數怎麼使用當即執行呢。
因爲匿名函數不能被調用,咱們就直接寫一個當即執行的匿名函數,代碼以下:
(function(){alert('nimin')})()
在控制檯執行了這段代碼之後,會當即彈出nimin彈出框,可是匿名函數後面沒有加括號的話,不會執行,只是在後臺打印了一個這個函數。
咱們注意到,在匿名函數外層加了一個括號 ,再在後面加括號,若是不在匿名函數外層加括號,用當即執行有效果嗎,用下面代碼:
function(){alert('nimin')}()
結果後臺就報錯了:
這個錯誤是說在解析到(位置的時候,原本應該是一個函數名的,可是是一個(,致使語法錯誤,說明當即執行函數只能在函數表達式中生效。
那匿名函數當即執行還有哪些寫法呢,下面是參考1 文中寫的幾種寫法:
(function(a){ console.log(a); //firebug輸出123,使用()運算符 })(123); (function(a){ console.log(a); //firebug輸出1234,使用()運算符 }(1234)); !function(a){ console.log(a); //firebug輸出12345,使用!運算符 }(12345); +function(a){ console.log(a); //firebug輸出123456,使用+運算符 }(123456); -function(a){ console.log(a); //firebug輸出1234567,使用-運算符 }(1234567); var fn=function(a){ console.log(a); //firebug輸出12345678,使用=運算符 }(12345678)
能夠看到輸出結果,在function前面加!、+、 -甚至是逗號等到均可以起到函數定義後當即執行的效果,而()、!、+、-、=等運算符,都將函數聲明轉換成函數表達式,消除了javascript引擎識別函數表達式和函數聲明的歧義,告訴javascript引擎這是一個函數表達式,不是函數聲明,能夠在後面加括號,並當即執行函數的代碼。
加括號是最安全的作法,由於!、+、-等運算符還會和函數的返回值進行運算,有時形成沒必要要的麻煩。
javascript中沒用私有做用域的概念,若是在多人開發的項目上,你在全局或局部做用域中聲明瞭一些變量,可能會被其餘人不當心用同名的變量給覆蓋掉,根據javascript函數做用域鏈的特性,可使用這種技術能夠模仿一個私有做用域,用匿名函數做爲一個「容器」,「容器」內部能夠訪問外部的變量,而外部環境不能訪問「容器」內部的變量,因此( function(){…} )()內部定義的變量不會和外部的變量發生衝突,俗稱「匿名包裹器」或「命名空間」。
JQuery使用的就是這種方法,將JQuery代碼包裹在( function (window,undefined){…jquery代碼…} (window)中,在全局做用域中調用JQuery代碼時,能夠達到保護JQuery內部變量的做用。
最後一個問題,調用函數加了括號會怎麼樣,咱們前面說的當即執行,在這裏是同樣的道理,若是一個變量引用了另個函數表達式的話,加上括號之後就是當即執行,若是沒有加括號的就是等函數被調用到的時候纔會被執行,也就是定時器回調後纔會執行,若是lo中加了括號,定時器就沒有效果了。
reference: