function w() {console.log(w)// 可以訪問w函數本身}

w()

var obj = {"abc":obj //無法訪問本身 為 undefined }

二者都是對象,為啥{}為啥 無法訪問本身么?


因為函數是延遲執行的。

function w() {console.log(w) }
w() // 只有在你調用 w() 的時候,才會去執行 console.log(w)

由於只有在你調用 w() 的時候,才會去執行 console.log(w),所以在時間上,你是先定義 w 再使用 w 的。

但是對象定義就不同了

var obj = {"abc":obj /*立即訪問 obj*/ }

由於你在 obj 還沒有被賦值完,就開始立即訪問 obj 了,所以實際上,你是先使用 obj 再給 obj 賦值的。那麼 "abc" 對應的 obj 實際上是 undefined(因為此時還沒給 obj 賦值)。

其實這是個好問題,大部分前端都遇到過這個問題。

完。


在函數執行前,會有執行上下文創建的過程,創建過程中會將變數定義根據變數定義的方式(var,function,let,const)的不同將變數放在不同的區域里。而用var和function的定義方式存在變數提升特性。

執行上下文中包括變數環境,詞法環境,this,可執行代碼

第一個例子,有兩個執行上下文

function w() {console.log(w) }
w()

全局上下文

變數環境:

w -&> function 可執行代碼:w()w函數上下文變數環境:空可執行代碼:console.log(w)當console.log(w)訪問w時先從當前的執行上下文查找w,沒有去外部上下文查找,因為js是詞法作用域(又稱定義時上下文)的,所有w函數執行上下文的外部上下文會指向全局上下文,所以列印的w是來自於全局上下文的

第二個例子

var obj = {"abc":obj }

全局上下文:

變數環境:obj -&> undefined // 變數提升可執行代碼:obj = { abc: obj}

當代碼執行時,先運行=右邊的代碼,{ abc: undefined },然後將這個對象的地址存放在obj中,所以就得到了undefined的結果


很正常啊,除開下面的函數/變數聲名提升到函數頂端(覆蓋下層作用域)

在『靜態語言』里也一樣的,雖說 obj.x = obj 這種遞歸數據不是不可以構造,但基本上在 var initializer 里都是不能訪問到正賦值的變數的(不然 var a = a + 1 怎麼辦)

func 定義體里就不一樣了,且不說 JS 語義上是先解析符號所引用的函數值再執行調用(而不是早就靜態存好了全稱名字)所以 func f() 的體里的 f() 調用因為不是先於 f 的定義執行所以(遞歸)使用絕無問題,就是需要靜態解析符號的語言,如果不能在體里引用函數自身,也就寫不了遞歸程序了(只能提前聲明好兩個函數環狀互調真彆扭),上個世紀的某ALGOL版本或許不支持,哈哈。

所謂函數也是對象主要是種概念,不代表語法上毫無區別的,函數體顯然不會立刻執行。


這個是因為javascript的一個特性,變數提升。

什麼是變數提升

因為變數申明是在任意代碼執行前處理的,在代碼區中任意地方申明變數和在最開始(最上面)的地方申明是一樣的。也就是說,看起來一個變數可以在申明之前被使用!這種行為就是所謂的「hoisting」,也就是變數提升,看起來就像變數的申明被自動移動到了函數或全局代碼的最頂上。

變數提升典型例子

var tmp = "tmp";
function f() {
console.log(tmp);
if(false){
var tmp = "false";
}
}

由於變數提升,真實的日誌列印是false,不是tmp。

回答題主的問題

函數定義w提升,當執行w()的之後,w已經定義了。所以可以直接使用。

對於var obj,obj提升為undefined,代碼變成:

var obj=undefined;
obj = {"abc":obj };

雖然obj為undefined,但Chrome 並沒有報錯。此處和樓主的問題不一致。不知道是不是用的其他瀏覽器。


聲明方式不同,var obj是聲明obj變數;而function w(){}是聲明一個w函數;

然而執行var obj = {"abc":obj }時,對象{"abc":obj }中的obj變數還未定義完,即是undefined。

而聲明函數,聲明完不會立刻執行,需要等待w()


推薦閱讀:
相关文章