亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

理解 JavaScript Scoping & Hoisting(二)

 更新時(shí)間:2015年11月18日 23:28:52   投稿:mdxy-dxy  
這篇文章主要介紹了理解 JavaScript Scoping & Hoisting,盡管對(duì)于有經(jīng)驗(yàn)的程序員來(lái)說(shuō)這只是小菜一碟,不過(guò)我還是順著初學(xué)者常見(jiàn)的思路做一番描述

Scoping & Hoisting

var a = 1;

function foo() {
  if (!a) {
    var a = 2;
  }
  alert(a);
};

foo();

上面這段代碼在運(yùn)行時(shí)會(huì)產(chǎn)生什么結(jié)果?

盡管對(duì)于有經(jīng)驗(yàn)的程序員來(lái)說(shuō)這只是小菜一碟,不過(guò)我還是順著初學(xué)者常見(jiàn)的思路做一番描述:

1.創(chuàng)建了全局變量 a,定義其值為 1
2.創(chuàng)建了函數(shù) foo
3.在 foo 的函數(shù)體內(nèi),if 語(yǔ)句將不會(huì)執(zhí)行,因?yàn)?!a 會(huì)將變量 a 轉(zhuǎn)變成布爾的假值,也就是 false
4.跳過(guò)條件分支,alert 變量 a,最終的結(jié)果應(yīng)該是輸出 1

嗯,看起來(lái)無(wú)懈可擊的推理啊,但讓人驚訝的是:答案竟然是 2!為什么?

別著急,我會(huì)解釋給你聽(tīng)。首先我要告訴你這不是什么錯(cuò)誤,而是 JavaScript 語(yǔ)言解釋器的一個(gè)(非官方的)特性,某人(Ben Cherry)把這個(gè)特性叫做:Hoisting(目前尚未有標(biāo)準(zhǔn)的翻譯,比較常見(jiàn)的是提升)。

聲明與定義

為了理解 Hoisting,我們先來(lái)看一個(gè)簡(jiǎn)單的情況:

var a = 1;

你是否想過(guò),上面這句代碼在運(yùn)行的時(shí)候到底發(fā)生了什么?
 你是否知道,就這句代碼而言,“聲明變量 a” 和 “定義變量 a”這兩個(gè)說(shuō)法哪一個(gè)才是正確的?
•下例叫做 “聲明變量”:

var a;

•下例叫做 “定義變量”:

var a = 1;

•聲明:是指你聲稱某樣?xùn)|西的存在,比如一個(gè)變量或一個(gè)函數(shù);但你沒(méi)有說(shuō)明這樣?xùn)|西到底是什么,僅僅是告訴解釋器這樣?xùn)|西存在而已;
•定義:是指你指明了某樣?xùn)|西的具體實(shí)現(xiàn),比如一個(gè)變量的值是多少,一個(gè)函數(shù)的函數(shù)體是什么,確切的表達(dá)了這樣?xùn)|西的意義。

總結(jié)一下:

var a;            // 這是聲明
a = 1;            // 這是定義(賦值)
var a = 1;        // 合二為一:聲明變量的存在并賦值給它

重點(diǎn)來(lái)了:當(dāng)你以為你只做了一件事情的時(shí)候(var a = 1),實(shí)際上解釋器把這件事情分解成了兩個(gè)步驟,一個(gè)是聲明(var a),另一個(gè)是定義(a = 1)。

這和 Hoisting 有何關(guān)系?

回到最開(kāi)始的那個(gè)令人困惑的例子,我告訴你解釋器是如何分析你的代碼的:

var a;
a = 1;

function foo() {
  var a;    // 關(guān)鍵在這里
  if (!a) {
    a = 2;
  }
  alert(a);   // 此時(shí)的 a 并非函數(shù)體外的那個(gè)全局變量
}

如代碼所示,在進(jìn)入函數(shù)體后解釋器聲明了新的變量 a,而無(wú)論 if 語(yǔ)句的條件如何,都將為新的變量 a 賦值為 2。你若不相信可以在函數(shù)體外面 alert(a),然后再執(zhí)行 foo() 對(duì)比一下結(jié)果就知道了。

Scoping(作用域)

有人可能會(huì)問(wèn)了:“為什么不是在 if 語(yǔ)句內(nèi)聲明變量 a?”

因?yàn)?JavaScript 沒(méi)有塊級(jí)作用域(Block Scoping),只有函數(shù)作用域(Function Scoping),所以說(shuō)不是看見(jiàn)一對(duì)花括號(hào) {} 就代表產(chǎn)生了新的作用域,和 C 不一樣!

當(dāng)解析器讀到 if 語(yǔ)句的時(shí)候,它發(fā)現(xiàn)此處有一個(gè)變量聲明和賦值,于是解析器會(huì)將其聲明提升至當(dāng)前作用域的頂部(這是默認(rèn)行為,并且無(wú)法更改),這個(gè)行為就叫做 Hoisting。

OK,大家都懂了,你懂了嗎……

懂了不代表就會(huì)用了,就拿最開(kāi)始的例子來(lái)說(shuō),如果我就是想要 alert(a) 出那個(gè) 1 可咋整呢?

創(chuàng)建新的作用域

alert(a) 在執(zhí)行的時(shí)候,會(huì)去尋找變量 a 的位置,它從當(dāng)前作用域開(kāi)始向上(或者說(shuō)向外)一直查找到頂層作用域?yàn)橹?,若是找不到就?bào) undefined。

因?yàn)樵?alert(a) 的同級(jí)作用域里,我們?cè)俅温暶髁吮镜刈兞?a,所以它報(bào) 2;所以我們可以把本地變量 a 的聲明向下(或者說(shuō)向內(nèi))移動(dòng),這樣 alert(a) 就找不到它了。

記住:JavaScript 只有函數(shù)作用域!

var a = 1;

function foo() {
  if (!a) {
    (function() {    // 這是上一篇說(shuō)到過(guò)的 IIFE,它會(huì)創(chuàng)建一個(gè)新的函數(shù)作用域
      var a = 2;    // 并且該作用域在 foo() 的內(nèi)部,所以 alert 訪問(wèn)不到
    }());        // 不過(guò)這個(gè)作用域可以訪問(wèn)上層作用域哦,這就叫:“閉包”
  };
  alert(a);
};

foo();

你或許在無(wú)數(shù)的 JavaScript 書籍和文章里讀到過(guò):“請(qǐng)始終保持作用域內(nèi)所有變量的聲明放置在作用域的頂部”,現(xiàn)在你應(yīng)該明白為什么有此一說(shuō)了吧?因?yàn)檫@樣可以避免 Hoisting 特性給你帶來(lái)的困擾(我不是很情愿這么說(shuō),因?yàn)?Hoisting 本身并沒(méi)有什么錯(cuò)),也可以很明確的告訴所有閱讀代碼的人(包括你自己)在當(dāng)前作用域內(nèi)有哪些變量可以訪問(wèn)。但是,變量聲明的提升并非 Hoisting 的全部。在 JavaScript 中,有四種方式可以讓命名進(jìn)入到作用域中(按優(yōu)先級(jí)):

1.語(yǔ)言定義的命名:比如 this 或者 arguments,它們?cè)谒凶饔糜騼?nèi)都有效且優(yōu)先級(jí)最高,所以在任何地方你都不能把變量命名為 this 之類的,這樣是沒(méi)有意義的
2.形式參數(shù):函數(shù)定義時(shí)聲明的形式參數(shù)會(huì)作為變量被 hoisting 至該函數(shù)的作用域內(nèi)。所以形式參數(shù)是本地的,不是外部的或者全局的。當(dāng)然你可以在執(zhí)行函數(shù)的時(shí)候把外部變量傳進(jìn)來(lái),但是傳進(jìn)來(lái)之后就是本地的了
3.函數(shù)聲明:函數(shù)體內(nèi)部還可以聲明函數(shù),不過(guò)它們也都是本地的了
4.變量聲明:這個(gè)優(yōu)先級(jí)其實(shí)還是最低的,不過(guò)它們也都是最常用的

另外,還記得之前我們討論過(guò) 聲明 和 定義 的區(qū)別吧?當(dāng)時(shí)我并沒(méi)有說(shuō)為什么要理解這個(gè)區(qū)別,不過(guò)現(xiàn)在是時(shí)候了,記住:

Hosting 只提升了命名,沒(méi)有提升定義

這一點(diǎn)和我們接下來(lái)要講到的東西息息相關(guān),請(qǐng)看:

函數(shù)聲明與函數(shù)表達(dá)式的差別

先看兩個(gè)例子:

function test() {
  foo();

  function foo() {
    alert("我是會(huì)出現(xiàn)的啦……");
  }
}

test();
function test() {
  foo();

  var foo = function() {
    alert("我不會(huì)出現(xiàn)的哦……");
  }
}

test();

同學(xué),在了解了 Scoping & Hoisting 之后,你知道怎么解釋這一切了吧?

在第一個(gè)例子里,函數(shù) foo 是一個(gè)聲明,既然是聲明就會(huì)被提升(我特意包裹了一個(gè)外層作用域,因?yàn)槿肿饔糜蛐枰愕南胂?,不是那么直觀,但是道理是一樣的),所以在執(zhí)行 foo() 之前,作用域就知道函數(shù) foo 的存在了。這叫做函數(shù)聲明(Function Declaration),函數(shù)聲明會(huì)連通命名和函數(shù)體一起被提升至作用域頂部。

然而在第二個(gè)例子里,被提升的僅僅是變量名 foo,至于它的定義依然停留在原處。因此在執(zhí)行 foo() 之前,作用域只知道 foo 的命名,不知道它到底是什么,所以執(zhí)行會(huì)報(bào)錯(cuò)(通常會(huì)是:undefined is not a function)。這叫做函數(shù)表達(dá)式(Function Expression),函數(shù)表達(dá)式只有命名會(huì)被提升,定義的函數(shù)體則不會(huì)。

尾記:Ben Cherry 的原文解釋的更加詳細(xì),只不過(guò)是英文而已。我這篇是借花獻(xiàn)佛,主要是更淺顯的解釋給初學(xué)者聽(tīng),若要看更多的示例,請(qǐng)移步原作,謝謝。

相關(guān)文章

  • JavaScript 判斷瀏覽器類型及版本

    JavaScript 判斷瀏覽器類型及版本

    你知道世界上有多少種瀏覽器嗎?除了我們熟知的IE, Firefox, Opera, Safari四大瀏覽器之外,世界上還有近百種瀏覽器。
    2009-02-02
  • JS基礎(chǔ)之undefined與null的區(qū)別分析

    JS基礎(chǔ)之undefined與null的區(qū)別分析

    在JavaScript開(kāi)發(fā)中,被人問(wèn)到:null與undefined到底有啥區(qū)別?一時(shí)間不好回答,特別是undefined,因?yàn)檫@涉及到undefined的實(shí)現(xiàn)原理。
    2011-08-08
  • 實(shí)用的Javascript調(diào)試技巧整理

    實(shí)用的Javascript調(diào)試技巧整理

    這篇文章介紹了Javascript的調(diào)試技巧,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 一篇文章帶你從零快速上手Rollup

    一篇文章帶你從零快速上手Rollup

    這篇文章主要給大家介紹了如何通過(guò)一篇文章快速?gòu)牧憧焖偕鲜諶ollup的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • JS產(chǎn)生隨機(jī)數(shù)的幾個(gè)用法詳解

    JS產(chǎn)生隨機(jī)數(shù)的幾個(gè)用法詳解

    下面小編就為大家?guī)?lái)一篇JS產(chǎn)生隨機(jī)數(shù)的幾個(gè)用法詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • AJAX 仿EXCEL表格功能

    AJAX 仿EXCEL表格功能

    仿Excel表格演示代碼,非常不錯(cuò)。
    2009-06-06
  • JScript實(shí)現(xiàn)表格的簡(jiǎn)單操作

    JScript實(shí)現(xiàn)表格的簡(jiǎn)單操作

    這篇文章主要為大家詳細(xì)介紹了JScript實(shí)現(xiàn)簡(jiǎn)單的表格操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Js 冒泡事件阻止實(shí)現(xiàn)代碼

    Js 冒泡事件阻止實(shí)現(xiàn)代碼

    當(dāng)一個(gè)元素上的事件被觸發(fā)的時(shí)候,比如說(shuō)鼠標(biāo)點(diǎn)擊了一個(gè)按鈕,同樣的事件將會(huì)在那個(gè)元素的所有祖先元素中被觸發(fā)
    2013-01-01
  • 延時(shí)加載JavaScript代碼提高速度

    延時(shí)加載JavaScript代碼提高速度

    這篇文章主要介紹了延時(shí)加載JavaScript代碼提高速度的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • H5+C3+JS實(shí)現(xiàn)雙人對(duì)戰(zhàn)五子棋游戲(UI篇)

    H5+C3+JS實(shí)現(xiàn)雙人對(duì)戰(zhàn)五子棋游戲(UI篇)

    這篇文章主要為大家詳細(xì)介紹了H5+C3+JS實(shí)現(xiàn)雙人對(duì)戰(zhàn)五子棋游戲,實(shí)現(xiàn)雙人對(duì)戰(zhàn)模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-09-09

最新評(píng)論