1. 程式人生 > >《JavaScript語言精粹》--第4章:函式

《JavaScript語言精粹》--第4章:函式

/*
	函式物件
		JS中的函式就是物件,函式物件連線到Function.prototype,而Function.prototype物件本身連線到Object.prototype
	每個函式在建立時會附加兩個隱藏屬性:
		函式物件數的上下文
		實現函式行為的程式碼
	每個函式的prototype擁有一個constructor屬性,這個屬性的值為該函式的物件
*/
var someFunction = function() {};
console.log(someFunction.prototype.constructor === someFunction);
//輸出:
//true

//---------------------------------------------------------------------------------------- 

/*
	函式字面量
		一個內部函式除了可以訪問自己的引數和變數,同時它也能自由訪問它巢狀在其中的付函式的引數與變數
		通過函式字面量建立的函式物件包含一個練到外部上下文的連線,稱為閉包
	呼叫
		每個函式接收兩個附加的引數:
			this:值取決於呼叫的模式,一共有四種模式
			arguments:
				如果實際引數過多,超出的引數值會被忽略,但是可以從arguments訪問到
				如果實際引數過少,缺失的值會被替換為undefined
*/

someFunction = function(param1,param2){
	//為函式呼叫模式,所以this指向全域性物件,瀏覽器中為window
	console.log(this);
	console.log(param1,param2);
	console.log(arguments.length,arguments);
};
someFunction("a");
someFunction("a","b","c");
//輸出:
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// a undefined
// 1 ["a"]
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// a b
// 3 ["a", "b", "c"] 

//---------------------------------------------------------------------------------------- 

/*
	四種呼叫模式:
*/

/*++++++++++++++方法呼叫模式++++++++++++++*/
/*
	方法呼叫模式:
		一個函式被儲存為物件的一個屬性時,稱為一個方法
		一個方法被呼叫時,this訪問該方法所屬的物件
		如果呼叫表示式包含一個提取屬性的動作,那麼就是當做一個方法來呼叫
*/
var obj = {
	name : "obj name",
	method : function(){
		console.log(this);
	}
};
obj.method();
//輸出:
// Object {name: "obj name", method: function}

/*++++++++++++++函式呼叫模式++++++++++++++*/

/*
	函式呼叫模式:
		一個函式並非一個物件的屬性,那麼它就是被當做一個函式來呼叫
		this被繫結到全域性物件
*/

//函式表示式形式
var functionInvocation_1 = function(){
	console.log(this);
};
//函式宣告形式
function functionInvocation_2(){
	console.log(this);
}
//函式所在作用域不為全域性作用域,this也指向全域性物件
function functionInvocation_3_parent(){
	//作用域為函式functionInvocation_3_parent內部作用域
	function functionInvocation_3(){
		//this指向全域性物件
		console.log(this);
	}
	functionInvocation_3();
}
functionInvocation_1();
functionInvocation_2();
functionInvocation_3_parent();
//輸出:
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

/*++++++++++++++構造器呼叫模式++++++++++++++*/

/*
	構造器呼叫模式:
		一個函式前面帶上new來呼叫,那麼將會建立一個連線到該函式prototype成員的新物件,同時this指向這個新物件
		不推薦使用new形式的構造器函式
		如果使用了構造器呼叫方式,且返回值不是一個物件,則返回this所指的物件
*/

var SomeConstructor = function(){
	console.log(this);
};
new SomeConstructor();
//輸出:
// SomeConstructor {} 

/*++++++++++++++apply呼叫模式++++++++++++++*/

/*
	apply呼叫模式:
		this指向第一個引數,如果為null,則指向全域性物件
*/

var applyInvocation = function(){
	console.log(this);
};
var applyInvocationObj = {
	name : "applyInvocationObj name"
};
applyInvocation = applyInvocation.apply(applyInvocationObj);
//輸出:
// Object {name: "applyInvocationObj name"} 

//---------------------------------------------------------------------------------------- 

/*
	引數
		當函式被呼叫時,會得到免費配送的引數:arguments
		arguments並不是一個數組,而是一個類似於陣列的物件,它擁有length屬性,但沒有任何陣列的方法
	返回
		一個函式總會返回一個值,如果沒有指定,將返回undefined
*/

someFunction = function(){
	console.log([].splice);
	console.log(arguments.splice);
};
console.log(someFunction());
//輸出:
// function splice() { [native code] }
// undefined
// undefined

/*
	解釋:
		第一行輸出陣列的splice方法
		第二行輸出arguments,可以看到它沒有陣列的splice方法,它並不是一個數組
		第三行輸出someFunction的返回值,由於沒有顯式返回,為undefined
*/ 

//---------------------------------------------------------------------------------------- 

/*
	擴充型別的功能
		基本型別的原型是公用結構,所以在類庫混用時需要小心,保險的做法就是隻在確定沒有該方法的時候才新增它
	遞迴
		JS不提供尾遞迴的優化(所謂尾遞迴優化,就是在一個函式返回自身遞迴嗲用的結果,那麼呼叫過程會被替換為一個迴圈,可以顯著提高速度)
	作用域
		作用域控制著變數與引數的可見性及生命週期
		只有函式作用域,沒有塊級作用域
		在函式體的頂部宣告函式中可能用到的所有變數
	閉包
		內部函式可以訪問定義他們的外部函式的引數和變數
		通過閉包,內部函式可以擁有比它的外部函式更長的生命週期
		應當避免在迴圈中建立函式
	模組
		模組是一個提供介面卻隱藏狀態與事項的函式或物件
		模組模式利用了函式作用於和閉包來建立被繫結物件與私有成員的關聯
		使用模組模式可以摒棄全域性變數的使用,促進了資訊隱藏和其他優秀的設計實踐
	級聯
		讓某些不返回任何值得方法返回this,就可以啟用級聯
*/