1. 程式人生 > 其它 >尚矽谷JavaScript高階學習筆記

尚矽谷JavaScript高階學習筆記

資料型別

  • 分類

    • 基本(值)型別
      • String: 任意字串
      • Number: 任意數字
      • boolean: true / false
      • null: null
      • undefined: undefined
    • 物件型別(引用型別)
      • Object: 任意物件
      • Function: 一種特別的物件(可以執行)
      • Array: 一種特別的物件(數值下標, 內部資料是有序的)
  • 判斷

    • typeof: 可以判斷 undefined, 數值, 字串, 布林值, function
      • 不能判斷 null與object, object與array
      typeof null 為 'object'
      typeof array 為 'object'
      
    • instanceof: 判斷物件的型別
    • ===: 可以判斷undefined和null (因為他們只有一個值)
    判斷是否為字元: typeof xxx === 'string'
    判斷是否為數值: typeof xxx === 'number'
    判斷是否為函式: typeof xxx === 'function'
    判斷是否為陣列: Array.isArray(xxx)
        Object.prototype.toString.call(arg) === '[object Array]'
    
  • 相關問題

    • undefined 與 null的區別
      • undefined代表沒有賦值
      • null代表賦值了, 值為null
    • 什麼時候給變數賦值為null呢
      • 初始化: var a = null, 表示a指向一個物件, 但物件此時還沒有確定
      • 生命週期結束: a = null, 讓a指向的物件成為垃圾物件
    • 變數型別與數值型別
      • js的變數是沒有型別的, 變數的型別實際上是變數記憶體中資料的型別
      • 變數型別: 基本型別(儲存基本資料型別的變數)和引用型別(儲存物件地址值的變數)
      • 資料型別: 基本型別和物件型別
    • js呼叫函式傳遞引數時, 是值傳遞還是引用傳遞
      • 理解1: 只有值傳遞, 沒有引用傳遞. 傳遞的都是變數的值, 只是這個值可能是基本資料, 也可能是地址(引用)資料
      • 理解2: 如果後一種看成引用傳遞, 那就是兩者都有

物件和函式

  • 物件屬性訪問
    • .屬性名: 編碼簡單, 但有時不能用
    • ['屬性名']: 編碼麻煩, 但通用
      • 情形一: 屬性名不是合法的標識名 比如: p['content-type'] = 'text/json'
      • 情形二: 屬性名不確定(變數) 比如: p[prop] = value
  • IIEF: 匿名函式自呼叫, Immediately-Invoked Function Expression 立即呼叫函式表示式
    • 隱藏內部實現
    • 不汙染外部環境
    (function (i){
        ....
    })()
    

原型與原型鏈

  1. 原型(prototype)
    • 函式的prototype屬性
      • 每個函式都有一個prototype屬性, 它預設指向一個Object空物件(即稱為: 原型物件)
      • 原型物件中有一個屬性constructor, 它指向函式物件
    • 作用: 給原型物件新增屬性(一般都是方法), 函式的所有例項物件自動擁有原型中的屬性(方法)
  2. 顯式原型和隱式原型
    • 顯式原型: 每個函式function都有一個prototype, 預設為Object空物件(在定義函式時新增)
    • 隱式原型: 每個例項物件都有一個__proto__, 預設值為建構函式的prototype屬性值(在建立物件時自動新增的)
      person.__proto__ === Person.prototype
    • 程式設計師能直接操作顯式原型, 但不能直接操作隱式原型(ES6之前)
  3. 原型鏈(隱式原型鏈): 訪問物件屬性
    • 讀取: 自動到原型鏈中查詢
      a. 先在自身屬性中查詢,找到返回
      b. 如果沒有, 再沿著__proto__這條鏈向上查詢, 找到返回
      c. 如果最終沒找到, 返回undefined
    • 設定
      • 設定物件的屬性值時不會查詢原型鏈, 如果當前物件中沒有此屬性, 直接新增此屬性並設定其值
      • 方法一般定義在原型中, 屬性一般通過建構函式定義在物件本身上
  4. 擴充套件
    • instanceof是如何判斷的?
      • 表示式: A instanceof B
      • 如果B函式的顯式原型物件在A物件的原型鏈上, 返回true, 否則返回false
    • Function是通過new自己產生的例項

執行上下文與執行上下文棧

  • 程式碼分類: 全域性程式碼, 函式程式碼
  • 全域性執行上下文
    • 在執行全域性程式碼前將window確定為全域性執行上下文
    • 對全域性資料進行預處理
      • var定義的全域性變數==>undefined, 新增為window的屬性
      • function宣告的全域性函式==>賦值(fun), 新增為window的方法
      • this==>賦值(window)
    • 開始執行全域性程式碼
  • 函式執行上下文
    • 在呼叫函式, 準備執行函式體之前, 建立對應的函式執行上下文物件
    • 對區域性資料進行預處理
      • 形參變數>賦值(實參)>新增為執行上下文的屬性
      • arguments==>賦值(實參列表), 新增為執行上下文的屬性
      • var定義的區域性變數==>undefined, 新增為執行上下文的屬性
      • function宣告的函式 ==>賦值(fun), 新增為執行上下文的方法
      • this==>賦值(呼叫函式的物件)
    • 開始執行函式體程式碼
  • 執行上下文棧
    1. 在全域性程式碼執行前, JS引擎就會建立一個棧來儲存管理所有的執行上下文物件
    2. 在全域性執行上下文(window)確定後, 將其新增到棧中(壓棧)
    3. 在函式執行上下文建立後, 將其新增到棧中(壓棧)
    4. 在當前函式執行完後,將棧頂的物件移除(出棧)
    5. 當所有的程式碼執行完後, 棧中只剩下window

作用域與作用域鏈

  • 作用域
    1. 理解
      • 就是一塊"地盤", 一個程式碼段所在的區域
      • 它是靜態的(相對於上下文物件), 在編寫程式碼時就確定了
    2. 分類
      • 全域性作用域
      • 函式作用域
      • 沒有塊作用域(ES6有了)
    3. 作用
      • 隔離變數,不同作用域下同名變數不會有衝突
  • 作用域與執行上下文
    1. 區別1
      • 全域性作用域之外,每個函式都會建立自己的作用域,作用域在函式定義時就已經確定了。而不是在函式呼叫時
      • 全域性執行上下文環境是在全域性作用域確定之後, js程式碼馬上執行之前建立
      • 函式執行上下文環境是在呼叫函式時, 函式體程式碼執行之前建立
    2. 區別2
      • 作用域是靜態的, 只要函式定義好了就一直存在, 且不會再變化
      • 上下文環境是動態的, 呼叫函式時建立, 函式呼叫結束時上下文環境就會被釋放
    3. 聯絡
      • 上下文環境(物件)是從屬於所在的作用域
      • 全域性上下文環境==>全域性作用域
      • 函式上下文環境==>對應的函式使用域

閉包

  • 閉包
    1. 如何產生閉包?
      • 當一個巢狀的內部(子)函式引用了巢狀的外部(父)函式的變數(函式)時, 就產生了閉包
    2. 閉包到底是什麼?
      • 使用chrome除錯檢視
      • 理解一: 閉包是巢狀的內部函式(絕大部分人)
      • 理解二: 包含被引用變數(函式)的物件(極少數人)
      • 注意: 閉包存在於巢狀的內部函式中
    3. 產生閉包的條件?
      • 函式巢狀
      • 內部函式引用了外部函式的資料(變數/函式)
  • 常見的閉包
    1. 將函式作為另一個函式的返回值
    2. 將函式作為實參傳遞給另一個函式呼叫
  • 閉包的作用
    1. 使用函式內部的變數在函式執行完後, 仍然存活在記憶體中(延長了區域性變數的生命週期)
    2. 讓函式外部可以操作(讀寫)到函式內部的資料(變數/函式)
  • 閉包的生命週期
    1. 產生: 在巢狀內部函式定義執行完時就產生了(不是在呼叫)
    2. 死亡: 在巢狀的內部函式成為垃圾物件時
  • 閉包的應用
    • 具有特定功能的js檔案
    • 將所有的資料和功能都封裝在一個函式內部(私有的)
    • 只向外暴露一個包含n個方法的物件或函式
    • 模組的使用者, 只需要通過模組暴露的物件呼叫方法來實現對應的功能
  • 閉包的缺點和解決
    1. 缺點
      • 函式執行完後, 函式內的區域性變數沒有釋放, 佔用記憶體時間會變長
      • 容易造成記憶體洩露
    2. 解決
      • 能不用閉包就不用
      • 及時釋放

記憶體溢位和記憶體洩露

  • 記憶體溢位
    • 一種程式執行出現的錯誤
    • 當程式執行需要的記憶體超過剩餘記憶體時, 就丟擲記憶體溢位的錯誤
  • 記憶體洩露
    • 佔用的記憶體沒有及時釋放
    • 記憶體洩露積累多了就容易導致記憶體溢位
    • 常見的記憶體洩露
      • 意外的全域性變數
      • 沒有及時清理的定時器或回撥函式
      • 閉包

物件的建立模式

  • Object建構函式: 先建立空Object物件, 再動態新增屬性/方法
  • 物件字面量: 使用{}建立物件, 同時指定屬性/方法
  • 工廠模式: 通過工廠函式動態建立物件並返回
  • 自定義構造器模式: 自定義建構函式, 通過new建立物件
  • 自定義構造器+原型的組合模式: 自定義建構函式, 屬性在函式中初始化, 方法新增到原型上

繼承模式

  • 原型鏈繼承: 子型別的原型為父型別的一個例項物件
  • 借用建構函式繼承: 在子型別建構函式中通用call()呼叫父型別建構函式
  • 建構函式+原型: 原型鏈+借用建構函式的組合繼承

執行緒機制與事件機制

  • 程序與執行緒

    • 瀏覽器執行是單程序還是多程序?
      • 單程序: firefox, 老版IE
      • 多程序: chrome, 新版IE
    • 瀏覽器執行是單執行緒還是多執行緒? 都是多執行緒的
  • 瀏覽器核心

    • Chrome, Safari: webkit
    • Firefox: Gecko
    • IE: trident
  • 瀏覽器核心的組成

    • html,css文件解析模組 : 負責頁面文字的解析

    • dom/css模組 : 負責dom/css在記憶體中的相關處理

    • 佈局和渲染模組 : 負責頁面的佈局和效果的繪製

    • 佈局和渲染模組 : 負責頁面的佈局和效果的繪製

    • 定時器模組 : 負責定時器的管理

    • 網路請求模組 : 負責伺服器請求(常規/Ajax)

    • 事件響應模組 : 負責事件的管理

  • JS是單執行緒的

    1. 如何證明js執行是單執行緒的?
      • setTimeout()的回撥函式是在主執行緒執行的
      • 定時器回撥函式只有在執行棧中的程式碼全部執行完後才有可能執行
    2. 為什麼js要用單執行緒模式, 而不用多執行緒模式?
      • JavaScript的單執行緒,與它的用途有關。
      • 作為瀏覽器指令碼語言,JavaScript的主要用途是與使用者互動,以及操作DOM。
      • 這決定了它只能是單執行緒,否則會帶來很複雜的同步問題
    3. 程式碼的分類:
      • 初始化程式碼
      • 回撥程式碼
    4. js引擎執行程式碼的基本流程
      • 先執行初始化程式碼: 包含一些特別的程式碼
      • 設定定時器
      • 繫結監聽
      • 傳送ajax請求
      • 後面在某個時刻才會執行回撥程式碼
  • 事件迴圈模型: 事件管理模組 和 回撥佇列

  • Web Workers

    1. H5規範提供了js分執行緒的實現, 取名為: Web Workers
    2. 相關API
      • Worker: 建構函式, 載入分執行緒執行的js檔案
      • Worker.prototype.onmessage: 用於接收另一個執行緒的回撥函式
      • Worker.prototype.postMessage: 向另一個執行緒傳送訊息
    3. 不足
      • worker內程式碼不能操作DOM(更新UI)
      • 不能跨域載入JS
      • 不是每個瀏覽器都支援這個新特性