天天看點

javaScript變量提升以及函數提升

變量的聲明指派

var a = 1;      

上面的代碼先聲明變量 ​

​a​

​​,然後在變量 ​

​a​

​​ 與數值 ​

​1​

​​ 之間建立引用關系,稱為将數值 ​

​1​

​​ “指派”給變量 ​

​a​

​​。以後,引用變量名 ​

​a​

​​ 就會得到數值 ​

​1​

​​。最前面的 ​

​var​

​​,是變量聲明指令。它表示通知解釋引擎,要建立一個變量 ​

​a​

​。

  • 實際變量的聲明和指派,是分開的兩個步驟,如下
var a;
a = 1;      
  • 如果隻是聲明變量而沒有指派,則該變量的值是​

    ​undefined​

    ​​。​

    ​undefined​

    ​ 是一個特殊的值,表示“無定義”。
var a;
a // undefined      
  • 沒有寫​

    ​var​

    ​​ 去聲明變量,不寫​

    ​var​

    ​​ 的做法,會建立全局變量,​

    ​a = 1​

    ​​ 等同于​

    ​window.a = 1​

var a = 1;
// 基本等同
a = 1;      

多次聲明,後者會覆寫前者

什麼是變量提升

直覺上會認為 ​

​JavaScript​

​ 代碼在執行時是由上到下一行一行執行的。但實際上這并不完全正确,有一種特殊情況會導緻這個假設是錯誤的。

示例如下

  • 考慮以下代碼:
a = 2;
var a;
console.log(a);      

​console.log(..)​

​ 聲明會輸出什麼呢?

很多人會認為是 ​

​undefined​

​​,因為 ​

​var a​

​​ 聲明在 ​

​a = 2​

​​ 之後,他們自然而然地認為變量被重新指派了,是以會被賦予預設值 ​

​undefined​

​​。但是,真正的輸出結果是 ​

​2​

​。
  • 考慮另外一段代碼:
console.log(a);
var a = 2;      
你可能會認為這個代碼片段也會有同樣的行為而輸出 ​

​2​

​​。還有人可能會認為,由于變量 ​

​a​

​​ 在使用前沒有先進行聲明, 是以會抛出 ​

​ReferenceError​

​​ 異常。不幸的是兩種猜測都是不對的。輸出來的會是 ​

​undefined​

​。

為什麼會出現這種情況?

​​

​JavaScript​

​​ 引擎的工作方式是,先解析代碼,擷取所有被聲明的變量,然後再一行一行地運作。

這造成的結果,就是所有的變量的聲明語句,都會被提升到代碼的頭部,而指派或其他運作邏輯會留在原地,這就叫做變量提升

包括變量和函數在内的所有聲明都會在任何代碼被執行前首先被處理,這種現象稱為提升。

變量聲明提升:

  • ​JavaScript​

    ​​ 的變量提升是針對​

    ​var​

    ​​ 的,而​

    ​let​

    ​​ 和​

    ​const​

    ​​ 不存在變量提升這一特性(​

    ​let​

    ​​ 與​

    ​const​

    ​ 具有一個臨時死區的概念)
  • 通過​

    ​var​

    ​​ 定義的變量,在定義語句之前就可以通路到 值:​

    ​undefined​

  • 變量提升就是變量會被提升到作用域的最頂上去,也就是該變量不管是在作用域的哪個地方聲明的,都會提升到作用域的最頂上去。
  • ​JS​

    ​​ 解釋器會找出需要提升的變量和函數,并且給他們提前在記憶體中開辟好空間,函數的話會将整個函數存入記憶體中,變量隻聲明并且指派為​

    ​undefined​

    ​,
  • 如果變量一直都沒有聲明過,則會抛出​

    ​ReferenceError​

    ​​,比如直接輸出:​

    ​console.log(b) // Uncaught ReferenceError: b is not defined​

  • 當你看到​

    ​var a = 2​

    ​​; 時,可能會認為這是一個聲明。但​

    ​JavaScript​

    ​​ 實際上會将其看成兩個聲明:​

    ​var a;​

    ​​ 和​

    ​a = 2​

    ​;。
  • 第一個定義聲明是在編譯階段進行的。第二個指派聲明會被留在原地等待執行階段
  • 處理如下
a = 2;
var a;
console.log(a);
// 上面這段代碼會被 js 處理成如下代碼
var a;
a = 2;
console.log(a);      

函數聲明提升

  • ​JavaScript​

    ​​ 引擎将函數名視同變量名,是以采用​

    ​function​

    ​ 指令聲明函數時,整個代碼塊會提升到它所在的作用域的最開始執行
  • 通過​

    ​function​

    ​ 聲明的函數,在之前就可以直接調用
  • 函數提升隻會提升函數聲明,而不會提升函數表達式。
fx() // fx is a great girl 之前之後都可調用
function fx () {
  console.log('fx is a great girl')
}
fx() // fx is a great girl 之前之後都可調用      
  • 函數表達式聲明的函數
console.log(fx) // undefined
var fx = function () {
  console.log('fx is a great girl')
}

// ======================

fx() // 不是 ReferenceError, 而是 TypeErr
var fx = function () {
  console.log('fx is a great girl')
}      
這段程式中的變量辨別符 ​

​fx()​

​​ 被提升并配置設定給所在作用域(在這裡是全局作用域),是以 ​

​fx()​

​​ 不會導緻 ​

​ReferenceError​

​​。

但是 ​​

​fx​

​​ 此時并沒有指派(如果它是一個函數聲明而不是函數表達式,那麼就會指派)。​

​fx()​

​​ 由于對 ​

​undefined​

​​ 值進行函數調用而導緻非法操作, 是以抛出 ​

​TypeError​

​ 異常。

函數聲明和變量聲明使用同一個變量名稱

函數的優先級高于變量的優先級

// 會輸出fx定義的函數
console.log(fx)
function fx () {
  console.log('fx is a great girl')
}
var fx = 'fx'
console.log(fx) // fx      

多個同名函數聲明

由最後面的函數聲明來替代前面的

fx() // fx is a great girl
function fx () {
  console.log('fx')
}
function fx () {
  console.log('fx is a great girl')
}      

示例

示例一

每個作用域都會進行提升操作

var a = 100
function fn () {
    console.log(a)
    var a = 200
    console.log(a)
}
fn()
console.log(a)
var a
console.log(a)
var a = 300
console.log(a)      

這段代碼将會依次輸出 ​

​undefined 200 100 100 300​

在 ​

​fn()​

​​ 函數中由于聲明了 ​

​var a = 200​

​​, 是以 ​

​var a​

​​ 會被提升到 ​

​fn​

​​ 的作用域頂端,第一輸出則為 ​

​undefined​

示例二

下面這段代碼,由于 ​

​es6​

​​ 之前,​

​js​

​​ 是沒有塊級作用域的,是以 ​

​if​

​​ 中聲明的 ​

​a​

​ 變量會被當成全局變量處理

var a = 1
if (true) {
    var a = 2
}
console.log(a) // 2      

示例三

var a = 10                        
function fx () {
    console.log(a) // undefined
    var a = 20
    console.log(a) // 20
}
fx()
console.log(a) // 10

// ===

var a = 10
function fx () {
    console.log(a) // 10
    a = 20
    console.log(a) // 20
}
fx()
console.log(a) // 20      

示例四

function fx () {
    console.log(a) // undefined
    if (false) {
        var a = 1
    }
    console.log(a) // undefined
    console.log(b) // Uncaught ReferenceError: b is not defined
}
fx()      

示例五

function fx () {
    console.log('fx is a great girl')
}
var fx
console.log(typeof fx) // function

// =======================

function fx () {
    console.log('fx is a great girl')
}
var fx = 'good girl'
console.log(typeof fx) // string

// =======================

console.log(typeof fx) // function fx(){}函數提升
var fx = 'good girl'
function fx () {
    console.log('fx is a great girl')
}
console.log(typeof fx) // string

// =======================

console.log(typeof fx) // function
var fx
function fx () {
    console.log('fx is a great girl')
}
console.log(typeof fx) // function      

示例六

if(!(fx in window)) {
    var fx = 1
}
console.log(fx) // undefined      

示例七

var c = 1
function c(c) {
  console.log(c)
}
c(2) // c is not a function
 
// ========
 
c(2) // 2
var c = 1
function c(c) {
  console.log(c)
}
console.log(c) // 1
// ========
 
var c = function(c) {
  console.log(c)
}
c(2) // 2
var c = 1
 
// ========
var c = function(c) {
  console.log(c)
}
var c = 1
c(2) // Uncaught TypeError: c is not a function
 
// ========
 
c(2) // Uncaught TypeError: c is not a function
var c = function(c) {
  console.log(c)
}
var c = 1      

繼續閱讀