函數javascript
形參在函數中像局部變量同樣工做。java
函數使用它們實參的值來計算返回值,成爲該函數調用表達式的值。除了實參以外。每次調用還會擁有另外一值-本次調用的上下文-這就是this關鍵字的值數組
若是函數掛載在一個對象上,做爲對象的一個屬性,就稱它爲對象的方方法。當經過這個對象來調用函數時,該對象就是這次調用的上下文,也是改函數的this的值。用於初始化一個新建立的對象的函數稱爲構造函數閉包
若是一個函數定義表達式包含名稱,函數的局部做用域將會包含一個綁定到函數對象的名稱,實際上函數的名稱將成爲函數內部的一個局部變量app
function f(){ console.log(f); } f();//
function f(){
window.runnerWindow.proxyConsole.log(f);
}函數
var t=function test(){ console.log(t);//
function test(){
window.runnerWindow.proxyConsole.log(t);
window.runnerWindow.proxyConsole.log(test);
}this
console.log(test);//
function test(){
window.runnerWindow.proxyConsole.log(t);
window.runnerWindow.proxyConsole.log(test);
}spa
};
t();
函數聲明語句被提早到外部腳本或外部函數做用域的頂部,因此以這種方式聲明的函數,能夠被在它定義以前出現的代碼所調用,以表達式定義的函數就另當別論了,爲了調用一個函數,必需要能引用它,而要使用一個以表達式定義的函數以前,必須把它賦值給一個變量。變量的聲明提早了,可是被變量賦值是不會提早的,因此,以表達式方式定義的函數在定義以前是沒法調用的prototype
有4種方法來調用javascript函數code
做爲函數
做爲方法
做爲構造函數
經過它們的call和apply方法間接調用
關鍵值this沒有做用域的限制,嵌套的函數不會從調用它的函數中繼承this。若是嵌套函數做爲方法調用,其this的值指向調用它的對象。若是嵌套函數做爲函數調用。其this是全局對象
var o={m:function(){ var self=this; console.log(this===o);//true f(); function f(){ console.log(this===o);//false console.log(self===o);//true } }}; o.m();
若是函數或者方法調用以前帶有關鍵字new,它就構成構造函數調用
構造函數調用建立一個新的空對象,這個對象繼承自構造函數的prototype屬性,構造函數試圖初始化這個新建立的對象,並將這個對象用作其調用上下文,所以構造函數可使用this關鍵字來引用這個新建立的對象。注意,儘管構造函數看起來像一個方法調用,但它依然會使用這個新對象做爲調用上下文。也就是說,在表達式new o.m()中,調用上下文並非o。
構造函數一般不使用return關鍵字,它們一般初始化新對象,當構造函數的函數體執行完畢時,它會顯示返回。在這種狀況下,構造函數調用表達式的計算結果就是這個新對象的值。然而若是構造函數顯式的使用return語句返回一個對象,那麼調用表達式的值就是這個對象。若是構造函數使用return語句但沒有指定返回值或者返回一個原始值,那麼這時將忽略返回值,同時使用這個新對象做爲調用結果。
可選形參
function getPropertyNames(o,a){ a=a||[]; for(var property in o){ a.push(property); } return a; } var o={x:1,y:2}; var test=getPropertyNames(o); console.log(test);//["x", "y"]
function max(){ var result=Number.NEGATIVE_INFINITY; for(var i=0;i<arguments.length;i++){ if(arguments[i]>result){ result=arguments[i]; } } return result; } console.log(max(4,1,8));//8
當一個函數包含若干形參。實參對象的數組元素是函數形參所對應實參的別名,實參對象中以數字索引,而且形參名稱能夠認爲是相同變量的不一樣命名。經過實參名字來修改實參值的話,經過arguments[]數組也能夠獲取到更改後的值
arguments.callee和arguments.caller 屬性指代當且正在執行的函數
函數做用域的概念:在函數中聲明的變量在整個函數體內都是可見的(包括在嵌套的函數中),在函數的外部是不可見的。不在任何函數內聲明的變量是全局變量,在整個javascript程序中都是可見的
函數的執行依賴於變量做用域,這個做用域是函數定義是決定的,而不是函數調用是決定的
閉包
每次調用javascrip函數的時候,都會爲之建立一個新的對象用來保存局部變量,把這個對象添加至做用域鏈中。當函數返回的時候,就從做用域鏈中將這個綁定變量的對象刪除。若是不存在嵌套的函數,也沒有其餘引用指向這個綁定對象,它就會被看成垃圾回收掉。若是定義了嵌套的函數,每一個嵌套的函數都各自對應一個做用域鏈,而且這個做用域鏈指向一個變量綁定對象。但若是這些嵌套的函數對象在外部函數中保存下來,那麼它們也會和所指向的變量綁定對象同樣當作垃圾回收。可是若是這個函數定義了嵌套的函數,並將它做爲返回值返回或者存儲在某處的屬性裏,這時就會有一個外部引用指向這個嵌套的函數。它就不會被看成垃圾回收,而且它所指向的變量綁定對象也不會被看成垃圾回收。
做用:保存私有變量
var uniqueInteger=(function(){ var counter=0; return function(){ return counter++; }; }()); console.log(uniqueInteger());//0 console.log(uniqueInteger());//1
function constfuncs(){ var funcs=[]; for(var i=0;i<10;i++){ funcs[i]=function(){ return i; }; } return funcs; } var funcs=constfuncs(); console.log(funcs[5]());//10
函數的屬性:
length 形參的數量
prototype每一個函數都包含一個prototype屬性,這個屬性是指向一個對象的引用,這個對象稱爲原型對象,每一個函數都包含不一樣的原型對象,當將函數用做構造函數的時候,新建立的對象會從原型對象上繼承屬性。
call apply看做某個對象的方法,經過調用方法的形式來間接調用函數,它們的第一個實參是要調用函數的母對象,它是調用上下文,在函數體內經過this來得到對它的引用,傳入apply的參數數組能夠是類數組對象也能夠是真實數組。實際上,能夠將當前函數的arguments數組直接傳入apply來調用另外一個函數
bind 將函數綁定到某個對象
由ecma5 bind方法所返回的函數並不包含prototype屬性(普通函數固有的prototype屬性是沒法刪除的),而且將這些綁定的函數用做構造函數時所建立的對象從原始的未綁定的構造函數中繼承繼承prototype。一樣,在使用instanceof運算符時,綁定構造函數和未綁定構造函數並沒有兩樣。
if(!Function.prototype.bind){ Function.prototype.bind=function(o){ var self=this,boundArgs=arguments; return function(){ var arg=[],i; for(i=1;i<boundArgs.length;i++){ arg.push(boundArgs[i]); } for( i=0;i<arguments.length;i++){ arg.push(arguments[i]); } return self.apply(o,arg); }; }; }
Function 關於這個構造函數很是重要的一點,就是它所建立的函數並非使用詞法做用域,想反,函數體代碼的編譯老是會在頂層函數執行
var scope="global"; function construction(){ var scope="local"; return new Function("return scope"); } var p=construction(); console.log(p());//global
將Function構造函數被認爲是在全局做用域中執行eval
判斷是不是函數
function isFunction(x){
return Object.prototype.toString.call(x)==="[object Function]";
}
if((typeof Array.prototype.map)!="function"){ Array.prototype.map=function(f){ var results=[]; var a=this; for(var i=0,len=a.length;i<len;i++){ if(i in a){ results[i]=f.call(null,a[i],i,a); } } }; } if(typeof (Array.prototype.reduce)!="function"){ Array.prototype.reduce=function(f,inital){ var a=this; var i=0,len=a.length,accumulator; if(arguments.length>1){ accumulator=inital; } else{ if(len===0) { throw TypeError(); } while(i<len){ if(i in a){ accumulator=a[i++]; break; } else i++; } if(i==len){ throw TypeError(); } } while(i<len){ if(i in a) { accumulator=f.call(undefined,accumulator,a[i],i,a); } i++; } }; } var sum=function(x,y){return x+y;}; var square=function(x){return x*x;}; var data=[1,1,3,5,5]; var mean=data.reduce(sum)/data.length; var deviations=data.map(function(x){return x-mean;}); console.log(deviations);//[-2, -2, 0, 2, 2] var stddev=Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1)); console.log(stddev);[2]
高階函數就是操做函數的函數
function not(f){ return function(){ console.log(this); var result=f.apply(this,arguments); return !result; }; } var even=function(x){ return x%2===0; }; var odd=not(even);//判斷是否爲奇數 [1,1,3,5,5].every(odd);//true
function compose(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); }; } var square=function(x){return x*x;}; var sum=function(x,y){return x+y;}; var squareofsum=compose(square,sum); console.log(squareofsum(2,3));//25 先求和,再求平方
function compose(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); }; } function array(a,n){ return Array.prototype.slice.call(a,n||0); } function partialLeft(f){ var args=arguments; return function(){ var a=array(args,1); a=a.concat(array(arguments)); return f.apply(this,a); }; } function partialRight(f) { var args=arguments; return function(){ var a=array(arguments); a=a.concat(array(args,1)); return f.apply(this,a); }; } function partial(f){ var args=arguments; return function(){ var a=array(args,1); var i=0,j=0; for(;i<a.length;i++){ if(a[i]===undefined){ a[i]=arguments[j++]; } } a=a.concat(array(arguments,j)); return f.apply(this,a); }; } var f=function(x,y,z){ return x*(y-z); };
console.log(partialLeft(f,2)(3,4));//-2 2*(3-4)
console.log(partialRight(f,2)(3,4));6 3*(4-2)
console.log(partial(f,undefined,2)(3,4));-6
function compose(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); }; } function array(a,n){ return Array.prototype.slice.call(a,n||0); } function partialLeft(f){ var args=arguments; return function(){ var a=array(args,1); a=a.concat(array(arguments)); return f.apply(this,a); }; } function partialRight(f) { var args=arguments; return function(){ var a=array(arguments); a=a.concat(array(args,1)); return f.apply(this,a); }; } function partial(f){ var args=arguments; return function(){ var a=array(args,1); var i=0,j=0; for(;i<a.length;i++){ if(a[i]===undefined){ a[i]=arguments[j++]; } } a=a.concat(array(arguments,j)); return f.apply(this,a); }; } var f=function(x,y,z){ return x*(y-z); }; console.log(partialLeft(f,2)(3,4)); console.log(partialRight(f,2)(3,4)); console.log(partial(f,undefined,2)(3,4)); String.prototype.first=partial(String.prototype.charAt,0); String.prototype.last=partial(String.prototype.substr,-1); var text="javascript"; console.log(text.charAt(0)); console.log(text.first()); console.log(text.charAt(text.length-1)); console.log(text.last()); var not=partialLeft(compose,function(x){return !x;}); var even=function(x){return x%2===0;}; var odd=not(even); var isNumber=not(isNaN); console.log(odd(3)); console.log(isNumber("23123num"));