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

資訊專欄INFORMATION COLUMN

【譯】理解JavaScript:閉包

岳光 / 2196人閱讀

摘要:當面試中讓我解釋一下閉包時我懵逼了。這個解釋開始可能有點晦澀,讓我們抽絲剝繭摘下閉包的真面目。此文不詳述作用域有專門的主題闡述,不過作用域是理解閉包原理的基礎(chǔ)。這才是閉包的真正便利之處。閉包使用不當就會很坑。

原文鏈接

為什么深度學(xué)習(xí)JavaScript?

JavaScript如今是最流行的編程語言之一。它運行在瀏覽器、服務(wù)器、移動設(shè)備、桌面應(yīng)用,也可能包括冰箱。無需我舉其他再多不相干的例子,只要你正從事web開發(fā),你就不可避免地要寫JavaScript。

很多web開發(fā)者僅僅因為能寫可以運行的代碼就聲稱了解JavaScript。對于JavaScript,你可以用一個月就能寫代碼,掌握它之后終生收益。(If there are no errors and nobody’s complaining why should you need to learn more?)(譯者注:不知所云)

好吧,我就是曾經(jīng)聲稱很了解此語言的一員。幾年前我用AngularJS和Node寫應(yīng)用,當時對自己的能力非常自信。拋開功能,我堅信我已經(jīng)征服了JavaScript。

當面試中讓我解釋一下閉包時我懵逼了。我感覺自己知道一點,和回調(diào)有關(guān),我當時一直用回調(diào)(當時還不知道Promise),但就是不知道怎么描述其原理。

在我的開發(fā)職業(yè)生涯中那次失敗的JavaScript面試是最恥辱和最具教育意義的經(jīng)歷。從那時起我歷時一年半致力于JavaScript的高價段位,并決定分享于世人。先從一個最常見的JavaScript面試題開始:

什么是閉包?

毫無疑問你已經(jīng)在各種應(yīng)用中使用過閉包。你每次為事件處理器添加回調(diào)時你都在用閉包的神奇屬性。

我遇到過很多關(guān)于此概念的解釋,但我最信服是Kyle Simpson下的定義:

當一個方法執(zhí)行完脫離了自己的詞法作用域,但仍然能夠記住并訪問其詞法作用域,這就是閉包。

這個解釋開始可能有點晦澀,讓我們抽絲剝繭摘下閉包的真面目。

此文不詳述作用域(有專門的主題闡述),不過作用域是理解閉包原理的基礎(chǔ)。作用域就是包含某些屬性和方法的區(qū)域。每個JavaScript方法都會創(chuàng)建一個新的作用域,它內(nèi)部的變量和入?yún)⒍贾荒茉谄鋬?nèi)部訪問。

如果你在函數(shù)內(nèi)聲明一個變量,函數(shù)外是訪問不到的。不過,我們可以在函數(shù)內(nèi)部定義擁有作用域的內(nèi)部函數(shù)。這些內(nèi)嵌函數(shù)的特別之處在于它們可以訪問父作用域的變量。

坦白說這也算不上什么特別之處,因為每一個在全局作用域中定義的函數(shù)都能訪問全局變量。雖然我們提到的這些內(nèi)嵌函數(shù)可以訪問父函數(shù)的作用域,但它們不能在父函數(shù)之外被調(diào)用。除非我們將其暴露出來。

我們將內(nèi)部函數(shù)暴露出來就可以在全局作用域中使用。牛逼!現(xiàn)在我們就可以隨心所欲了。不過,暴露出來的內(nèi)部函數(shù)實際上引用了它父作用域的變量,會不會有問題?不會!絕對不會,這就是閉包!

閉包是暴露出來的內(nèi)嵌方法

我不確定這是否是給閉包下的最好的定義,但這確實能夠很好地抓住此術(shù)語的本質(zhì)。閉包就是我們在函數(shù)外部就能訪問其父作用域的內(nèi)部函數(shù)。你能否通過我們之前提到的詞法作用域理解此解釋呢?

function person(name) {
  return {
    greet: function() {
      console.log("hello from " + name)
    }
  }
}

let alex = person("alex");
alex.greet(); // hello from alex
console.log(alex.name); // undefined
console.log(name); // will throw ReferenceError

我們在此定義了只有一個參數(shù)nameperson函數(shù)。它返回一個以greet為屬性的對象?,F(xiàn)在我們知道,暴露出的greet函數(shù)可以訪問父函數(shù)參數(shù)。盡管name變量并沒有定義在greet的作用域中,因為它是閉包,所以greet可以從其父作用域中獲取。

并不是特別難理解,你可能都用了很多次了。我學(xué)閉包前從沒把它想象的多難,理解了其背后的原理,我就明白了封裝并使用模塊。

哇唔,哇唔...模塊?封裝?出乎意料。

模塊和用閉包封裝

我深陷JavaScript漩渦之前首先了解到其中很多高深詞匯都有實踐解釋。模塊和封裝就是這類術(shù)語很完美的例子。我先從封裝開始,用相同的策略各個擊破去理解它們。

封裝是基本的編程原則之一。學(xué)過OOP(面向?qū)ο缶幊蹋┑娜藢Υ烁拍罘浅J煜ぃ珜τ跊]學(xué)過的人來說---封裝就是允許我們保持數(shù)據(jù)私有的基本隱藏機制。我們不想把方法的所有內(nèi)容暴露給全局作用域,我們想讓大多數(shù)內(nèi)容保持私有且不可訪問。

這才是閉包的真正便利之處。我們可以利用閉包訪問父作用域,甚至在外部訪問的時候獲得適當?shù)胤庋b。在父函數(shù)中可能有很多方法和變量,通過利用閉包我們可以將其暴露給我們需要的函數(shù)。

我們可以用閉包為我們的方法定義一個公共API,并保持方法中所有東西私有。

我們現(xiàn)在已經(jīng)掌握了封裝,只需實踐即可。在JavaScript中對此概念的實踐就是使用模塊。

模塊

在ES6中可以使用importexport關(guān)鍵字產(chǎn)生以文件為基礎(chǔ)的模塊,但要注意這些只是語法糖而已。

function Person(firstName, lastName, age) {
  var private = "this is a private member";

  return {
    getName: function() {
      console.log("My name is " + firstName + " " + lastName);
    },
    getAge: function() {
      console.log("I am " + age + " years old")
    }
  }
}

let person = new Person("Alex", "Kondov", 22);
person.getName();
person.getAge();
console.log(person.private); //undefined

這是一個我們可以保持一些數(shù)據(jù)私有的簡單例子。我們可以有其他內(nèi)嵌方法,盡管導(dǎo)出后可以使用,但并沒有都暴露出來。

function Order (items) {
  const total = items => {
    return items.reduce((acc, curr) => {
      return acc + curr.price
    }, 0)
  }
  
  const addTaxToPrice = price => price + (price * 0.2)
  
  return {
    calculateTotal: () => {
      return addTaxToPrice(total(items)).toFixed(2)
    }
  }
}

const items = [
  { name: "Toy", price: 14.99 },
  { name: "Candy", price: 7.99 }
]

const order = Order(items)
console.log(order.total) // undefined
console.log(order.addTaxToPrice) // undefined
console.log(order.calculateTotal()) // 27.58

在這個更接近真實的例子中方法返回了一個order對象,唯一暴露出來的方法是calculateTotal。Order函數(shù)有一個閉包,允許此閉包使用它的變量和入?yún)?。在你計算訂單總價時隱藏了內(nèi)部邏輯,也方便以后擴展。

怪異之處

JavaScript也有其怪異之處。實際上有些怪異之處讓人非常蛋疼。閉包使用不當就會很坑。

下面的代碼經(jīng)常出現(xiàn)在JavaScript面試中讓猜它的輸出。

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer () {
    console.log(i);
  }, i * 1000);
}

從1循環(huán)到5并在一段時間后打印出當前的數(shù)字。正常感覺會輸出1,2,3,4,5,對嗎?

讓我驚奇的是上面的代碼會在輸出臺上連續(xù)5次打印出6。如果循環(huán)之中沒有setTimeout不會有任何問題,因為日志輸出會被立即執(zhí)行。很明顯,排隊操作引發(fā)了這個問題。

我們期望每次調(diào)用setTimeout都會獲取i變量自身的拷貝,但實際情況卻是它訪問的是它的父作用域。又因為都在排隊,第一個日志會在它排隊1秒后發(fā)生。當1000毫秒過去的時候,循環(huán)早已結(jié)束,i變量也早已被賦值為6。

我明白了這個問題但如何修復(fù)呢?setTimeout會在全局作用域?qū)ふ?b>i變量,無法打印出我們想要的數(shù)字。我們可以把setTimeout包裹到一個方法中并將我們想要輸出的變量傳進去。這樣setTimeout會從它的父作用域而不是全局作用域進行訪問。

for (var i = 1; i <= 5; i++) {
  (function(index) {
    setTimeout(function timer () {
      console.log(index);
    }, index * 1000);
  })(i)
}

我們使用IIFE(立即執(zhí)行函數(shù),Immediately Invoked Function Expression)并把想輸出的數(shù)字傳進去。IIFE是一種定義后立即調(diào)用的函數(shù),它常用于這種情況---我們想要創(chuàng)建作用域。這種方式每次函數(shù)調(diào)用都用它們自己的變量拷貝,這也意味著setTimeout運行時會訪問對應(yīng)的數(shù)字。所以上面的例子我們會達到期待的結(jié)果:1,2,3,4,5

結(jié)束語

此文介紹了閉包的本質(zhì),但還有很多需要學(xué)習(xí)和更多的邊際情況需要考慮。如果你想更進一步了解閉包,我強烈推薦Kyle Simpson的書中Scope & Closures的部分。

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/95033.html

相關(guān)文章

  • [] 你想知道的關(guān)于 JavaScript 作用域的一切

    摘要:原文鏈接原文作者你想知道的關(guān)于作用域的一切譯中有許多章節(jié)是關(guān)于的但是對于初學(xué)者來說甚至是一些有經(jīng)驗的開發(fā)者這些有關(guān)作用域的章節(jié)既不直接也不容易理解這篇文章的目的就是為了幫助那些想更深一步學(xué)習(xí)了解作用域的開發(fā)者尤其是當他們聽到一些關(guān)于作用域的 原文鏈接: Everything you wanted to know about JavaScript scope原文作者: Todd Mott...

    Flands 評論0 收藏0
  • JavasScript重難點知識

    摘要:忍者級別的函數(shù)操作對于什么是匿名函數(shù),這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數(shù)是一個很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機制,如果...

    forsigner 評論0 收藏0
  • [] addEventListener 與 onclick,孰優(yōu)孰劣?

    摘要:上面的例子應(yīng)用了匿名函數(shù)這個特性,還可以使用構(gòu)造函數(shù)或者閉包來添加事件監(jiān)聽器另一個重要特性,則是上面這段代碼中最后一行的最后一個參數(shù),用來控制監(jiān)聽器對于冒泡事件的響應(yīng)。在這里你不能使用閉包或者匿名函數(shù),并且控制域也是有限的。 原文出處:addEventListener vs onclick 之所以會想到這個話題,是因為在回顧自己之前寫的為 button 動態(tài)綁定事件的函數(shù)時,腦海里忽...

    wums 評論0 收藏0
  • 2017-08-23 前端日報

    摘要:前端日報精選免費的計算機編程類中文書籍英文技術(shù)文檔看不懂看印記中文就夠了的內(nèi)部工作原理美團點評點餐前后端分離實踐讓你的動畫坐上時光機中文譯有多棒簡書譯別再使用圖片輪播了掘金譯如何在中使用掘金個讓增長成億美元公司的獨特方法眾成翻 2017-08-23 前端日報 精選 FPB 2.0:免費的計算機編程類中文書籍 2.0英文技術(shù)文檔看不懂?看印記中文就夠了!Virtual DOM 的內(nèi)部工作...

    lordharrd 評論0 收藏0
  • 】stackoverflow 關(guān)于JavsScript的熱門問答

    摘要:例如,考慮比較由字符串構(gòu)造函數(shù)創(chuàng)建的字符串對象和字符串字面量這里的操作符正在檢查這兩個對象的值并返回,但是鑒于它們不是相同類型并且返回。我的建議是完全繞過這個問題,只是不使用字符串構(gòu)造函數(shù)創(chuàng)建字符串對象。 Q1:javascript的閉包是如何工作的? 正如愛因斯坦所說的: 如果你不能把它解釋給一個六歲的小孩,說明你對它還不夠了解。 我曾嘗試向一個27歲的朋友解釋js閉包并且完全失敗了...

    趙春朋 評論0 收藏0

發(fā)表評論

0條評論

岳光

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<