帶你瞭解變量提高閉包做用域

變量提高(聲)

當瀏覽器開闢出供代碼執行的棧內存後,代碼並無自上而下當即執行,而是繼續作了一些事情:把當前做用域中全部帶var/function關鍵字的進行提早的聲明和定義 =>變量提高機制javascript

  • 帶var的只是提早聲明(declare) 「var a;」 若是隻聲明沒有賦值,默認值是undefined
  • 帶function的不只聲明,並且還定義了(defined) 「a=13」定義其實就是賦值,準確來講就是讓變量和某個值進行關聯

1.帶var和不帶var的區別java

//=>在全局做用域下的區別
/* * 不帶var的:至關於給全局對象window設置了一個屬性a * window.a = 13; */
a = 13;
console.log(a); //=>window.a

/* * 棧內存變量存儲空間 * b * 帶var的:是在全局做用域下聲明瞭一個變量b(全局變量),可是在全局下聲明的變量也一樣至關於給window增長了一個對應的屬性(只有全局做用域具有這個特色) */
var b = 14; //=>建立變量b & 給window設置了屬性b
console.log(b); //=>14
console.log(window.b); //=>14
複製代碼

閉包做用域

  1. 建立函數es6

    • 開闢一個堆內存
    • 把函數體中的代碼當作字符串存儲進去
    • 把堆內存的地址賦值給函數名/變量名
    • 函數在哪建立,那麼它執行時候所須要查找的上級做用域就是誰
  2. 函數執行web

    • 造成一個全新的私有做用域、執行上下文、私有棧內存(執行一次造成一個,多個之間也不會產生影響)
    • 形參賦值 & 變量提高
    • 代碼執行(把所屬堆內存中的代碼字符串拿出來一行行執行)
    • 遇到一個變量,首先看它是否爲私有變量(形參和在私有做用域中聲明的變量是私有變量),是私有的就操做本身的變量便可,不是私有的則向上級做用域中查找...一直找到全局做用域爲止 =>做用域鏈查找機制
    • 私有變量和外界的變量沒有必然關係,能夠理解爲被私有棧內存保護起來了,這種機制其實就是閉包的保護機制
  3. 關於堆棧內存釋放問題(以谷歌webkit內核爲例子)瀏覽器

    函數執行就會造成棧內存(從內存中分配的一塊空間),若是內存都不銷燬釋放,很容易就會致使棧內存溢出(內存爆滿,電腦就卡死了),堆棧內存的釋放問題是學習JS的核心知識之一閉包

    • 堆內存釋放問題
    //=>建立一個引用類型值,就會產生一個堆內存
    //若是當前建立的堆內存不被其它東西所佔用了(瀏覽器會在空閒的時候,查找每個內存的引用情況,不被佔用的都會給回收釋放掉),則會釋放
    let obj = {
       name : 'zhufeng'
    };
    let oop = obj;
    //此時obj和oop都佔用着對象的堆內存,想要釋放堆內存,須要手動解除變量和值的關聯(null:空對象指針)
    obj = null;
    oop = null;
    複製代碼
    • 棧內存釋放函數

      //=>打開瀏覽器造成的全局做用域是棧內存
      //=>手動執行函數造成的私有做用域是棧內存
      //=>基於ES6中的let/const造成的塊做用域也是棧內存
      //=>....
      
      /* * 全局棧內存:關掉頁面的時候纔會銷燬 * 私有棧內存: * 1.通常狀況下,函數只要執行完成,造成的私有棧內存就會被銷燬釋放掉(排除出現無限極遞歸、出現死循環的模式) * 2.可是一旦棧內存中的某個東西(通常都是堆地址)被私有做用域之外的事物給佔用了,則當前私有棧內存不能當即被釋放銷燬(特色:私有做用域中的私有變量等信息也保留下來了) =>市面上認爲的閉包:函數執行造成不能被釋放的私有棧內存,這樣的纔是閉包 */
      function fn(){
          //...
      }
      fn(); //=>函數執行造成棧內存,執行完成棧內存銷燬
      
      function X(){
          return function(){
              //...
          }
      }
      let f=X(); //=>f佔用了X執行造成的棧內存中的一個東西(返回小函數對應的堆),則X執行造成的棧內存不能被釋放了
      複製代碼

let/const和var的區別

1.let和const不存在變量提高機制oop

建立變量的六種方式中:var/function有變量提高,而let/const/class/import都不存在這個機制學習

2.var容許重複聲明,而let是不容許的ui

在相同的做用域中(或執行上下文中)

  • 若是使用var/function關鍵詞聲明變量而且重複聲明,是不會有影響的(聲明第一次以後,以後再遇到就再也不重複聲明瞭)
  • 可是使用let/const就不行,瀏覽器會校驗當前做用域中是否已經存在這個變量了,若是已經存在了,則再次基於let等從新聲明就會報錯
//=>在瀏覽器開闢棧內存供代碼自上而下執行以前,不只有變量提高的操做,還有不少其它的操做=>「詞法解析」或者「詞法檢測」:就是檢測當前即將要執行的代碼是否會出現「語法錯誤 SyntaxError」,若是出現錯誤,代碼將不會再執行(第一行都不會執行)
console.log(1); //=>這行代碼就已經不會再被執行了
let a = 12;
console.log(a);
let a = 13;  //=>Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);
複製代碼
//=>所謂重複是:無論以前經過什麼辦法,只要當前棧內存中存在了這個變量,咱們使用let/const等重複再聲明這個變量就是語法錯誤
console.log(a);
var a = 12;
let a = 13; //=>Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);
複製代碼

3.let能解決typeof檢測時出現的暫時性死區問題(LET比VAR更嚴謹)

es6.ruanyifeng.com/#docs/let

// console.log(a); 
//=>Uncaught ReferenceError: a is not defined

// console.log(typeof a); 
//=>"undefined" 這是瀏覽器BUG,本應該報錯由於沒有a(暫時性死區)

console.log(typeof a); 
//=>Uncaught ReferenceError: Cannot access 'a' before initialization
let a;
複製代碼
相關文章
相關標籤/搜索