function
函數(shù)實(shí)際上是一個(gè)對(duì)象。
每個(gè)韓式都是
Function
類型的實(shí)例,而且都與其他引用類型一樣具有屬性和方法。
由于函數(shù)式對(duì)象,因此函數(shù)名實(shí)際上也是一個(gè)指向函數(shù)對(duì)象的指針,不會(huì)與某個(gè)函數(shù)綁定。
函數(shù)定義
function sum(num1, num2) {
return num1 + num2;
}
var sum = function(num1, num2) {
return num1 + num2;
}
var sum = new Function("num1", "num2", "return num1 + num2") // 不推薦
函數(shù)名是指向函數(shù)的指針,因此函數(shù)名與包含對(duì)象指針的其他變量沒有什么不同。
一個(gè)函數(shù)可以有多個(gè)名字(指針),例如:
function sum(num1, num2) {
return num1 + num2;
}
alert(sum(10, 10)); // 20
var anotherSum = sum;
alert(anotherSum(10, 10)); //20
sum = null;
alert(anotherSum(10, 10)); //20
上面例子可以看出,指向函數(shù)的變量,實(shí)際上是指向函數(shù)對(duì)象的指針,所以當(dāng)其中一個(gè)賦值為 null
時(shí),并不會(huì)影響另一個(gè)(指針)變量。
沒有重載(深入理解)
將函數(shù)名想象為指針,也有助于理解為什么ECMAScript中沒有函數(shù)重載的概念。
function addSomeNumber(num) {
return num + 100;
}
function addSomeNumber(num) {
return num + 200;
}
var result = addSomeNumber(100); // 300
var addSomeNumber = function(num) {
return num + 100;
}
addSomeNumber = function(num) {
return num + 200;
}
雖然聲明了兩個(gè)函數(shù),結(jié)果是后面的函數(shù)覆蓋了前面的函數(shù)。
函數(shù)聲明與函數(shù)表達(dá)式
解析器在向執(zhí)行環(huán)境中加載數(shù)據(jù)時(shí),對(duì)函數(shù)聲明和函數(shù)表達(dá)式并非一視同仁。
解析器會(huì)率先讀取函數(shù)聲明,并使其在執(zhí)行任何代碼之前可用(可以訪問)。
函數(shù)表達(dá)式則必須等到解析器執(zhí)行到它們所在的代碼行,才會(huì)真正被執(zhí)行。
alert(sum(10, 10)); // 20
function sum(num1, num2){
return num1 + num2;
}
alert(sum1(10, 10)) // 報(bào)錯(cuò):TypeError: sum1 is not a function(…)
var sum1 = function(num1, num2){
return num1 + num2;
}
除了聲明時(shí)候可以通過變量訪問函數(shù)這一點(diǎn)區(qū)別之外,函數(shù)聲明與函數(shù)表達(dá)式的語法其實(shí)是等價(jià)的。
可以同時(shí)使用函數(shù)聲明和函數(shù)表達(dá)式,不過這種語法在
Safari
中會(huì)導(dǎo)致錯(cuò)誤。
作為值的函數(shù)
因?yàn)楹瘮?shù)本身就是變量,所以函數(shù)也可以作為值來使用。
函數(shù)可以像參數(shù)一樣把一個(gè)函數(shù)傳遞給另一個(gè)函數(shù),而且可以將一個(gè)函數(shù)作為另一個(gè)函數(shù)的結(jié)果返回。
function callSomeFunction(someFunction, someArgument) {
return someFunction(someArgument);
}
function add10(num) {
return num + 10;
}
var result1 = callSomeFunction(add10, 10);
alert(result1); // 20
function getGreetine(name) {
return "Hello, " + name;
}
var result2 = callSomeFunction(getGreetine, "Bert");
alert(result2); // Hello, Bert
函數(shù)內(nèi)部屬性
函數(shù)內(nèi)部有兩個(gè)特殊對(duì)象:arguments 和 this。
arguments
arguments
是一個(gè)數(shù)組對(duì)象,包含著傳入函數(shù)中的所有參數(shù)。
雖然
arguments
的主要用途是保存函數(shù)參數(shù),但這個(gè)對(duì)象還有一個(gè)名叫callee
的屬性,該屬性是一個(gè)指針,指向擁有這個(gè)arguments
對(duì)象的函數(shù)。
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num -1);
}
}
factorial(3) // 6
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num -1);
}
}
factorial(5) // 120
使用函數(shù)名的階乘如果函數(shù)名變了,遞歸調(diào)用的函數(shù)的名字也要跟著改變。
但是,如果使用的是 arguments.callee
則不管函數(shù)名如何變化,內(nèi)部代碼都不用擔(dān)心會(huì)出現(xiàn)錯(cuò)誤。
this
函數(shù)內(nèi)部的另一個(gè)特殊對(duì)象,其行為與Java
和 C#
中的 this
大致類似。
this
引用是函數(shù)據(jù)以執(zhí)行的環(huán)境對(duì)象 ---- 或者也可以說是 this
值(當(dāng)在網(wǎng)頁的全局作用域中調(diào)用函數(shù)時(shí),this
對(duì)象引用就是 window
)。
window.color = "red";
var o = { color: "blue" };
function sayColor() {
alert(this.color);
}
sayColor(); // red
o.sayColor = sayColor;
o.sayColor(); // blue
根據(jù)作用域的不同, this
指針指向的對(duì)象也不同。
當(dāng)在全局作用域中調(diào)用 sayColor()
時(shí),this引用的是全局對(duì)象 window
;換句話說 this.color
求值會(huì)轉(zhuǎn)換成對(duì) window.color
求值,
當(dāng)把函數(shù)賦給對(duì)象 o
并調(diào)用 o.sayColor()
時(shí), this
引用的是對(duì)象 o
,因此對(duì) this.color
求值會(huì)轉(zhuǎn)換成對(duì) o.color
求值,結(jié)果就返回了 bule
。
注意:函數(shù)的名字僅僅是一個(gè)包含指針的變量而已。因此,即使是在不同的環(huán)境中執(zhí)行,全局的
sayColor()
函數(shù)與o.sayColor()
指向的仍然是同一個(gè)函數(shù)
caller
ECMAScript 5 也規(guī)范了另一個(gè)函數(shù)對(duì)象的屬性: caller 。除了Opera的早期版本不支持,其他瀏覽器都支持這個(gè) ECMAScript 3 并沒有定義的屬性。
這個(gè)屬性保存著調(diào)用當(dāng)前函數(shù)的函數(shù)的引用,如果是在全局作用域中調(diào)用當(dāng)前函數(shù),他的值為 null。
function outer() {
inter();
}
function inter() {
alert(inter.caller);
alert(arguments.callee.caller);
}
outer()
IE、Firefox、Chrome和Safari的所有版本以及Opera 9.6都支持 caller 屬性。
嚴(yán)格模式下訪問arguments.callee會(huì)導(dǎo)致錯(cuò)誤。
嚴(yán)格模式下不能為函數(shù)caller屬性賦值。
函數(shù)屬性和方法
length
函數(shù)希望接收的命名參數(shù)個(gè)數(shù)
function sayName(name) {
alert(name);
}
function sum(num1, num2) {
return num1 + num2;
}
function sayHi() {
alert("hi");
}
alert(sayName.length); // 1
alert(sum.length); // 2
alert(sayHi.length); // 0
prototype
在 ECMAScript 核心所定義的全局屬性中,最耐人尋味的就要數(shù) prototype 屬性了。
對(duì)于 ECMAScript 中的引用類型而言, prototype 是保存它們所有實(shí)例方法的真正所在。
如 toString() 和 valueOf() 等方法實(shí)際上都保存在 prototype 名下,只不過是通過各自對(duì)象的實(shí)例訪問罷了。
在創(chuàng)建自定義引用類型以及實(shí)現(xiàn)繼承時(shí), prototype 屬性的作用是極為重要的。
prototype 屬性是不可枚舉的,因此使用 for-in 無法發(fā)現(xiàn)。
每個(gè)函數(shù)都包含兩個(gè)非繼承而來的方法:apply() 和 call()。
這兩個(gè)方法的用途都是在特定的作用域中調(diào)用函數(shù),實(shí)際上等于設(shè)置函數(shù)體內(nèi)this對(duì)象的值。
apply()
接收兩個(gè)參數(shù):一個(gè)是在其中運(yùn)行函數(shù)的作用域,另一個(gè)是參數(shù)數(shù)組。其中,第二個(gè)參數(shù)可以是 Array 的實(shí)例,也可以是 arguments 對(duì)象。
function sum(num1, num2) {
return num1 + num2
}
function callSum1(num1, num2) {
return sum.apply(this, arguments);
}
function callSum2(num1, num2) {
return sum.apply(this, [num1, num2]);
}
alert(callSum1(10, 10)); // 20
alert(callSum2(10, 10)); // 20
在嚴(yán)格模式下, 未指定環(huán)境對(duì)象而調(diào)用函數(shù),則 this 值不會(huì)轉(zhuǎn)型為 window。除非明確把函數(shù)添加到某個(gè)對(duì)象或者調(diào)用 apply() 或 call(),否則 this 值將是 undefined
call()
call() 方法與 apply() 方法的作用相同,它們的區(qū)別僅在于接受的參數(shù)的方式不同。對(duì)于 call() 方法而言,第一個(gè)參數(shù)是 this 值沒有變化,變化的是其參數(shù)都直接傳遞給函數(shù)。
function sum(num1, num2) {
return num1 + num2
}
function callSum(num1, num2) {
return sum.call(this, num1, num2);
}
alert(callSum(10, 10)); // 20
傳遞參數(shù)并非 apply() 和 call() 真正的用武之地;它們真正強(qiáng)大的地方是能夠擴(kuò)充函數(shù)依賴以運(yùn)行的作用域。
window.color = "red";
var o = {color: "blue"};
function sayColor() {
alert(this.color);
}
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue
上例說明:使用 call() 或 apply() 來擴(kuò)充作用域的最大好處,就是對(duì)象不需要與方法有任何耦合關(guān)系。
bind()
這個(gè)方法會(huì)創(chuàng)建一個(gè)函數(shù)實(shí)例,其 this 值會(huì)被綁定到傳給 bind() 函數(shù)的值。
window.color = "red";
var o = {color: "blue"};
function sayColor() {
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
支持 bind() 方法的瀏覽器有 IE9+、Firefox 4+、Safari 5.1+、Opera 12+ 和Chrome。