博文

JavaScript 核心觀念(9)-JS回收機制

图片
 今天要來簡單講一下,關於JS的回收機制究竟是什麼東西 主要的概念會是記憶體的存放與釋放 執行環境中,會創造屬於它的記憶體空間,離開執行環境時,釋放記憶體。 *會在沒有任何物件使用到的時候,回收記憶體。 以下是依些小撇步 需要一直使用 → 放全域變數 函式fn()內需要而已 → 放區域變數 consoel.log也會占用記憶體空間哦 *Chrome小工具 F12開啟開發者工具 → 選擇Memory頁籤 → 按下Take heap snapshot按鈕 可以看網頁佔用了多少記憶體哦。 『如果你能做你擅長的事同時又快樂, 你在生命中以走的比大多數人還遠。』-李奧納多.狄卡皮歐

JavaScript 核心觀念(8)-執行緒與同步、非同步

图片
 嗨,大家好 我是Kris,今天要來分享關於JS的執行緒、事件佇列是如何進行的 那就開始吧~ 單執行緒:一次只能做一件事,做完才會做下一件事,JS就是如此,像是單工的感覺。 多執行緒:可以同時執行多個任務。 同步:依照程式碼依序執行fn() 非同步:針對程式語言本身,放到最後才做。 如同上面範例,doWork()會依序執行三個fn(),且都要等上一個完成才能進行下一個。 非同步:因應JS的某些程式的特性,能達成非同步的樣子。 如同上方範例的setTimeout的用意是隔3秒再執行,JS的處理方式是會先放置事件佇列,讓我們繼續瞭解事件佇列是什麼吧! 事件佇列:非同步行為先移到事件佇列,JS還是依照同步的概念在執行程式碼哦! 依照之前分享過的執行堆疊的概念,會先進入do work()、接著eatBreakfast()完成後離開堆疊,再執行callSomeone() 由於callSomeone內有setTimeout屬於非同步事件,會先放到事件佇列Event Queue,也就是圖片中的右邊方格,等待同步執行結束後再進行。 *注意setTimeout不管秒數設定幾秒都是這樣執行,就算是0秒也是哦* 非同步的例子 addEventListener是屬於監聽事件,那範例寫的意思是當有人點擊時,再去執行clickThis 跟setTimeout一樣是一個非同步事件,會先放到事件佇列等待被點擊的時候才會觸發。 補充 事件佇列有多個非同步事件時 *setTimeout有確定秒數,就可以知道誰先執行 *如果沒有確定,其結果會是隨機的,可以用Promise解決 *AJAX也是非同步事件,需要等待回傳資料 *監聽事件是等使用者觸發才會執行 *非同步的概念是因為JS在執行需要呼叫其他資源時,不能準確知道他的完成時間,所以才會先放到事件佇列。 『用現在的努力,換自己喜歡的人生』

JavaScript 核心觀念(7)-執行環境與作用域-提升Hoisting

图片
嗨,大家好嗎? 我是Kris 今天要來分享一個我個人認為很有趣的觀念 以下截至  MDN  說明 提升(Hoisting)是在 ECMAScript® 2015 Language Specification 裡面找不到的專有名詞。它是一種釐清 JaveScript 在執行階段內文如何運行的思路(尤其是在創建和執行階段)。然而,提升一詞可能會引起誤解:例如,提升看起來是單純地將變數和函式宣告,移動到程式的區塊頂端,然而並非如此。變數和函數的宣告會在編譯階段就被放入記憶體,但實際位置和程式碼中完全一樣。 所以提升只是一個用來釐清JS如何運行的觀念,瞭解JS創造階段、執行階段做了什麼 對程式碼的結果又有什麼影響呢? 首先 JS在函式、變數宣告時,會先建立一個記憶體空間來存放他們的key&value,如下圖 var   a  =  '1'; 在創造階段時建立a的儲存空間,但尚未賦予值,在執行階段時才會賦予值 所以可以將它看作這樣  ↓ //創造階段 var a; //執行階段 a = '1'; 函式陳述式在創造階段就會優先載入,並且將整個函式內容一起寫入 我們直接來看一個範例碼 * 函式比變數有更高的優先權 ,所以會比變數更優先被建立 * 所以,答案如下 console.log(myName);   //小明 在創造階段時,myName尚未被賦予值,在那個時候會儲存的值為undefined。 是因為JS必須告訴我們它已準備好記憶體空間,若沒有給值會出現Error。 因此! 上面的範例程式碼,並不會進入if條件式! If裡的 myName 在創造階段時值為 undefined,故判斷式不會執行 後面 myName 又被賦予“小明”,所以myName 的值會印出小明 *if(myName)  是在判斷myName是否為 falsy值,是的話不會執行,否的話才執行 myName是falsy值,所以不會進入if(myName)的判斷式裡。 *常見 falsy值有 false、null、undefined、NaN、 0、 -0、""、'' *參考範例: https://developer.mozilla.org/zh-CN/docs/Glossary/Falsy 額外補充 一.!是一個NOT語句,會將結果反轉

JavaScript 核心觀念(6)-執行環境與作用域-範圍鍊

图片
嗨~大家好,我是Kris 今天要來說明 為什麼明明宣告在外層的變數甚至是不同地方的函式,卻可以正確的呼叫與執行。 範圍鍊 函式裡面沒有需要的變數時,就會向外層尋找 變數在外層宣告,但內層卻可以取得 JavaScript是語法作用域,所以並不會與執行堆疊有任何關係。 因此fu1()不會因為他是在fu2()下被呼叫 就去尋找fu2()裡的var value = 2;     而是會去外層尋找外面的var value =1;。 範圍鍊真正的意思是「當函式本身沒有相對應的變數或函式時,就會向外層去尋找」 而這個尋找的過程就是【範圍鍊】,因此這邊與執行堆疊、執行環境不會有任何直接關係。 JavaScript 因為是語法作用域的關係,在程式碼撰寫完畢時,就已經決定了作 用域。 以下提供幾個範例,大家可以跟著做做看哦 不同情況下分別會列印出什麼呢? 答案放在最下方 1. 2. 3. 4. 5. 解答分隔線 ---------------------------------------------------------------------------------------------------------------------------------------------------------------- 1.  哈囉~老媽 2.  哈囉~老媽 3.  哈囉~漂亮阿姨 4.  哈囉~老爸 5.  哈囉~老媽 //1. 只執行sayHi(),sayHi()裡沒有person此變數,因此向外層尋找,找到全域變數的'老媽' 其他兩個fn裡的變數皆是區域變數,不影響sayHi() //2. doMorningWork()裡的變數為區域變數,不影響sayHi(); *函式沒有被呼叫就不會執行哦,所以meetAuntie()不會執行 //3. 執行meetAuntie()時內部有person,故印出'漂亮阿姨' 不被doMorningWork()的'老爸'影響 //4. 執行meetAuntie()時內部沒有person變數,所以向外層doMorningWork()尋找所以印出'老爸' //5. 執行meetAuntie()時內部沒有person變數,所以向外層doMorningWork()尋找也找不到時 就會再

JavaScript 核心觀念(5)-執行環境(Execution context)與執行堆疊(Execution stack)

图片
 嗨,大家今天開心嗎? 我是Kris,持續發筆記來到了第六篇,有慢慢習慣這個方式來幫助自己釐清觀念 可以在上課以後再進行複習一次,加深印象,有感受到自己在慢慢進步。 持之以恆,加油! 今天要來講執行環境&執行堆疊,這也是面試考題裡很常出的觀念題目哦! 執行環境(Execution context) 函式的作用域是限制在function內 (變數可以使用的範圍) 但是如果沒有執行函式的話是不會有任何變數產生的! 執行以後會產生一個執行環境以及限制作用域的功能。 函式撰寫時就確定作用域,執行它才能產生執行環境,這個執行環境才有屬於它的變數跟this。 this會隨著不同的執行環境改變(後面課程會再詳細介紹this) *函式呼叫以後才會產生執行環境,反覆呼叫就會一直產生新的執行環境* * 函式宣告的變數,只在函式內能被讀取,包括宣告在函式內的函式!!* global全域也有自己的全域執行環境 網頁一開啟 or 後端Node.js 一開啟,就會建立全域執行環境 同時會產生全域變數→ 瀏覽器:window;Node.js:global 也會有全域執行環境的this,內容會等於window或是global。 執行堆疊 Execution stack 用執行的順序來堆疊,跟函式宣告的順序沒有關係。 就是JS執行以後,哪個函式先運行,後運行的就一層一層向上堆疊,可以更加瞭解JS是如何運行的。 以下程式碼來當範例   網頁一開起就會建立全域執行環境 然後這段codec會先執行doSomething() → sayHi() 所以執行堆疊的順序如下方影片所示 3.sayHi()執行環境  A 2.doSomething()執行環境  B 1.全域執行環境 照數字123順序執行堆疊,照英文字母ABC離開執行環境,最後回到全域執行環境。 補充說明 如何用chrome開發者工具看執行堆疊以及目前作用域 開啟chrome → 執行程式 → 按下F12開啟開發者工具 → 點選console旁邊的sources頁籤 → 按下暫停鍵⏸️ → 按下F5重整網頁 → 按下一步逐步觀察JS執行堆疊跟作用域的變化。 call Stack:執行堆疊;Scope:目前作用域;(anonymous):代表全域執行環境 *在程式碼呼叫doSomething();的上一行加入debugger,可強制停在doSo

JavaScript 核心觀念(4)-變數作用域(Scope)

图片
嗨,大家好,我是Kris,今天要來講JS的小知識 不同作用域到底差在哪裡?在這一篇裡面跟大家介紹三種不同的作用域。 作用域(Scope) 如同上一篇JS的語法作用域裡面提到的- 連結按這裡 在 JavaScript 中,當一個變數被宣告的時候,它就開始有影響力,而它所影響到的地方,我們就稱之為作用域。 每一個變數都會有自己的作用域,當離開時它就不能作用了,而作用域取決於這個變數產生的地方跟方式。 作用域的等級 JavaScript 中的作用域分為三個等級 Global Level Scope Function Level Scope (Local Scope) Block Level Scope (ES6) Global Level Scope 全域作用域 在每個 JavaScript 環境中,都一定會有一個全域物件(全域環境),在 HTML 中我們稱之為 window。 在全域環境宣告的變數就稱之為全域變數,而它能影響的地方就是全域作用域。 全域變數的影響範圍非常廣大,不論是函式(function)內外,他的影響力遍布整個程式碼。 Function Level Scope (Local Scope)  區域作用域 在函式內被宣告的變數就稱為區域變數,所影響的地方就是區域作用域,也就是函式的大括號內,一旦離開 function 使用的話,就會出現 ReferenceError:xxx is not defined 未定義的錯誤訊息。 *在函式內,若變數沒有被宣告,就會被自動轉為全域變數 如上圖,b 在函式內沒有被宣告,所以變成全域變數,所以外面的 console.log 讀得到,而宣告過的 a 作用域只在test函式內作用 *如果在函式內外有相同的變數名稱,並且想要在函式內取得此變數的值,會優先使用區域變數的值,除非使用 window 才能去取得全域變數的值 Block Level Scope (ES6)  區塊作用域 是比區域更小的範圍,影響力只有變數被宣告的大括號和子區塊。 其實 function scope(區域作用域) 也可以被視為一種區塊作用域,在ES6之前的版本是不支持此區塊作用域的 在以前的版本使用var宣告變數時,是沒有區塊作用域的。 在 ES6 後才出現的 let、const ,就是支持區塊作用域,準確來說是他們只支持區塊作用域。 可以看到var宣

JavaScript 核心觀念(3)-的語法作用域(Lexical scope)

图片
 嗨大家好嗎? 我是Kris,今天要來跟大家講一個我覺得很重要的東西 算是解題時很常用到的基本觀念哦~ 相信大家應該多少都有聽過全域變數、區域變數吧 今天要來講的語法作用域就跟這個有關係 讓我們開始吧! JS是採用語法作用域,那什麼是語法作用域呢? 就是我們定義的變數能夠發揮功能的區域! 接下來可以跟著動手做做看 圖1 建立一個function 叫做callName 在函式內定義變數Ming並且給予'小明'的值 再用console.log列印出Ming的值。 在主程式呼叫callName(); //這樣callName這個function才會運行 會得到結果:上方圖1右方網頁上反白的資料(會印出小明) 接著假設我們把 console.log那一行移至呼叫callName(); 的下一行 如同下圖2 圖2 會得到結果:圖2右方網頁上寫著錯誤訊息-Ming is not defined(Ming未定義) 這是因為JS有語法作用域的關係 因此var Ming = '小明' 能夠發揮功能的區域只在callName這個function內(大括號{}裡面) 在外層是讀不到的,這就是因為作用域的關係~ 語法作用域也跟JS怎麼運行有很大的關係 講到語法作用域呢會牽扯到靜態作用域&動態作用域 靜態作用域也就是語法作用域(JS使用的) 指的是在語法解析什就確定能夠發揮功能的區域(就像上面例子寫callName函式時就確定了) 而動態作用域指的是在函式被調用的時候才會決定作用域 重點筆記👇 靜態作用域 變數的作用域在語法解析時,就已經確定作用域,並且不會改變。 動態作用域 變數的作用域在函式調用時才決定。 JS的作用域是一層一層向內的 最外層有一個全域的作用域,內層是由function所包住(全域作用域所定義的變數=全域變數) 如下圖,callMe和fn2就是兩個獨立的作用域 如果內層作用域有需要一些變數,但是此作用域內沒有這個變數的時候 就會向外查找,找到的話就會拿來使用 找不到時就會出現 變數is not defined 的錯誤訊息 接下來用程式碼來解釋一下靜態作用域&動態作用域 先將剛剛的code註解,再來打新的code 首先定義變數value = 1 再建立兩個function分別是fn1()和fn2() fn1()裡使用cons