函數運算式(JavaScript高程筆記)
函式宣告特點:函式宣告提升(執行代碼之前解析器會先讀取函式宣告,並使其在執行任何代碼之前可用,意味著可以把函式宣告放在調用語句之後)
function functionName(arg0,arg1) {//do something}
函數運算式特點:無提升(須等到解析器執行到它所在的程式碼才會真正被解析執行)1.最常見-匿名函數
var functionName = function() {//do something};2.命名函數運算式var functionName = (function f() {//do something});
一、遞迴1.arguments.callee一個指向正在執行的函數的指標,代替函數名
function factorial(num) {if(num <= 1){return 1;} else {return num * arguments.callee(num-1);}}
2.命名函數運算式strict 模式下arguments.callee報錯,可用命名函數運算式代替
var factorial = (function f(num) {if(num <= 1){return 1;} else {return num * f(num-1);}});
二、閉包閉包是指有權訪問另一個函數範圍中變數的函數建立閉包最常見的方式是在一個函數內部建立另外一個函數由於閉包會攜帶包含它的函數的範圍,因此過度使用閉包可能會導致記憶體佔用過多閉包只能取得包含函數中任何變數的最後一個值(不是很理解)
//錯誤栗子function createFunc() {var result = new Array();for(var i=0; i < 10; i++) {result[i] = function() {return i; //閉包,均返回i的最後一個值10};}return result;}//正確栗子function createFunc() {var result = new Array();for(var i=0; i < 10; i++) {result[i] = function(num){return function() {return num;};}(i); //立即執行,傳遞當前值給參數num}return result;}
關於this:匿名函數的執行環境具有全域性,其this通常指向windowIE9-:記憶體泄露-如果閉包的範圍鏈儲存了一個HTML元素,則該元素無法被銷毀
//錯誤栗子function asignHandler() {var element = document.getElementById('someElem');element.onclick = function = () { //閉包alert(element.id);};}//正確栗子function asignHandler() {var element = document.getElementById('someElem');var id = element.id; //儲存副本element.onclick = function = () { //閉包alert(id); //彈出副本};element = null; //解除引用,回收記憶體}
三、模仿塊級範圍JavaScript無塊級範圍的概念
//無塊級範圍function outputNumbers(count) {for(var i=0; i < count; i++) { //變數i在outputNumbers()的範圍中alert(i);}alert(i); //在for外仍可訪問變數i}//模仿塊級範圍function outputNumbers(count) {(function() {//這裡是塊級範圍,閉包,可訪問外部變數for(var i=0; i < count; i++) {alert(i);}})();alert(i); //報錯!}//運用:避免全域變數和函數//無指向匿名函數的引用,故能減少閉包佔用的記憶體,只要函數執行完畢即可銷毀其範圍鏈//把以下代碼放在全域範圍中(function() {var now = new Date(); //now為匿名函數的局部變數if(now.getMonth() == 0 && now.getDate() == 1) {alert('Happy new year!');}})();
四、私人變數與特權方法1.在建構函式中定義特權方法特點:每個執行個體都會建立同樣一組新方法
function Person(name) { //name為私人變數//兩個特權方法this.getName() = function() {return name;};this.setName() = function(value) {name = value;};}
2.在私人範圍中定義私人變數和函數(靜態私人變數)特點:私人變數和函數是每個執行個體共用的
(function() {var name = ''; //共用的,靜態私人變數,Person = function(value) {name = value;};Person.prototype.getName = function() {return name;};Person.prototype.setName = function(value) {name = value; //在一個執行個體上調用setName()會影響所有執行個體的name};})();
3.模組模式為單例(只有一個執行個體的對象)建立私人變數和特權方法適用於需要對單例進行某些資料的初始化,同時又要維護其私人變數的情形 v
ar application = function() {//私人變數和函數var components = new Array();//初始化component.push(new BaseComponent()); //向數組添加了一個BaseComponent(無關緊要)的新執行個體//特權方法return {getComponentCount : function() {return components.length;},registerComponent : function(component) {if(typeof component == 'object') {components.push(component);}}};}();
4.增強模組模式(還沒鬧明白,明白後再補充) 本書其它章節中關於函數的內容使用不帶圓括弧的函數名是訪問函數指標,而非調用函數!1.return函數會在執行完return語句後停止並立即退出,因此,位於return語句之後的任何代碼都永遠不會執行return語句也可以無傳回值,函數停止執行後將返回undefined 2.參數函數內可通過arguments對象(函數的兩個特殊對象之一)來訪問參數數組(但arguments並非Array的執行個體),可與具名引數一起使用arguments與對應具名引數的記憶體空間是分別獨立的,但它們的值會同步參數並非必須命名未傳遞值的具名引數將自動賦予undefinedarguments.callee:是一個指標,指向擁有這個arguments對象的函數(消除緊密耦合) 4.this(函數的兩個特殊對象之一)引用的是函資料以執行的環境對象 3.沒有重載兩個名字相同的函數,該名字只屬於後定義的函數 4.作為值的函數ECMAscript中的函數名本身就是變數,所以函數可以作為值來使用-可以像傳遞參數一樣把一個函數傳遞給另一個函數(無圓括弧)-可以將一個函數作為另一個函數的結果返回 5.函數的內部屬性arguments對象this對象caller屬性:指向調用當前函數的函數,如果是在全域中調用當前函數則其值為null,可通過arguments.callee.caller實現更鬆散的耦合 6.函數的屬性和方法length屬性:表示函數希望接受的具名引數的個數prototype屬性:ECMAscript5不可枚舉 *設定this的值的方法,能擴充函數的範圍(非繼承方法) -apply(範圍,[參數])-call(範圍,參數1,參數2,參數3) bind():建立一個執行個體,其this值綁定到bind()的參數 *繼承方法 toLocaleString()、toString()、valueOf()以上均返回函數的代碼