第七章 函數(shù)表達(dá)式
1. 定義函數(shù)的兩種方法 :
//函數(shù)聲明法
function sayHi(){
alert("Hi!");
}
//關(guān)于函數(shù)聲明法,他的一個(gè)重要特征就是函數(shù)聲明提升,即在執(zhí)行代碼之前會先讀取函數(shù)聲明,意味著我們可以把函數(shù)聲明放在調(diào)用它語句的后面
//函數(shù)表達(dá)式
var sayHi = function(){
alert("Hi!");
}
//關(guān)于兩者的區(qū)別
//這樣做會報(bào)錯(cuò)
if(condition){
function sayHi(){
alert("Hi"!);
}
}
else{
function sayHi(){
alert("Yo!");
}
}
//但這樣就不會
if(condition){
sayHi = function(){
alert("Hi"!);
}
}
else{
sayHi = function(){
alert("Yo!");
}
}
2. 遞歸 :
遞歸函數(shù)是在一個(gè)函數(shù)通過名字調(diào)用自身的情況下構(gòu)成的。
function factorial(num){
if(num <= 1){
return num;
}
else{
return num * factorial(num - 1);
}
}
//這是一個(gè)經(jīng)典的遞歸階乘函數(shù),雖然這個(gè)函數(shù)表面看起來沒什么問題,但下面的代碼卻可能導(dǎo)致塔出錯(cuò)
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出錯(cuò)
//原因是調(diào)用anotherFactorial()時(shí),由于必須執(zhí)行factorial(),所以就會導(dǎo)致錯(cuò)誤
//解決方法
function factorial(num){
if(num <= 1){
return num;
}
else{
return num * arguments.callee(num - 1);
}
}
//但在嚴(yán)格模式下不可以通過腳本來訪問arguments.callee,不過可以通過命名函數(shù)表達(dá)式來達(dá)到相同的結(jié)果
var factorial(num){
if(num <= 1)
{
return num;
}
else
{
return num * f(num - 1);
}
}
//以上代碼創(chuàng)建了一個(gè)名為f的命名函數(shù)表達(dá)式,然后將它賦值給變量factorial,即使把函數(shù)賦值給了另一個(gè)函數(shù),函數(shù)的名字f仍然有效,所有遞歸調(diào)用照樣完成,這種方式在嚴(yán)格或非嚴(yán)格模式下都可以行得通
3. 閉包 :
-
閉包 :
指的是有權(quán)訪問另一個(gè)函數(shù)作用域中變量的函數(shù) -
閉包的用途 :
可以讀取函數(shù)內(nèi)部的變量并對其作出修改。
可以讓這些變量的值始終保存在內(nèi)存中。
//讀取變量的值
function f1(){
var n = 10;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); //10
//對變量的值作出修改
function f1(){
var n = 10;
function f2(){
alert(n + 1);
}
return f2;
}
var result = f1();
result(); //11
//或者這樣對函數(shù)作出修改
function f1(){
var n = 10;
function add(n){
n += 1;
}
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); //10
add();
result(); //11
//在這段代碼中,result實(shí)際上就是閉包f2函數(shù)。它一共運(yùn)行了兩次,第一次的值是10,第二次的值是11。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒有在f1調(diào)用后被自動清除。
//為什么會這樣呢?原因就在于f1是f2的父函數(shù),而f2被賦給了一個(gè)全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中,不會在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
在閉包模式下,this 對象有時(shí)候也可能會出現(xiàn)問題。
var name = "the window";
var object = {
name: "the object",
getNameFun: function(){
return function (){
return this.name;
}
}
};
alert(object.getNameFun()()); //the window
如果想訪問對象內(nèi)部的屬性,只需要這樣就可以了。
var name = "the window";
var object = {
name: "the object",
getNameFun: function(){
var that = this;
return function (){
return that.name;
}
}
};
alert(object.getNameFun()()); //the object
4. 模仿塊級作用域 :
如前所述,JavaScript中沒有塊級作用域的概念,這意味著在 塊語句 中定義的變量,實(shí)際上是在函數(shù)內(nèi)部創(chuàng)建的(并不是 塊語句 內(nèi)部)。
function numbers(count){
for(var i=0;i < 10;i++)
{
alert(i);
}
alert(i); //計(jì)數(shù),i變量仍然可以被訪問
}
//上述即使你再塊級語句下面再聲明一次變量i也無濟(jì)于事
匿名函數(shù)可以用來模仿塊級作用域來解決這個(gè)問題。
//通用語法如下所示
(function (){
//這里是塊級作用域
})(); //后面的括號表示聲明函數(shù)后立即執(zhí)行匿名函數(shù)
所以上述例子引用塊級作用域后應(yīng)該為…
function numbers(count){
(function (){
for(var i=0;i < 10;i++)
{
alert(i);
}
})();
alert(i); //導(dǎo)致錯(cuò)誤
}