javascript
谈javascript变量声明
本文同時發表在另一獨立博客上http://qingbob.com/blog/%E8%B0%88javascript%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E
這篇文章還是對基礎的復習,對面試經歷的一個總結。
之前的面試中遇到過一道面試題
var a =10;(function(){console.log(a);
var a =20;}
)()
- 短短5行代碼log的結果是什么?
- 如果把var a = 20;和console.log(a)語句順序對調呢?
這道題目的答案是undefined。不是10。
關鍵在于javascript的變量聲明有一個hoisting機制,變量聲明永遠都會被提升至作用域的最頂端(注意測試還只是聲明,還沒有賦值)。其實上面的語句相當于:
var a =10;(function(){
var a;//在這里對變量hoisting,先聲明
console.log(a); a =20;//再賦值
})()
?
再精簡一點:
bla =2var bla;
// 這是分割線,上下代碼的效果其實是一樣的
var bla; bla =2;
也就是先使用,再聲明(注意是聲明,還沒有賦值),這樣一來,聲明和賦值就被分開來了。所以最佳實踐都推薦最好在函數的頂端把需要使用的變量首先聲明一遍。
同理,我們可以理解下面的代碼也是會報錯的
f()//明顯這里有錯,因為f還沒有被賦一個函數var f =function(){console.log("Hello");
}
但有一個問題,如果將上例f的函數聲明修改一下,還會報錯嗎
f()//可以運行嗎?function f(){console.log("Hello");
}
這里我其實想強調的是兩種函數聲明的var f = function () {}和function f() {}差別。
事實上,javascript中所有的函數聲明(function declarations)和變量聲明(variable declarations)都會被提升(hoisted)至它們所在作用域的最頂端。需要注意的是函數聲明只有一種,也就是function f() {}的形式。而var f = function () {}是什么?你可以理解為它是將一個匿名函數(當然也可以取函數名,下面會解釋)賦值給了一個變量。
就哪上面兩個例子來說,同樣是想實現先使用再定義的效果。只有第二種有用,雖然函數f在使用之后才定義,但是在javascript解釋器中,它仍然是先于執行語句被定義的。
而第一個例子,執行的效果是這樣的
var f; f()//沒有定義任何函數,當然無法執行 f =function(){console.log("Hello");}
這么看來,雖然javascript是允許先執行再聲明,但切勿這么做,請遵循先聲明再使用的好習慣。
再看看另一種情況,如果我把之前的函數定義
var f =function(){};答案是否定的,因為上面的代碼對f函數的定義是以命名函數表達式(Named Function Expressions),而并非真正的函數聲明,注意該函數名只在該函數的作用域內有用。下面這段代碼充分說明了它的意義:
var f =function foo(){return typeof foo;
};
typeof foo;// "undefined" f();// "function"
?
那么如此聲明還有什么意義呢?好吧,就我目前找到的資料而言,這樣做的好處就是便于調試。
接下來考慮一些意想不到的邊緣,雖然我覺得一個程序員寫出下面的代碼有點自找苦吃,而且應該是在實戰中避免的,但作為考試的題目來說是值得一說的。比如對比下面兩段代碼:
function value(){return 1;
}
var value; alert(typeof value);//"function"
function value(){
return 1;
}
var value =1; alert(typeof value);//"number"
第一段代碼想說明的是函數聲明會覆蓋變量聲明,注意是聲明,還沒有賦值。如代碼中,雖然同名變量在函數后再次聲明,但是typeof的結果仍然是function
第二段代碼想說明的是函數聲明不會覆蓋變量賦值或者說初始化,如代碼所示
Name Resolution Order
為什么會有上面的結果,為什么函數的聲明會覆蓋變量的聲明。就是因為name resolution order。我不知道怎么翻譯這個名詞,暫且就翻譯為名稱解析順序吧。
在javascript中,一個變量名(name)有四種方式進入作用域(scope)中
- 語言內置,所有的作用域中都有this和arguments關鍵字
- 形式參數,函數的參數在整個作用域中都是有效的
- 函數聲明
- 變量聲明
上面列出的四種順序也正是由高到底的優先級的順序(關于這點我有所保留,我測試的結果是參數和函數的優先級都會比語言內置的優先級高,你可以把形式參數取名為arguments,或者定義一個函數名為arguments,結果內置的argument說被覆蓋了),一旦一個變量名已經聲明了,那么它就不可能被其他更低優先級的變量聲明形式所覆蓋。
參考文章:
- Named function expressions demystified
- function-declarations-vs-function-expressions
- JavaScript Scoping and Hoisting
- Answering Baranovskiy’s JavaScript quiz
轉載于:https://www.cnblogs.com/hh54188/archive/2013/05/12/3074358.html
總結
以上是生活随笔為你收集整理的谈javascript变量声明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小技巧之chm文件无法显示
- 下一篇: 建立二叉树A【openjudge】