function compare(val1,val2){
return val1 - val2; }
var result = compare(5,10);
1,函數的定義沒什麼意義,以後建立一個字符串,就是函數代碼數組
2,函數執行(被調用)的時候發生的事情:(以上面的代碼爲例)瀏覽器
建立一個執行環境execution context ,該對象有一個特殊的屬性叫[scope chain] 做用域鏈,屬性的值是一個類數組對象,如上圖所示,第一個包含了arguments,val1和val2的活動對象,第二個是包含了compare和result,的活動對象。注意,這裏的活動對象都沒有加進去this關鍵字,這是由於,當函數內部經過做用域鏈尋找一個變量時候,與this關鍵字沒有任何關係,因此不會從做用域鏈上面解析。閉包
理解函數的基本原理對於函數的理解函數閉包的概念頗有幫助。app
1.函數做爲參數傳遞dom
最經典的例子就是毀掉函數函數
var fs = require('fs');
fs.readFile('test.txt',function(data,err){ console.log(data); });
2.函數做爲返回值性能
做爲返回值時候,要注意此時的this指向。ui
3.函數柯里化this
函數柯里化指首先接受一些參數,接受到的參數後不當即執行,而是返回一個新函數,剛纔傳入的參數在函數造成的閉包中被保存起來,待到真正求值的時候剛纔保存的參數纔會真正的求值。spa
var cost = (function(){
var args = []; return function(){ if(arguments.length===0){ var money =0; for(var i-0;i<args.length;i++){ money+=args[i]; } return money; }else{ [].push.apply(args,arguments); } } })(); cost(100);//100 cost(200);//200 cost();//300
4.函數節流
函數節流的思想就是讓一些頻繁執行的函數減小執行頻率;好比由於瀏覽器窗口變化引發resize事件的頻繁執行,mouseover,上傳進度等等。
var throttle = function(fn,interval){
var _self = fn,timer,firstTime; return function(){ var args = arguments,_me = this; if(firstTime){ _self.apply(_me,args); return firstTime = false; } if(timer){ return false; } timer = setTimeout(function(){ clearTimeout(timer); timer = null; _self.apply(_me,args); },interval||500); } }; window.onresize = throttle(function(){ console.log(1)},500);
代碼的解決辦法是利用定時器延遲執行,若是定時器在規定時間後還沒執行完,那麼,下一次執行的時候就不會執行,直接返回;
5.分時函數
分時函數應用的場景好比,你的QQ好友有上千個,每個好友是一個dom,這是加載的時候瀏覽器可能吃不消,就要用到setInterval函數來延遲加載。
//ary須要加載的數據,fn加載邏輯,count每一批加載的個數
var timeChunk = function(ary,fn,count){ var obj, t; var len = ary.length; var start = function(){ for(var i=0;i<Math.min(count||1,ary.length);i++){ var obj = ary.shift(); fn(obj); } }; return function(){ t = setInterval(function(){ if(ary.length===0){ return clearInterval(t); } start(); },200); } }
var ary = [];
for(var i=0;i<1000;i++){
ary.push(i);
}
var renderFirendList = timeChunk(ary,function(n){
var div = document.createElement('div');
div.innerHTML = n;
document.body.appendChild(div);
},8);
renderFirendList();
6.惰性加載函數
惰性 加載函數也很常見,好比瀏覽器嗅探中的時間綁定函數
var addEvent = function(elem,type,handler){
if(window.addEventListener){ return elem.addEventListener(type,handler,false); } if(window.addEvent){ return elem.addEvent('on'+type,handler); } }
以上代碼在非IE瀏覽器下每次都不會走第二個分支,而且每次添加一個事件就會執行一次判斷,雖然這不會增長性能開銷,可是能夠利用惰性加載來解決
var addEvent = function(elem, type, handler){
if(window.addEventListener){ addEvent = function(elem, type, handler){ elem.addEventListener(type, handler, false) } }else if(window.addEvent){ addEvent = function(elem, type, handler){ elem.addEvent('on'+type, handler); } } addEvent(elem,type,handler); }
this的判斷只要記住一點,就是在執行的時候動態綁定的,而不是函數聲明時候綁定,如下是優先級
if(hava new){
this 就是new返回的這個對象
}else if(hava call,apply 綁定){
apply,call綁定的那個對象就是this
}else if(有對象調用){
this就是這個調用的對象
}else{
默認綁定到window //這種狀況通常是閉包
}
window.name = 'globalname';
var obj = {}; obj.name = 'lucy'; obj.show = (function(){ console.log(this.name); return function(){ console.log(this.name)} })() obj.show(); VM928:6 globalname VM928:7 lucy
對於上面的代碼,obj.show定義爲一個對象的方法,可是該方法當即執行,而且是在全局的做用域下執行的,因此輸出爲globalname,當obj.show執行完以後返回了一個函數賦值給obj.show,說以obj.show此時才真正是對象的方法,因此第二個返回lucy,這個例子完美的證實了運行時動態綁定this。
另外,有一點就是對於事件綁定函數,this指向那個綁定的元素,settimeout中的延遲函數中的this指向window。
this在遞歸調用中是個坑!!!!!!!!!!!!!!注意注意
閉包是有權訪問另一個函數做用域中的變量的函數。《JavaScript高級程序設計第三版》。
典型的例子是一個內部函數訪問外部函數的變量,即便這個內部函數返回了或者是被調用了,仍然能夠訪問外部變量,以下
function com(propertyName){
return function(obj1,obj2){ var value1 = obj1[propertyName]; var value2 = obj2[propertyName]; return value1 - value2; } } var obj1 ={'name':1}; var obj2 ={'name':2}; var compare = com('name'); console.log(compare(obj1,obj2));
上面例子中,匿名函數訪問了外部函數的局部變量propertyName,而且當它返回了,並且是在其餘地方被調用了仍然能夠訪問。好比com函數返回了一個匿名函數,而且在其餘地方被調用了這個函數,可是仍然能夠訪問propertyName變量對象,可是有一點就是,這裏的propertyName是一個變量對象(活動對象)而不是變量自己,若是是在for等循環語句中就會出現錯誤。以下面的例子:
function foo(){
var result = []; for(var i = 0; i < 5; i++){ result[i] = function(){ return i; } } return result; } var s = foo(); console.log(s[1]());//5
上面代碼輸出結果是5的緣由是每個內部匿名函數包含的活動對象是i這個變量對象,因此最終foo()執行返回的每個result[i](這裏的i沒有變成5是由於它沒在匿名函數內),都是外部foo活動對象,因此最終結果就是5.避免這種結果的方法就是在外面繼續增長一層做用域,使每個result[i]函數都持有本身i的活動對象。
function bar(){
var result = []; for(var j = 0; j < 5; j++){ result[j] = (function(num){ return function(){ return num; } })(j) } return result; }
這2段代碼的函數活動對象圖以下:
第一個代碼全部的result都引用函數foo中活動對象i因此當foo執行完返回後,i變量的值是5因此出現如上所示。
第二段代碼中,因爲參數是按值複製傳遞的,因此j會一次賦值給num,最內層的匿名函數保存了3個活動對象,分別是當即執行函數,bar,和window,而且當即執行函數也是有5個,而且保存了5個num值,這樣就能夠達到預期的效果。
1.setTimeout函數中的應用
for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 0) } //4 //4 //4
這個運行結果的解釋就是i變量只有一個,可是有3個匿名函數都引用了。
2.事件綁定與閉包
for( var i=0; i<5; i++ ) { document.getElementById("p"+i).onclick=function() { alert(i); //訪問了父函數的變量i, 閉包,結果都彈出5 }; };
若是用戶點擊每個按鈕,它彈出的都是5,由於當用戶點擊的時候這裏的i值都變成了5,有點相似於運行時動態綁定的概念,也相似於C語言中的指針,由於i變量是一個雖然存在於外部window對象上,可是內部函數引用了這個變量,因此它不管函數返回都常駐內存,alert的時候就訪問了這個內存中的區域。
1.《JavaScript高級程序設計》
2.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Method_binding