前言 |
![]()
|
||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Function Objects |
Function 是 JavaScript 的基礎模形單元 , 用來 reuse , information hiding , and composition. also 具體指定物件的行為 . Objects 是成對的 Name/Value , 它與 prototype object 之間具有一個隱形的連結 Objects 從 object literals 產生 , 與 object.prototype 相連 Function 物件與 Function.prototype . 相關連 每一個 function object 也都會產生 prototype 屬性 . function物件的值凡是帶有 constructor property 的, 這個property的值也是 function . 這樣子的function 有別於與 Function.prototype相關聯的function , 就是 function invocation pattarn 由於 Function 在 JavaScript 中也是物件, 所以它可以存在於 objects , variables , arrays 之中, 它也可以像參數一樣傳遞, 也可以回傳 function (當事件) |
||||||||||||||||||||||||||||||||||
function statement (函式宣告述句) |
function funStatement(x) { console.log(x); // Displays the initial value of the argument alert(x); } |
||||||||||||||||||||||||||||||||||
function literal (函式字面值) 也稱為 function definition expression (函式定義運算式) |
var add = function (a, b) { return a + b; }; 有四部份組成 closure (閉鎖) , 注意一下 . 在巢狀的 function , 內部的function (是一個外部function產生的function object , 故維持著一個連結的關係) 仍能自由地存取外層的參數及變數 var i = 0; var fs = []; function run_fs() { for (var j = 0, f = fs[j]; f; f = fs[++j]) { f(); } } document.write('Pure anonymous function'); for (i = 0; i < 3; ++i) { fs[i] = function () { document.write(i + ""); } } run_fs(); document.write('Closure'); for (i = 0; i < 3; ++i) { fs[i] = (function (i) { return function () { document.write(i + ""); } })(i); } |
||||||||||||||||||||||||||||||||||
Invocation |
Invoking a function , 意謂著暫停目前的程式 , 將控制權轉移至新的function中.
參數可以是任何的 expression (變數,運算式,function皆可) .
|
||||||||||||||||||||||||||||||||||
Arguments |
arguments是一個特別的參數物件, 所有傳進 function的參數都會存到裡面. ** 傳遞引數時, 漏掉的話在 function 裡會是 undefined . 多的話會被忽略 (除非自己在 arguments 裡去找出來) , 呼叫時並不會因為引數的不對而出現 exception //選擇性參數的設計方法 function getPropertyNames(o, a) { if (a === undefined) a = []; a = a || []; //可以用這一行取代 if 判斷式. for (var property in o) { a.push(property); } return a; } //var a = getPropertyNames(o); //var b = getPropertyNames(p, a); //說明 arguments 物件 var sum = function (v1, v2) { var i, sum = ""; for (i = 0; i < arguments.length; i++) { sum += arguments[i] + ", "; } console.log(v1 + v2); //為 12 arguments[0] = 50; // 直接將值 assign 時, 相對應的參數也會變動, v1 也會變成 50; console.log(arguments.arity); // undefined 沒有這個方法或屬性 console.log(arguments.length); // 6 console.log(arguments[0]); // 4 return sum; }; var su = sum(4, 8, 15, 16, 23, 42); console.log(su); //4, 8, 15, 16, 23, 42,
在 ECMAScript 5 的 strict 模式中, arguments 行為有不一樣, 所以還是照參數去呼叫即可.
|
||||||||||||||||||||||||||||||||||
Functions As Values |
指函式可以當做值一樣地進行傳遞 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; } var add2 = function (x, y) { return x + y; } //add 可以改寫為這樣, 比較容易理解它可以當成變數或參數值 function operate(op, arg1, arg2) { return op(arg1, arg2); } //直接將函式當做參數值傳遞去運算 var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5)); // i 值為 25 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(prop, arg1, arg2) { if (typeof operators[prop] === "function") { return operators[prop](arg1, arg2); } else { throw "unknown operator"; } } var j = operate2("add", "hello", operate2("add", " ", "world")); // => "hello world" var k = operate2("pow", 10, 2); // => 100
|
||||||||||||||||||||||||||||||||||
Functions As Namespaces | |||||||||||||||||||||||||||||||||||
Return |
return 可以迫使 function 提早結束 . return 一定會傳回值 , 如果不指定 , 它傳回的是 undefined . 如果 function 是以 new 來建置的話, 而且回傳的不是object , 那麼 this 就是 return值了 |
||||||||||||||||||||||||||||||||||
Closures (閉鎖) |
參考資料
Closures 是一種將變數設為特定內容來運作的表達式,通常是函式。說的簡單一點,當巢狀函式的內層函式存取了上層函式的變數,而且內層函式回傳至外部可被 invoke 就會產生 closure . // 這是W3C closure 的範例 計數器, 直接明白 , 它回傳的是一個已執行的 function 物件 , 所以是一個 instance (存在於記憶體的) , // 由於 Function scope 所以一直記得 counter 這個變數, 在 add() ...add()... add() 三次後依然使用這個 // counter變數回傳數值 所以是3 var add = (function () { var counter = 0; return function () { return counter += 1; } })(); add(); // => 1 add(); // => 2 add(); // => 3
*** js 為語彙範疇 (lexical scoping) , 這表示 "函式執行時使用的變數範疇是 "函式定義時生效的範疇" ",而非 invoke 時的變數範疇.
***這裏是觀念的重點 , 要好好瞭解
//這個計數器比較難用 因為它直接產生一個 instance 算是 static 概念 var uniqueInteger = (function () { var counter = 0; return function () { return counter++; }; }()); uniqueInteger(); // => 0 uniqueInteger(); // => 1 uniqueInteger(); // => 2 //這個計數器比較符合物件需求 function counter() { var n = 0; return { count: function () { return n++; }, reset: function () { n = 0; } }; }; var c1 = new counter(); var d1 = new counter(); //獨立的 instance console.log(c1.count()); console.log(c1.count()); console.log(d1.count()); c1.reset(); console.log(c1.count()); //定義一個能夠在物件中附加屬性的方法 , 第三個參數是檢查必須是字串的函式 function addPrivateProperty(o, name, predicate) { var value; o["get" + name] = function () { return value; }; o["set" + name] = function (v) { if (predicate && !predicate(v)) throw Error("set" + name + ": invalid value " + v); else value = v; }; } var ks = {}; var kk = {}; //在 ks 中附加 Name 方法 (有點像是委派) addPrivateProperty(ks, "Name", function (x) { return typeof x == "string"; }); ks.setName("Frank"); console.log(ks.getName()); ks.setName(0); //因為有檢查變數, 不是字串的會丟出例外 // 一般物件的用法 // myObject. 有一個值及一個 method //藉由呼叫一個function 初始化 myObject 並傳回一個 object literal .也就是實體化的物件 // 這個function 定義一個value 變數 , 而這個變數一直都可以被 increment 及 getValue 這二個方法所用 // value 可以直接被 get 或 set by 外部呼叫 var myA = { value: 0, increment: function (inc) { //因為它用屬性, 所以得用 this.value this.value += (typeof(inc) === 'number') ? inc : 1; } }; //Closure 的用法 // 注意最後的 () , 執行後回傳物件 所以不能 var objB = new myB(); 這也是 jQuery的用法.. var myB = function () { var value = 0; return { increment: function (inc) { value += (typeof (inc) === 'number') ? inc : 1; }, getValue: function () { return value; } }; }(); //注意這個括號 //最後沒有加 () 它就是一般的建構函式了. var myC = function () { var value = 0; return { increment: function (inc) { value += (typeof (inc) === 'number') ? inc : 1; }, getValue: function () { return value; } }; }; $(document).ready(function () { //一般物件的用法 myA.increment(5); myA.value; //==> 5 myA.increment(30) myA.value; //==> 35 //myB 是一個物件 var newobB = myB; newobB.increment(10); newobB.getValue(); // => 10 myB.increment(37); myB.getValue(); // => 47 (因為都已經變成物件所以 newobB 與 myB都是參考同一個) newobB.getValue(); // => 47 // 回傳一個方法 ...這裏要注意一下... 因為 var obC = myC(); obC.increment(5); obC.getValue(); // => 5 myC().increment(10); // 一個新的 myObjectC() 加十...可以這樣用, 但getValue沒什麼意義 myC().getValue(); // => 0 , 仍是零, 因為myC() 回傳的是一個全新的 instance obC.increment(17); obC.getValue(); //==> 22 }); //一個變背景色的 fadeIn.... var fade = function (node) { var level = 1; var step = function () { var hex = level.toString(16); //***這裏是個特別的用法...之前都不知道 node.style.backgroundColor = '#FFFF' + hex + hex; if (level < 15) { level += 1; setTimeout(step, 100); } }; setTimeout(step, 100); }; fade(document.body); //我要傳入一個節點的集合 , 例如table 的 td 的集合 , 然後給序它 onclick的事件 , 每點一個列出它的序號 $(document).ready(function () { add_the_handlers2($('.LearnDiv table tr td')); }); //這樣的設計方法結果每次 alert 都是最後一個數量 (假設nodes 數量42) // 會什麼呢? 因為跑完定義後 i 值 = 42 , 故 每次都會 alert 42的值 var add_the_handlers_no = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i); }; } //如果在這裏指定 555 那 每次 alert 出來的就是 555 i = 555; }; //與上方的差異在與 helper(n) 回傳的是一個 function 物件 , 在onclick 事件時才會觸發這個物件 // 所以 nodes.length 如果有 42 個 , 那就是產生了 42 個匿名function物件給 onclick 事件等它觸發 var add_the_handlers2 = function (nodes) { //避免宣告在廻圈裡, 這樣只是浪費效能 var helper = function (num) { return function () { alert(num); }; }; for (i = 0; i < nodes.length; i += 1) { v = i; nodes[i].onclick = helper(i); } };
另一件 closures 要記住的事為 this 是一個關鍵字而不是變數 , 每個函式都有一個 this 值, 而 closure 無法存取它外層函式的 this 值,除非 that = this
然後再使用於巢狀函式中.
(arguments關鍵字也是 outArguments = arguments 然後再給 nasted function使用). |
||||||||||||||||||||||||||||||||||
函式的propertys、Methods及建構式 |
|
||||||||||||||||||||||||||||||||||
Exceptions throw 物件 |
//加入 throw 功能 , 但是外圈如果沒有 try catch 的處理的話 , 就javascript 來說是沒有意義的 var msg; try { msg = " typeof(a) :" + typeof (a) + "\n typeof(b) : " + typeof (b); if (typeof (a) !== 'number' || typeof (b) !== 'number') { // throw 要配合try catch(e) 來使用, 否則會是 uncatch error 的javascript錯誤 throw { name: 'TypeError', message: 'add needs numbers' }; } msg += ", a+b = " + a + b; } catch (e) { msg += "\n發生exception\n" + e.name + "\n" + e.message; } finally { alert(msg); } |
||||||||||||||||||||||||||||||||||
jump 的用法 | |||||||||||||||||||||||||||||||||||
Augmenting Types (應該是擴充型別) |
JavaScript 允許基本型別被 augmented (擴充) 在上面 . 我們可以使用 Object.prototype 使得方法可以應用到所有instance物件//Function 加入擴充方法 . 使所有的 物件 (constructor invocation funcation)的不必使用 .prototype去擴充方法 // 不過這東東要放在程式的最高處..不然會沒作用 Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }; var constructorObject = function (v1, v2) { this.value1 = v1; this.value2 = v2; } constructorObject.prototype.Add = function () { return this.value1 + this.value2; } constructorObject.method('GoHappy', function () { alert('我真的很悲觀!! 我覺得未來沒希望' + this.value1 ); }); //然後我就可以用 var vi = new constructorObject(2, 3); vi.GoHappy(); Number 擴充方法來處理 integer 的問題 Number.method('Tointeger', function () { return Math[this < 0 ? 'ceil' : 'floor'](this); }); String 擴充方法來處理 trim 的問題 String.method('trim', function () { return this.replace(/^\s+|\s+$/g, ''); }); 由於 JavaScript 的動態特性 , 我們可以很容易地賦與物件新的methods , 為了避免與原 methods 發生 衝突 , 我們可以使用下方的特性來處理 Function.prototype.method = function (name, func) { //判斷原來方法存不存在 . 不存在時再新增方法 (public structor) if (!this.prototype[name]) { this.prototype[name] = func; return this; } }; |
||||||||||||||||||||||||||||||||||
Recursion ( 遞廻 ) |
河內塔 :
有三根木頭 , 有 n 個圓環排在一起呈金字塔狀 規則一 : 每次只能移動一個圓環金屬 規則二 : 上到下都必須圓環直徑的小到大 , 也就是須呈金字塔狀... 結果是將一根木頭上的圓環統統移動到另一根木頭上... ![]() //河內塔遊戲 hanoi(4, 'A', 'B', 'C'); var hanoi = function hanoi(disc, src, aux, dst) { if (disc > 0) { hanoi(disc - 1, src, dst, aux); document.writeln('Move disc ' + disc + ' from ' + src + ' to ' + dst + '< br />'); hanoi(disc - 1, aux, src, dst); } }; 樹狀結構的瀏灠方式 //使用此方法來瀏灠HTML下的每一個節點 var walk_the_DOM = function walk(node, func) { func(node); node = node.firstChild; while (node) { walk(node, func); node = node.nextSibling; } }; //取得 document.body 下某個 tag 有某個值的....傳回陣列 // 配合上方遞廻的方式來尋找 var getElementsByAttribute = function (att, value) { var results = []; walk_the_DOM(document.body, function (node) { var actual = node.nodeType === 1 && node.getAttribute(att); if (typeof actual === 'string' && (actual === value || typeof value !== 'string')) { results.push(node); } }); return results; }; JavaScript 並沒有提供 tail recursion optimization , 所以在 Function resursion 到很深的節點時 , 可能會發生 fail by exhausting the return stack // var factorial = function factorial(i, a) { a = a || 1; if (i < 2) { return a; } return factorial(i - 1, a * i); }; document.writeln(factorial(4)); |
||||||||||||||||||||||||||||||||||
Scope |
最新的語言都建議在變數使用之前才宣告它 , 但這對JavaScript來說卻不是好的 . 因為 JavaScript 沒有 block scope 所以要全域使用的參數最好在 程式一開始最前方就宣告它 , 以免在途中因為某程式的變數而被影響. JavaScript 是 function scope , 這表示 變數及參數在 function 裡宣告的話 , 外部是看不到的 , 但是在內部宣告時 inner function 的任何地方都是看的到的 ???? |
||||||||||||||||||||||||||||||||||
Callbacks |
//這是一個 call back 的範本 request = prepare_the_request( ); send_request_asynchronously(request, function (response) { display(response); }); |
||||||||||||||||||||||||||||||||||
Module |
使用 functions 及 closure 來建立 modulemodule 是一個呈現像介面但是隱藏它的狀態或實做的function 或 object. Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }; String.method('deentityify', function () { var entity = { quot: '"', lt: '<', gt: '>' }; // 傳回 deentityify method return function () { // 正規表示式 start with '&' and end with ';'. If the characters in return this.replace(/&([^&;]+);/g, function (a, b) { var r = entity[b]; return typeof r === 'string' ? r : a; }); }; } ()); The general pattern of a module is a function that defines private variables and functions; creates privileged functions which, through closure, will have access to the private variables and functions; and that returns the privileged functions or stores them in an accessible place. 使用 module pattarn 可以有效地減低 global variables 的使用 , 並且 promotes 資料的隱藏及 promotes 其他好的設計的實踐 var serial_maker = function ( ) { var prefix = ''; var seq = 0; return { set_prefix: function (p) { prefix = String(p); }, set_seq: function (s) { seq = s; }, gensym: function () { var result = prefix + seq; seq += 1; return result; } }; }; //以上方法都沒有用到 this 或 that , 這樣的設計隱藏了 prefix 及 seq 變數, 使用外程式無論如何都無法直接 //改變 prefix 及 seq 的值 (只能透過, set_prefix 及 set_seq 這兩個方法來改變它) //呼叫方法及範例 var seqer = serial_maker(); seqer.set_prefix('Q'); seqer.set_seq(1000); var unique = seqer.gensym(); for (var i = 0; i < 10; i++) { if (i === 5) { //unique; } alert(seqer.gensym()); } |
||||||||||||||||||||||||||||||||||
Cascade |
應該就是連綴呼叫. 只要呼叫的方法回傳 this 就可以達到此種效果 |
||||||||||||||||||||||||||||||||||
Curry 咖喱 |
Currying allows us to produce a new function by combining a function and an argument
它主要是產生一個 closure , 這個 closure hold住了原本的function跟arguments. //同樣需要 Function 的擴充方法 , curry Function.method('curry', function () { //slice 是因為 arguments 並不是陣列 , 所以沒有 concat 方法 , 所以這樣.apply來處理它們的結合 var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function () { return that.apply(null, args.concat(slice.apply(arguments))); }; }); |
||||||||||||||||||||||||||||||||||
Memoization |
function 可以使用變數來記住上個操作所產生的結果, 以避免不必要的重工 . 這就是 memoization 的說明 , 物件及陣列很適合來處理這事 .
var fibonacci = function (n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); }; //以下的處理我們呼叫了 11次 fibonacci , 但是它自己卻遞迴了453 次 for (var i = 0; i <= 10; i += 1) { document.writeln('// ' + i + ': ' + fibonacci(i)); } 進階版為了避免數量極為龐大的遞迴所導致的效能低落, 我們可以使用 var fibonacci = (function () { var memo = [0, 1]; var fib = function (n) { var result = memo[n]; if (typeof result !== 'number') { result = fib(n - 1) + fib(n - 2); memo[n] = result; } return result; }; return fib; } ()); var memoizer = function (memo, formula) { var recur = function (n) { var result = memo[n]; if (typeof result !== 'number') { result = formula(recur, n); memo[n] = result; } return result; }; return recur; }; var fibonacci = memoizer([0, 1], function (recur, n) { return recur(n - 1) + recur(n - 2); }); var factorial = memoizer([1, 1], function (recur, n) { return n * recur(n - 1); }); |
||||||||||||||||||||||||||||||||||
其他的用法 |
//這樣的用法好像沒有用過 var setBGColor; function selfBuiltNewFunction() { setBGColor = new Function("document.body.bgColor='arguments[0]'"); } setBGColor('red'); |
||||||||||||||||||||||||||||||||||
caller constructor |
.caller : 傳回呼叫來源的程式 .constructor : |
||||||||||||||||||||||||||||||||||
constructor |
function queryObjConstructor(){ varobjD = new Date( ); var objArr = new Array( ); alert( objD.constructor ); alert( objArr.constructor ); } |
||||||||||||||||||||||||||||||||||
顯示函式陣列物件 之類的程式原始程式碼 |
IE好像不支援 |