九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Javascript作用域和變量提升

[轉(zhuǎn)載請(qǐng)注明來(lái)自: http://blog.csdn.net/sunxing007]

下面的程序是什么結(jié)果?

[javascript] view plain copy
  1. var foo = 1;  
  2. function bar() {  
  3.     if (!foo) {  
  4.         var foo = 10;  
  5.     }  
  6.     alert(foo);  
  7. }  
  8. bar();  
結(jié)果是10;

那么下面這個(gè)呢?

[javascript] view plain copy
  1. var a = 1;  
  2. function b() {  
  3.     a = 10;  
  4.     return;  
  5.     function a() {}  
  6. }  
  7. b();  
  8. alert(a);  
結(jié)果是1.

嚇你一跳吧?發(fā)生了什么事情?這可能是陌生的,危險(xiǎn)的,迷惑的,同樣事實(shí)上也是非常有用和印象深刻的javascript語(yǔ)言特性。對(duì)于這種表現(xiàn)行為,我不知道有沒(méi)有一個(gè)標(biāo)準(zhǔn)的稱呼,但是我喜歡這個(gè)術(shù)語(yǔ):“Hoisting (變量提升)”。這篇文章將對(duì)這種機(jī)制做一個(gè)拋磚引玉式的講解,但是,首先讓我們對(duì)javascript的作用域有一些必要的理解。

Javascript的作用域

對(duì)于Javascript初學(xué)者來(lái)說(shuō),一個(gè)最迷惑的地方就是作用域;事實(shí)上,不光是初學(xué)者。我就見(jiàn)過(guò)一些有經(jīng)驗(yàn)的javascript程序員,但他們對(duì)scope理解不深。javascript作用域之所以迷惑,是因?yàn)樗绦蛘Z(yǔ)法本身長(zhǎng)的像C家族的語(yǔ)言,像下面的C程序:

  1. #include <stdio.h>  
  2. int main() {  
  3.     int x = 1;  
  4.     printf("%d, ", x); // 1  
  5.     if (1) {  
  6.         int x = 2;  
  7.         printf("%d, ", x); // 2  
  8.     }  
  9.     printf("%d\n", x); // 1  
  10. }  
輸出結(jié)果是1 2 1,這是因?yàn)镃家族的語(yǔ)言有塊作用域,當(dāng)程序控制走進(jìn)一個(gè)塊,比如if塊,只作用于該塊的變量可以被聲明,而不會(huì)影響塊外面的作用域。但是在Javascript里面,這樣不行??纯聪旅娴拇a:
[javascript] view plain copy
  1. var x = 1;  
  2. console.log(x); // 1  
  3. if (true) {  
  4.     var x = 2;  
  5.     console.log(x); // 2  
  6. }  
  7. console.log(x); // 2  
結(jié)果會(huì)是1 2 2。因?yàn)閖avascript是函數(shù)作用域。這是和c家族語(yǔ)言最大的不同。該程序里面的if并不會(huì)創(chuàng)建新的作用域。

對(duì)于很多C,c++,java程序員來(lái)說(shuō),這不是他們期望和歡迎的。幸運(yùn)的是,基于javascript函數(shù)的靈活性,這里有可變通的地方。如果你必須創(chuàng)建臨時(shí)的作用域,可以像下面這樣:

[javascript] view plain copy
  1. function foo() {  
  2.     var x = 1;  
  3.     if (x) {  
  4.         (function () {  
  5.             var x = 2;  
  6.             // some other code  
  7.         }());  
  8.     }  
  9.     // x is still 1.  
  10. }  
這種方法很靈活,可以用在任何你想創(chuàng)建臨時(shí)的作用域的地方。不光是塊內(nèi)。但是,我強(qiáng)烈推薦你花點(diǎn)時(shí)間理解javascript的作用域。它很有用,是我最喜歡的javascript特性之一。如果你理解了作用域,那么變量提升就對(duì)你顯得更有意義。

變量聲明,命名,和提升

在javascript,變量有4種基本方式進(jìn)入作用域:

  • 1 語(yǔ)言內(nèi)置:所有的作用域里都有this和arguments;(譯者注:經(jīng)過(guò)測(cè)試arguments在全局作用域是不可見(jiàn)的)
  • 2 形式參數(shù):函數(shù)的形式參數(shù)會(huì)作為函數(shù)體作用域的一部分;
  • 3 函數(shù)聲明:像這種形式:function foo(){};
  • 4 變量聲明:像這樣:var foo;

函數(shù)聲明和變量聲明總是會(huì)被解釋器悄悄地被“提升”到方法體的最頂部。這個(gè)意思是,像下面的代碼:

[javascript] view plain copy
  1. function foo() {  
  2.     bar();  
  3.     var x = 1;  
  4. }  
實(shí)際上會(huì)被解釋成:
[javascript] view plain copy
  1. function foo() {  
  2.     var x;  
  3.     bar();  
  4.     x = 1;  
  5. }  
無(wú)論定義該變量的塊是否能被執(zhí)行。下面的兩個(gè)函數(shù)實(shí)際上是一回事:
[javascript] view plain copy
  1. function foo() {  
  2.     if (false) {  
  3.         var x = 1;  
  4.     }  
  5.     return;  
  6.     var y = 1;  
  7. }  
  8. function foo() {  
  9.     var x, y;  
  10.     if (false) {  
  11.         x = 1;  
  12.     }  
  13.     return;  
  14.     y = 1;  
  15. }  
請(qǐng)注意,變量賦值并沒(méi)有被提升,只是聲明被提升了。但是,函數(shù)的聲明有點(diǎn)不一樣,函數(shù)體也會(huì)一同被提升。但是請(qǐng)注意,函數(shù)的聲明有兩種方式:
[javascript] view plain copy
  1. function test() {  
  2.     foo(); // TypeError "foo is not a function"  
  3.     bar(); // "this will run!"  
  4.     var foo = function () { // 變量指向函數(shù)表達(dá)式  
  5.         alert("this won't run!");  
  6.     }  
  7.     function bar() { // 函數(shù)聲明 函數(shù)名為bar  
  8.         alert("this will run!");  
  9.     }  
  10. }  
  11. test();  
這個(gè)例子里面,只有函數(shù)式的聲明才會(huì)連同函數(shù)體一起被提升。foo的聲明會(huì)被提升,但是它指向的函數(shù)體只會(huì)在執(zhí)行的時(shí)候才被賦值。

上面的東西涵蓋了提升的一些基本知識(shí),它們看起來(lái)也沒(méi)有那么迷惑。但是,在一些特殊場(chǎng)景,還是有一定的復(fù)雜度的。

變量解析順序

最需要牢記在心的是變量解析順序。記得我前面給出的命名進(jìn)入作用域的4種方式嗎?變量解析的順序就是我列出來(lái)的順序。一般來(lái)說(shuō),如果一個(gè)名稱已經(jīng)被定義,則不會(huì)被其他相同名稱的屬性覆蓋。這是說(shuō),(譯者沒(méi)理解這句,所以先做刪除樣式) 函數(shù)的聲明比變量的聲明具有高的優(yōu)先級(jí)這并不是說(shuō)給那個(gè)變量賦值不管用,而是聲明不會(huì)被忽略了。 (譯者注: 關(guān)于函數(shù)的聲明比變量的聲明具有高的優(yōu)先級(jí),下面的程序能幫助你理解)

[javascript] view plain copy
  1. <script>  
  2. function a(){     
  3. }  
  4. var a;  
  5. alert(a);//打印出a的函數(shù)體  
  6. </script>  
  7.   
  8. <script>  
  9.   
  10. var a;  
  11. function a(){     
  12. }  
  13. alert(a);//打印出a的函數(shù)體  
  14. </script>  
  15. //但是要注意區(qū)分和下面兩個(gè)寫(xiě)法的區(qū)別:  
  16. <script>  
  17. var a=1;  
  18. function a(){     
  19. }  
  20. alert(a);//打印出1  
  21. </script>  
  22.   
  23. <script>  
  24. function a(){     
  25. }  
  26.   
  27. var a=1;  
  28.   
  29. alert(a);//打印出1  
  30. </script>  

這里有3個(gè)例外:
1 內(nèi)置的名稱arguments表現(xiàn)得很奇怪,他看起來(lái)應(yīng)該是聲明在函數(shù)形式參數(shù)之后,但是卻在函數(shù)聲明之前。這是說(shuō),如果形參里面有arguments,它會(huì)比內(nèi)置的那個(gè)有優(yōu)先級(jí)。這是很不好的特性,所以要杜絕在形參里面使用arguments;
2 在任何地方定義this變量都會(huì)出語(yǔ)法錯(cuò)誤,這是個(gè)好特性;
3 如果多個(gè)形式參數(shù)擁有相同的名稱,最后的那個(gè)具有優(yōu)先級(jí),即便實(shí)際運(yùn)行的時(shí)候它的值是undefined;

命名函數(shù)

你可以給一個(gè)函數(shù)一個(gè)名字。如果這樣的話,它就不是一個(gè)函數(shù)聲明,同時(shí),函數(shù)體定義里面的指定的函數(shù)名( 如果有的話,如下面的spam, 譯者注)將不會(huì)被提升, 而是被忽略。這里一些代碼幫助你理解:

[javascript] view plain copy
  1. foo(); // TypeError "foo is not a function"  
  2. bar(); // valid  
  3. baz(); // TypeError "baz is not a function"  
  4. spam(); // ReferenceError "spam is not defined"  
  5.   
  6. var foo = function () {}; // foo指向匿名函數(shù)  
  7. function bar() {}; // 函數(shù)聲明  
  8. var baz = function spam() {}; // 命名函數(shù),只有baz被提升,spam不會(huì)被提升。  
  9.   
  10. foo(); // valid  
  11. bar(); // valid  
  12. baz(); // valid  
  13. spam(); // ReferenceError "spam is not defined"  

怎么寫(xiě)代碼

現(xiàn)在你理解了作用域和變量提升,那么這對(duì)于javascript編碼意味著什么?最重要的一點(diǎn)是,總是用var定義你的變量。而且我強(qiáng)烈推薦,對(duì)于一個(gè)名稱,在一個(gè)作用域里面永遠(yuǎn)只有一次var聲明。如果你這么做,你就不會(huì)遇到作用域和變量提升問(wèn)題。

語(yǔ)言規(guī)范怎么說(shuō)

我發(fā)現(xiàn)ECMAScript參考文檔總是很有用。下面是我找到的關(guān)于作用域和變量提升的部分:
如果變量在函數(shù)體類聲明,則它是函數(shù)作用域。否則,它是全局作用域(作為global的屬性)。變量將會(huì)在執(zhí)行進(jìn)入作用域的時(shí)候被創(chuàng)建。塊不會(huì)定義新的作用域,只有函數(shù)聲明和程序(譯者以為,就是全局性質(zhì)的代碼執(zhí)行)才會(huì)創(chuàng)造新的作用域。變量在創(chuàng)建的時(shí)候會(huì)被初始化為undefined。如果變量聲明語(yǔ)句里面帶有賦值操作,則賦值操作只有被執(zhí)行到的時(shí)候才會(huì)發(fā)生,而不是創(chuàng)建的時(shí)候。

我期待這篇文章會(huì)對(duì)那些對(duì)javascript比較迷惑的程序員帶來(lái)一絲光明。我自己也盡最大的可能去避免帶來(lái)更多的迷惑。如果我說(shuō)錯(cuò)了什么,或者忽略了什么,請(qǐng)告知。

譯者補(bǔ)充

xu281828044提醒了我發(fā)現(xiàn)了IE下全局作用域下命名函數(shù)的提升問(wèn)題:
我翻譯文章的時(shí)候是這么測(cè)試的:

  1. <script>  
  2. functiont(){  
  3. spam();  
  4. var baz = function spam() {alert('this is spam')};  
  5. }  
  6. t();  
  7. </script>  
這種寫(xiě)法, 即非全局作用域下的命名函數(shù)的提升,在ie和ff下表現(xiàn)是一致的. 我改成:
  1. <script>  
  2. spam();  
  3. var baz = function spam() {alert('this is spam')};  
  4. </script>  
則ie下是可以執(zhí)行spam的,ff下不可以. 說(shuō)明不同瀏覽器在處理這個(gè)細(xì)節(jié)上是有差別的.

這個(gè)問(wèn)題還引導(dǎo)我思考了另2個(gè)問(wèn)題,1:對(duì)于全局作用于范圍的變量,var與不var是有區(qū)別的. 沒(méi)有var的寫(xiě)法,其變量不會(huì)被提升。比如下面兩個(gè)程序,第二個(gè)會(huì)報(bào)錯(cuò):

  1. <script>  
  2. alert(a);  
  3. var a=1;  
  4. </script>  
  1. <script>  
  2. alert(a);  
  3. a=1;  
  4. </script>  

2: eval中創(chuàng)建的局部變量是不會(huì)被提升的(它也沒(méi)辦法做到).

  1. <script>  
  2. var a = 1;  
  3. function t(){  
  4.     alert(a);  
  5.     eval('var a = 2');  
  6.     alert(a);  
  7. }  
  8. t();  
  9. alert(a);  
  10. </script>  

原文地址:http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

轉(zhuǎn)載請(qǐng)注明來(lái)自: http://blog.csdn.net/sunxing007

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Javascript
JavaScript 之 作用域
Javascript中的變量以及如何定義靜態(tài)變量 | 盛夏蓮花
JavaScript執(zhí)行環(huán)境+變量對(duì)象+作用域鏈+閉包
javascript變量的作用域
每個(gè)前端開(kāi)發(fā)者必會(huì)的 20 個(gè) JavaScript 面試題 – 碼農(nóng)網(wǎng)
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服