1. 程式人生 > >JavaScript語言精粹_第四章

JavaScript語言精粹_第四章

前綴 原型對象 高度 單例 write on() 整體 方法調用 通過

4.1 函數對象

  在JavaScript中,函數就是對象。對象是“名/值”對的集合並擁有一個連到原型對象的隱藏鏈接。對象字面量產生的對象連接到Object.prototype。函數對象連接到Function.prototype(該原型對象本身連接到object.prototype)

  每個函數對象在創建是也隨帶一個prototype屬性,它的值是一個擁有constructor屬性且至即為該函數的對象

  函數可以存放變量,可以被當作參數傳遞給其他函數,也可以在返回函數。

  因為函數是對象,所以它可以像其他的值一樣被使用,也可以擁有方法

4.3 調用

  調用一個函數將暫停當前函數的執行,傳遞控制權和參數給新函數。每個函數接受兩個附加的參數:this和arguments。

  在JavaScript中有四種調用模式:方法調用模式,函數調用模式,構造器調用模式和apply調用模式。這些模式在如何初始化關鍵參數this上存在差異。

  方法調用模式

    函數保存為對象的一個屬性時,它被稱為方法。方法可以通過this去訪問,this到對象的綁定發生在調用的時候,這個“超級”遲綁定使得函數可以對this高度復用。

  函數調用模式

    當一個函數並非一個對象的屬性時,它被當作一個函數來調用(在對象的方法中再次定義的方法)。

    當函數以此模式調用時,this被綁定到全局對象(這是一個bug),可以該方法定義一個變量並給它賦值為this,那麽內部函數就可以通過那個變量訪問到this

  構造器調用模式

    如果在一個函數前面帶上new來調用,那麽將創建一個隱藏鏈接到該函數的prototype成員的新對象,同時this將對綁定到那個新對象上。

    結合new前綴調用的函數被稱為構造器函數,它們保存在以大寫格式命名的變量裏,如果調用構造器函數時沒有在前面加上new,可能會發生非常糟糕的事情(在其他地方報錯,可能你要改一天。。。),既沒有編譯時警告,也沒有運行時警告,所以大寫約定非常重要。

  Apply調用模式

    apply讓我們構建一個參數數組並用其去調用函數,它允許我們選擇this的值。apply方法接收兩個參數,第一個是將綁定給this的值,第二個就是參數數組

4.4 參數

  當函數被調用時,會得到arguments數組,這使得編寫一個無須指定參數個數的函數成為可能。

  可以構造一個將很多個值相加的函數。

  因為這個設計錯誤,使得arguments並非一個真正的數組,它只是一個“類似數組”的對象,它擁有length屬性,但缺少所有的數組方法。

4.5 返回

  return語句可以用來是函數提前返回。一個函數總是會返回一個值,如果沒有制定返回值,則返回undefined。

  如果函數以在前面加上new前綴的方式來調用,且返回值不是一個對象,則返回this(該新對象)

4.7 給類型增加方法

  JavaScript允許給語言的基本類型增加方法。

  JavaScript沒有單獨的整數類型,我們可以通過Number.prototype添加一個integer來改善它。

    Number.prototype.integer = function (){

      return Math(this < 0 ? ‘ceiling‘ : ‘floor‘](this);

    }

  基本類型的原型是公共的結構,所以只在確定沒有改方法時才添加它。

4.8 遞歸

  遞歸函數可以非常高效地操作樹形結構,比如瀏覽器端的文檔對象模型(DOM),每次遞歸調用時處理給定樹的一小段。

  var walk_the_DOM = function walk(node,func){

    func(node);

    node = node.firstChild;

    while(node){

      walk(node,func);

      node = node.nextSibling;

    }

  };

  var getElementsByAttrbute = function(attr,value){

    var rusults = [];

    walk_the_DOM(document.body,function(node){

      var actual = node.nodeType === 1 && node.getAttrbute(att);

      if(typeof actual === ‘string‘ && (actual === value || typeof value !== ‘string‘)){

        results.push(node);

      }

    });

    return results;

  };

  一些語言提供了尾遞歸優化,這意味著如果一個函數返回自身遞歸調用的結果,那麽調用的過程會被替換為一個循環,它可以顯著提高速度。但這裏JavaScript沒有提供尾遞歸優化,深度遞歸的函數可能會因為返回堆棧溢出而運行失敗。

4.9 作用域

  JavaScript有函數作用域,但是沒有塊級作用域。最好的做法是在函數整體的頂部聲明函數中可能用到的所有變量

4.10 閉包

  內部函數擁有比它的外部更長的生命周期

  一個常見的例子

    var add_the_handlers = function (nodes) {

      var i;

      for(i=0; i < nodes.length; i += 1){

        nodes[i].onclick = function (e){

          alert(i);

        }

      }

    };

    它總是會顯示節點的數目,是因為事件處理器函數綁定了變量i,而不是函數在構造是的變量i的值

    var add_the_handlers = function (nodes) {

      var i;

      for(i=0; i < nodes.length; i += 1){

        nodes[i].onclick = function (i){

          return function(e){

            alert(i);

          };

        }(i);

      }

    };

4.11 回調

  函數可以讓不連續事件的處理變得更容易,可以發起異步的請求,提供一個當服務器的響應到達時將被調用的回調函數。

4.12 模塊

  通過使用閉包和函數來構造模塊,模塊是一個提供接口卻隱藏狀態與實現的函數或對象,通過使用函數去產生模塊,我們可以擯棄全局變量的使用,從而緩解這個糟糕的特性帶來的影響。

  模塊的一般形式:一個定義了私有變量和函數的函數;利用閉包創建可以訪問私有變量和函數的特權函數;最後返回這個特權函數,或者把他們保存到一個可訪問到的地方。

  模塊模式通常通過單例模式使用。

4.13 級聯

  讓一些方法返回this而不是undefined,就可以啟用級聯。

  在一個級聯中,我們可以在單獨一條的語句中依次調用同一個對象的很多方法。

4.14 套用

  可以將函數與傳遞給它的參數相結合去產生出一個新的函數

4.15 記憶

  在計算機領域中,記憶是主要用於加速程序計算的一種優化技術,是的函數避免重復演算之前已被處理的輸入,而返回以緩存的結果。

  JavaScript的對象和數組要實現這種優化是非常方便的

  var fibonacci = function (n) {

    return n < 2 ? n : fibonacci (n - 1) + fibonacci (n - 2);

  } 

  for (var i = 0; i <= 10; i += i){

    document.writeIn( i + ‘=‘ + fibonacci(i));

  }

  這裏調用了11次,而它自身調用了442次去計算可能已被剛計算過的值。

  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;

  }();

  這個函數返回同樣的結果,調用了它11次,它自身調用了18次去取得之前存儲的結果。  

JavaScript語言精粹_第四章