提升(Hoisting)
提升(Hoisting)
在執行任何程式碼前,JavaScript 會把函式宣告放進記憶體裡面,這樣做的優點是:可以在程式碼宣告該函式之前使用它
個人一直覺得 hoisting 是一種非常不直覺的特性,因為一般在寫程式的時候,都會事先定義好變數或函式才去使用它,如果在尚未定義的情況下,直接去使用這個變數或函式,通常都會出現錯誤訊息!
然而,在 JavaScript 中有一個蠻特別的概念是hoisting,讓你可以在程式碼宣告該變數或函式之前使用它。不過只有用 var 宣告的變數才有 hoisting,而 let 則沒有!
舉例來說:若你試圖在宣告 var 變數前印出該變數,這是可以的,而且不會報錯,只是會被告知 undefined。但如果是使用 let 宣告的變數,若在宣告前使用,會得到一個 no defined 的 error。
雖然 undefined 與 not defined,中文都可以翻成是未定義或無定義。不過在 JavaScript 中,代表的意思有些不同。在編譯階段,JavaScript 會預留記憶體給變數,執行時才會將變數內容指進記憶體中。如果是留了記憶體,但是未被賦值,因此會印出變數的值是 undefined。但若得到的是 not defined ,則是代表這個東西並沒有被定義其存在,因此沒有幫它留記憶體的意思。
作用域
var 作用域是 function scope,而 let 作用域是 block。
當使用 var 來宣告迴圈變數 i 時,會輸出 10次 這執行第10次,而非預期中 這執行第0~9次。
這原因主要作作用域問題,以這個 case 來說,i 並不屬於 for 迴圈的區域變數,而是全域變數(因為外面沒有包 function,因此直接掛到 window 下面了…)。等到 for 迴圈結束後,會從事件佇列中依序執行 setTimeout 中的 function 內容。此時 function 會去找 i 來印出,它只能找到全域變數下 i ,而 i 在迴圈結束後被設成了 10 ,因此會印出 10 次這執行第10次。
而若是用 let 宣告變數,它的作用域是 block,因此每一次 for 迴圈執行 setTimeout 中的 function 都引用到 for 這個 block 作用域下的 i。不過需要注意的是,因為這樣的引用關係,這些變數 i 所佔的記憶體,在 setTimeout 未執行前都不會被釋放。
詳細內容可以看看這篇文章。
重複命名
var 同個區塊上重複命名沒關係,而 let 同個區塊上不能重複命名。
const 是宣告常數用,一旦宣告就不能更改。
不過若是宣告物件,由於物件本身的記錄方式是記錄參考值,因此物件內的屬性是可以被修改,但是重新指定一個新的物件給常數的話還是會跳錯。
答:用立即函式傳入 i ,製造一個更小的作用域,讓 console.log 引用。
用 ...
將陣列中的值一個個取出來再 return 回去
JavaScript 的物件或是陣列的儲存方式是記錄記憶體位置,因此當將 groupA 指給 groupB 對 groupB 進行賦值時,是使用淺複製的方式將 groupA 所記錄的記憶體位置傳給 groupB ,最終會導致當對 groupB 進行操作時, groupA 也會後受到影響,即下圖左。
為了避免這種影響到互相影響發生,可以改採深複製的方式(下圖右),借助上一節所提到的展開語法,將 groupA 值一一取出後,放入一個新的陣列中,處理物件也是相同宣告方法。
不過我個人偏好使用 lodash 函式庫的 cloneDeep,比較直覺,尤其當你的變數存在物件中包物件的情況時。
Shallow and Deep Copying (圖片來源:在使用 Node.childNodes 或 document.querySelectorAll 時會回傳包含指定節點的子節點的集合,此集合稱為 Node List ,是種類似陣列的資料結構,但是不能使用陣列的部份方法,因此又被稱為 類陣列。
若要將 Node List 轉成陣列,一樣是使用展開語法
就是不定長度參數的宣告,在 Python 中是使用 *args
、Hava 中是使用 int... nums
,而在 JavaScript 中的宣告有點類似 Java 是使用 ...nums
,傳入的結果會是一個名為 nums 的陣列。
跟其他語言的不定參數一樣需遵守兩個規定:
JavaScript 還有一個(個人認為)比較神奇的特性,如果你傳入參數多於你宣告的個數,多餘的部份會變成名為 arguments 的類陣列物件,我之前學的語言多直接報錯的說…
解構全名為解構賦值,其概念是將右方資料鏡射到左方。下面舉些應用情境:
若要捨棄或略過陣列中部份的值
如果要拆成字元陣列
可先給予各變數一個預設值,若在進行解構賦值時,沒有傳入值,會直接採用預設值。
應用情境:
當屬性,也就是要傳數的變數名稱,與 key 值名稱相同時可省略,只寫一個即可。
這種做法在使用 CLI 建構 project 時很常被使用到,例如引用 router 時,將 import 命名直接命名 router,在 new Vue 元件時,就不需要寫成 router:router
,直接使用 router 即可:
下列兩段 function 的是相同的
在展開與其餘參數中說明不定長度參數時,有提過一個(個人認為)JS 比較神奇的特性,就是如果你傳入參數多於你宣告的個數,多餘的部份會變成名為 arguments 的類陣列物件。
不過,這件事情在使用箭頭函式時是不成立的,你會直接得到一個 error。
Uncaught ReferenceError: arguments is not defined
at updateEasyCard (arrow_function.html:118)
at arrow_function.html:120
另外一點需要注意的是,兩種的函式所綁定的 this 是不同的,
超級有點難懂,所以後來找了相關的資料來看 鐵人賽:箭頭函式 (Arrow functions) | 卡斯伯 Blog - 前端,沒有極限
PS. 我記得這是講師的網誌!?
使用反引號加錢字號 $,就可以在 string 中引用變數,避免做字串串接。
大括號的範圍內,除了引用變數,也可以直接引用 JavaScript code。
forEach 顧名思義是對陣列的每個元素做操作,它是一個 in-place 的方法,不會回傳新的值,直接在原陣列上做修改。是說實做上很少傳那的 array。
這個方法會建立一個新的陣列,其內容為原陣列的每個元素經由函式運算後所回傳的結果之集合。
由原陣列中濾出符合條件的元素所構成的新陣列。
只會回傳第一個滿足所提供條件的元素的值。
它會檢查陣列中,是否所有元素皆符合所提供條件。
它會檢查陣列中,是否存在任意元素符合所提供條件。
這是一個累加器,它會將陣列中每項元素由左至右送入函數進行運算後回傳單一值。
也可以拿來做比較器
問題 1: const 是宣告一個常數,宣告後的變數不能隨意修改。 但是 const 所宣告的如果是物件變數,則物件內的屬性可隨意修改?
[V] 1. 對
[ ] 2. 不對,就說不能亂改了,還想挑戰我
問題 2: var 與 let 的作用域分別為何?
[V] 1. var 是 function; let 是 block "{}"。
[V] 2. var 是 block "{}"; let 是 function。
[V] 2. var 是全域宣告; let 是 block "{}"。
問題 3:在物件中使用縮寫的 function 如下: 此時的 aa.doSomething() 等同於?
[V] 1. 傳統 function 函式
[ ] 2. 箭頭函式
[ ] 3. 傳統 function 與 箭頭函式 是一樣的,所以是沒差的
問題 4:使用 ES6 時不需要在意瀏覽器的支援性,用就對了
[ ] 1. 對。
[V] 2. 錯,還是要注意一下能使用的瀏覽器,或者使用編譯工具(如: Babel)編譯。
問題 5:陣列方法中的 forEach 基本上要搭配 jQuery 或 Vue 才能使用。
[ ] 1.對,這是框架所提供的方法。
[V] 2.不對,現在 JavaScript 已經可以使用這類型的陣列方法。
本文作者: 辛西亞.Cynthia
本文連結: 辛西亞的技能樹 / hackmd 版本
版權聲明: 部落格中所有文章,均採用 姓名標示-非商業性-相同方式分享 4.0 國際 (CC BY-NC-SA 4.0) 許可協議。轉載請標明作者、連結與出處!