成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

《JavaScript高級(jí)程序設(shè)計(jì)》(第3版)讀書(shū)筆記 第7章 函數(shù)表達(dá)式

鄒立鵬 / 576人閱讀

摘要:定義函數(shù)表達(dá)式的方式有兩種函數(shù)聲明。不過(guò),這并不是匿名函數(shù)唯一的用途??梢允褂妹瘮?shù)表達(dá)式來(lái)達(dá)成相同的結(jié)果閉包匿名函數(shù)和閉包是兩個(gè)概念,容易混淆。匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其對(duì)象通常指向通過(guò)改變函數(shù)的執(zhí)行環(huán)境的情況除外。

定義函數(shù)表達(dá)式的方式有兩種:

函數(shù)聲明。它的重要特征就是 函數(shù)聲明提升(function declaration hoisting) 即在執(zhí)行代碼之前會(huì)先讀取函數(shù)聲明。這就意味著可以把函數(shù)聲明放在調(diào)用它的語(yǔ)句后面。

函數(shù)表達(dá)式。

// 函數(shù)聲明
function functionName(params) {
  ...
}
// 函數(shù)表達(dá)式有幾種不同的方式,下面是最常見(jiàn)的一種
var functionName = function(params) {
  ...
}

上面這種形式看起來(lái)好像是常規(guī)的變量賦值語(yǔ)句。而右邊這種形式創(chuàng)建的函數(shù)叫做 匿名函數(shù) (anonymous function)(有時(shí)候也叫 拉姆達(dá)函數(shù) lambda),因?yàn)閒unction關(guān)鍵字后面沒(méi)有標(biāo)識(shí)符。

函數(shù)表達(dá)式與其他表達(dá)式一樣,在使用前必須先賦值,否則會(huì)導(dǎo)致出錯(cuò)。

sayHi();         // 錯(cuò)誤,函數(shù)還不存在
var sayHi = function () {
  console.log("Hi!");
};

表面上看,下面的代碼沒(méi)有問(wèn)題,conditiontrue時(shí),使用一個(gè)定義,否則使用另一個(gè)定義。實(shí)際上,在ECMAScript中屬于無(wú)效語(yǔ)法,JavaScript引擎會(huì)嘗試修正錯(cuò)誤,將其轉(zhuǎn)換為合理狀態(tài)。但問(wèn)題是瀏覽器嘗試修正錯(cuò)誤的做法并不一致。大多數(shù)瀏覽器會(huì)返回第二個(gè)聲明,忽略condition的值;Firefox會(huì)在condition為true的時(shí)候返回第一個(gè)聲明。因此這種做法很危險(xiǎn),不應(yīng)該出現(xiàn)在你的代碼中。

// 不要這樣做!
if (condition) {
  function sayHi() {
    console.log("Hi!");
  }
} else {
  function sayHi() {
    console.log("Yo!");
  }
}

上述代碼改為函數(shù)表達(dá)式就沒(méi)有問(wèn)題

// 可以這樣做
var sayHi;

if (condition) {
  sayHi = function() {
    console.log("Hi!");
  }
} else {
  sayHi = function() {
    console.log("Yo!");
  }
}

能夠創(chuàng)建函數(shù)再賦值給變量,也就能把函數(shù)作為其他函數(shù)的返回值。createComparisonFunction() 就返回了一個(gè)匿名函數(shù)。

function createComparisonFunction (propertyName) {
  return function (object1, object2) {
    var value1 = object1[propertyName];
    var value2 = object2[propertyName];

    if (value1 < value2) {
      return -1;
    } else if (value1 > value2) {
      return 1;
    } else {
      return 0;
    }
  }
}

在把函數(shù)當(dāng)成值來(lái)使用的情況下,都可以使用匿名函數(shù)。不過(guò),這并不是匿名函數(shù)唯一的用途。

遞歸

遞歸函數(shù)是在一個(gè)函數(shù)通過(guò)名字調(diào)用自身的情況下構(gòu)成的

// 經(jīng)典的遞歸階乘函數(shù)
function factorial (num) {
  if (num <= -1) {
    return 1;
  } else {
    return num * factorial(num - 1);
  }
}

雖然遞歸階乘函數(shù)表面看沒(méi)有什么問(wèn)題,但下面的代碼卻可能導(dǎo)致它出錯(cuò)

// 把factorial() 函數(shù)保存在變量anotherFactorial中
var anotherFactorial = factorial;

// 將factorial設(shè)置為null
// 現(xiàn)在指向原始函數(shù)的引用只剩下anotherFactorial
factorial = null;

// 原始函數(shù)必須執(zhí)行factorial()
// 但factorial不再是函數(shù),所以導(dǎo)致出錯(cuò)
anotherFactorial(4);      // throw error!

可以使用 arguments.callee (指向正在執(zhí)行函數(shù)的指針)實(shí)現(xiàn)函數(shù)的遞歸調(diào)用

// 非嚴(yán)格模式
function factorial (num) {
  if (num <= -1) {
    return 1;
  } else {
    return num * arguments.callee(num - 1);
  }
}

但在嚴(yán)格模式下不能通過(guò)腳本訪問(wèn) arguments.callee,會(huì)導(dǎo)致出錯(cuò)??梢允褂妹瘮?shù)表達(dá)式來(lái)達(dá)成相同的結(jié)果

var factorial = (function f(num) {
  if (num <= -1) {
    return 1;
  } else {
    return num * f(num - 1);
  }
})
閉包

匿名函數(shù)閉包 是兩個(gè)概念,容易混淆。 閉包 是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)。

創(chuàng)建閉包的常見(jiàn)方式,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),仍以前面的 createComparisonFunction() 函數(shù)為例

function createComparisonFunction (propertyName) {
  
  return function (object1, object2) {

    // 下面兩行代碼訪問(wèn)了外部函數(shù)中的變量propertyName
    // 即使這個(gè)內(nèi)部函數(shù)被返回了,而且是在其他地方被調(diào)用了
    // 它仍然可以訪問(wèn)變量 propertyName
    var value1 = object1[propertyName];
    var value2 = object2[propertyName];

    if (value1 < value2) {
      return -1;
    } else if (value1 > value2) {
      return 1;
    } else {
      return 0;
    }
  }
}

上述例子,即使內(nèi)部函數(shù)被返回了,在其他地方調(diào)用,它仍然可以訪問(wèn)propertName。因?yàn)檫@個(gè)內(nèi)部函數(shù)的作用域鏈中包含 createComparisonFunction() 的作用域。要搞清楚其中細(xì)節(jié),必須從理解函數(shù)被調(diào)用的時(shí)候都會(huì)發(fā)生什么入手。

第4章介紹過(guò) 作用域鏈。當(dāng)某個(gè)函數(shù)被 調(diào)用 時(shí)會(huì)發(fā)生下列事情:

創(chuàng)建一個(gè) 執(zhí)行環(huán)境(execution context) 及相應(yīng)的 作用域鏈

使用 arguments 和其他命名參數(shù)的值來(lái)初始化函數(shù)的 活動(dòng)對(duì)象(activation object)

形成作用域鏈。外部函數(shù)的活動(dòng)對(duì)象始終處于第二位,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象處于第三位...直至作為作用域終點(diǎn)的全局執(zhí)行環(huán)境

函數(shù)執(zhí)行過(guò)程中,為讀寫(xiě)變量的值,就需要在作用域鏈中查找變量。

function compare(value1, value2) {
  if (value1 < value2) {
    return -1;
  } else if (value1 > value2) {
    return 1;
  } else {
    return 0;
  }
}

var result = compare(5, 10);

后臺(tái)的每個(gè)執(zhí)行環(huán)境都有一個(gè)表示變量的對(duì)象——變量對(duì)象。全局環(huán)境的變量對(duì)象始終存在,而像compare()函數(shù)這樣的局部環(huán)境的變量對(duì)象,則只在函數(shù)執(zhí)行的過(guò)程中存在。

在創(chuàng)建 compare() 函數(shù)時(shí),會(huì)創(chuàng)建一個(gè)預(yù)先包含全局變量對(duì)象的作用域鏈,這個(gè)作用域鏈被保存在內(nèi)部的[[scope]]屬性中。

當(dāng)調(diào)用 compare() 函數(shù)時(shí),會(huì)為函數(shù)創(chuàng)建一個(gè)執(zhí)行環(huán)境,然后通過(guò)復(fù)制函數(shù)的[[scope]]屬性中的對(duì)象構(gòu)建起執(zhí)行環(huán)境的作用域鏈。

此后,又有一個(gè)活動(dòng)對(duì)象(在此作為變量對(duì)象使用)被創(chuàng)建并被推入執(zhí)行環(huán)境作用域鏈的前端。

對(duì)于本例, compare() 函數(shù)的執(zhí)行環(huán)境而言,其作用域鏈中包含兩個(gè)變量對(duì)象:

本地活動(dòng)對(duì)象

全局活動(dòng)對(duì)象

作用域鏈本質(zhì)上是一個(gè)指向變量對(duì)象的指針列表,它只引用但不實(shí)際包含變量對(duì)象。

無(wú)論什么時(shí)候在函數(shù)中訪問(wèn)一個(gè)變量時(shí),就會(huì)從作用域鏈中搜索具有相應(yīng)名字的變量。一般而言,當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷(xiāo)毀,內(nèi)存中近保存全局作用域(全局執(zhí)行環(huán)境的變量對(duì)象)。

但是閉包的情況有所不同

function createComparisonFunction (propertyName) {
  
  return function (object1, object2) {

    // 下面兩行代碼訪問(wèn)了外部函數(shù)中的變量propertyName
    // 即使這個(gè)內(nèi)部函數(shù)被返回了,而且是在其他地方被調(diào)用了
    // 它仍然可以訪問(wèn)變量 propertyName
    // 即為 createComparisonFunction 的活動(dòng)對(duì)象
    var value1 = object1[propertyName];
    var value2 = object2[propertyName];

    if (value1 < value2) {
      return -1;
    } else if (value1 > value2) {
      return 1;
    } else {
      return 0;
    }
  }
}

// 創(chuàng)建比較函數(shù)
// 調(diào)用了 createComparisonFunction() 方法
// 創(chuàng)建了 createComparisonFunction 的活動(dòng)對(duì)象
// 返回內(nèi)部的匿名函數(shù) 保存在 compareNames
// createComparisonFunction 執(zhí)行完畢
// 但它的活動(dòng)對(duì)象仍被 內(nèi)部匿名函數(shù)引用,所以活動(dòng)對(duì)象仍然存在,不會(huì)銷(xiāo)毀
var compareNames = createComparisonFunction("name");

// 此時(shí)result調(diào)用了 保存在 compareNames 的匿名函數(shù)
// 該匿名函數(shù)保持了對(duì) createComparisonFunction 活動(dòng)對(duì)象的引用
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });

// 即使 compareNames 執(zhí)行完畢,createComparisonFunction 活動(dòng)對(duì)象依然存在
// 需要手動(dòng)解除對(duì)匿名函數(shù)的引用(以便釋放內(nèi)存)
compareNames = null;

首先,創(chuàng)建的比較函數(shù)被保存在變量compareNames中,而通過(guò)將其設(shè)置為null解除引用,就等于通知垃圾回收例程將其消除。隨著匿名函數(shù)的作用域鏈被銷(xiāo)毀,其他作用域鏈(除了全局作用域)也都可以安全地的銷(xiāo)毀了。圖7-2展示了調(diào)用compareNames()的過(guò)程中產(chǎn)生的作用域鏈之間的關(guān)系

由于閉包會(huì)攜帶包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過(guò)度使用閉包可能導(dǎo)致內(nèi)存占用過(guò)多,我們建議讀者只在絕對(duì)必要時(shí)再考慮使用閉包。雖然像V8等優(yōu)化后的JavaScript引擎會(huì)嘗試回收被閉包占用的內(nèi)存,但請(qǐng)還是要謹(jǐn)慎使用。

閉包與變量

作用域鏈的這種配置機(jī)制,引出了一個(gè)值得注意的副作用,即閉包只能取得包含函數(shù)中任何變量的最后一值。別忘了閉包所保存的是整個(gè)變量對(duì)象,而不是某個(gè)特殊的變量。

function createFunctions() {
  var result = new Array();

  for (var i=0; i < 10; i++) {
    // 賦值給數(shù)組元素的是匿名函數(shù)本身,不是具體的值
    // 所以在 createFunctions() 執(zhí)行完畢后,調(diào)用數(shù)組內(nèi)的函數(shù),返回的是變量i的值
    // 而變量i在執(zhí)行完畢后,等于 10
    result[i] = function() {
      // 返回指向變量 i 的指針
      return i;
    };
  }

  return result;
}

這個(gè)函數(shù)會(huì)返回一個(gè) 函數(shù)數(shù)組。表面上看result里的每一項(xiàng)函數(shù)都應(yīng)該返回自己的索引值。但實(shí)際上每一個(gè)函數(shù)返回的都是10

因?yàn)槊總€(gè)函數(shù)的作用域中都保存著createFunctions()函數(shù)的活動(dòng)對(duì)象,所以它們引用的都是同一個(gè)變量i。當(dāng)createFunctions()函數(shù)返回后,變量i的值是10,此時(shí)每個(gè)函數(shù)都引用著保存變量i的同一個(gè)變量對(duì)象,所以在每個(gè)函數(shù)內(nèi)部i的值都是10.

可以通過(guò)創(chuàng)建另一個(gè)匿名函數(shù)強(qiáng)制讓閉包的行為符合預(yù)期

function createFunctions() {
  var result = new Array();

  for (var i=0; i < 10; i++) {
    // 此時(shí)返回的里層匿名函數(shù)調(diào)用了外層匿名函數(shù)的 num
    // 里層匿名函數(shù)創(chuàng)建并返回了一個(gè)訪問(wèn) num 的閉包
    // 如此一來(lái) result 數(shù)組中的每個(gè)函數(shù)都有自己的num變量副本
    result[i] = function(num) {
      // 返回創(chuàng)建的另一個(gè)匿名函數(shù)
      return function() {
        return num;
      };
    }(i);
  }

  return result;
}
關(guān)于this對(duì)象

在閉包中使用this對(duì)象也可能會(huì)導(dǎo)致一些問(wèn)題。this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:

在全局函數(shù)中,this等于window

當(dāng)函數(shù)被作為某個(gè)對(duì)象的方法調(diào)用時(shí),this等于那個(gè)對(duì)象。

匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其this對(duì)象通常指向window(通過(guò)call() apply()改變函數(shù)的執(zhí)行環(huán)境的情況除外)。但有時(shí)候由于變成寫(xiě)閉包的方式不同,這一點(diǎn)可能不會(huì)那么明顯

var name = "The Window";

var object = {
  name: "My Object",

  getNameFunc: function() {
    return function() {
      return this.name;
    };
  }
};

// 在非嚴(yán)格模式下
object.getNameFunc()();                    // "The Window"

每個(gè)函數(shù)在被調(diào)用時(shí)都會(huì)自動(dòng)取得兩個(gè)特殊變量: thisarguments。內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會(huì)搜索到其活動(dòng)對(duì)象為止,因此永遠(yuǎn)不可能直接訪問(wèn)外部函數(shù)中的這兩個(gè)變量(這一點(diǎn)通過(guò)圖7-2可以看的清楚)。

不過(guò),把外部作用域中的this對(duì)象保存在一個(gè)閉包能夠訪問(wèn)到的變量里,就可以讓閉包訪問(wèn)該對(duì)象了。

var name = "The Window";

var object = {
  name: "My Object",

  getNameFunc: function() {
    var that = this;
    return function() {
      return that.name;
    };
  }
};

object.getNameFunc()();                    // "My Object"

在幾種特殊情況下,this的值可能會(huì)意外的改變。

var name = "The Window";

var object = {
  name: "My Object",

  getName: function() {
    return this.name;
  }
};

// this 指向 object
object.getName();          // "My Object"

// 加上了括號(hào),看似在引用一個(gè)函數(shù)
// 但 (object.getName) 和 object.getName 的定義是相同的
// 所以這行代碼與上面的代碼無(wú)異
(object.getName)();      // "My Object"

// 非嚴(yán)格模式
// 賦值語(yǔ)句會(huì)返回 object.getName 的匿名函數(shù)
// 相當(dāng)于將匿名函數(shù)在全局環(huán)境下運(yùn)行
(object.getName = object.getName)();          // "The Window"

第三行代碼先執(zhí)行了一條賦值語(yǔ)句,然后再調(diào)用賦值后的結(jié)果。因?yàn)檫@個(gè)賦值表達(dá)式的值是函數(shù)本身,所以this的值不能得到維持,結(jié)果就返回了 "The Window" 。

內(nèi)存泄漏

由于IE9之前的版本對(duì)JScript對(duì)象和COM對(duì)象使用不同的垃圾收集例程(第4章介紹過(guò)),因此閉包在IE的這些版本中會(huì)導(dǎo)致一些特殊的問(wèn)題。具體來(lái)說(shuō),如果閉包的作用域鏈中保存著一個(gè)HTML元素,那么就意味著該元素將無(wú)法被銷(xiāo)毀

function assignHandler() {
  var element = document.getElementById("someElement");
  element.onclick = function() {
    console.log(element.id);
  };
}

以上代碼創(chuàng)建了一個(gè)作為element元素處理程序的閉包,而這個(gè)閉包則又創(chuàng)建了一個(gè)循環(huán)引用(事件將在第13章討論)。由于匿名函數(shù)保存了一個(gè)對(duì)assignHandler()的活動(dòng)對(duì)象的引用,因此就會(huì)導(dǎo)致無(wú)法減少element的引用數(shù)。只要匿名函數(shù)存在,element的引用數(shù)至少是1。

// 以下修改可以避免這個(gè)問(wèn)題
function assignHandler() {
  var element = document.getElementById("someElement");
  var id = element.id;

  element.onclick = function() {
    console.log(id);
  };

  element = null;
}

閉包中引用包含函數(shù)的整個(gè)活動(dòng)對(duì)象,而其中包含著element。即使閉包不直接引用element,包含函數(shù)的活動(dòng)對(duì)象也仍然會(huì)保存一個(gè)引用。因此有必要把element變量設(shè)置為null

模仿塊級(jí)作用域

JavaScript沒(méi)有塊級(jí)作用域。這意味著在塊語(yǔ)句中定義的變量,實(shí)際上是在包含函數(shù)中而非語(yǔ)句中創(chuàng)建的。

function outputNumerbs(cout) {
  for (var i=0; i < cout; i++) {
    console.log(i);
  }
  console.log(i);    // 計(jì)數(shù)
}

在Java, C++等語(yǔ)言中,變量i只會(huì)在for循環(huán)的語(yǔ)句塊中有定義,循環(huán)一旦結(jié)束,變量i就會(huì)被銷(xiāo)毀??墒窃贘avaScript中,變量i是定義在outputNumbers()的活動(dòng)對(duì)象中的,因此從它有定義開(kāi)始,就可以在函數(shù)內(nèi)部隨處訪問(wèn)它。即使像下面這樣錯(cuò)誤的重新聲明變量也不會(huì)改變值。

function outputNumerbs(cout) {
  for (var i=0; i < cout; i++) {
    console.log(i);
  }

  var i;             // 重新聲明變量
  console.log(i);    // 計(jì)數(shù)
}

JavaScript從來(lái)不會(huì)告訴你是否多次聲明了同一個(gè)變量,遇到這種情況,它只會(huì)對(duì)后續(xù)的聲明視而不見(jiàn)(不過(guò),它會(huì)執(zhí)行后續(xù)聲明中的變量初始化)。

匿名函數(shù)可以用來(lái)模仿塊級(jí)作用域并避免這個(gè)問(wèn)題。用作塊級(jí)作用域(通常稱為 私有作用域 )的匿名函數(shù)的語(yǔ)法如下:

(function() {
  // 這里是塊級(jí)作用域
  ...
})()
// 常見(jiàn)的代碼片段
// 定義了一個(gè)函數(shù),然后立即調(diào)用它
var someFunction = function() {
  // 這里是塊級(jí)作用域
  ...
};
someFunction();

那這里如果將函數(shù)名也去掉呢?答案是不行,會(huì)導(dǎo)致出錯(cuò)。因?yàn)?b>JavaScript將function關(guān)鍵字當(dāng)做一個(gè)函數(shù)聲明的開(kāi)始,而函數(shù)聲明后面不能跟圓括號(hào)。(函數(shù)表達(dá)式可以)

function() {
  // 這里是塊級(jí)作用域
  ...
}()    // 出錯(cuò)!

要將函數(shù)聲明轉(zhuǎn)換成函數(shù)表達(dá)式,只要外面包裹圓括號(hào)即可

(function() {
  // 這里是塊級(jí)作用域
  ...
}())

無(wú)論在什么地方,只要臨時(shí)需要一些變量,就可以使用私有作用域

function outputNumbers(cout) {
  // 這里是一個(gè)閉包,匿名函數(shù)可以訪問(wèn) cout
  (function () {
    for (var i=0; i < cout; i++) {
      console.log(i);
    }
  })();

  // 在這里調(diào)用變量 i 會(huì)報(bào)錯(cuò)
  console.log(i);                  // throw error
}

這種技術(shù)經(jīng)常在全局作用域中被用在函數(shù)外部,從而限制向全局作用域中添加過(guò)多的變量和函數(shù)。

(function() {

  var now = new Date();
  if (now.getMonth() == 0 && now.gettDate() == 1) {
    console.log("Haapy new year!");
  }
})();

這種做法可以減少閉包占用的內(nèi)存,因?yàn)闆](méi)有指向匿名函數(shù)的引用。只要函數(shù)執(zhí)行完畢,就可以立即銷(xiāo)毀其作用域鏈了。

私有變量

嚴(yán)格來(lái)講,JavaScript中沒(méi)有私有成員的概念;所有對(duì)象屬性都是共有的。不過(guò)倒是有一個(gè)私有變量的概念。

任何在函數(shù)中定義的變量,都可以認(rèn)為是私有變量,因?yàn)椴荒茉诤瘮?shù)的外部訪問(wèn)這些變量。

如果在函數(shù)內(nèi)部創(chuàng)建一個(gè)閉包,那么閉包通過(guò)自己的作用域鏈可以訪問(wèn)這些私有變量。利用這一點(diǎn),我們就可以創(chuàng)建用于訪問(wèn)私有變量的公有方法。

有權(quán)訪問(wèn)私有變量和私有函數(shù)的公有方法稱為 特權(quán)方法(privileged method) 。有兩種創(chuàng)建特權(quán)方法的方式:

在構(gòu)造函數(shù)中定義特權(quán)方法(靜態(tài)私有變量)

模塊模式

構(gòu)造函數(shù)中定義,基本的模式如下

// 構(gòu)造函數(shù)Person
// 入?yún)?name 是它的私有變量
function Person(name) {
  this.getName = function() {
    return name;
  };

  this.setName = function(value) {
    name = value;
  };
}

var person = new Person("Nicholas");
console.log(person.getName());              // "Nicholas"

person.setName("Greg");
console.log(person.getName());              // "Greg"

這種模式有一個(gè)缺點(diǎn),那就是必須使用構(gòu)造函數(shù)來(lái)達(dá)到目的,而第6章討論過(guò),構(gòu)造函數(shù)模式的確定是針對(duì)每一個(gè)實(shí)例都會(huì)創(chuàng)建出同樣的一組新方法

而使用靜態(tài)私有變量來(lái)實(shí)現(xiàn)特權(quán)方法就可以避免這個(gè)問(wèn)題。

靜態(tài)私有變量
(function() {

  // 私有變量
  var privateVariable = 10;

  // 私有函數(shù)
  function privateFunction() {
    return false;
  }

  // 構(gòu)造函數(shù)
  // 這里沒(méi)有使用var操作符,自動(dòng)創(chuàng)建全局變量
  // 嚴(yán)格模式下不能使用
  MyObject = function() {};

  // 公有/特權(quán)方法
  MyObject.prototype.publicMethod = function() {
    privateVariable++;
    return privateFunction();
  };
})();

公有方法是在原型上定義的,避免了重復(fù)創(chuàng)建方法的情況。

需要注意的是,這個(gè)模式在定義構(gòu)造函數(shù)時(shí)沒(méi)有使用函數(shù)聲明,而是使用了函數(shù)表達(dá)式。函數(shù)聲明只能創(chuàng)建局部函數(shù),但那并不是我們想要的。

這個(gè)特權(quán)方法,作為一個(gè)閉包,總是保存著對(duì)包含作用域的引用。

(function() {

  var name = "";

  Person = function(value) {
    name = value;
  };

  Person.prototype.getName = function() {
    return name;
  };

  Person.prototype.setName = function (value) {
    name = value;
  };
})();

var person1 = new Person("Nicholas");
console.log(person1.getName());                          // "Nicholas"
person1.setName("Greg");
console.log(person1.getName());                          // "Greg"

var person2 = new Person("Michael");
console.log(person1.getName());                          // "Michael"
console.log(person2.getName());                          // "Michael"

這個(gè)例子中的Person構(gòu)造函數(shù)與getName() setName() 方法一樣,都有權(quán)訪問(wèn)私有變量name

name變成了一個(gè)靜態(tài)的、由所有實(shí)例共享的屬性。

以這種方式創(chuàng)建靜態(tài)私有變量會(huì)因?yàn)槭褂迷投鲞M(jìn)代碼復(fù)用,但每個(gè)實(shí)例都沒(méi)有自己的私有變量。

多查找作用域鏈中的一個(gè)層次,就會(huì)在一定程度上影響查找速度。而這正是使用閉包和私有變量的一個(gè)明顯的不足之處。

模塊模式

模塊模式通過(guò)為單例添加私有變量和特權(quán)方法使其得到增強(qiáng)

這種模式在需要對(duì)單例進(jìn)行某些初始化,同時(shí)又需要維護(hù)其私有變量時(shí)非常有用

var application = function() {

  // 私有變量和函數(shù)
  var components = new Array();

  // 初始化
  components.push(new BaseComponent());

  // 公共
  return {
    getComponentCuont: function() {
      return components.length;
    },

    registerComponent: function(component) {
      if (typeof component == "object") {
        components.push(component)
      }
    }
  };
}();

在WEB應(yīng)用程序中,經(jīng)常需要使用一個(gè)單例來(lái)管理應(yīng)用程序級(jí)的信息。這個(gè)簡(jiǎn)單的例子創(chuàng)建了一個(gè)用于管理組件的application對(duì)象。

簡(jiǎn)而言之,如果必須創(chuàng)建一個(gè)對(duì)象并以某些數(shù)據(jù)對(duì)其進(jìn)行初始化,同時(shí)還要公開(kāi)一些能夠訪問(wèn)這些私有數(shù)據(jù)的方法,那么就可以使用模塊模式。

以模塊模式創(chuàng)建的每個(gè)單例都是Object的實(shí)例,因?yàn)樽罱K要通過(guò)一個(gè)對(duì)象字面量來(lái)表示他。

增強(qiáng)的模塊模式

增強(qiáng)的模塊模式適合那些單例必須是某種類(lèi)型的實(shí)例,同時(shí)還必須添加某些屬性和(或)方法對(duì)其加以增強(qiáng)的情況。

如果前述例子中的application對(duì)象必須是BaseComponent的實(shí)例,可以如下代碼

var application = function() {

  // 私有變量和函數(shù)
  var components = new Array();

  // 初始化
  components.push(new BaseComponent());

  // 創(chuàng)建 application 的一個(gè)局部副本
  var app = new BaseComponent();

  // 公共接口
  app.getComponentCuont = function() {
    return components.length;
  };

  app.registerComponent = function(component) {
    if (typeof component == "object") {
      components.push(component);
    }
  };

  // 返回這個(gè)副本
  return app;
}();

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109501.html

相關(guān)文章

  • JavaScript高級(jí)程序設(shè)計(jì)》(3讀書(shū)筆記 4 變量、作用域和內(nèi)存問(wèn)題

    摘要:具體說(shuō)就是執(zhí)行流進(jìn)入下列任何一個(gè)語(yǔ)句時(shí),作用域鏈就會(huì)得到加長(zhǎng)語(yǔ)句的塊。如果局部環(huán)境中存在著同名的標(biāo)識(shí)符,就不會(huì)使用位于父環(huán)境中的標(biāo)識(shí)符訪問(wèn)局部變量要比訪問(wèn)全局變量更快,因?yàn)椴挥孟蛏纤阉髯饔糜蜴湣? 基本類(lèi)型和引用類(lèi)型的值 ECMAscript變量包含 基本類(lèi)型值和引用類(lèi)型值 基本類(lèi)型值值的是基本數(shù)據(jù)類(lèi)型:Undefined, Null, Boolean, Number, String ...

    lidashuang 評(píng)論0 收藏0
  • JavaScript高級(jí)程序設(shè)計(jì)》(3讀書(shū)筆記 1~2

    摘要:表示應(yīng)該立即下載腳本,但不應(yīng)妨礙頁(yè)面中的其他操作可選。表示通過(guò)屬性指定的代碼的字符集。表示腳本可以延遲到文檔完全被解析和顯示之后再執(zhí)行。實(shí)際上,服務(wù)器在傳送文件時(shí)使用的類(lèi)型通常是,但在中設(shè)置這個(gè)值卻可能導(dǎo)致腳本被忽略。 第1章 JavaScript 簡(jiǎn)介 雖然JavaScript和ECMAScript通常被人們用來(lái)表達(dá)相同的含義,但JavaScript的含義比ECMA-262要多得多...

    Corwien 評(píng)論0 收藏0
  • JavaScript高級(jí)程序設(shè)計(jì)》(3讀書(shū)筆記 9 客戶端檢測(cè)

    摘要:用戶代理檢測(cè)用戶代理檢測(cè)是爭(zhēng)議最大的客戶端檢測(cè)技術(shù)。第二個(gè)要檢測(cè)是。由于實(shí)際的版本號(hào)可能會(huì)包含數(shù)字小數(shù)點(diǎn)和字母,所以捕獲組中使用了表示非空格的特殊字符。版本號(hào)不在后面,而是在后面。除了知道設(shè)備,最好還能知道的版本號(hào)。 檢測(cè)Web客戶端的手段很多,各有利弊,但不到萬(wàn)不得已就不要使用客戶端檢測(cè)。只要能找到更通用的方法,就應(yīng)該優(yōu)先采用更通用的方法。一言蔽之,先設(shè)計(jì)最通用的方案,然后再使用特定...

    ispring 評(píng)論0 收藏0
  • JavaScript高級(jí)程序設(shè)計(jì)》(3讀書(shū)筆記 3

    摘要:本質(zhì)上是由一組無(wú)序名值對(duì)組成的。浮點(diǎn)數(shù)值的最高精度是位小數(shù),但在進(jìn)行計(jì)算時(shí)其精度遠(yuǎn)遠(yuǎn)不如證書(shū)。例如這是使用基于數(shù)值的浮點(diǎn)計(jì)算的通病,并非獨(dú)此一家數(shù)值范圍。 函數(shù)名不能使用關(guān)鍵字(typeof不行但typeOf可以,區(qū)分大小寫(xiě)) 標(biāo)識(shí)符就是指變量、函數(shù)、屬性的名字,或者函數(shù)的參數(shù)。 第一個(gè)字符必須是一個(gè)字母、下劃線(_)或者一個(gè)美元符號(hào)($) 其他字符可以是字母、下劃線、美元符號(hào)或...

    renweihub 評(píng)論0 收藏0
  • JavaScript高級(jí)程序設(shè)計(jì)》(3讀書(shū)筆記 5 引用類(lèi)型

    摘要:引用類(lèi)型的值對(duì)象是引用類(lèi)型的一個(gè)實(shí)例。引用類(lèi)型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組織在一起。對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)任一項(xiàng)返回,則返回。組零始終代表整個(gè)表達(dá)式。所以,使用非捕獲組較使用捕獲組更節(jié)省內(nèi)存。 引用類(lèi)型的值(對(duì)象)是引用類(lèi)型的一個(gè)實(shí)例。 引用類(lèi)型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組織在一起。它同行被稱為類(lèi),但這種稱呼并不妥當(dāng),盡管ECMAScript從技術(shù)上講...

    zero 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<