注意
以下內容是大爺學習Javascript的整理筆記,絕無侵犯作者版權之意,如作者不希望小弟截錄,請來信告知謝謝。
以下內容截錄「Javascript大全-第五版」這本書。部份章節內容拷貝至其它網站,都在標題有文章的相關連結,如果內容有錯誤,麻煩還留個言,謝謝。
1.函式實字
函式實字是定義「不具名函式」的運算式。
例:
var f= function fact (x) {
if (x <=1)
return 1;
else
return X*fact(x-1); };
2.選擇性參數
以「不夠的參數」呼叫(不是宣告)函式時,其他的參數值會定義成「undefined」。
function copyPropertyNamesToArray(o, /* optional */ a){
if (!a) a=[ ]; //如果是 undefined 或 null,則使用空陣列
for ( var property in o )
a.push (property);
return a;
}
更有彈性的呼叫:
var a = copyPropertyNamesToArray(o); //取得o的屬性加到新陣列
copyPropertyNamesToArray(p,a); //附加 p 的屬性到那個陣列
3.長度變動的參數清單:Arguments 物件
查驗函式是否以正確的參數數目被呼叫:
function f (x, y, z){
//檢查是不是傳來正確的參數數目。
if (arguments.length != 3) {
throw new Error("function f called with " + arguments.length + "arguments, but it expects 3 arguments.");
}
//…以下是函式主體
}
函式接受任意數目參數
function max (/*…*/){
var m = Number.NEGATIVE_INFINITY;
//繞遍所有的參數,找出並記住最大的值
for ( var i=0; i < arguments.length; i++)
if (arguments[i] > m) m = arguments[i];
//傳回最大值
return m;
}
Arguments 不是真正的陣列,它是一個Arguments物件。雖然每個Arguments物件也定義有編號的陣列元素和length屬性,但技術上它仍不是陣列;最好將它視為剛好具有「編號屬性的物件」。
Arguments
物件有一個非常獨特的功能。當「函數有具名的參數」時,Arguments物件的「陣列元素」是「存放函式參數的區域變數的同義詞」。arguments
[ ] 陣列和具名參數不過是指向同一變數的兩種不同方式。利用「參數名稱」改變參數之值,將同時改變透過arguments [ ]
陣列取得的值。相反地,經由arguments [ ] 陣列改變參數的值,同樣會改變以參數名稱取得的參數值。
4.callee屬性
這個屬性代表現在正在執行的函式,它可以讓未命名的函式自已遞迴呼叫自已。
計算階乘
if (x <= 1) return 1;
return x * arguments.callee(x-1);
}
5.物件屬性作為參數
利用傳遞物件實字的方式「name / value」 來達成任意順序傳遞,但這個方法較沒效率。
function easycopy(args){
arraycopy(args.from,
args.from_start || 0, //提供預設值
args.to,
args.to_start || 0,
args.length);
}
//呼叫easycopy();
var a = [1,2,3,4];
var b = new Array (4);
easycopy({from: a, to: b, length: 4});
6.參數型態
檢查傳遞型態是否正確
//回傳陣列 ( 或類似陣列物件) 元素的總和 a
// a 元素必須都是數字,null 和 undefined 元素則忽略。
function sum (a) {
if (( a instanceof Array) || ( a && typeof a == "object" && "length" in a)) { /判斷是否為陣列,或是否類似陣列
var total=0;
for ( var i=0; i < a.length; i++){
var element = a[i];
if (!element) continue; //略過 null 和 undefined參數
if (typeof element == "number") total += element;
else throw new Error ( " sum() : all array elements must be numbers");
}
return total;
}
else throw new Error ( "sum(): argument must be an array");
}
把非數值轉成數值,或按型態處理
function flexisum(a){
var total =0;
for(var i=0; i < arguments.length; i++){
var element = arguments[i];
if (!element) continue; //忽略 null 或 undefined參數
//轉換參數型態成數字n,或以它的型態主
switch ( typeof element){
case "number":
n = element; //不用轉換
break;
case "object":
if ( element instanceof Array) //
n = flexisum.apply(this, element); //給陣列遞迴
else
n= element.valueOf(); //給其他物件的 valueOf method
break;
case "function":
n = element(); //嘗試呼叫函式
break;
case "string":
n = parseFloat(element); //嘗試解析字串
break;
case "boolean":
n= NaN; //無法轉換布林值
break;
}//switch end
//如果得到有效的數字,加到總和。
if (typeof n == "number" && !isNaN(n))
total += n;
else
throw new Error ( "sum(): can't convert " + element + "to number"); //否則傳回錯誤訊息
}//for end
return total;
}end function
7.函式資料
函式指定給物件屬性,這些函式就叫做method
var o=new Object
o.square= function (x) { return x*y; } //函式實字
y=o.square(16); //y=256
函式指定給陣列元素,函式不需名稱
var a = new Array(3);
a[0] = function (x) { return x*y;}
a[1] = 20;
a[2] = a[0] ( a[1] ); // a[2] = 400
函式當作資料使用
範例1:
//定義一些簡單的函式
function add (x,y) { return x+y; }
function subtract (x,y) { return x-y; }
function multiply (x,y) {return x*y; }
function divide (x,y) { return x/y; }
//將上面函式之一當作參數,並用兩個運算元呼叫
function operate ( operator, operand1, operand2){
return operator ( operand1, operand2);
}
//呼叫函式,計算 (2+3) (4*5)
var i= operate(add, operate (add, 2, 3), operate (multiply, 4, 5));
範例2:
//在物件實字內使用函式實字
var operators = {
add: function (x,y) { return x+y; },
subtract: function (x,y) { return x-y; },
multiply: function (x,y) { return x*y; },
divide: function (x,y) { return x/y; },
pow: Math.pow //事先定義的函式也有用
}
//函式在物件中尋找運算子,取得它的名稱
//然後用所給的運算元呼叫函式
//注意這裡呼叫運算子函式用的語法
function operate2 (op_name, operand1, operand2){
if ( typeof operators[op_name] == "function")
return operators[op_name] (operand1, operand2);
else
throw "unknown operator";
}
//呼叫函式,計算 ("hellow" + " " + "world" ) 的值
var j= operate2 ("add', "hellow", operate2 ("add", " ", "world"));
//使用預先定義的 Math.pow() 函式
var k = operate2 ("pow", 10, 2)
8.length屬性
arguments陣列的length屬性是「指傳遞給函式的參數數目」。
函式自已的length屬性則是指函式「需要」的參數數目,並且這個屬性是唯讀的,並且在函式主體的裡面和外面皆可使用。
function check(args) {
var actual = args.length; //實際的參數個數
var expected = args.calle.length; //要求的參數個數
if (actual != expected) {
throw new Error(" Wrong number of arguments: expected: " + expected + "; actually passed " + actual);
}
}
function (x,y,z){
//檢查實際的參數個數與要求的參數個數是否相符
//如果不符合,丟出例外事件
check(arguments);
//正常執行函式其餘部份
return x+y+z;
}
9.自定函式屬性
當函式在呼叫期間,可以使用Function物件的屬性,來定義一個不變的變數值。
//建立並初始化「靜態」變數
//函式的宣告會在程式執行前被處理,所以可以在函式宣告前這麼做
uniqueInteger.counter = 0;
//這個函式在每次被呼叫時,都會傳回一個不同的值
//並用函式自已的「靜態」屬性記錄前一次傳回的值
function uniqueInteger(){
//遞增並傳回「靜態」變數
return uniqueInteger.counter++;
}
10.apply()與call() method
call()和apply()是用來呼叫(執行)函式,而使用call(或apply)呼叫函式與傳統使用( )運算符呼叫函式的差異在哪?
利用call和apply呼叫函數可以改變函數內部的this所指向的對象(Object)。
也可以解釋成
利用call和apply你可以在一物件(Object)內部引用屬於另一個物件的函式來自用。……
(請參考原作者網站)
11.函式範圍與Closure
語彙範圍(lexical scoping)、範圍鏈、Call物件
以下文章內容僅截錄一小部份,完整內容請連結至:http://caterpillar.onlyfun.net/Gossip/JavaScript/ScopeChain.html
在 變數 中談過變數範圍的問題,當時的說明是:使用var所宣告的變數,作用範圍是在當時所在環境,不使用var直接指定值而建立的變數,則是全域物件上的一個特性,也就是俗稱的全域範圍。單就使用JavaScript而言,這就足以瞭解使用變數時,有無使用var的差別。
在 closure(Closure) 中則以自由變數的觀念,從語法層面說明closure的意義與作用,也可應付絕大多數的情況。
事實上,JavaScript在查找變數時,會循著範圍鏈(Scope chain)一層一層往外找,範圍鏈的觀念可說明為何JavaScript沒有區域範圍,也可是JavaScript中closure的實現機制。
要瞭解範圍鏈,首先得瞭解一些名詞。首先是語彙範圍(Lexical Scope),這代表著JavaScript原始碼的物理位置(Physical placement)。例如:
var x = 10;
function outer() {
var y = 20;
function inner() {
var z = 30;
}
}
func();
(請參考原作者網站)
Closure
以下文章內容僅截錄一小部份,完整內容請連結至:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠closure實現。
下面就是我的學習筆記,對於Javascript初學者應該是很有用的。
一、變數的作用域
要理解closure,首先必須理解Javascript特殊的變數作用域。
變數的作用域無非就是兩種:全局變數和局部變數。
Javascript語言的特殊之處,就在於函數內部可以直接讀取全局變數。
var n=999;
function f1(){
alert(n);
}
f1(); // 999
另一方面,在函數外部自然無法讀取函數內的局部變數。
function f1(){
var n=999;
}
alert(n); // error
這裡有一個地方需要注意,函數內部聲明變數的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變數!
function f1(){
n=999;
}
f1();
alert(n); // 999
二、如何從外部讀取局部變數?
出於種種原因,我們有時候需要得到函數內的局部變數。但是,前面已經說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現。
那就是在函數的內部,再定義一個函數。
function f1(){
n=999;
function f2(){
alert(n); // 999
}
}
(請參考原作者網站)
三、closure的概念
上一節程式碼中的f2函數,就是closure。
各種專業文獻上的"closure"(closure)定義非常抽象,很難看懂。我的理解是,closure就是能夠讀取其他函數內部變數的函數。
由於在Javascript語言中,只有函數內部的子函數才能讀取局部變數,因此可以把closure簡單理解成"定義在一個函數內部的函數"。
所以,在本質上,closure就是將函數內部和函數外部連接起來的一座橋樑。
四、closure的用途
closure可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。
(請參考原作者網站)
五、使用closure的注意點
1)由於closure會使得函數中的變數都被保存在記憶體中,記憶體消耗很大,所以不能濫用closure,否則會造成網頁的性能問題,在IE中可能導致記憶體洩露。解決方法是,在退出函數之前,將不使用的局部變數全部刪除。
2) (請參考原作者網站)
六、思考題
如果你能理解下面兩段程式碼的運行結果,應該就算理解closure的運行機制了。
程式碼片段一。
var name = "The Window";
var object = {
(請參考原作者網站)
程式碼片段二。
var name = "The Window";
var object = {
name : "My Object",
(請參考原作者網站)
12.function()建構式
var f = function ("x", "y", "return x*y;");
相同於
function f(x, y) { return x*y; }