函數(shù)、變量提升

變量和 函數(shù)聲明提升

js自上而下的執(zhí)行順序受到很多新手和部分老手的共識(shí),但這其實(shí)并不完全正確。這涉及到j(luò)s的編譯過程。

提升

js代碼執(zhí)行前,會(huì)先對(duì)代碼進(jìn)行編譯。編譯的一部分工作就是找到所有的聲明,然后建立作用域?qū)⑵潢P(guān)聯(lián)起來,因此,在當(dāng)前作用域內(nèi)包括變量和函數(shù)在內(nèi)的所有聲明都會(huì)在任何代碼被執(zhí)行前首先被處理。這里的聲明提前但是賦值并沒有,也就是聲明提升了,賦值還留在原地。

預(yù)解釋

在js中帶var 關(guān)鍵字或以function開頭的都要進(jìn)行預(yù)解釋。
預(yù)解釋的時(shí)候不管你條件是否成立,帶var的都要提前聲明。
預(yù)解釋的時(shí)候只預(yù)解釋等號(hào)左邊的,右邊的值不參與預(yù)解釋。
自執(zhí)行函數(shù)在全局作用域下是不進(jìn)行預(yù)解釋的。
預(yù)解釋的時(shí)候,如果名字已經(jīng)聲明過了,就不會(huì)重新聲明,但會(huì)重新賦值。
在函數(shù)體內(nèi)return下面的代碼雖然不執(zhí)行,但要進(jìn)行預(yù)解釋,return后面的都是跟著我們的返回值,不進(jìn)行預(yù)解釋。

變量提升

變量聲明,被提升在作用域頂端。如var a;
變量定義,聲明部分會(huì)被提升,賦值部分不被提升。如var b='test'
函數(shù)表達(dá)式其實(shí)就是變量定義,只不過恰好被賦值的類型是函數(shù)。所以只提升變量名,不提升函數(shù)值

console.log(a);
var a=2;

等價(jià)于:

var a;
console.log(a);//undefind
a=2;

這就用到了我們說的聲明提升了,賦值還留在原地。

function hoist(){
    if(!foo){
        var foo=5;
    }
    console.log(foo);
}
hoist();//5

如果當(dāng)前作用域中存在此變量聲明,無論它在什么地方聲明,引用此變量的時(shí)候就會(huì)在當(dāng)前作用域中查找,不會(huì)去外層作用域了。
經(jīng)過一次預(yù)編譯,上面的代碼邏輯如下:

function hoist(){
    var foo;
    if(!foo){
        foo=5;
    }
    console.log(foo);
}
hoist()

變量聲明提升到了函數(shù)的頂部,初始值為undefind,自然if語句就會(huì)被執(zhí)行,foo變量賦值為5。
類似的,還有下面一個(gè)例子:

var foo=3;
function hoist(){
    var foo=foo||5;
    console.log(foo)//5
}
hoist();

結(jié)果是5不是3.雖然外層作用域有個(gè)foo變量,但函數(shù)內(nèi)是不會(huì)去引用的。
如果當(dāng)前作用域中聲明了多個(gè)同名變量,那么根據(jù)我們的推斷,它們的同一個(gè)標(biāo)識(shí)符會(huì)被提升至作用域的頂部,其他部分按順序執(zhí)行,比如:

function hoist(){
    var foo=3;
    {
        var foo=5;
    }
    console.log(foo);//5
}
hoist();

由于js沒有塊級(jí)作用域。只有全局作用域和函數(shù)作用域。所以預(yù)編譯之后的代碼邏輯為:

function hoist(){
    var foo;
    foo=3;
    {
        foo=5;
    }
    console.log(foo);//5
}
hoist();

函數(shù)提升

函數(shù)聲明全部被提升,包括優(yōu)先級(jí)比變量聲明要高,名字和它相同的變量聲明會(huì)被忽略。沒有被調(diào)用就不會(huì)被解析
如下代碼:

function hoist(){
    foo();//output : i am hoisted
    funciton foo(){
        console.log('i am hoisted')
    }
}
hoist();

為什么函數(shù)聲明在調(diào)用之前就可以聲明,其實(shí)。引擎是把函數(shù)聲明的整個(gè)代碼提升到了當(dāng)前作用域的頂部。

function hoist(){
    function foo(){
        console.log('i am hoisted')
    }
    foo()//output: i am hoisted;
}
hoist();

相似的,如果在同一個(gè)作用域中存在多個(gè)同名函數(shù)聲明,后面出現(xiàn)的將會(huì)覆蓋前面的函數(shù)聲明。

function hoist(){
    function foo(){
        console.log(1)
    }
    foo();//output:2
    function foo(){
        console.log(2)
    }
}
hoist();

當(dāng)函數(shù)聲明遇到函數(shù)表達(dá)式時(shí),會(huì)有什么樣的結(jié)果呢?先看下面這段代碼:

function hoist(){
    foo();//2
    var foo=function (){
        console.log(1)
    }
    foo();//1
    function foo(){
        console.log(2)
    }
    foo();//1
}

運(yùn)行之后我們會(huì)發(fā)現(xiàn),輸出的結(jié)果依次是211,因?yàn)楹瘮?shù)是一等公民,函數(shù)聲明的優(yōu)先級(jí)最高,會(huì)被提升至當(dāng)前作用域的最頂端,所以第一次調(diào)用時(shí)實(shí)際執(zhí)行了下面定義的函數(shù)聲明,然后第二次調(diào)用時(shí),由于前面的函數(shù)表達(dá)式與之前的函數(shù)聲明同名,故將其覆蓋,以后的調(diào)用也會(huì)打印同樣的結(jié)果。

function hoist(){
    var foo;
    foo=function foo(){
        console.log(2)
    }
    foo();//2
    foo=function (){
        console.log(1)
    }
    foo();//1
    foo();//1
}

我們也不難理解。函數(shù)和變量重名時(shí),會(huì)如何執(zhí)行:

var foo=3;
function hoist(){
    console.log(foo);//function foo(){}
    var foo=5;
    console.lgo(foo);//5
    function foo(){}
}
hoist();
console.log(foo);//3

預(yù)編譯后是這樣的:

var foo=3;
function hoist(){
    var foo;
    foo=function foo(){};
    console.log(foo);//function foo(){}
    foo=5;
    console.log(foo);//5
}
hoist();
console.log(foo);//3

參數(shù)重名

function fn(fn){
  console.log(fn);
  var fn=3;
  console.log(fn);
}
fn(10)
function fn(10){
  var fn=10;已初始化的一般變量大于未初始化的一般變量
  var fn;
  console.log(fn);
  fn=3;
  console.log(fn)//
}

在js中如果重名則已初始化的一般變量>函數(shù)>函數(shù)參數(shù)>未初始化一般變量。

var a;
function a(){
  var b=0;
}//結(jié)果為function a(){var b=0}因?yàn)楹瘮?shù)的優(yōu)先級(jí)大于未初始化的一般變量。
var a=1;
function a(){
  var b=0;
}//結(jié)果為1,因?yàn)橐殉跏蓟囊话阕兞績(jī)?yōu)先于函數(shù)。

面試題解析

console.log(foo);//undefined
console.log(bar);//function bar(){}
var foo=function (){};
function bar(){}

預(yù)編譯之后是:

var foo;
function bar(){}
console.log(foo);
console.log(bar);
foo=function(){};
function foo(){
    a=5;
    console.log(window.a);//undefind
    console.log(a);//5
    var a=10;
    console.log(a)//10
}
foo()

預(yù)編譯如下:

function foo(){
    var a;
    a=5;//因?yàn)樵谧约旱淖饔糜蛴衋聲明的存在,a并不會(huì)污染全局。而是綁定到本作用域的a上。
    console.log(window.a);
    console.log(a);
    a=10;
    console.log(a)
}
foo()
function foo(){
    var a=1;
    function b(){
        a=10;
        return '';
        function a(){}
    }
    b();
    console.log(a)
}
foo();

編譯如下:

function foo(){
    var a;//a和b一起提升到作用域的頂部
    function b(){
        function a(){}//b里面的a也提升到作用域b的頂部
        a=10;//因?yàn)樯厦嬗凶兞縜,所以a也不會(huì)污染到上一層
        return '';
    }
    a=1;
    b();//這個(gè)有兩點(diǎn)要搞清楚b的a沒有污染到這個(gè)作用域;
    console.log(a)//就近原則,本函數(shù)的log(a),找離自己最近的a變量
}
foo();
4.

console.log(a);
var a = 1;
console.log(b);

`console.log(a)`輸出undefined;因?yàn)閍變量提升,相當(dāng)于

var a;
console.log(a);

只聲明了沒有賦值,為undefined。
console.log(b)會(huì)報(bào)錯(cuò),因?yàn)榧葲]有聲明也沒有賦值。
5. 
   sayName('world');
sayAge(10);
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};
sayName函數(shù)會(huì)輸出'hello''world',sayAge會(huì)報(bào)錯(cuò)
原因:第一個(gè)是函數(shù)聲明,會(huì)函數(shù)聲明前置,所以函數(shù)聲明不必放在調(diào)用的前面。而第二個(gè)是函數(shù)表達(dá)式,相當(dāng)于普通變量,所以函數(shù)調(diào)用必須放在函數(shù)聲明的后面,否則會(huì)報(bào)錯(cuò)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 面試題 首先,讓我們來看一題面試題; 作用域 作用域(scope)指的是變量存在的范圍。Javascript只有兩...
    Ghj_小樹閱讀 1,933評(píng)論 0 3
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,506評(píng)論 0 13
  • 農(nóng)婦,山泉,有點(diǎn)田一個(gè)小院子,開滿鮮花的籬笆自己手工做的桌椅板凳,衣服,籃子......清晨摘下新鮮的蔬菜和鮮花夜...
    東籬琴酒黃昏后閱讀 120評(píng)論 0 0
  • 綠樹桃花別樣紅, 蝶戀芬芳肆意舞。 慨嘆輪回又一度, 只怨難把春光留。 揮手緊扼時(shí)空喉, 怒囂光陰勿荏苒。
    金堡閱讀 251評(píng)論 0 0
  • 引言:個(gè)人發(fā)展也是打造個(gè)人品牌,自然有策略選擇,個(gè)人所在行業(yè)也是商業(yè)的外部環(huán)境,兩者具有相關(guān)性。 一、個(gè)人處于競(jìng)爭(zhēng)...
    懷才閱讀 354評(píng)論 0 1

友情鏈接更多精彩內(nèi)容