国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

js閉包探秘

iamyoung001 / 2332人閱讀

摘要:閉包是這門語(yǔ)言中有些復(fù)雜并且充滿誤解的特性。說(shuō)明返回了內(nèi)部方法,調(diào)用了的參數(shù),閉包創(chuàng)建。過(guò)度使用閉包會(huì)導(dǎo)致腳本執(zhí)行變慢并消耗額外內(nèi)存。本節(jié)我們說(shuō)幾種場(chǎng)景要注意避免閉包的產(chǎn)生。循環(huán)中循環(huán)中創(chuàng)建出閉包會(huì)導(dǎo)致結(jié)果異常。

譯者:閉包都被討論爛了,不理解閉包都不好意思說(shuō)自己會(huì)js,但我看到這篇文章還是感覺(jué)眼前一亮,也讓我對(duì)閉包有了一些新的理解,并且涉及了一些類和原型鏈的知識(shí),這是一篇2012年的文章,稍微有點(diǎn)早,內(nèi)容也略微基礎(chǔ),但是很明晰,希望能給讀者帶來(lái)新的理解。

閉包(Closure) 是javascript這門語(yǔ)言中有些復(fù)雜并且充滿誤解的特性。簡(jiǎn)言之,閉包是一個(gè)對(duì)象,這個(gè)對(duì)象包含一個(gè)方法(function)和該方法創(chuàng)建時(shí)環(huán)境的引用(reference to the enviroment)。為了完全理解閉包,我們還需要理解兩個(gè)js中的特性,一個(gè)是一級(jí)方法(first-class function),另一個(gè)是內(nèi)部方法(inner function)。

一級(jí)方法/First-Class Functions

在js中,方法是頭等公民,因?yàn)樗梢员惠p易轉(zhuǎn)換成其他數(shù)據(jù)類型。比如,一級(jí)方法可以實(shí)時(shí)構(gòu)建并且賦值給一個(gè)變量。也可以傳遞給其他方法,或者通過(guò)其他方法返回。除了滿足這些標(biāo)準(zhǔn)以外,方法也擁有自己的屬性和方法。
通過(guò)下述例子,我們來(lái)看一下一級(jí)方法的能力。

var foo = function() {
  alert("Hello World!");
};

var bar = function(arg) {
  return arg;
};

bar(foo)();
譯者注:省略原文對(duì)代碼的文字解釋,這里體現(xiàn)的是一級(jí)方法可以返回參數(shù),參數(shù)可以是另外一個(gè)一級(jí)函數(shù),返回的結(jié)果還可以調(diào)用。
內(nèi)部方法/Inner Functions

內(nèi)部方法或者說(shuō)嵌套方法,是指定義在其他方法內(nèi)部的方法,每當(dāng)外部方法被喚起,內(nèi)部方法的實(shí)例就被創(chuàng)建。下面的例子反應(yīng)內(nèi)部方法的使用,add方法是外部方法,doAdd是內(nèi)部方法。

function add(value1, value2) {
  function doAdd(operand1, operand2) {
    return operand1 + operand2;
  }

  return doAdd(value1, value2);
}

var foo = add(1, 2);
// foo equals 3

這個(gè)例子中,一個(gè)重要的特性是,內(nèi)部方法獲取到了外部方法的作用域,這意味著內(nèi)部方法能夠使用外部方法的變量,參數(shù)等。例子中add()的參數(shù)value1,value2傳遞給doAdd()的operand1,operand2參數(shù)。然而這并沒(méi)有必要,因?yàn)閐oAdd可以直接獲取value1,value2。所以上面的例子我們還可以這么寫:

function add(value1, value2) {
  function doAdd() {
    return value1 + value2;
  }

  return doAdd();
}

var foo = add(1, 2);
// foo equals 3
創(chuàng)建閉包/Creating Closures

內(nèi)部方法獲取外部方法的作用域,便形成了一個(gè)閉包。典型的場(chǎng)景是外部函數(shù)將其內(nèi)部方法返回,內(nèi)部方法保持了外部環(huán)境的引用,并保存了作用域下的所有變量。
一下例子展示閉包如何創(chuàng)建并使用。

function add(value1) {
  return function doAdd(value2) {
    return value1 + value2;
  };
}

var increment = add(1);
var foo = increment(2);
// foo equals 3

說(shuō)明:

add返回了內(nèi)部方法doAdd,doAdd調(diào)用了add的參數(shù),閉包創(chuàng)建。

value1是add方法的本地變量,對(duì)doAdd來(lái)說(shuō)是非本地變量(非本地變量指變量既不在函數(shù)體本身,也不在全局),value2是doAdd的本地變量。

當(dāng)add(1)被調(diào)用,一個(gè)閉包被創(chuàng)建并儲(chǔ)存在increment中,在該閉包的引用環(huán)境中,value1綁定了1,被綁定的1相當(dāng)于“封鎖”在這個(gè)函數(shù)中,這也是“閉包”這個(gè)名字的由來(lái)。

當(dāng)increment(2)被調(diào)用,進(jìn)入閉包函數(shù),這意味著攜帶著value1為1的doAdd被調(diào)用,因此該閉包本質(zhì)上可以當(dāng)做如下函數(shù):

function increment(value2) {
  return 1 + value2;
}
何時(shí)使用閉包?

閉包可以實(shí)現(xiàn)很多功能。比如將回調(diào)函數(shù)綁定指定參數(shù)。我們說(shuō)兩個(gè)讓你的生活和開發(fā)變得更簡(jiǎn)單的場(chǎng)景。

配合定時(shí)器

閉包結(jié)合setTimeout和setInterval非常有用,閉包允許你向回調(diào)函數(shù)傳入指定參數(shù),比如下面的例子,每秒鐘在給指定dom插入字符串。




  Closures
  
  


  

遺憾的是,IE不支持向setInterval的回調(diào)傳參,IE中頁(yè)面不會(huì)展現(xiàn)“some message”而是“undefined”(無(wú)值傳入showMessage()),解決這個(gè)問(wèn)題,可以通過(guò)閉包將期望值綁定于回調(diào)函數(shù)里,我們可以改寫如上代碼:

window.addEventListener("load", function() {
  var showMessage = getClosure("some message
"); window.setInterval(showMessage, 1000); }); function getClosure(message) { function showMessage() { document.getElementById("message").innerHTML += message; } return showMessage; }

2.模擬私有屬性
絕大多數(shù)面向?qū)ο蟮某绦蛘Z(yǔ)言支持對(duì)象的私有屬性,然而js不是純正的面向?qū)ο蟮恼Z(yǔ)言,因此也沒(méi)有私有屬性的概念。不過(guò),我們可以通過(guò)閉包來(lái)模擬私有屬性。回想一下,閉包包含了一份其創(chuàng)建環(huán)境的引用,這份引用已經(jīng)不在當(dāng)前作用域中了,因此這份引用只能在閉包中訪問(wèn),這本質(zhì)上就是私有屬性。
看如下例子(譯者:省略對(duì)代碼的文字描述):

function Person(name) {
  this._name = name;

  this.getName = function() {
    return this._name;
  };
}

這里有一個(gè)嚴(yán)重的問(wèn)題,因?yàn)閖s不支持私有屬性,所以我們沒(méi)法阻止別人修改實(shí)例的name字段,比如我們創(chuàng)建一個(gè)Person實(shí)例叫Colin,然后可以將他的名字改成Tom。

var person = new Person("Colin");

person._name = "Tom";
// person.getName() now returns "Tom"

沒(méi)有人愿意不經(jīng)同意就被別人改名字,為了阻止這種情況的發(fā)生,通過(guò)閉包讓_name字段變成私有。看如下代碼,注意這里的_name是Person構(gòu)造器的本地變量,而不是對(duì)象的屬性,閉包形成了,因?yàn)橥鈱臃椒≒erson對(duì)外暴露了一個(gè)內(nèi)部方法getName。

function Person(name) {
  var _name = name;// 注:區(qū)別在這里

  this.getName = function() {
    return _name;
  };
}

現(xiàn)在,當(dāng)getName被調(diào)用,能夠保證返回的是最初傳入類構(gòu)造器的值。我們依然可以為對(duì)象添加新的_name屬性,但這并不影響閉包getName最初綁定的值,下面的代碼證明,_name字段,事實(shí)私有。

var person = new Person("Colin");

person._name = "Tom";
// person._name is "Tom" but person.getName() returns "Colin"
什么時(shí)候不要用閉包?

正確理解閉包如何工作何時(shí)使用非常重要,而理解什么時(shí)候不應(yīng)該用它也同樣重要。過(guò)度使用閉包會(huì)導(dǎo)致腳本執(zhí)行變慢并消耗額外內(nèi)存。由于閉包太容易創(chuàng)建了,所以很容易發(fā)生你都不知道怎么回事,就已經(jīng)創(chuàng)建了閉包的情況。本節(jié)我們說(shuō)幾種場(chǎng)景要注意避免閉包的產(chǎn)生。
1.循環(huán)中
循環(huán)中創(chuàng)建出閉包會(huì)導(dǎo)致結(jié)果異常。下例中,頁(yè)面上有三個(gè)按鈕,分別點(diǎn)擊彈出不同的話術(shù)。然而實(shí)際運(yùn)行,所有的按鈕都彈出button4的話術(shù),這是因?yàn)椋?dāng)按鈕被點(diǎn)擊時(shí),循環(huán)已經(jīng)執(zhí)行完畢,而循環(huán)中的變量i也已經(jīng)變成了最終值4.




  Closures
  
  


  
  
  

去解決這個(gè)問(wèn)題,必須在循環(huán)中去掉閉包(譯者:這里的閉包指的是click事件回調(diào)函數(shù)綁定了外層引用i),我們可以通過(guò)調(diào)用一個(gè)引用新環(huán)境的函數(shù)來(lái)解決。下面的代碼中,循環(huán)中的變量傳遞給getHandler函數(shù),getHandler返回一個(gè)閉包(譯者:這個(gè)閉包指的是getHandler返回的內(nèi)部方法綁定傳入的i參數(shù)),獨(dú)立于原來(lái)的for循環(huán)。

function getHandler(i) {
  return function handler() {
    alert("Clicked button " + i);
  };
}

window.addEventListener("load", function() {
  for (var i = 1; i < 4; i++) {
    var button = document.getElementById("button" + i);

    button.addEventListener("click", getHandler(i));
  }
});

2.構(gòu)造函數(shù)里的非必要使用
類的構(gòu)造函數(shù)里,也是經(jīng)常會(huì)產(chǎn)生閉包的錯(cuò)誤使用。我們已經(jīng)知道如何通過(guò)閉包設(shè)置類的私有屬性,而如果當(dāng)一個(gè)方法不需要調(diào)用私有屬性,則造成的閉包是浪費(fèi)的。下面的例子中,Person類增加了sayHello方法,但是它沒(méi)有使用私有屬性。

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };

  this.sayHello = function() {
    alert("Hello!");
  };
}

每當(dāng)Person被實(shí)例化,創(chuàng)建sayHello都要消耗時(shí)間,想象一下有大量的Person被實(shí)例化。更好的實(shí)踐是將sayHello放入Person的原型鏈里(prototype),原型鏈里的方法,會(huì)被所有的實(shí)例化對(duì)象共享,因此節(jié)省了為每個(gè)實(shí)例化對(duì)象去創(chuàng)建一個(gè)閉包(譯者:指sayHello),所以我們有必要做如下修改:

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Person.prototype.sayHello = function() {
  alert("Hello!");
};
需要記得一些事情

閉包包含了一個(gè)方法,以及創(chuàng)建它的代碼環(huán)境引用

閉包會(huì)在外部函數(shù)包含內(nèi)部函數(shù)的情況下形成

閉包可以輕松的幫助回調(diào)函數(shù)傳入?yún)?shù)

類的私有屬性可以通過(guò)閉包模擬

類的構(gòu)造器中使用閉包不是一個(gè)好主意,將它們放到原型鏈中

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/98458.html

相關(guān)文章

  • 2017-09-30 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選劉海打理指北中的錯(cuò)誤處理模式與反模式譯圖解和譯你并不知道中文裝飾器讓你的代碼更簡(jiǎn)潔眾成翻譯第期每個(gè)程序員第一份工作前應(yīng)該知道的件事中的不變性眾成翻譯寫的一次小結(jié)掘金內(nèi)部機(jī)制探秘和文末附彩蛋和源碼前端雜談開發(fā)實(shí)戰(zhàn) 2017-09-30 前端日?qǐng)?bào) 精選 iPhone X 劉海打理指北React16中的錯(cuò)誤處理ES6 Promise:模式與反模式「譯」圖解 ArrayBuffer...

    darryrzhong 評(píng)論0 收藏0
  • 探秘 Node.js 10 新功能的背后:V8 release 6.6

    摘要:語(yǔ)言新特性現(xiàn)在返回源代碼的所有內(nèi)容,包括空格和注釋。隨著去年發(fā)布的新的字節(jié)碼解釋器,我們擴(kuò)展了這個(gè)功能,以便在后臺(tái)線程上將源代碼編譯為字節(jié)碼。 每六周,我們都會(huì)創(chuàng)建一個(gè) V8 的新分支,作為我們發(fā)布流程的一部分。每個(gè)版本都是在 Chrome Beta 里程碑之前從 V8 的 Git master 分支出來(lái)的。今天(2018-03-27),我們很高興地宣布,我們發(fā)布了一個(gè)新的分支:V8 ...

    springDevBird 評(píng)論0 收藏0
  • 探秘 Node.js 10 新功能的背后:V8 release 6.6

    摘要:語(yǔ)言新特性現(xiàn)在返回源代碼的所有內(nèi)容,包括空格和注釋。隨著去年發(fā)布的新的字節(jié)碼解釋器,我們擴(kuò)展了這個(gè)功能,以便在后臺(tái)線程上將源代碼編譯為字節(jié)碼。 每六周,我們都會(huì)創(chuàng)建一個(gè) V8 的新分支,作為我們發(fā)布流程的一部分。每個(gè)版本都是在 Chrome Beta 里程碑之前從 V8 的 Git master 分支出來(lái)的。今天(2018-03-27),我們很高興地宣布,我們發(fā)布了一個(gè)新的分支:V8 ...

    Labradors 評(píng)論0 收藏0
  • JavaScript事件探秘

    摘要:一事件流事件流描述的是從頁(yè)面中接受事件的順序。級(jí)事件處理程序級(jí)事件定義了兩個(gè)方法用于處理指定和刪除事件處理程序的操作和。第二個(gè)方法是,它返回事件的目標(biāo)。第三個(gè)方法是,用于取消事件的默認(rèn)行為。首先嘗試使用方法阻止事件流,否則就使用屬性。 一、事件流 事件流描述的是從頁(yè)面中接受事件的順序。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕獲流 1、事件冒泡 事件冒泡,即事件最開始...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<