好程式設計師web前端培訓分享JavaScript學習筆記閉包與繼承

好程式設計師發表於2020-07-02

  好程式設計師web 前端培訓分享 JavaScript 學習筆記閉包與繼承,閉包:閉包是我們函式的一種高階使用方式, 在聊閉包之前我們要先回顧一下  函式

函式的兩個階段

  • 我們一直說函式有兩個階段
  1. 定義階段
  2. 呼叫階段
  3. 開闢一個  儲存空間
  4. 把函式體內的程式碼一模一樣的放在這個空間內(不解析變數)
  5.   儲存空間 的地址給函式名
  6. 按照函式名的地址找到函式的  儲存空間
  7. 形參賦值
  8. 預解析
  9. 將函式  儲存空間 中的程式碼拿出來執行(才解析變數)
  10. 按照函式名的地址找到函式的  儲存空間
  11. 形參賦值
  12. 預解析
  13. 在記憶體中開闢一個  執行空間
  14. 將函式  儲存空間 中的程式碼拿出來在剛剛開闢的  執行空間 中執行
  15. 執行完畢後,記憶體中開闢的  執行空間 銷燬

函式定義階段

函式呼叫階段

重新定義函式呼叫階段

function fn() {

    console.log('我是 fn 函式')}fn() 

  • 函式執行的時候會開闢一個  執行空間 (我們暫且叫他 xxff00
  • console.log('我是 fn 函式') 這個程式碼就是在 xxff00 這個空間中執行
  • 程式碼執行完畢以後,這個 xxff00 空間就銷燬了
  • 每一個函式會有一個  儲存空間
  • 但是每一次呼叫都會生成一個完全不一樣的  執行空間
  • 並且  執行空間 會在函式執行完畢後就銷燬了,但是  儲存空間 不會
  • 那麼這個函式空間執行完畢就銷燬了,還有什麼意義呢?
  • 我們可以有一些辦法讓這個空間  不銷燬
  • 閉包 ,就是要利用這個  不銷燬的執行空間
  • 函式的  執行空間 會在函式執行完畢之後銷燬
  • 但是,一旦函式內部返回了一個  引用資料型別 ,並且  在函式外部有變數接受 的情況下
  • 那麼這個函式  執行空間 就不會銷燬了

函式執行空間

函式執行空間不銷燬

function fn() {

    const obj  = {

       name : 'Jack',

       age : 18,

       gender : '男'

   }

    return obj} const o  = fn()

  • 函式執行的時候,會生成一個函式  執行空間 (我們暫且叫他 xxff00
  • 程式碼在 xxff00 空間中執行
  •  xxff00 這個空間中聲名了一個 物件空間(xxff11
  •  xxff00 這個執行空間把 xxff11 這個物件地址返回了
  • 函式外部 0 接受的是一個物件的地址沒錯
  • 但是是一個在 xxff00 函式執行空間中的 xxff11 物件地址
  • 因為 o 變數一直在和這個物件地址關聯著,所以 xxff00 這個空間一直不會銷燬
  • 等到什麼時候,執行一句程式碼 o = null
  • 此時, o 變數比在關聯在 xxff00 函式執行空間中的 xxff11 物件地址
  • 那麼,這個時候函式執行空間 xxff00 就銷燬了
  • 閉包就是利用了這個函式執行空間不銷燬的邏輯
  • 有幾個條件組成閉包
  • 閉包的第一個條件就是利用了不銷燬空間的邏輯
  • 只不過不是返回一個  物件資料型別
  • 而是返回一個  函式資料型別

閉包

不銷燬的空間

function fn() {

     return  function () {}} const f  = fn()

  • f 變數接受的就是一個  fn的執行空間 中的 函式
  • 涉及到兩個函式
  • 內部函式要檢視或者使用著外部函式的變數

內部函式引用外部函式中的變數

function fn() {

    const num  = 100

    // 這個函式給一個名字,方便寫筆記    return  function a() {

       console.log(num)

   }} const f  = fn()

  • fn() 的時候會生成一個 xxff00 的執行空間
  •  xxff00 這個執行空間內部,定義了一個 a 函式的  儲存空間xxff11
  • 全域性 f 變數接受的就是 xxff00 裡面的 xxff11
  • 所以 xxff00 就是不會銷燬的空間
  • 因為 xxff00 不會銷燬,所以,定義再裡面的變數 num 也不會銷燬
  • 將來 f() 的時候,就能訪問到 num 變數
  • 為什麼要叫做特點,就是因為他的每一個點都是優點同時也是缺點

閉包的特點

  1. 作用域空間不銷燬
  • 優點:  因為不銷燬,變數頁不會銷燬,增加了變數的生命週期
  • 缺點:  因為不銷燬,會一直佔用記憶體,多了以後就會導致記憶體溢位

 

  1. 可以利用閉包訪問再一個函式外部訪問函式內部的變數
  • 優點:  可以再函式外部訪問內部資料
  • 缺點:  必須要時刻保持引用,導致函式執行棧不被銷燬

 

  1. 保護私有變數
  • 優點:  可以把一些變數放在函式里面,不會汙染全域性
  • 缺點:  要利用閉包函式才能訪問,不是很方便
  • 有一個 A 函式,再 A 函式內部返回一個 B 函式
  •  A 函式外部有變數引用這個 B 函式
  • B 函式內部訪問著 A 函式內部的私有變數
  • 以上三個條件缺一不可
  • 繼承是和建構函式相關的一個應用
  • 是指, 讓一個建構函式去繼承另一個建構函式的屬性和方法
  • 所以繼承一定出現在  兩個建構函式之間
  • 我們之前說,建構函式(類)是對一類行為的描述
  • 那麼我們類這個概念其實也很抽象
  • 比如:
  • 我們說  國光 /  富士 都是 蘋果的品種,那麼我們就可以寫一個  蘋果類 來例項化很多品種出來
  •   蘋果 /   這些東西都是水果的一種,那麼我們就可以寫一個  水果類
  • 說過的統一特點就是   /  水分大 ,而不同的水果有不同的特徵
  • 那麼我們就可以讓  蘋果類 來繼承  水果類 的內容,然後再用  水果類 去例項化物件
  • 那麼例項化出來的就不光有  蘋果類 的屬性和方法,還有  水果類 的屬性和方法
  • 其實說到底,到底什麼是繼承
  • 我們之前說,在我們書寫建構函式的時候,為了解決一個函式重複出現的問題
  • 我們把建構函式的  方法 寫在了 prototype 上

閉包概念(熟讀並背誦全文)

繼承

一個小例子

繼承的作用

圖片1

  • 這樣,每一個例項使用的方法就都是來自建構函式的 prototype 上
  • 就避免了函式重複出現佔用記憶體得到情況
  • 那麼,如果兩個建構函式的 prototype 中有一樣的方法呢,是不是也是一種浪費
  • 所以我們把建構函式䣌 prototype 中的公共的方法再次盡心提取

    圖片2

  • 我們準備一個更公共的建構函式,讓建構函式的 __proto__ 指向這個公共的建構函式的 prototype
  • 我們有一些常見的繼承方式來實現和達到繼承的效果
  • 我們先準備一個父類(也就是要讓別的建構函式使用我這個建構函式的屬性和方法)

常見的繼承方式

function Person() {

     this.name  = 'Jack'}Person.prototype.sayHi  =  function () {

    cosnole.log('hello')}

  • 這個 Person 建構函式為父類
  • 讓其他的建構函式來繼承他
  • 當別的建構函式能夠使用他的屬性和方法的時候,就達到了繼承的效果
  • 原型繼承,就是在本身的原型鏈上加一層結構

原型繼承

function Student() {}Student.prototype  =  new Person()

借用建構函式繼承

  • 把父類建構函式體借用過來使用一下而已

function Student() {

    Person.call( this)}

組合繼承

  • 就是把  原型繼承 和  借用建構函式繼承 兩個方式組合在一起

function Student() {

    Person.call( this)}Student.prototype  =  new Person

ES6 的繼承

  • es6 的繼承很容易,而且是固定語法
    // 下面表示創造一個 Student 類,繼承自 Person 類

class Student  extends Person {

    constructor () {

         // 必須在 constructor 裡面執行一下 super() 完成繼承          super()

    }}

  • 這樣就繼承成功了


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913864/viewspace-2701900/,如需轉載,請註明出處,否則將追究法律責任。

相關文章