顯示廣告
隱藏 ✕
看板 KnucklesNote
作者 Knuckles (站長 那克斯)
標題 [JS] 克服JS的奇怪部分 ch2 執行環境與詞彙環境
時間 2016-11-22 Tue. 07:50:38


Udemy課程: JavaScript全攻略:克服JS的奇怪部分
https://www.udemy.com/javascriptjs/learn/v4/overview
上完第二章的心得筆記

章節2 執行環境與詞彙環境 (Execution Contexts and Lexical Environments)


7. 名稱/值配對與物件

JS中的物件,就是一堆名稱/值的組合而已,不像其他語言的物件有一堆特性
var Address = {
	
Street: 'Main',
	
Number: 100,
	
Apartment:{
	
	
Floor: 3,
	
	
Number: 301
	
}
}
   
物件中也可以包含物件,像是上面的 Address 物件包含了一個 Apartment 物件


9. 全域環境與全域變數

JS一開始在還沒寫任何程式碼時,就會先自動建立一個全域的執行環境,以及一個 this 變數
在瀏覽器的執行環境下,會先建立個 window 的全域變數

全域(Global): 程式碼或變數不在一個函數裡,就是全域的

全域的變數或函數會被放在 window 物件之下
[圖]



10. 執行環境:創造與提升(Hoisting)

在同一個執行環境中,會先將使用到的變數存至記憶體,例如
b(); // 顯示 Called b!
console.log(a); // 顯示 undefined

var a = 'Hello World!';

function b() {
    console.log('Called b!');
}
函數b定義的內容會先存至記憶體,所以雖然定義寫在後面,但在前面也可以正確的呼叫

變數 a 的定義寫在後面,雖然可以呼叫,但卻顯示 undefined
(如果後面沒有 var a 的話,使用 console.log(a) 會出現警告: a is not defined)

在使用等號賦值的情況下,只會先將變數放至記憶體
再依執行順序將等號的值填進去

可以想像程式碼變成這樣
var a;

function b() {
    console.log('Called b!');
}

b(); // 顯示 Called b!
console.log(a); // 顯示 undefined

a = 'Hello World!';
var a 被移到最前面去了,而賦值的地方還是一樣在console.log(a)後面
而函數b是連內容都移到前面去了

但其實不是JS真的會先把程式碼改成這樣
而是執行的順序會變得像這樣


11. 觀念小叮嚀:JavaScript 與 undefined

undefined 是JS的一種特殊值,代表還沒定義

當使用 var a; 建立一個變數a,而沒有賦值時,a的值就是 undefined
可以用 === 來判斷 a 是否為 undefined
if(a === undefined){
	
console.log("a is undefined");
}else{
	
console.log("a is defined");
}

也可以用 a = undefined; 將 a 的值設定 undefined
但不建議這麼做,應該讓 undefined 就代表一個變數剛建立還沒賦值


15. 函數、環境與變數環境

在函數中的程式會建立一個新的執行環境

function b() {
	
var myVar;
	
console.log(myVar);
}

function a() {
	
var myVar = 2;
	
console.log(myVar);
	
b();
}

var myVar = 1;
console.log(myVar); // 顯示 1
a(); // 顯示 2 然後顯示 undefined
console.log(myVar); //顯示 1
上面的例子先顯示了全域變數 myVar = 1;
然後執行 a(),在函數a中有自己的執行環境,建立了一個函數a中的 myVar = 2;
接著在函數a中執行b(),在函數b中也建立一個 myVar = undefined
最後回到全域環境,此時顯示 myVar 的值還是一開始全域環境給的 1
不會被函數a或函數b給改掉

function b() {
	
//var myVar; //不使用 var 建立變數時
	
console.log(myVar);
}

function a() {
	
var myVar = 2;
	
console.log(myVar);
	
b();
}

var myVar = 1;
console.log(myVar); // 顯示 1
a(); // 顯示 2 然後顯示 1
console.log(myVar); //顯示 1
若是在函數b中,不使用 var 建立 myVar 的話
此時在函數b中的 console.log(myVar) 會顯示全域時給的 1
而不是函數a中給的2,為什麼呢?

當函數中使用到沒有定義的變數時,會接著到外部環境尋找
而函數b的外部環境,是依照函數定義的地方來決定,而不是執行的地方
函數b是在全域環境定義的,所以函數b的外部環境為全域環境,而不是函數a

這就是JS的詞彙環境(Lexical Environments) 特性

若將程式改為
function a() {
	
function b() {
	
	
//var myVar; //不使用 var 建立變數時
	
	
console.log(myVar);
	
}

	
var myVar = 2;
	
console.log(myVar);
	
b();
}

var myVar = 1;
console.log(myVar); // 顯示 1
a(); // 顯示 2 然後顯示 2
console.log(myVar); //顯示 1
此時函數b的定義寫在函數a的執行環境了
所以當呼叫函數b裡的 console.log(myVar); 時
因為函數b的執行環境沒有 var myVar
會到函數b的外部環境尋找,而函數b的外部環境為函數a的執行環境
所以會顯示在函數a裡設定的 2


18. 關於非同步回呼(Asynchronous Callbacks)

JS是一個單執行緒同步的程式,那是如何達到非同步執行的效果呢?

在瀏覽器中除了執行JS外,還有執行許多事件像是介面控制、網路傳輸等
只看JS本身的話是同步的,但其他事件是可以非同步執行的
當有其他事件發生,例如滑鼠點擊,需要呼叫JS程式的時候
會先將事件放在事件佇列(Event Queue)
當JS將執行環境的程式都完成後,就會從事件佇列取一個事件放進執行環境

以下程式測試看看當執行環境跑一個需要三秒的程式時,
點擊事件是否能在三秒結束前執行
// long running function
function waitThreeSeconds() {
    var ms = 3000 + new Date().getTime();
    while (new Date() < ms){}
    console.log('finished function');
}

function clickHandler() {
    console.log('click event!');   
}

// listen for the click event
document.addEventListener('click', clickHandler);

waitThreeSeconds();
console.log('finished execution');

執行此程式,並在 'finished function' 出現前點擊頁面
執行結果為
'finished function'
'finished execution'
'click event!'

點擊事件只會在 waitThreeSeconds() 執行完成
並且執行完全域的 console.log('finished execution');
之後才會發生




--
※ 作者: Knuckles 時間: 2016-11-22 07:50:38
※ 編輯: Knuckles 時間: 2016-11-23 00:53:49
※ 看板: KnucklesNote 文章推薦值: 4 目前人氣: 0 累積人氣: 5627 
分享網址: 複製 已複製
( ̄︶ ̄)b linja, lkw781030, smallfish 說讚!
r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇