定義函數的兩種方式:函數聲明 和 函數表達式
"use strct";
// 函數聲明,指定函數名
function functionName(arg0,arg1,arg2){
// 函數體
}
// 函數聲明提升,即執行代碼之前會先讀取函數聲明
sayHi(); //再調用
function sayHi(){ //先讀聲明
alert("hello world"); // hello worold
}
匿名函數
"use strct";
// 函數表達式
// 匿名函數,即創建一個函數並將它賦值給變量functionName
// 沒有函數提升
var functionName = function(arg0,arg1,arg2){
//函數體
};
遞歸
遞歸函數是一個在函數通過名字調用自身的情況下構成。
//經典的遞歸階乘函數
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}
var anotherFactorial = factorial;
// 存在弊端
//如果factorial=null;anotherFactorial //error
alert(anotherFactorial(3));
// argument.callee 指向正在執行的函數指針
"use strict"; // 存在弊端 嚴格模式無法運行
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}
var anotherFactorial=factorial;
factorial=null;
//命名函數表達式,在嚴格模式也能正常運行
"use strict";
var factorial=(function f(num){
if(num<=1){
return 1;
}else{
return num*f(num-1);
}
})
var anotherFactorial= factorial;
factorial=null;
alert(anotherFactorial(3));
閉包
閉包是指有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。
function createComparisonFunction(propertyName){
return function(obj1,obj2){
var val1=obj1[propertyName];
var val2=obj2[propertyName];
if(val1<val2){
return -1
}else if(val1>val2){
return 1;
}else{
return 0;
}
};
}
createComparisonFunction() 函數執行完畢之後,其活動對象不會被銷燬,因爲匿名函數的作用域鏈仍然在引用這個活動對象。
即createComparisonFunction() 函數返回後,其執行環境環境的作用域鏈會被銷燬,但它活動對象仍然會留在內存中;知道匿名函數被銷燬後。
銷燬匿名函數 compareName=null
作用域鏈查找對象
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
var result = compare(5,10);
閉包與變量
閉包只能取得包含函數中任何變量的最後一個值。閉包所保存的是整個變量對象,而不是某個特殊變量
//每個函數的作用域鏈中都保存着createFunction() 函數的活動對象,引用都是同一變量i
function createFunction(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(){
return i;
};
}
return result;
}
//創建另一個匿名函數強制讓閉包的行爲符合預期
//重寫createFunction() 函數後,每個函數就會返回不同的索引值
//沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將立即執行該匿名函數的結果賦給數組
function createFunction(){
var result = new Array();
for(var i=0;i<10;i++){
result[i]=function(num){
return function(){
return num;
}
}(i);
}
return result;
}
關於this 對象
在閉包中使用this 對象可能會導致一些問題。this 對象是運行時基於函數執行環境綁定的:在全局函數中,this 等於window,而當函數被作爲某個對象方法調用時,this 等於那個對象。
匿名函數的執行環境具有全局性,因此this 通常指向window(通過call() 或apply() 改變函數的環境下,this會指向其他對象)
var num=15;
function num1(){
var num=2;
return function(){
return this.num;
}
}
alert(num1()());
//把外部作用域中的this 對象保存在一個閉包能夠訪問的變量裏,就可以讓閉包訪問該對象
var name = "The Window";
var object={
name:"My Object",
getNameFunc : function(){
var that =this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //My Object
內存泄漏
function assignHandler(){
var element=document.getElementById("someElement");
var id=element.id;
element.onclick=function(){
alert(id);
};
element=null;
}
模仿塊級作用域
<script>
//JavaScript 沒有塊級作用域。
//意味着在塊級語句中定義的變量,實際上是包含函數中而非語句中創建的。
//在Java、C++語句中,變量i只會在for 循環的語句塊中有定義,循環一旦結束,變量i就會被銷燬
"use strict";
function outputNumbers(count){
for(var i=0;i<count;i++){
document.write(i);
}
document.write(i);
}
outputNumbers(5);
</script>
// JavaScript 不會告訴是否多次聲明同一個變量;
// 遇到這種情況,它只會對後續的聲明視而不見
var i;
document.write(i);
匿名函數可以用來模仿塊級作用域並避免重新聲明變量這個問題。
<script>
"use strict";
//塊級作用域(通常稱爲私有作用域)的匿名函數的語法如下
//代碼定義並立即調用了一個匿名函數。將函數聲明包含在一對圓括號中,表示它實際上是一個函數表達式。而緊隨其後的另一對括號會立即調用這個函數。
(function(){
//這裏是塊級作用域
})();
</script>
理解步驟:
<script>
"use strict";
var count =5;
outputNumbers(count);
//把值直接傳遞給函數
outputNumbers(5); // 變量只不過是值的另一種表現形式,因此可以用實際值替換
//定義一個匿名函數,並把匿名函數賦值給變量someFunction。
//調用函數的方式是在函數名稱後面添加一對圓括號,即someFunction()
var someFunction=function(){
// 這裏是塊級作用域
};
someFunction();
//通過上面例子,可以使用實際值來替代變量count
//那是否可以用函數值直接取代函數名?
function(){
//這裏是塊級作用域
}(); //出錯
//上面代碼會導致語法錯誤,因爲JavaScript 將function 關鍵字當作一個函數聲明的開始
//函數聲明後面不能跟圓括號,而函數表達式可以跟圓括號
//將函數聲明轉換成函數表達式,只需要象下面給它加一對圓括號即可
(function(){
//這裏是塊級作用域
})();
</script>
<script>
//無論什麼地方,只要臨時需要一些變量,就可以使用私有作用域
//匿名函數中定義的任何變量,都會在執行結束時被銷燬
function outputNumbers(count){
(function () {
for(var i=0;i<count;i++){
document.write(i);
}
})();
}
outputNumbers(5);
</script>
這種做法可以減少閉包占用內存的問題,因爲沒有指向匿名函數的引用。只要函數執行完畢,就可以立即銷燬其作用鏈。
私有變量
嚴格講,JavaScript 中沒有私有成員的概念;所有對象屬性都是公有的。但有一個私有變量概念,任何在函數中定義的變量,都可以認爲是私有變量,因爲不能在函數的外部訪問這些變量
<script>
//私有變量包括函數參數、局部變量和在函數內部定義的其他函數
function add(num1,num2){
var sum = sum1+sum2;
return sum;
}
</script>
訪問私有變量共有辦法:通過函數內部創建一個閉包,閉包可以通過作用域鏈訪問。
把有權訪問私有變量和私有函數的共有方式稱爲特權方式