JS變量提升機制


變量提升機制

變量提升

當棧內存(作用域)形成,JS代碼自上而下執行之前,瀏覽器首先會把所有帶“VAR/FUNCTION”關鍵字的進行提前的“聲明”或者“定義”,這種預先處理機制稱之為“變量提升”

  • 聲明: var a (默認undefined)
  • 定義: a = 12(定義其實就是賦值操作)

變量提升階段

  • 帶“VAR”的只聲明未賦值
  • 帶“FUNCTION”的聲明和賦值都完成了

變量提升只發生在當前作用域(例如:開始加載頁面的時候只對全局作用域下的進行提升,因此此時函數中存儲的都是字符串而已)在全局作用域下聲明的函數或者變量是“全局變量”,同理在私有作用域下聲明的變量是“私有變量”【帶VAR/FUNCTION的才是聲明】

瀏覽器很懶,做過的事情不會重復執行第二遍,也就是,當代碼執行遇到創建函數這部分代碼后,直接跳過即可(因為在提升階段就已經完成函數的賦值操作了)

私有作用域形成后,也不是立即執行代碼,而是先進行變量提升(變量提升前,先形參賦值)

在ES3/ES5語法規范中,只有全局作用域和函數執行的私有作用域(棧內存),其他不生成棧內存

console.log(a);
var a = 12;
b = 13;
function sum(){
    var total = null;
}
sum()

->undefined

帶VAR和不帶VAR的區別

在全局作用域下聲明一個變量,也相當於給WINDOW全局對象設置了一個屬性,變量的值就是屬性值(私有作用域中的聲明的私有變量和WINDOW沒啥關系)

console.log(a);
console.log(window.a)
console.log('a' in window);

-> undefined
-> undefined
-> undefined

全局變量和window中的屬性存在“映射機制”一個改變另一個也改變

var a = 12;
console.log(a);
console.log(window.a);
a = 13;
console.log(window.a);
window.a = 14;
console.log(a);

-> 12
-> 12
-> 13
-> 14

可以使用屬性名 in 對象,檢測屬性名是否隸屬於這個對象

 console.log(a);
 console.log(window.a);
 console.log('a' in window);
 a = 12;
 console.log(a)
 console.log(window.a)

-> a is not defined
-> undefined
-> false
-> 12
-> 12

全局變量和WINDOW中的屬性存在“映射機制”一個改變另一個也改變

console.log(a);
console.log(window.a);
console.log('a' in window);
a = 12;
consoel.log(a);
console.log(window.a);

-> 報錯:a is not defined
注釋掉報錯的第一行
-> undefined
->false
-> 12
-> 12
-> 12

連續賦值運算下的VAR

var a = 12, b = 13;

這樣寫是帶VAR的

var a = b = 12;

這樣寫b是不帶VAR的

私有作用域域中帶VAR和不帶也有區別

  • 帶VAR的在私有作用域變量提升階段,都聲明未私有變量,和外界沒有任何的關系
  • 不帶VAR不是私有變量,會向它的上級作用域查找,看是否為上級的變量,不是,繼續向上查找,一直找到WINDOW為止(這種查找叫做:“作用域鏈”),也就是我們在私有作用域中操作的非私有變量,是一直操作別人的。
var a = 12, b = 12;
function fn(){
    console.log(a, b);
    var a = b = 13;
    console.log(a,b);
}
fn();
console.log(a,b);

-> undefined 12
-> 13 , 13
-> 12 ,13

變量提升階段, 私有變量a被提升,聲明a為私有變量並賦值為undefined
var a = b = 13;
把私有的A賦值為13,通過作用域鏈查找B並賦值為13

函數的提升

  • FUNCTION:聲明的函數在變量提升階段被聲明
  • 匿名函數之函數表達式: var fn = function(){...},左邊被提升,未執行到該行時,fn為undefined

在條件的變量提升

在當前作用域下,不管條件是否成立都要進行變量提升

  • 帶VAR的還只是聲明
  • 帶FUNCTION的在老版本瀏覽器渲染機制下,聲明+定義都處理,但為了迎合ES6中的塊級作用域,新版瀏覽器對於在條件判斷中的函數,不管條件是否成立,都只是先聲明,沒有定義,類似VAR

重名處理

帶VAR和FUNCTION關鍵字聲明相同的名字,這種也算是重名了(其實是一個FN,只是存儲值的類型不一樣)

var fn = 12;
function fn(){};

關於重命名的處理:
如果名字重復了,不會重新聲明,但是會重新定義(重新賦值)(不管是變量提升還是代碼執行階段皆是如此)

LET

LET創建的變量不存在變量提升問題,且不會與window產生映射

在相同作用域中,基於LET不能聲明相同名字的變量(不管用什么方式在當前作用於下聲明了變量,再次使用LET創建變量都會)

雖然沒有變量提升機制,但是在當前作用域代碼自上而下執行之前,瀏覽器會做一個重復性檢測:

自上而下查找當前作用域下所有變量,一旦發現有重復的,直接拋出異常,代碼也不會在執行了


免責聲明!

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



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