《JavaScript 語言精粹》 學習筆記 —— 第四章 函數

來源:互聯網
上載者:User

第四章 函數

4.1、在JavaScript中,函數就是對象。
4.2、函數的對象串連到Function.prototype(該原型對象本身被串連到Object.prototype)。
4.3、函數在建立的時候附有兩個附加的隱藏屬性:函數上下文和實現函數行為的代碼。
4.4、函數與其它對象的不同之處在於它可以被調用。
4.5、函數對象可以通過函數字面量來建立:

//  建立一個名為 add 的變數,並用來把兩個數位相機的函數賦值給它。var add = function (a, b) {return a + b;};

  函數字面量包括四個部分。
  第一部分是保留字function。
  第二部分是函數名,它可以被省略——即“匿名函數”。函數名可以用來遞迴地調用自己。
  第三部分是包圍在圓括弧中的一組參數。其中每個參數用逗號分隔。
  第四部分是包圍在花括弧中的一組語句。這些語句是函數的主體。它們在函數被調用是執行。
4.6、函數字面量可以出現在任何錶達式允許出現的地方,函數也可以被定義在其它函數中。一個內建函式自然可以訪問自己的參數和變數,同時它也能方便地訪問它被嵌套在其中的那個函數的參數和變數。通過函數字面量建立的函數對象包含一個串連到外部內容的串連。這被稱為“閉包”。它是JavaScript強大表現力的根基。
4.7、調用一個函數將暫停當前函數的執行,傳遞控制權和參數給新函數。
4.8、除聲明時定義的形參,每個函數還接收兩個附加的參數:this和arguments。
4.9、參數this的值取決於調用的模式。在JavaScript中一共有四種調用模式:方法調用模式、函數調用模式、構造器調用模式和apply調用模式
4.10、當實際參數的個數和形式參數的個數不匹配時不會導致執行階段錯誤。如果實際參數值過多,超出的參數值將被忽略。如果實際的參數值過少,缺失的值將會被替換為undefined。
4.11、對參數值不會進行類型檢查,任何類型的值都可以被傳遞給參數。
4.12、方法調用模式:當一個函數被儲存為對象的一個屬性時,稱它為一個“方法”。當一個方法被調用時,this被綁定到該對象。

// 建立 myObject。它有一個 value 屬性和 increment 方法。// increment 方法接受一個可選的參數。如果參數不是數字,那麼預設使用數字1。var myObject = {value : 0,increment : function (inc) {this.value += typeof inc === 'number' ? inc : 1;}};myObject.increment();document.writeln(myObject.value); // 1myObject.increment(2);document.writeln(myObject.value); // 3

示範Demo
4.13、函數調用模式:當一個函數並非一個對象的屬性時,那麼它被當作一個函數來調用:

var sum = add(3, 4); // sum 的值為 7

  當函數以此模式被調用時,this被綁定到全域對象。這是語言設計上的一個錯誤。倘若正確,this應該綁定到外部函數的this變數。這個設計錯的後果是方法不能利用內建函式來協助它工作,因為內建函式被綁定了錯誤的值,所以不能共用該方法對對象的訪問權。解決方案是該方法定義一個變數並給它賦值為this。
4.14、構造器調用模式:如果在一個函數前面帶上new來調用,那麼將建立一個隱藏串連到該函數的 prototype 成員的新對象,同時 this 將會被綁定到那個新對象上。

// 建立一個名為 Quo 的構造器函數。它構造一個帶有 status 屬性的對象。var Quo = function (string) {this.status = string;};// 給 Quo 的所有執行個體提供一個名為 get_status 的公用方法。Quo.prototype.get_status = function () {return this.status;};// 構造一個 Quo 執行個體var myQuo = new Quo("confused");document.writeln(myQuo.get_status()); // 令人困惑

示範Demo
注: 作者不推薦這種寫法,原因在下章闡述。
4.15、Apply 調用模式:因為 JavaScript 是一門函數式的物件導向程式設計語言,所以函數可以擁有方法。
apply 方法可以構建一個參數數組並勇氣去調用函數。它也允許我們選擇 this 的值。
apply 方法接收兩個參數。第一個是將被綁定給 this 的值。第二個就是參數數組。

// 構造一個包含兩個數位數組,並將它們相加。var array = [3, 4];var sum = add.apply(null, array); // sum 值為 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'。

示範Demo
4.16、函數可以通過 arguments 數組訪問所有它被調用時傳遞給它的參數列表。因為語言的一個設計錯誤,arguments 並不是一個真正的數組,它只是一個類似數組的對象。arguments 擁有一個 length 屬性,但它缺少所有數組的方法。
4.17、一個函數總有一個傳回值,如果沒有指定則返回 undefined。
4.18、如果函數在前面加上 new 首碼來調用,且傳回值不是一個對象,則返回 this (該新對象)。
4.19、throw 語句中斷函數的執行。它拋出一個 exception 對象,該對象包含可識別異常類型的 name 屬性和一個描述性的 message 屬性。也可以添加其它屬性。
4.20、遞迴函式是直接或間接地調用自身的函數。經典遞迴函式例子——漢諾塔(更多可見wiki:http://zh.wikipedia.org/wiki/%E6%B1%89%E8%AF%BA%E5%A1%94)。

var hanoi = function (disc, src, aux, dst) {if (disc > 0) {hanoi(disc - 1, src, dst, aux);document.writeln('Move disc ' + disc + ' from ' + src + ' to ' + dst);hanoi(disc ? 1, aux, src, dst);}};hanoi(3, 'Src', 'Aux', 'Dst');

 圓盤數量為3時返回這樣的解法
Move disc 1 from Src to Dst
Move disc 2 from Src to Aux
Move disc 1 from Dst to Aux
Move disc 3 from Src to Dst
Move disc 1 from Aux to Src
Move disc 2 from Aux to Dst
Move disc 1 from Src to Dst
示範Demo
4.21、尾遞迴是一種在函數的最後執行遞迴調用語句的特殊形式的遞迴。
4.22、尾遞迴可被替換為一個迴圈,但JavaScript沒有對尾遞迴作出最佳化。
4.23、範圍控制著變數與參數的可見度及生命週期。作用:1、減少變數名稱衝突;2、提供自動記憶體管理。
4.24、JavaScript不支援代碼塊的塊級範圍。
4.25、JavaScript有函數範圍。定義在函數中的參數和變數在函數外部是不可見的,在函數中的任何位置定義的變數在該函數中的任何地方都是可見的。
4.26、JavaScript因缺少塊級範圍,因此建議在函數體的頂部聲明函數中可能用到的所有變數。
4.27、範圍的好處是內建函式可以訪問定義它們的外部函數的參數和變數(除了 this 和 arguments)。
4.28、閉包例子一:一個有趣的情形是內建函式擁有比它外部函數更長的生命週期

var myObject = function () {var value = 0; // 私人變數,對 increment 和 getValue 可見return {increment : function (inc) {value += typeof inc === 'number' ? inc : 1;},getValue : function () {return value;}};}();document.writeln(myObject.value);// undefineddocument.writeln(myObject.getValue());// 0myObject.increment(2);document.writeln(myObject.getValue());// 2myObject.increment(4);document.writeln(myObject.getValue());// 6

示範Demo
仔細一看,並沒有把一個函數賦值給myObject。而是把嗲用該函數後返回的結果賦值給它。該函數返回了包括兩個方法的對象,並且這些方法繼續享有訪問value變數的特權。
4.29、閉包例子二:建立一個名為 quo 的建構函式

// 它構造出帶有 get_status 方法和 status 私人屬性的一個對象var quo = function(status){   return {     get_status:function(){       return status;     }  };};var myQuo = quo("amazed"); document.writeln(myQuo.get_status());    //amazed

示範Demo
這個 quo 函數被設計成無須在前面加上 new 來使用,所以名字首字母也沒有大寫。當我們調用 quo 時,它返回包含 get_status 方法的一個新對象。該對象的一個引用儲存在 myQuo 中。即使 quo 已經返回了,但 get_status 方法仍然享有訪問 quo 對象的 status 屬性的特權。get_status 方法並不是訪問該參數的一個拷貝;它訪問的就是該參數本身。這是可能的,因為該函數可以訪問它被建立時所處的上下文環境。這被稱為閉包
4.30、閉包例子三:一個更有用的例子

// Define a function that sets a DOM node's color// to yellow and then fades it to white.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);

示範Demo
setTimout 存在會讓fade函數中的level變數再次被賦值,fade函數在之前已經返回了,但只要fade內建函式需要,它的變數就會持續保留。
4.31、內建函式訪問外部函數的變數是不需要複製的。
4.32、“模組”是一個提供介面卻隱藏狀態與實現的函數或對象。可以使用函數和閉包來構造模組。
4.33、模組模式利用了函數範圍和閉包來建立綁定對象與私人成員的關聯。
4.34、模組模式的一般形式是:最後返回這個特權函數,或者把它們儲存到一個可以訪問到的地方。
4.35、使用模組模式可以摒棄全域變數的使用。它促進了資訊隱藏和其它優秀的設計實踐。對於應用程式的封裝,或者構造其它單例對象,模組模式非常有效。
4.36、使用模組模式的具有單例的特性產生一個安全的對象 serial_maker。

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 is "Q1000"document.writeln(unique);document.writeln(seqer.gensym());document.writeln(seqer.gensym());document.writeln(seqer.gensym());

示範Demo
4.37、讓一些沒有傳回值的方法返回 this 而不是 undefined,就啟用了級聯。

總結:
  這一章最抽象且難以理解的就是“閉包”。我的理解是,“閉包”是一個函數(假設該函數名為Fa),函數Fa內部有一個子函數(假設該函數名為Fb),子函數Fb能讀取其外部父函數Fa的私人變數,它使得內建函式Fb與外部函數Fa之間有一種通訊機制,但這一切都在Fa內部有效,離開Fa通訊中斷,它因與外部不能聯絡而封閉,所以可以稱為“closure”,翻譯成中文就成了“閉包”,包可以被理解成其因“閉”而獨立。
  這篇文章比較通俗易懂地解釋了什麼是閉包——《學習Javascript閉包(Closure)》

Refer:
英文參考文檔:《JavaScript: The Good Parts》
提供學習協助的網站:
1、《Javascript的10個設計缺陷》
2、《學習Javascript閉包(Closure)》

3、漢諾塔示範

轉載請註明出處:http://blog.csdn.net/xxd851116/article/details/7669803

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.