JS難點同步異步和作用域與閉包及原型和原型鏈詳解

JS三座大山

同步異步

前端中隻有兩個操作是異步的:

  • 定時器異步執行;
  • ajax異步請求

編譯器解析+執行代碼原理:

1.編譯器從上往下逐一解析代碼

2.判斷代碼是同步還是異步

  • 同步:立即執行
  • 異步:不執行。放入事件隊列池

3.等所有同步執行完畢開始執行異步

同步異步區別

api : 異步有回調,同步沒有回調

性能 : 異步性能好(不阻塞線程) 同步會阻塞線程

順序 : 同步有序執行,異步無序執行

另:回調函數:如果一個函數的參數是一個函數,那麼這個參數函數叫做回調函數

作用域、閉包

函數作用域鏈

  1. 隻有 var 聲明的變量認的是函數作用域
  2. 隻有函數才能開辟新的函數作用域
  3. 默認全局區域,我們稱之為0級作用域,在0級上聲明的函數,會開一個新的作用域,我們稱之為1級作用域
  4. 在1級裡聲明的函數,開辟的函數開辟作用域,我們就2級作用域,以此類推像這樣的作用域
  5. 像一個鏈條一樣稱之為作用域鏈

在這裡插入圖片描述

塊作用域

1.一個 {} 就是一個塊級作用域,let 認塊級作用域

2.對於let而言,默認的全局也稱之為0級塊作用域,然後除瞭對象以外,大括號開辟新的塊作用域,0級上開辟的就是1級,1級裡開辟的就是2級以此類推

3.跟我們之前學的函數作用域鏈是一樣的,隻不過函數作用域隻有函數開辟函數作用域,塊作用是大括號開辟塊作用域,var認函數作用域,let認塊級作用域

閉包

閉包就是一個函數, 能夠讀取其他函數內部變量的函數

代碼示例:

    function foo () {
      // foo的內部變量
      let num = 10
      // 這個inner就叫閉包
      function inner() {
        console.log(num)
      }
    }

閉包有什麼用呢?

  • 閉包相當於是溝通外界和函數內部的橋梁
  • 也就是使用閉包就可以讓外界訪問到函數內部的變量瞭
閉包和直接返回值有差別

直接返回值,其實本質上隻是返回這個值的數據,後面的更改跟函數內部的變量沒有任何關系

所以用閉包,就能讓外界間接的訪問到函數內部的變量

閉包的幾種寫法

簡單來說:隻要符合閉包特征的都算閉包

閉包:是一個函數,一個能讓外界訪問函數內部變量的函數

閉包作用1:延長變量生命周期

生命周期: 指從聲明到銷毀的周期

全局變量生命周期 從網頁打開到網頁結束

局部變量生命周期 從它所在的作用域開始到結束

正是因為閉包能延長變量的生命周期,所以外界依然能一直訪問局部變量

所以大量使用閉包,可能會導致內存泄漏

內存泄漏:一個數據後面已經不需要用瞭,但是它還占著內存空間,沒有被回收

解決辦法:

  • 把閉包賦值為null即可
  • 賦值為null,就代表這個數據沒有“人”引用瞭,沒人引用瞭那麼就會被標記為“垃圾”,然後會被GC在合適的時候回收
  • GC:垃圾回收機制
  • chrome瀏覽器用瞭這個機制
閉包作用2: 限制訪問

把變量變為局部變量外界就無法直接訪問

它隻能通過我們寫的閉包(橋梁)來間接訪問,這樣做我們就可以對閉包裡面做一些限制,這樣就起到瞭限制訪問的目的

閉包調用註意事項

註意:

產生閉包的外層函數調用多少次,就會產生多少個閉包,那麼到時候操作的是不同的數據,

如果產生閉包的外層函數調用1次,那麼就隻產生1個閉包,到時候操作的數據還是同一個

閉包解決用var導致下標錯誤的問題

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    ul {
      list-style: none;
      padding: 0;
      margin: 0;
    }
    li {
      width: 30px;
      height: 30px;
      background-color: yellow;
      line-height: 30px;
      text-align: center;
      float: left;
      margin-right: 10px;
    }
  </style>
</head>
<body>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>
  <script>
    // 以前有let嗎?沒有,所以我們看用var如何解決下標問題
    // 找到所有的li
    var lis = document.getElementsByTagName('li')
    for (var i = 0; i < lis.length; i++) {
      // 現在這裡的i,隻有1個變量,所以最終點擊的時候,大傢訪問的都是一個i
      // 而i最終值是5,所以無論點誰都是5
      // 解決辦法:我有5個li就要有5個變量,變量的值分別是0,1,2,3,4
      // 如何才能產生5個不一樣的變量?就要開辟函數作用域
      (function () {
        // 因為這個循環會產生5個自執行函數
        // 就意味著有5個不同的index變量
        // 然後它的值需要分別是0,1,2,3,4,而i的值剛好是0,1,2,3,4
        // 所以把i賦值給index即可
        var index = i
        // 給每個li加點擊事件
        lis[i].onclick = function () {
          alert(index)
        }
      })()
    }
  </script>
</body>

</html>

投票機

    // 要做一個能投票,能得到當前票數的功能
    function votor() {
      // 有一個變量,用來存票數
      let ticket = 0
      return {
        getTicket: function () {
          console.log('當前票數為:' + ticket)
        },
        // 投票
        add: function () {
          ticket++
        }
      }
    }
    let tp = votor()
    tp.add()
    tp.add()
    tp.add()
    tp.add()
    tp.getTicket()

閉包兩個面試題

		// 思考題 1:
        window.name = "The Window"; 
        let object = {
            name: "My Object",  
            getNameFunc: function () { 
                return function () {
                    return this.name;
                };
            }
        };
        console.log(object.getNameFunc()());  
                 // 思考題 2:
        window.name = "The Window";
        let object = {
            name: "My Object",  
            getNameFunc: function () { 
                let that = this; 
                return function () {
                    return that.name;
                };
            }
        };
        console.log(object.getNameFunc() ());

原型、原型鏈

原型對象

作用:

​ 構造函數中共有的屬性和方法添加到原型對象中,可以節省內存

原型鏈

無論任何一個對象開始出發, 按照 proto 開始向上查找, 最終都能找到 Object.prototype, 這個使用 __proto__串聯起來的對象鏈狀結構, 叫做原型鏈

完整原型鏈圖

示意圖前置知識:

每一個對象都有__proto__屬性, 指向所屬構造函數的原型對象;

每一個對象都有prototype屬性, 是一個對象;

函數也是對象;

當一個對象, 沒有準確的構造函數來實例化的時候, 我們都看作是內置構造函數

Object 的實例;Object的protype是頂級原型對象, 他的__protype__指向是null;

Function是頂級構造函數, 它自己是自己的構造函數, 同時自己是自己的實例對象(通俗來說, 就是它自己創建瞭自己)

完整原型示意圖:

在這裡插入圖片描述

以上就是JS難點同步異步的作用域與閉包原型及原型鏈詳解的詳細內容,更多關於JS同步異步作用域與閉包原型及原型鏈的資料請關註WalkonNet其它相關文章!

推薦閱讀: