js-高級(原型與原型鏈、作用域與作用域鏈、閉包)


## 原型與原型鏈
* 所有函數都有一個特別的屬性:
  * `prototype` : 顯式原型屬性
* 所有實例對象都有一個特別的屬性:
  * `__proto__` : 隱式原型屬性
* 顯式原型與隱式原型的關系
  * 函數的prototype: 定義函數時自動賦值, 值默認為{}, 即用為原型對象
  * 實例對象的__proto__: 在創建實例對象時被自動添加, 並賦值為構造函數的prototype值
  * 原型對象即為當前實例對象的父對象
原型鏈
  * 所有的實例對象都有__proto__屬性, 它指向的就是原型對象
  * 這樣通過__proto__屬性就形成了一個鏈的結構---->原型鏈
  * 當查找對象內部的屬性/方法時, js引擎自動沿着這個原型鏈查找
  * 當給對象屬性賦值時不會使用原型鏈, 而只是在當前對象中進行操作

## 執行上下文與執行上下文棧
* 變量提升與函數提升
  * 變量提升: 在變量定義語句之前, 就可以訪問到這個變量(undefined)
  * 函數提升: 在函數定義語句之前, 就執行該函數
  * 先有變量提升, 再有函數提升
* 理解
  * 執行上下文: 由js引擎自動創建的對象, 包含對應作用域中的所有變量屬性
  * 執行上下文棧: 用來管理產生的多個執行上下文
* 分類:
  * 全局: window
  * 函數: 對程序員來說是透明的
* 生命周期
  * 全局 : 准備執行全局代碼前產生, 當頁面刷新/關閉頁面時死亡
  * 函數 : 調用函數時產生, 函數執行完時死亡
* 包含哪些屬性:
  * 全局 : 
    * 用var定義的全局變量  ==>undefined
    * 使用function聲明的函數   ===>function
    * this   ===>window
  * 函數
    * 用var定義的局部變量  ==>undefined
    * 使用function聲明的函數   ===>function
    * this   ===> 調用函數的對象, 如果沒有指定就是window 
    * 形參變量   ===>對應實參值
    * arguments ===>實參列表的偽數組

## 繼承模式
* 原型鏈繼承 : 得到方法
  ```
  function Parent(){}
  Parent.prototype.test = function(){};
  function Child(){}
  Child.prototype = new Parent(); // 子類型的原型指向父類型實例
  Child.prototype.constructor = Child
  var child = new Child(); //有test()
  ```
* 借用構造函數 : 得到屬性
  ```
  function Parent(xxx){this.xxx = xxx}
  Parent.prototype.test = function(){};
  function Child(xxx,yyy){
      Parent.call(this, xxx);//借用構造函數   this.Parent(xxx)
  }
  var child = new Child('a', 'b');  //child.xxx為'a', 但child沒有test()
  ```
* 組合
  ```
  function Parent(xxx){this.xxx = xxx}
  Parent.prototype.test = function(){};
  function Child(xxx,yyy){
      Parent.call(this, xxx);//借用構造函數   this.Parent(xxx)
  }
  Child.prototype = new Parent(); //得到test()
  var child = new Child(); //child.xxx為'a', 也有test()
  ```
* new一個對象背后做了些什么?
  * 創建一個空對象
  * 給對象設置__proto__, 值為構造函數對象的prototype屬性值   this.__proto__ = Fn.prototype
  * 執行構造函數體(給對象添加屬性/方法)
* 執行上下文創建和初始化的過程
  * 全局:
    * 在全局代碼執行前最先創建一個全局執行上下文(window)
    * 收集一些全局變量, 並初始化
    * 將這些變量設置為window的屬性
  * 函數:
    * 在調用函數時, 在執行函數體之前先創建一個函數執行上下文
    * 收集一些局部變量, 並初始化
    * 將這些變量設置為執行上下文的屬性
## 作用域與作用域鏈
* 理解:
  * 作用域: 一塊代碼區域, 在編碼時就確定了, 不會再變化
  * 作用域鏈: 多個嵌套的作用域形成的由內向外的結構, 用於查找變量
* 分類:
  * 全局
  * 函數
  * js沒有塊作用域(在ES6之前)
* 作用
  * 作用域: 隔離變量, 可以在不同作用域定義同名的變量不沖突
  * 作用域鏈: 查找變量
* 區別作用域與執行上下文
  * 作用域: 靜態的, 編碼時就確定了(不是在運行時), 一旦確定就不會變化了
  * 執行上下文: 動態的, 執行代碼時動態創建, 當執行結束消失
  * 聯系: 執行上下文環境是在對應的作用域中的

## 閉包 
* 理解:
  * 當嵌套的內部函數引用了外部函數的變量時就產生了閉包
  * 通過chrome工具得知: 閉包本質是內部函數中的一個對象, 這個對象中包含引用的變量屬性
* 作用:
  * 延長局部變量的生命周期
  * 讓函數外部能操作內部的局部變量
* 寫一個閉包程序
  ```
  function fn1() {
    var a = 2;
    function fn2() {
      a++;
      console.log(a);
    }
    return fn2;
  }
  var f = fn1();
  f();
  f();
  ```
* 閉包應用:
  * 模塊化: 封裝一些數據以及操作數據的函數, 向外暴露一些行為
  * 循環遍歷加監聽
  * JS框架(jQuery)大量使用了閉包
* 缺點:
  * 變量占用內存的時間可能會過長
  * 可能導致內存泄露
  * 解決:
    * 及時釋放 : f = null; //讓內部函數對象成為垃圾對象
    
## 內存溢出與內存泄露
1. 內存溢出
  * 一種程序運行出現的錯誤
  * 當程序運行需要的內存超過了剩余的內存時, 就出拋出內存溢出的錯誤
2. 內存泄露
  * 占用的內存沒有及時釋放
  * 內存泄露積累多了就容易導致內存溢出
  * 常見的內存泄露:
    * 意外的全局變量
    * 沒有及時清理的計時器或回調函數
    * 閉包
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM