函數只定義一次,但可能被執行或調用任意次。JS函數是參數化的,函數的定義會包括一個稱為形參的標識符列表,這些參數在函數體中像局部變量一樣工作。函數調用會為形參提供實參的值。函數使用它們實參的值來計算返回值,成為該函數調用表達式的值。除了實參之外,每次調用還會擁有另一個值,本次調用的上下文——this關鍵字的值。
如果函數掛載在一個對象上,作為對象的一個屬性,就稱它為對象的方法。當通過這個對象來調用函數時,該對象就是此次調用的上下文,也就是該函數的this的值。用于初始化一個新創建的對象的函數稱為構造函數。
在JS里,函數即對象,JS可以把函數賦值給變量,或者作為參數傳遞給其他函數。因為函數是對象,所以可以給它們設置屬性,甚至調用它們的方法。
JS的函數可以嵌套在其他函數中定義,這樣它們就可以訪問它們被定義時所處的作用域中的任何變量。這意味著JS函數構成了一個閉包。
函數定義
函數使用function關鍵字來定義,它可以用在函數定義表達式或函數聲明語句里。在這兩種形式中,函數定義都從function關鍵字開始,其后跟隨這些組成部分:
函數名稱標識符。新定義的函數對象會賦值給這個變量。對函數定義表達式來說,這個名字是可選的:如果存在,該名字只存在于函數體中,并指代該函數對象本身。
一對圓括號。其中包含0個或多個用逗號隔開的標識符組成的列表。這些標識符是函數的參數名稱,它們就像函數體中的局部變量一樣。
一對花括號。其中包含0或多條JS語句。這些語句構成了函數體,一旦調用函數就會執行這些語句。
//輸出o的每個屬性的名稱和值,返回undefined
function printprops(o){
for(var p in o){
console.log(p+":"+o[p]+"\n")
}
}
//這個函數表達式定義了一個函數用來求傳入參數的平方
var square=function(x){return xx;}
//函數表達式可以包含名稱,這在遞歸時很有用
var f=function fact(x){
if(x<=1){return 1;}else{ return xfact(x-1);}
};
//函數表達式也可以作為參數傳給其它參數
data.sort(function(a,b){return a-b;});
//函數表達式有時定義后立即調用
var tansquared=(function(x){return x*x;}(10))
以表達式方式定義的函數,函數的名稱是可選的。一條函數聲明語句實際上聲明了一個變量,并把一個函數對象賦值給它。定義函數表達式時并沒有聲明一個變量。如果一個函數定義表達式包含名稱,函數的局部作用域將會包含一個綁定到函數對象的名稱。實際上,函數的名稱將成為函數內部的一個局部變量。
函數聲明語句被提前到外部腳本或外部函數作用域的頂部,所以以這種方式聲明的函數,可以被在它定義之前出現的代碼調用。以表達式方式定義的函數在定義之前無法調用,因為變量的聲明雖然提前了,但給變量賦值是不會提前的。
return語句導致函數停止執行,并返回它的表達式的值給調用者。如果return語句沒有一個與之相關的表達式,則它返回undefined值。如果一個函數不包含return語句,那它就只執行函數體中的每條語句,并返回undefined值給調用者。
嵌套函數
函數可以嵌套在其他函數里。
function hypotenuse(a,b){
function square(x){return x*x;}
return Math.sqrt()(square(a)+square(b));
}
嵌套函數的變量作用域規則:它們可以訪問嵌套它們的函數的參數和變量。
函數聲明語句并非真正的語句,ECMAScript規范只是允許它們作為頂級語句。它們可以出現在全局代碼里,或者內嵌在其他函數中,但它們不能出現在循環語句、條件判斷語句、或try/catch/finally和with語句中。函數定義表達式可以出現在JS代碼的任何地方。
函數調用
構成函數主體的JS代碼在定義之時并不會執行,只有調用該函數時,它們才會執行。有4種方式來調用函數:
作為函數
作為方法
作為構造函數
通過它們的call()和apply()方法間接調用
函數調用
使用調用表達式可以進行普通的函數調用也可進行方法調用。一個調用表達式由多個函數表達式組成,每個函數表達式都是由一個函數對象和左圓括號、參數列表和右圓括號組成,參數列表是由逗號分隔的0個或多個參數表達式組成。如果函數表達式是一個屬性訪問表達式,即該函數是一個對象的屬性或數組中的一個元素,那么它就是一個方法調用表達式。
printprops({x:1});
var total=distance(0,0,2,1)+distance(2,1,3,5);
var probability=factorial(5)/factorial(13);
在一次調用中,每個參數表達式都會計算出一個值,計算的結果作為參數傳遞給另外一個函數。這些值作為實參傳遞給聲明函數時定義的形參。在函數體中存在一個形參的引用,指向當前傳入的實參列表,通過它可以獲得參數的值。
對于普通的函數調用,函數的返回值成為調用表達式的值。如果該函數返回是因為解釋器到達結尾,返回值就是undefined。如果函數返回是因為解釋器執行到一條return語句,返回值就是return之后的表達式的值,如果return語句沒有值,則返回undefined。
以函數形式調用的函數通常不適用this關鍵字。不過this可以用來判斷當前是否是嚴格模式。根據ES3和非嚴格的ES5對函數調用的規定,調用上下文(this的值)是全局對象。在嚴格模式下調用上下文則是undefined。
//定義并調用一個函數來確定當前腳本運行時是否為嚴格模式
var strict=(function(){return !this;}());
方法調用
一個方法就是保存在一個對象的屬性里的函數。如果有一個函數f和一個對象o,則給o定義一個名為m()的方法可用下面的方法:
o.m=f;
o.m(); //調用m()方法
對方法調用的參數和返回值的處理,和普通函數調用一致。方法調用和函數調用有一個重要區別,即調用上下文。屬性訪問表達式由兩部分組成:一個對象(o)和屬性名稱(m)。在像這樣的方法調用表達式里,對象o成為調用上下文,函數體可以使用this引用該對象。
var calculator={
operand1:1,
operand2:1,
add:function(){
this.result=this.operand1+this.operand2;
}
};
calculator.add(); //calculator.result=>2
大多數方法調用使用點符號來訪問屬性,使用方括號也可以進行屬性訪問操作。
o"m"; //o.m(x,y)的另一種寫法
a0; //假設a[0]是一個函數
方法調用可能包括更復雜的屬性訪問表達式:
customer.surname.toUpperCase(); //調用customer.surname的方法
f().m(); //在f()調用結束后繼續調用返回值中的方法m()
任何函數只要作為方法調用實際上都會傳入一個隱式的實參。這個實參是一個對象,方法調用的母體就是這個對象。通常來講,基于那個對象的方法可以執行多種操作,方法調用的語法表明了函數將基于一個對象進行操作。
rect.setSize(width,height);
setRectSize(width,height);
第一行的方法調用表明這個函數執行的載體是rect對象,函數中的所有操作都將基于這個對象。
this是一個關鍵字,不是變量也不是屬性名。JS的語法不允許給this賦值。
和變量不同,this沒有作用域的限制,嵌套的函數不會從調用它的函數中繼承this。如果嵌套函數作為方法調用,其this的值指向調用它的對象。如果嵌套函數作為函數調用,其this值不是全局對象(非嚴格模式下)就是undefined(嚴格模式下)。如果想訪問外部函數的this值,需要將this的值保存在一個變量里,這個變量和內部函數都同在一個作用域內。
var o={
m:function(){
var self=this;
console.log(this===o); //true
f();
function f(){ //定義一個嵌套函數f()
console.log(this===o); //false,this的值是全局對象或undefined
console.log(self===o); //true
}
}
};
o.m();
構造函數調用
如果函數或方法調用之前帶有關鍵字new,它就構成構造函數調用。如果構造函數調用在圓括號內包含一組實參列表,先計算這些實參表達式,然后傳入函數內,這和函數調用和方法調用是一致的。但如果構造函數沒有形參,構造函數調用的語法允許省略實參列表和圓括號。凡是沒有形參的構造函數調用都可以省略圓括號。
var o=new Object();
var o=new Object;
構造函數調用創建一個新的空對象,這個對象繼承自構造函數的prototype屬性。構造函數試圖初始化這個新創建的對象,并將這個對象用做其調用上下文,因此構造函數可以使用this關鍵字來引用這個新創建的對象。盡管構造函數看起來像一個方法調用,它依然會使用這個新對象作為調用上下文。也就是說,在表達式new o.m()中,調用上下文并不是o。
構造函數通常不使用return關鍵字,它們通常初始化新對象,當構造函數的函數體執行完畢時,它會顯式返回。在這種情況下,構造函數調用表達式的計算結果就是這個新對象的值。如果構造函數顯式地使用return語句返回一個對象,那么調用表達式的值就是這個對象。如果構造函數使用return語句但沒有指定返回值,或返回一個原始值,那么這時將忽略返回值,同時使用這個新對象作為調用結果。
間接調用
函數也是對象,函數對象也可以包含方法。其中的兩個方法call()和apply()可以間接地調用函數。兩個方法都允許顯式指定調用所需的this值,也就是說任何函數可以作為任何對象的方法來調用,哪怕這個函數不是那個對象的方法。兩個方法都可以指定調用的實參。call()方法使用它自有的實參列表作為函數的實參。apply()方法則要求以數組的形式傳入參數。
函數的實參和形參
函數定義并未指定函數形參的類型,函數調用也未對傳入的實參值做任何類型的檢查。函數調用甚至不檢查傳入形參的個數。
可選形參
當調用函數的時候傳入的實參比函數聲明時指定的形參個數要少,剩下的形參都將設置為undefined值。因此在調用函數時形參是否可選以及是否可以省略應當保持較好的適應性。為做到這一點,應當給省略的參數賦一個合理的默認值。
//將對象o中可枚舉的屬性名追加至數組a中,并返回這個數組a
//如果省略a,則創建一個新數組并返回這個新數組
function getPropertyNames(o,/optional/a){
if(a===undefined){a=[];} //如果未定義則使用新數組或使用a=a||[];
for(var property in o){a.push(property);}
return a;
}
//這個函數調用可傳入1個或2個實參
var a=getPropertyNames(o); //將o的屬性存儲到一個新數組中
getPropertyNames(p,a); //將p的屬性追加至數組a中
當用這種可選實參來實現函數時,需要將可選實參放在實參列表的最后。調用函數是沒辦法省略第一個實參并傳入第二個實參的,它必須將undefined作為第一個實參顯式傳入。在函數定義中使用/optional/來強調形參是可選的。
可變長的實參列表:實參對象
當調用函數的時候傳入的實參個數超過函數定義時的形參個數時,沒有辦法直接獲得未命名值的引用。參數對象解決了這個問題。在函數體中,標識符arguments是指向實參對象的引用,實參對象是一個類數組對象,這樣可以通過數字下標就能訪問傳入函數的實參值,而不用非要通過名字來得到實參。
假設定義了函數f,它的實參只有一個x。如果調用這個函數時傳入兩個實參,第一個實參可以通過參數名x來獲得,也可以通過arguments[0]來得到。第二個實參只能通過arguments[1]來得到。arguments也包含一個length屬性。,用以標識其所包含元素的個數。
function f(x,y,z){
//首先驗證傳入實參的個數是否正確
if(arguments.length!=3){
throw new Error("function f called with"+arguments.length+"arguments")
}
}
省略的實參都將是undefined,多出的參數會自動省略。
實參對象有一個重要的用處,就是讓函數可以操作任意數量的實參。
function max(/.../){
var max=Number.NEGATIVE_INFINITY;
//遍歷實參,查找并記住最大值
for(var i=0;i<arguments.length;i++){
if(arguments[i]>max) {max=arguments[i];}
return max; //返回最大值
}
}
var largest=max(1,10,100,2,3,10000); 10000
類似這種函數可以接收任意個數的參數,這種函數也稱為不定實參函數。不定實參函數的實參個數不能為0,arguments[]對象最適合在這樣一類函數中,這類函數包含固定個數的命名和必須參數,以及隨后個數不定的可選實參。
arguments并不是真正的數組,它是一個實參對象。每個實參對象都包含以數字為索引的一組元素以及length屬性。
數組對象包含一個特性。在非嚴格模式下,當一個函數包含若干形參,實參對象的數組元素是函數形參所對應實參的別名,實參對象中以數字索引,并且形參名稱可以認為是相同變量的不同命名。通過實參名字來修改實參值的話,通過arguments[]數組也可以獲取到更改后的值。
function f(x){
console.log(x); //輸出實參的初始值
arguments[0]=null; //修改實參數組的元素同樣會修改x的值
console.log(x); //輸出null
}
在ES5中移除了實參對象的這個特性。在非嚴格模式中,函數里的arguments僅僅是一個標識符,在嚴格模式中,它變成了一個保留字。嚴格模式中的函數無法使用arguments作為形參名或局部變量名,也不能給arguments賦值。
callee和caller屬性
除了數組元素,實參對象還定義了callee和caller屬性。在ES5嚴格模式中,對這兩個屬性的讀寫操作都會產生一個類型錯誤。在非嚴格模式下callee屬性指代當前正在執行的函數。caller是非標準的,它指代調用當前正在執行的函數的函數。通過caller屬性可以訪問調用棧。callee屬性在某些時候非常有用,比如在匿名函數中通過callee來遞歸的調用自身。
var factorial=function(x){
if(x<=1) {return 1;}
return x*arguments.callee(x-1);
};
將對象屬性用做實參
當一個函數包含超過三個形參時,最好通過名/值對的形式來傳入參數,這樣參數的順序就無關緊要了。為了實現這種調用,定義函數的時候,傳入的實參都寫入一個對象中,在調用的時候傳入一個對象,對象中的名/值對是真正需要的實參數據。
//將原始數組的length元素復制至目標數組
//開始復制原始數組的from_start元素
//并且將其復制至目標數組的to_start中
function arraycopy(/array/from,/index/from_start,/array/to,/index/to_start,/integer/length){
//邏輯代碼
}
function easycopy(args){
arraycopy(args.from,args.from_start||0,args.to,args.to_start||0,args.length);
}
var a=[1,2,3,4],b=[];
easycopy({from:a,to:b,length:4});
實參類型
JS函數的形參并未聲明類型,在形參傳入函數體之前也未做任何類型檢查。當一個函數可以接收任意數量的實參時,可以使用省略號。
function max(/number.../){}
如果函數期望接收一個字符串實參,而調用函數時傳入其他類型值,所傳入的值會在函數體內將其用做字符串的地方轉換為字符串類型。所有的原始類型都可以轉換為字符串,所有的對象都包含toString()方法(盡管不一定有),所以不會報錯。
JS是一種靈活的弱類型語言,有時適合編寫實參類型和實參個數不確定的函數。
//可以接收任意數量的實參,并可以遞歸地處理實參是數組的情況
//這個方法盡可能的在拋出異常之前將非數字轉換為數字
function flexisum(a){
var total=0;
for(var i=0;i<arguments.length;i++){
var element=arguments[i],n;
if(element==null){continue;} //忽略null和undefined實參
if(isArray(element)){n=flexisum.apply(this,element);} //如果是數組遞歸計算累加和
else if(typeof element=="function"){ //如果是函數
n=Number(element()); //調用它并做類型轉換
}
else {n=Number(element);} //否則直接做類型轉換
if(isNaN(n)){throw Error("flexisum():can't convert "+element+"to number")};
total+=n;
}
return total;
}
作為值的函數
可以將函數賦值給變量,存儲在對象的屬性或數組的元素中,作為參數傳入另外一個函數。
fucntion square(x){return x*x;}
這個定義創建一個新的函數對象,并將其賦值給變量square。函數的名字實際上是看不見的,它(square)僅僅是變量的名字,這個變量指代函數對象。函數還可以賦值給其他變量或賦值給對象的屬性。當函數作為對象的屬性調用時,函數就稱為方法。
fucntion square(x){return xx;}
var s=square; //square(4)=s(4)
var o={square:function(){return xx;}};
var y=o.square(16); // y=256
var a=[function(x){return x*x;},20];
a0; //400
自定義函數屬性
函數并不是原始值,而是一種特殊的對象,也就是說,函數可以擁有屬性。當函數需要一個“靜態”變量來在調用時保持某個值不變,最方便的方法就是給函數定義屬性。
//由于函數聲明被提前了,因此可以在函數聲明之前給它的成員賦值
uniqueInteger.counter=0;
//每次調用這個函數都會返回一個不同的整數
function uniqueInteger(){
return uniqueInteger.counter++; //先返回計數器的值,然后計數器自增1
}
//第二個例子,計算階乘并將結果緩存至函數的屬性中
function factorial(n){
if(isFinite(n)&&n>0&&n==Math.round(n)){ //有限的正整數
if(!(n in factorial)){ //如果沒有緩存結果
factorial[n]=n*factorial(n-1); //計算結果并緩存
}
return factorial[n]; //返回緩存結果
}else{return NaN;} //如果輸入有誤
}
factorial[1]=1; //初始化緩存以保存這種基本情況
作為命名空間的函數
函數作用域的概念:在函數中聲明的變量在整個函數體內都是可見的,在函數的外部是不可見的。不在任何函數內聲明的變量是全局變量。在JS中是無法聲明只在一個代碼塊內可見的變量的,基于這個原因,我們常常簡單的定義一個函數用做臨時的命名空間,在這個命名空間內定義的變量不會污染全局環境。
閉包
函數的執行依賴于變量作用域,這個作用域是在函數定義時決定的,而不是函數調用時決定的。函數對象可以通過作用域鏈相互關聯起來,函數體內的變量都可以保存在函數作用域內,這種特性稱為閉包。
所有的函數都是閉包:它們都是對象,它們都關聯到作用域鏈。定義大多數函數時的作用域鏈在調用函數時依然有效,但這并不影響閉包。
var scope="global scope";
function checkscope(){
var scope="local scope";
function f(){return scope;}
return f();
}
checkscope(); //local scope
//改變一下代碼
function checkscope(){
var scope="local scope";
function f(){return scope;}
return f;
}
checkscope()(); //local scope
改動代碼后checkscope()現在僅僅返回函數內嵌套的一個函數對象,而不是直接返回結果。函數的執行用到了作用域鏈,這個作用域鏈是函數定義時創建的。嵌套的函數f()定義在這個作用域鏈里,其中的變量scope一定是局部變量,不管在何時何地執行f(),這種綁定在執行f()時依然有效。因此返回local scope。
var uniqueInteger=(function(){
var counter=0;
return function(){return counter++;}
}());
這段代碼定義了一個立即調用函數,因此是這個函數的返回值賦值給變量uniqueInteger。這個函數返回另外一個函數,這是一個嵌套的函數,我們將它賦值給變量uniqueInteger,嵌套的函數是可以訪問作用域內的變量的,而且可以訪問外部函數中定義的counter變量。當外部函數返回之后,其他任何代碼都無法訪問counter變量,只有內部的函數可以訪問到它。
像counter一樣的私有變量不是只能用在一個單獨的閉包內,在同一個外部函數內定義的多個嵌套函數也可以訪問它,這多個嵌套函數都共享一個作用域鏈。
function counter(){
var n=0;
return {
count:function(){return n++;}
reset:function(){n=0;}
};
}
var c=counter(),d=counter();
c.count(); //0
d.count(); //0
c.reset(); //reset()和count()方法共享狀態
c.count(); //0
d.count(); //1
reset()方法和count()方法都可以訪問私有變量n。每次調用counter()都會創建一個新的作用域鏈和一個新的私有變量。因此如果調用counter()兩次,則會得到兩個計數器對象,而且彼此包含不同的私有變量,調用其中一個計數器對象的count()或reset()不會影響到另外一個對象。
函數屬性、方法和構造函數
length屬性
在函數體里,arguments.length表示傳入函數的實參的個數。函數的length屬性是只讀屬性,它代表函數實參的數量,這里的參數指的是“形參”而非“實參”,也就是在函數定義時給出的實參個數,通常也是在函數調用時期望傳入函數的實參個數。
//這個函數使用arguments.callee,因此它不能在嚴格模式下工作
function check(args){
var actual=args.length; //實參的真實個數
var expected=args.callee.length; //期望的實參個數
if(actual!==expected){
throw Error("Expected"+expected+"args;got"+actual);
}
}
function f(x,y,z){
check(arguments);
return x+y+z;
}
prototype屬性
每一個函數都包含一個prototype屬性,這個屬性是指向一個對象的引用,這個對象稱做“原型對象”。每一個函數都包含不同的原型對象。當將函數用做構造函數的時候,新創建的對象會從原型對象上繼承屬性。
call()方法和apply()方法
我們可以將call()和apply()看做是某個對象的方法,通過調用方法的形式來間接調用函數。call()和apply()的第一個實參是要調用函數的母對象,它是調用上下文,在函數體內通過this來獲得對它的引用。要想以對象o的方法來調用函數f(),可以像下面這樣:
f.call(o);
f.apply(o);
//每行代碼和下面的代碼功能類似
//假設對象o中預先不存在名為m的屬性
o.m=f; //將f存儲為o的臨時方法
o.m();
delete o.m;
在ES5的嚴格模式中,call()和apply()的第一個實參都會變為this的值,哪怕傳入的實參是原始值甚至是null或undefined。在ES3和非嚴格模式中,傳入的null和undefined都會被全局對象替代,而其他原始值則會被相應的包裝對象所替代。
對于call()來說,第一個調用上下文實參之后的所有實參就是要傳入待調用函數的值。
//以對象o
的方法的形式調用函數f
,并傳入兩個參數
f.call(o,1,2);
apply()方法傳入的實參都放入一個數組中。
f.apply(o,[1,2])
如果一個函數的實參可以是任意數量,給apply()傳入的參數數組可以是任意長度的。
var biggest=Math.max.apply(Math,array_of_numbers);
傳入apply()的參數數組可以是類數組對象也可以是真實數組。可以將當前函數的arguments數組直接傳入(另一個函數的)apply()來調用另一個函數。
//將對象o中名為m()的方法替換為另一個方法
//可以在調用原始的方法之前和之后記錄日志消息
function trace(o,m){
var original=o[m]; //在閉包中保存原始方法
o[m]=function(){
console.log(new Date(),"entering:",m); //輸出日志消息
var result=original.apply(this,arguments); //調用原始函數
console.log(new Date(),"exiting:",m);
return result;
}
}
bind()方法
bind()是ES5中新增的方法。這個方法主要作用是將函數綁定至某個對象。當在函數f()上調用bind()方法并傳入一個對象o作為參數,這個方法將返回一個新的函數。(以函數調用的方式)調用新的函數將會把原始的函數f()當做o的方法來調用。傳入新函數的任何實參都將傳入原始函數。
function f(y){return this.x+y;} // 待綁定的函數
var o={x:1}; // 將要綁定的對象
var g=f.bind(o); // 通過調用g(x)來調用o.f(x)
//g(2) =>3
可以通過如下代碼實現這種綁定。
//返回一個函數,通過調用它來調用o
中的方法f()
,傳遞它所有的實參
function bind(f,o){
if(f.bind) {return f.bind(o);} //如果bind()方法存在,使用bind()方法
else return function(){
return f.apply(o,arguments);
}
}
ES5中的bind()方法不僅僅是將函數綁定至一個對象,它還附帶一些其它應用:除了第一個實參之外,傳入bind()的實參也會綁定至this,這個附帶的應用是一種常見的函數式編程技術,有時也被稱為“柯里化”。
var sum=function(x,y){return x+y;}
//創建一個類似sum的新函數,但this的值綁定到null
//并且第一個參數綁定到1,這個新的函數期望只傳入一個實參
var succ=sum.bind(null,1);
succ(2); //3,x綁定到1,并傳入2作為實參y
function f(y,z){return this.x+y+z;};
var g=f.bind({x:1,2}); //g(3)=6,this.x綁定到1,y綁定到2,z綁定到3
bind()方法返回一個函數對象,這個函數對象的length屬性是綁定函數的形參個數減去綁定實參的個數。bind()方法可以順帶用作構造函數,如果bind()返回的函數用作構造函數,將忽略傳入bind()的this,原始函數就會以構造函數的形式調用,其實參也已經綁定。由bind()方法所返回的函數并不包含prototype屬性,并且將這些綁定的函數用作構造函數時所創建的對象從原始的未綁定的構造函數中繼承prototype。同樣,在使用instanceof運算符時,綁定構造函數和未綁定構造函數并無兩樣。
toString()方法
函數的toString()方法返回一個字符串,這個字符串和函數聲明語句的語法有關。實際上,大多數的toString()方法的實現都返回函數的完整源碼。內置函數往往返回一個類似[native code]的字符串作為函數體。
Function()構造函數
除了函數聲明和函數表達式,函數還可以通過Function()構造函數來定義。
var f=new Function("x","y","return x*y;");
Function()構造函數可以傳入任意數量的字符串實參,最后一個實參所表示的文本就是函數體,它可以包含任意的JS語句,每兩條語句之間用分號分隔。傳入構造函數的其他所有的實參字符串是指定函數的形參名字的字符串。如果定義的函數不包含任何參數,只需給構造函數傳入一個字符串——函數體即可。
Function()構造函數并不需要通過傳入實參以指定函數名。就像函數字面量一樣,Function()構造函數創建一個匿名函數。
關于Function()構造函數有幾點需要注意:
Function()構造函數允許JS在運行時動態的創建并編譯函數。
每次調用Function()構造函數都會解析函數體,并創建新的函數對象。如果是在一個循環或者多次調用的函數中執行這個構造函數,執行效率會受影響。相比之下,循環中的嵌套函數和函數定義表達式則不會每次執行時都重新編譯。
Function()構造函數創建的函數并不是使用詞法作用域,相反,函數體代碼的編譯總是會在頂層函數執行。
var socpe="global";
function constructFunction(){
var scope="local";
return new Function("return scope"); //無法捕獲局部作用域
}
constructFunction()(); //global