3C科技 娛樂遊戲 美食旅遊 時尚美妝 親子育兒 生活休閒 金融理財 健康運動 寰宇綜合

Zi 字媒體

2017-07-25T20:27:27+00:00
加入好友
此篇文章瀏覽量: 1,190 此文章摘錄自’JavaScript優良部分’一書,純粹個人備忘之用。如欲看全文,請參閱該書。 函式物件 JavaScript的函式都是物件。函式物件聯繫到Function.prototype(本身再聯繫到Object.prototype)。既然函式是物件,就能像其他任何值一樣使用。函式能儲存在變數、物件或陣列裡。函式能被當成引數傳給另一個函式、函式也能被另一個函式回傳。最後,函式既然是物件,它也可以有方法。函式能被呼叫。 函式實字 函式物件以函式實字建立: //建立變數add,其中儲存一個相加兩數的函式 var add = function (a, b){ return a + b; } 函式實字有四個部份 保留字function 函式名稱(選用),以上述範例來說,並沒有函式名稱。所以稱為匿名函式(anonymous function) 函式參數的集合 一組以大括號圍起的敘述,此敘述是函式的本體,在函式被呼叫時執行 函式實字建立的函式物件,包含對外圍環境的聯繫,稱為closure(閉包),這是種具有無窮表達能力的資源。 方法呼叫模式 // 建立 myObject 物件,其中有個值value // 以及一個執行遞增計算的方法 // 遞增方法可接收一個選用參數 // 如果引數不是數值,則使用預設值1。 var myOjbect = { value: 0; increment: function(inc){ this.value += typeof inc === 'number' ? inc : 1; } }; myObject.increment(); document.writeln(myObject.value); //輸出1 myObject.increment(2); document.writeln(myObject.value); //輸出3 函式呼叫模式 當函式不是物件的特性時,則像函式一般被呼叫: var sum = add(3, 4); 使用此種呼叫模式時,this將結合至全域變數,這點是語言設計上的錯誤。如果設計正確,呼叫內層函式時,this應該仍會結合至外層函式的this。這項錯誤的後果之一,使得方法無法利用內層函式協助工作,因為內層函式並未享有方法對物件的存取,它的this結合到錯誤的地方了。有個輕鬆的解決之道:如果方法定義一個變數,並指派它的值為this,內層函式將透過這個變數存取this。一般習慣把這個變數命名為that: // 引數myObject具有double方法 myObject.double = function(){ var that = this; var helper = function(){ that.value = add(that.value, that.value); }; helper(); // 把helper視為函式而呼叫 }; // 把double視為函式而呼叫 myObject.double(); document.writeln(myObject.getValue()); // 輸出6 建構式呼叫模式(不建議使用) // 建立稱為Quo的建構式 // 它讓物件具有status的特性 var Quo = function(string){ this.status = string; }; //為所有Quo的實例,賦與一個稱為get_status的public方法 Quo.prototype.get_status = function(){ return this.status; }; //製作一個Quo實例 var myQuo = new Quo("confused"); document.writeln(myQuo.get_status()); // 輸出confused 與字首詞new合用的函式,稱為建構式(constructor)。 apply呼叫模式 apply方法讓我們建構一個引數陣列,用於呼叫函式,也能讓我們使用this的值。apply方法接受兩個參數,第一個是應該與this聯繫的值,第二個則是參數的陣列。 // 製作一個包含兩個數字的陣列,並予以相加 var array = [3, 4]; var sum = add.apply(null, array); // 總和為7 // 製作一個具有status成員的物件 var statusObject = { status: 'A-OK' }; // statusObject並非從Quo.prototype繼承而來 // 但我們可以在statusObject上呼叫get_status, // 儘管 statusObject 並不擁有get_status方法 var status = Quo.prototype.get_status.apply(statusObject); // status是A-OK arguments陣列 var sum = function(){ var i, sum = 0; for(i = 0; i < arguments.length; i += 1){ sum += arguments[i]; } return sum; }; document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108 上例並非特別好用的模式,因為設計上的錯誤,arguments並非真正的陣列,它是個像陣列的物件。arguments具有length特性,但缺乏所有陣列方法。 回傳 函式一定回傳某個值。如果未指定return值,則回傳undefined。 如果函式呼叫時附有字首詞new,而且return值不是物件,則改為回傳this(新物件)。 例外狀況 try敘述只有一個捕捉所有例外事件的catch區塊。如果你的處理方式需依照例外類型而定,則例外處理器必須檢視name,以判斷例外類型。 // throw敘述負責中斷函式的執行。它應該要拿到一個exception物件,物件中包含辨識例外類別的name特性, // 以及描述例外性質的message特性。你也可以再多加其它特性: var add = function(a, b){ if(typeof a !== 'number' || typeof b !== 'number'){ throw { name: 'TypeError', message: 'add needs numbers' } } return a + b; }; // 製作try_it函式,不正確地呼叫新的加法函式, // exception物件將被傳遞給try敘述的catch字句: var try_it = function(){ try{ add('seven'); } catch(e){ document.writeln(e.name + ': ' + e.message); } }; try_it(); 擴充型別 以擴充Function.prototype為例,增加一個方法到所有函式下: // 添method方法後,我們可不再需要鍵入prototype特性的名稱 Function.prototype.method = function(name, func){ if(!this.prototype[name]){ this.prototype[name] = func; } return this; }; JavaScript沒有獨立的整數型別(integer),所以有時需要單獨抽離數值中的整數部分。我們可以為Number.prototype添加一個integer方法:該方法可能使用Math.ceiling或Math.floor產生整數,根據數值的正負號而決定: Number.method('integer' function(){ return Math[this < 0 ? 'ceiling': 'floor'](this); }); // 例: document.writeln((-10 / 3).integer()); // -3 JavaScript缺少移除字串尾端空格的方法,以下可解決: String.method('trim', function(){ return this.replace(/^\s+|\s+$/g, ''); }); // 例: document.writeln('"' + " neat ".trim() + '"'); 範圍 var foo = function(){ var a = 3, b = 5; var bar = function(){ var b = 7, c = 11; //此處:a是3,b是7,c是11 a += b + c; //此處:a是21,b是7,c是11 }; //此處:a是3,b是5,c未定義 bar(); //此處:a是21,b是5 }; closure(閉包) 示範如何保護值不被未授權的來源隨意改變。 此時不再以物件實字做myObject的初始化,我們將透過呼叫一個回傳物件實字的函式而初始化myObject。這個函式定義了value變數,變數仍可被increment與getValue方法取用,但函式範圍把它隱藏起來,不讓程式的其餘部分見到: // 我們並非把函式本身指派給myObject,而是把函式呼叫的結果指派過去。請注意最後一行的()。 // 函式回傳一個包含兩個方法的物件,這兩個方法則繼續享有取用value變數的特權。 var myObject = function(){ var value = 0; return { increment: function(inc){ value += typeof inc === 'number' ? inc : 1; }, getValue: function(){ return value; } } }(); 稍早有提過Quo建構式,它會產生具有status特性與get_status方法的物件。只是這沒意思。可以直接取用的特性時,何必繞遠路呼叫getter方法?如果status是個private特性將更為有用,讓我們定義一個不同的quo函式: // 建立一個稱為quo的函式 // 製作具有get_status方法 // 與private status特性的物件 var quo = function(status){ return { get_status: function(){ return status; } }; }; // 製作一個quo實例 var myQuo = quo("amazed"); document.writeln(myQuo.get_status()); 上述的quo函式,設計為不需使用字首詞new,所以名稱也不需首字母大寫。當我們呼叫quo,它回傳包含get_status方法的新物件。對該物件的參考則儲存在myQuo。get_status方法仍有對quo的status特性的優先存取權,儘管quo已經被回傳了。get_status對參數複本沒有存取權,而是對參數本身有存取權。都是因為函式能取用建造它本身的背景情境,才有這種可能。這種狀況稱為closure(閉包)。 模組 我們可以使用函式與closure製造模組。模組是個函式或物件,用於呈現一個介面,但隱藏起它的狀態與實作。 // 為String擴充一個deentityify方法,它的功能是在字串裡尋找html實體(html entity),並以意義相等的字元取代。 // 在物件裡保存實體名稱及其相等事物頗為合理,但物件又該放在哪裡?最理想的方式,是把物件放入closure,或許再提供 // 一個新增實體的方法 String.method('deentityify', function(){ // 實體表。實體的名稱與字元的對照。 var entity = { quot: '"', lt: '' }; // 回傳deentityify方法 return function(){ // 以下為deentityify方法。它呼叫負責字串取代的方法。 // 尋找以&起始、以;結尾的子字串。如果其間的字元 // 儲存在實體表中,則以相等字元取代實體。 // 這個方法用到正規運算式 return this.replace(/&([^&;]+);/g, function(a, b){ var r = entity[b]; return typeof r === 'string' ? r : a; }); }; })(); // 以上請注意最後一行。我們立即呼叫使用()運算子製造的函式。此時的呼叫將建立並回傳變旁deentityify的函式。 document.writeln(''.deentityify()); 模組模式,一個「定義private變數與函式」的函式;它建立可(透過closure)取用private變數與函式的特許函式;而後把特許函式回傳或儲存於可存取的地方。 使用模組模式,可削減全域變數的使用。如此一來推廣了資訊隱藏及其它良好的設計習慣;在壓縮應用程式和其他singletons時非常有效。 模組也能用於製作具有安全性的件。假設想製作一個產生連續流水號的物件: var serial_maker = function(){ // 製作一個產生具唯一性字串的物件。 // 唯一性字串由兩個分組成:字首、與連續流水號。 // 物件包括設定字首與流水號的方法 // 還有一個產生唯一字串的gensym方法 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; } }; }(); var seqer = serial_maker(); seqer.set_prefix = 'Q'; seqer.set_seq = 1000; var unique = seqer.gensym(); //字串unique為"Q1000" 上例方法並未使用this與that。結果,則是不會外洩的seqer;除了同樣獲得方法的允許,否則無法取得或改變prefix或seq。seqer物件是可變的,所以方法雖可代取,但仍然不會開放對其秘密資料的存取。seqer不過是函式的集合,這些函式則保證具有使用或調整秘密資料的特殊能力。 如果我們把seqer.gensym傳送給第三方函式,函式將能產生唯一字串,但無法改變prefix或seq。 若覺得文章有幫助,請多分享,讓其他同好也能吸收網站技能知識。 Tweet

本文由carlos-studiocom提供 原文連結

寫了 5860316篇文章,獲得 23313次喜歡
精彩推薦