亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

深入理解Javascript作用域與變量提升

2019-11-20 21:30:21
字體:
來源:轉載
供稿:網友

下面的程序是什么結果?

復制代碼 代碼如下:

var foo = 1;
function bar() {
 if (!foo) {
  var foo = 10;
 }
 alert(foo);
}
bar();

結果是10;

那么下面這個呢?

復制代碼 代碼如下:

var a = 1;
function b() {
 a = 10;
 return;
 function a() {}
}
b();
alert(a);

結果是1.

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

Javascript的作用域

對于Javascript初學者來說,一個最迷惑的地方就是作用域;事實上,不光是初學者。我就見過一些有經驗的javascript程序員,但他們對scope理解不深。javascript作用域之所以迷惑,是因為它程序語法本身長的像C家族的語言,像下面的C程序:

復制代碼 代碼如下:

#include <stdio.h>
int main() {
 int x = 1;
 printf("%d, ", x); // 1
 if (1) {
  int x = 2;
  printf("%d, ", x); // 2
 }
 printf("%d/n", x); // 1
}

輸出結果是1 2 1,這是因為C家族的語言有塊作用域,當程序控制走進一個塊,比如if塊,只作用于該塊的變量可以被聲明,而不會影響塊外面的作用域。但是在Javascript里面,這樣不行。看看下面的代碼:
復制代碼 代碼如下:

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

結果會是1 2 2。因為javascript是函數作用域。這是和c家族語言最大的不同。該程序里面的if并不會創建新的作用域。

對于很多C,c++,java程序員來說,這不是他們期望和歡迎的。幸運的是,基于javascript函數的靈活性,這里有可變通的地方。如果你必須創建臨時的作用域,可以像下面這樣:

復制代碼 代碼如下:

function foo() {
 var x = 1;
 if (x) {
  (function () {
   var x = 2;
   // some other code
  }());
 }
 // x is still 1.
}

這種方法很靈活,可以用在任何你想創建臨時的作用域的地方。不光是塊內。但是,我強烈推薦你花點時間理解javascript的作用域。它很有用,是我最喜歡的javascript特性之一。如果你理解了作用域,那么變量提升就對你顯得更有意義。

變量聲明,命名,和提升

在javascript,變量有4種基本方式進入作用域:

•1 語言內置:所有的作用域里都有this和arguments;(譯者注:經過測試arguments在全局作用域是不可見的)

•2 形式參數:函數的形式參數會作為函數體作用域的一部分;

•3 函數聲明:像這種形式:function foo(){};

•4 變量聲明:像這樣:var foo;

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

復制代碼 代碼如下:

function foo() {
 bar();
 var x = 1;
}

實際上會被解釋成:
復制代碼 代碼如下:

function foo() {
 var x;
 bar();
 x = 1;
}

無論定義該變量的塊是否能被執行。下面的兩個函數實際上是一回事:
復制代碼 代碼如下:

function foo() {
 if (false) {
  var x = 1;
 }
 return;
 var y = 1;
}
function foo() {
 var x, y;
 if (false) {
  x = 1;
 }
 return;
 y = 1;
}

請注意,變量賦值并沒有被提升,只是聲明被提升了。但是,函數的聲明有點不一樣,函數體也會一同被提升。但是請注意,函數的聲明有兩種方式:
復制代碼 代碼如下:

function test() {
 foo(); // TypeError "foo is not a function"
 bar(); // "this will run!"
 var foo = function () { // 變量指向函數表達式
  alert("this won't run!");
 }
 function bar() { // 函數聲明 函數名為bar
  alert("this will run!");
 }
}
test();

這個例子里面,只有函數式的聲明才會連同函數體一起被提升。foo的聲明會被提升,但是它指向的函數體只會在執行的時候才被賦值。

上面的東西涵蓋了提升的一些基本知識,它們看起來也沒有那么迷惑。但是,在一些特殊場景,還是有一定的復雜度的。

變量解析順序

最需要牢記在心的是變量解析順序。記得我前面給出的命名進入作用域的4種方式嗎?變量解析的順序就是我列出來的順序。

復制代碼 代碼如下:

<script>
function a(){ 
}
var a;
alert(a);//打印出a的函數體
</script>

<script>

var a;
function a(){ 
}
alert(a);//打印出a的函數體
</script>
//但是要注意區分和下面兩個寫法的區別:
<script>
var a=1;
function a(){ 
}
alert(a);//打印出1
</script>

<script>
function a(){ 
}

var a=1;

alert(a);//打印出1
</script>


這里有3個例外:

1 內置的名稱arguments表現得很奇怪,他看起來應該是聲明在函數形式參數之后,但是卻在函數聲明之前。這是說,如果形參里面有arguments,它會比內置的那個有優先級。這是很不好的特性,所以要杜絕在形參里面使用arguments;

2 在任何地方定義this變量都會出語法錯誤,這是個好特性;

3 如果多個形式參數擁有相同的名稱,最后的那個具有優先級,即便實際運行的時候它的值是undefined;

命名函數

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

復制代碼 代碼如下:

foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
spam(); // ReferenceError "spam is not defined"

var foo = function () {}; // foo指向匿名函數
function bar() {}; // 函數聲明
var baz = function spam() {}; // 命名函數,只有baz被提升,spam不會被提升。

foo(); // valid
bar(); // valid
baz(); // valid
spam(); // ReferenceError "spam is not defined"


怎么寫代碼

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

語言規范怎么說

我發現ECMAScript參考文檔總是很有用。下面是我找到的關于作用域和變量提升的部分:

如果變量在函數體類聲明,則它是函數作用域。否則,它是全局作用域(作為global的屬性)。變量將會在執行進入作用域的時候被創建。塊不會定義新的作用域,只有函數聲明和程序(譯者以為,就是全局性質的代碼執行)才會創造新的作用域。變量在創建的時候會被初始化為undefined。如果變量聲明語句里面帶有賦值操作,則賦值操作只有被執行到的時候才會發生,而不是創建的時候。

我期待這篇文章會對那些對javascript比較迷惑的程序員帶來一絲光明。我自己也盡最大的可能去避免帶來更多的迷惑。如果我說錯了什么,或者忽略了什么,請告知。

譯者補充

有位朋友提醒了我發現了IE下全局作用域下命名函數的提升問題:

我翻譯文章的時候是這么測試的:

復制代碼 代碼如下:

<script>
functiont(){
spam();
var baz = function spam() {alert('this is spam')};
}
t();
</script>

這種寫法, 即非全局作用域下的命名函數的提升,在ie和ff下表現是一致的. 我改成:
復制代碼 代碼如下:

<script>
spam();
var baz = function spam() {alert('this is spam')};
</script>

則ie下是可以執行spam的,ff下不可以. 說明不同瀏覽器在處理這個細節上是有差別的.

這個問題還引導我思考了另2個問題,1:對于全局作用于范圍的變量,var與不var是有區別的. 沒有var的寫法,其變量不會被提升。比如下面兩個程序,第二個會報錯:

復制代碼 代碼如下:

<script>
alert(a);
var a=1;
</script>

復制代碼 代碼如下:

<script>
alert(a);
a=1;
</script>

2: eval中創建的局部變量是不會被提升的(它也沒辦法做到).
復制代碼 代碼如下:

<script>
var a = 1;
function t(){
 alert(a);
 eval('var a = 2');
 alert(a);
}
t();
alert(a);
</script>

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲的天堂在线中文字幕| 激情懂色av一区av二区av| 欧美日韩国产激情| 亚洲国产小视频| 国产最新精品视频| 美女视频黄免费的亚洲男人天堂| 2023亚洲男人天堂| 亚洲美女av网站| 亚洲欧美日韩天堂一区二区| 亚洲国产精久久久久久久| 清纯唯美亚洲激情| 欧美大片在线影院| 亚洲黄色成人网| 亚洲精品在线不卡| 亚洲欧美日韩一区二区在线| 成人av在线网址| 国产91精品久久久久久| 成人www视频在线观看| 欧美激情久久久| 国产精品偷伦免费视频观看的| 欧美午夜女人视频在线| 日韩一区二区av| 欧美黑人视频一区| 欧美一级免费看| 国产精品av免费在线观看| 亚洲欧美在线免费| 中文欧美在线视频| 国产剧情久久久久久| 中文字幕亚洲欧美日韩在线不卡| 久久久久久久久久久av| 91在线观看免费高清| 日韩网站免费观看| 亚洲精品电影网站| 国产精品美女呻吟| 国产视频福利一区| 国产91精品视频在线观看| 亚洲香蕉成视频在线观看| 国产精品久久久久久久久粉嫩av| 久久免费高清视频| 久久久久久久久爱| 亚洲男人av在线| 亚洲欧美色婷婷| 国产精品大片wwwwww| 欧美在线视频a| 日韩av黄色在线观看| 欧美日韩在线一区| 日韩一级黄色av| 这里只有视频精品| 国产成人免费av| 国产亚洲欧美视频| 色狠狠久久aa北条麻妃| 成人国产精品免费视频| 国产热re99久久6国产精品| 黑人巨大精品欧美一区二区三区| 高清欧美性猛交xxxx黑人猛交| 亚洲成av人影院在线观看| 日产精品久久久一区二区福利| 国产精品一区二区三区久久久| 久久久久久18| 欧美日韩国产中文精品字幕自在自线| 国产精品久久久久久久美男| 欧美激情二区三区| 成人国产精品av| 青草热久免费精品视频| 大量国产精品视频| 国产免费一区二区三区香蕉精| 欧美国产第二页| 国产999精品久久久影片官网| 中文字幕视频一区二区在线有码| 国产成人欧美在线观看| 久久久之久亚州精品露出| 欧美多人爱爱视频网站| 日韩大陆毛片av| 国产视频精品一区二区三区| 亚洲2020天天堂在线观看| 亚洲999一在线观看www| 欧美刺激性大交免费视频| 成人欧美一区二区三区在线湿哒哒| 亚洲精品一区二三区不卡| 久久影院资源站| 久久久噜噜噜久噜久久| 亚洲欧美日韩另类| 成人黄色免费网站在线观看| 日本中文字幕久久看| 91成人天堂久久成人| 精品视频久久久久久久| 亚洲free性xxxx护士白浆| 久久免费高清视频| 成人国产精品av| 国产精品欧美日韩久久| 国产精品免费视频xxxx| 成人夜晚看av| 国产精品都在这里| 久久影院资源站| 国产精品美女www| 国产综合在线视频| 欧美日韩加勒比精品一区| 国产精品青草久久久久福利99| 国产一区二区三区丝袜| 韩国三级电影久久久久久| 韩曰欧美视频免费观看| 久久久久久亚洲精品中文字幕| 欧美成人免费网| 97在线观看视频国产| 国产精品白嫩初高中害羞小美女| 欧美成人合集magnet| 亚洲国产精品大全| 亚洲嫩模很污视频| 日韩精品在线观看网站| 欧美乱大交xxxxx| 97久久精品国产| 午夜精品三级视频福利| 国产精品久久一区主播| 尤物精品国产第一福利三区| 欧美日韩亚洲91| 精品一区二区三区四区在线| 久久久精品视频成人| 国产精品中文久久久久久久| 色黄久久久久久| 国产精品一区二区久久精品| 日韩大片免费观看视频播放| 国产精品爱久久久久久久| 欧美亚洲激情视频| 亚洲第一精品夜夜躁人人躁| 亚洲国产成人av在线| 久久久免费av| 国产精品人人做人人爽| 亚洲欧美中文日韩v在线观看| 欧美最猛性xxxxx免费| 精品国产欧美一区二区三区成人| 久久久久久高潮国产精品视| 亚洲国产私拍精品国模在线观看| 在线精品国产欧美| 91久久国产精品| 欧美激情女人20p| 色老头一区二区三区在线观看| 国内精品久久影院| 不卡av在线播放| 国产亚洲精品久久久久久777| 欧美性xxxx极品hd欧美风情| 国产精品扒开腿做爽爽爽视频| 精品视频在线导航| 欧美日韩国内自拍| 国产精品欧美激情| 欧美国产日韩二区| 欧美精品www在线观看| 欧美一级高清免费| 亚洲男人天堂古典| 精品亚洲一区二区三区四区五区| 亚洲视频在线观看免费| 欧美在线视频一区二区| 亚洲欧美一区二区三区在线| 国产精品91免费在线| 亚洲精品免费一区二区三区| 青青青国产精品一区二区| 欧美高清在线观看| 成人福利网站在线观看| 日韩精品久久久久| 亚洲精品自拍视频| 国产丝袜一区二区| 91沈先生作品| 国产精品美腿一区在线看| 91av福利视频| 国产成人精品久久二区二区|