1. 程式人生 > >前端基礎知識2

前端基礎知識2

作用域

  1. 理解
  • 就是一塊"地盤", 一個程式碼段所在的區域
  • 它是靜態的(相對於上下文物件), 在編寫程式碼時就確定了
  1. 分類
  • 全域性作用域
  • 函式作用域
  • 沒有塊作用域(ES6有了)
  1. 作用
  • 隔離變數,不同作用域下同名變數不會有衝突
<script type="text/javascript">
/*  //沒塊作用域
  if(true) {
    var c = 3
  }
  console.log(c)*/

  var a = 10,
    b = 20
  function fn(x) {
    var a = 100,
      c = 300;
    console.log('fn()', a, b, c, x)
    function bar(x) {
      var a = 1000,
        d = 400
      console.log('bar()', a, b, c, d, x)
    }

    bar(100)
    bar(200)
  }
  fn(10)
</script>


作用域與執行上下文

  1. 區別1
  • 全域性作用域之外,每個函式都會建立自己的作用域,作用域在函式定義時就已經確定了。而不是在函式呼叫時
  • 全域性執行上下文環境是在全域性作用域確定之後, js程式碼馬上執行之前建立
  • 函式執行上下文是在呼叫函式時, 函式體程式碼執行之前建立
  1. 區別2
  • 作用域是靜態的, 只要函式定義好了就一直存在, 且不會再變化
  • 執行上下文是動態的, 呼叫函式時建立, 函式呼叫結束時就會自動釋放
  1. 聯絡
  • 執行上下文(物件)是從屬於所在的作用域
  • 全域性上下文環境==>全域性作用域
  • 函式上下文環境==>對應的函式使用域
<script type="text/javascript">
  var a = 10,
    b = 20
  function fn(x) {
    var a = 100,
      c = 300;
    console.log('fn()', a, b, c, x)
    function bar(x) {
      var a = 1000,
        d = 400
      console.log('bar()', a, b, c, d, x)
    }

    bar(100)
    bar(200)
  }
  fn(10)
</script>

作用域鏈

  1. 理解
  • 多個上下級關係的作用域形成的鏈, 它的方向是從下向上的(從內到外)
  • 查詢變數時就是沿著作用域鏈來查詢的
  1. 查詢一個變數的查詢規則
  • 在當前作用域下的執行上下文中查詢對應的屬性, 如果有直接返回, 否則進入2
  • 在上一級作用域的執行上下文中查詢對應的屬性, 如果有直接返回, 否則進入3
  • 再次執行2的相同操作, 直到全域性作用域, 如果還找不到就丟擲找不到的異常
<script type="text/javascript">
  var a = 1
  function fn1() {
    var b = 2
    function fn2() {
      var c = 3
      console.log(c)
      console.log(b)
      console.log(a)
      console.log(d)
    }
    fn2()
  }
  fn1()
</script>

閉包

  1. 如何產生閉包?
  • 當一個巢狀的內部(子)函式引用了巢狀的外部(父)函式的變數(函式)時, 就產生了閉包
  1. 閉包到底是什麼?
  • 使用chrome除錯檢視
  • 理解一: 閉包是巢狀的內部函式(絕大部分人)
  • 理解二: 包含被引用變數(函式)的物件(極少數人)
  • 注意: 閉包存在於巢狀的內部函式中
  1. 產生閉包的條件?
  • 函式巢狀
  • 內部函式引用了外部函式的資料(變數/函式)
<script type="text/javascript">
function fn1 () {
  var a = 2
  var b = 'abc'
  function fn2 () { //執行函式定義就會產生閉包(不用呼叫內部函式)
    console.log(a)
  }
  // fn2()
}
fn1()

function fun1() {
  var a = 3
  var fun2 = function () {
    console.log(a)
  }
}
fun1()
</script>

常見的閉包

  1. 將函式作為另一個函式的返回值
  2. 將函式作為實參傳遞給另一個函式呼叫
<script type="text/javascript">
  // 1. 將函式作為另一個函式的返回值
  function fn1() {
    var a = 2
    function fn2() {
      a++
      console.log(a)
    }
    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4

  // 2. 將函式作為實參傳遞給另一個函式呼叫
  function showDelay(msg, time) {
    setTimeout(function () {
      alert(msg)
    }, time)
  }
  showDelay('atguigu', 2000)

</script>

閉包的作用

  1. 使用函式內部的變數在函式執行完後, 仍然存活在記憶體中(延長了區域性變數的生命週期)
  2. 讓函式外部可以操作(讀寫)到函式內部的資料(變數/函式)

問題:

  1. 函式執行完後, 函式內部宣告的區域性變數是否還存在? 一般是不存在, 存在於閉中的變數才可能存在
  2. 在函式外部能直接訪問函式內部的區域性變數嗎? 不能, 但我們可以通過閉包讓外部操作它
<script type="text/javascript">
  function fn1() {
    var a = 2
    function fn2() {
      a++
      console.log(a)
      // return a
    }
    function fn3() {
      a--
      console.log(a)
    }
    return fn3
  }
  var f = fn1()
  f() // 1
  f() // 0
</script>

閉包的生命週期

  1. 產生: 在巢狀內部函式定義執行完時就產生了(不是在呼叫)
  2. 死亡: 在巢狀的內部函式成為垃圾物件時
<script type="text/javascript">
  function fn1() {
    //此時閉包就已經產生了(函式提升, 內部函式物件已經建立了)
    var a = 2
    function fn2 () {
      a++
      console.log(a)
    }
    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4
  f = null //閉包死亡(包含閉包的函式物件成為垃圾物件)
</script>

閉包的應用 (自定義JS模組)

閉包的應用2 : 定義JS模組

  • 具有特定功能的js檔案
  • 將所有的資料和功能都封裝在一個函式內部(私有的)
  • 只向外暴露一個包信n個方法的物件或函式
  • 模組的使用者, 只需要通過模組暴露的物件呼叫方法來實現對應的功能
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
  var module = myModule()
  module.doSomething()
  module.doOtherthing()
</script>

function myModule() {
  //私有資料
  var msg = 'My atguigu'
  //操作資料的函式
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露物件(給外部使用的方法)
  return {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
}

閉包的應用2 : 定義JS模組

  • 具有特定功能的js檔案
  • 將所有的資料和功能都封裝在一個函式內部(私有的)
  • 只向外暴露一個包信n個方法的物件或函式
  • 模組的使用者, 只需要通過模組暴露的物件呼叫方法來實現對應的功能
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
  myModule2.doSomething()
  myModule2.doOtherthing()
</script>

(function () {
  //私有資料
  var msg = 'My atguigu'
  //操作資料的函式
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露物件(給外部使用的方法)
  window.myModule2 = {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
})()


閉包的缺點及解決

  1. 缺點
  • 函式執行完後, 函式內的區域性變數沒有釋放, 佔用記憶體時間會變長
  • 容易造成記憶體洩露
  1. 解決
  • 能不用閉包就不用
  • 及時釋放
<script type="text/javascript">
  function fn1() {
    var arr = new Array[100000]
    function fn2() {
      console.log(arr.length)
    }
    return fn2
  }
  var f = fn1()
  f()

  f = null //讓內部函式成為垃圾物件-->回收閉包
</script>