JavaScript 觀念整理
塊級作用域
JavaScript 在 ES6 新增了塊級作用域,而盡可能不使用全域作用域。
優缺點如下:
全域作用域:
- 削弱了程式的彈性,盡量避免使用
塊級作用域:
1.用大括號 {} 的情況下成立,沒有的話會報錯
2.外層無法讀取內層作用域的變數
變數宣告
ES5 var
1.函式作用域(function scope)。
2.有變數提升(hoisting),就是在宣告之前可以使用,但值為 undefined。
1 | console.log(a); //undefined |
3.透過 var 宣告 變數 i,在全域範圍內都有效,所以每次的循環,新的值 i 都會覆蓋掉舊值,導致輸出有問題。
1 | for (var i = 0; i < 5; ++i) { |
4.區塊語句(if、else、 for、 while等)裡面用 var 宣告的變數,會洩漏到全域中。
ES6 let 與 const
1.不可重複宣告。
2.只在宣告的塊級作用域內有效,只要是被所屬{ }包起來的let變數,都不會跑到外面(全域)。
3.不存在變數提升,只能宣告後使用。
4.若在定義變數之前使用該變數則會拋出 ReferenceError 錯誤。
1 | p = 3; // ReferenceError |
let 變數
1.變數值會產生變動。
2.let 強調在 {} 的區塊執行環境,每次 let 都會產生一個新的作用域。
3.會在每次迭代時重新宣告變數,並將上一次迭代的結果作為這一次的初始值。
迭代:電腦程式雖然亦有反覆運算的含義,但一般指的是迴圈解。其目的通常是為了接近所需求的目標或結果。每一次對過程的重複被稱為一次”迭代”,每一次迭代得到的結果通常會被用來作為下一次迭代的初始值。
遞迴:通常指函式推疊呼叫。
以下情況會因為 let 特性,重新綁定值,產生正確的輸出
1 | for (let i = 0; i < 5; ++i) { |
1 | 可以看做 |
const 常數
1.宣告時就立即給予值,不可之後再改變值(修改會報錯)。
實際上並不是變數的值不能改變,而是變數指向的內存位址不能被改變。
1 | const a = []; |
上述例子中,因為在 JS 中陣列(array)和物件(object)都是屬於 reference type,因此實際上我們並沒有把這個常數指向(pointer)另一個東西,它仍然指稱到的是同一個記憶體位置。
2.可以用來宣告常數。為了方便區分哪些是常數那寫是變數,我們可以把常數在宣告的時候用大寫來表示。
Function(函式)
函式指的是將一或多段程式指令包裝起來,可以重複使用,也方便維護。而函式通常會有回傳值,使用 return 作為回傳值輸出,如果 return 後面還有程式碼,則不會被執行。因此,用 return 回傳空值也具有「中止」程式碼的功能。並非每種函式都有回傳值的需要,也有可能透過輸出的方式來將結果輸出。
函式是物件的一種,雖然用 typeof 去檢查的時候,得到 “function” 的結果,,但實際上它仍屬於 Object 的一種。
函式組成
1.函式的名稱(也可能沒有名稱)
2.在括號 ( ) 中的部分,稱為「參數 (arguments) 」,參數與參數之間會用逗號 , 隔開
3.在大括號 { } 內的部分,內含需要重複執行的內容,是函式功能的主要區塊。
Ps. arguments 是 JavaScript 預設的參數,可直接帶入,這種參數不須預先設定,所有函式都內建此參數,他會將呼叫函式所帶入的參數一並透過陣列的方式傳入。但 arguments 是類陣列,無法使用許多陣列相關的方式。
e.g.
1 | const originCash = 1000; |
語法:
function name(){}
函式(Function) 分成 Function Statement(函式陳述句)與Function Expression(函式表達式、表示式)。
Function Statement(函式陳述句)
程式碼的單位,這段程式碼不會產生一個值。
e.g.
1 | function greet(name){ |
Function Expression(函式表達式、表示式)。
會產生(回傳)一個值,但值不一定會被賦予變數。
e.g.
1 | a = 3; // console.log 會回傳 3 |
有時函式需要一些特定資訊才能完成它的工作,因此宣告函式時需要再給予它參數。在這樣的函式中,參數的功能就有如變數。參數放置的位置,在函式名稱後面的小括號裡。參數可一至多個。
函式也可以當作參數。
e.g.
1 | function calc(a,b) { |
有加上名稱的函式
函式呼叫方式是: 函式名稱加上括號(),以及在括號中傳入對應的參數值,即可呼叫這個函式執行。例如:
1 | function calc(a,b) { |
匿名函式
通常會另外指定給一個變數/常數,被指定後這些變數/常數就成了函式的名稱。通常用在:
1.當作其他函式的值傳入參數
2.一次性執行
語法:
function() {}
1 | // 函式表達式(FE) |
立即函式(IIFE)
立即執行程式碼,省略多餘的呼叫,用來分隔作用範圍,還可以用來避免汙染全域執行環境的東西,減少開發時因相同命名相衝的 bug。而且每次立即函式的執行環境都不一樣。
語法:
1 | // 擇一使用就好 |
e.g.
1 | var hello = function(name){ |
Callback(回調)
Callback(回調)其實跟一般函式沒什麼不同,差別在於被呼叫執行的時機。其實 Callback 就是把函式當作另一個函式的參數,然後在外部函式中調用該函式來完成某些事情
e.g.
1 | function a(callback) { |
純粹函式(Pure Functions)與副作用(Side Effects)概念
思考函式在使用上對外部環境造成的影響及效果。
副作用
代表著可能更動到外部環境,或者更動到傳入的參數值。不代表好、壞,看產生的結果而定。
e.g.
1 | // 有副作用 會改變值 |
純粹函式
純粹函式(pure function)
1.給定相同的輸入(傳入值),一定會回傳相同輸出值結果(回傳值)。
2.不會產生副作用-不會改變原始輸入參數,或是外部環境。
3.不依賴任何外部的狀態(例如變數)。
1 | const sum = function(value1, value2) { |
不純粹函式(impure function)
1.需要依賴外部的狀態值(變數值)
1 | let count = 1; |
箭頭函式
ES6的一種新語法,一般使用箭頭函式與 function 的用法大致一致,可以傳入參數、也有大括號包起來,除此之外箭頭函式也有更簡短的寫法,有以下幾種特性。
1.若只有單一行陳述可不加花括號{}
,也不用寫 return,預設就有,但如果有{}
是需要自行加入 return,如果沒有傳入值則會出現 undefined。
let calc = (x,y) => (x+y);
2.只有一個參數可以不加括號。
let callSomeone = someone => someone + '吃飯了'
3.沒有參數時,一定要有括號。
let callSomeone = () => '小明' + '吃飯了'
4.沒有 arguments 參數
5.綁定的 this 不同
傳統函式:依呼叫的方法而定
箭頭函式:綁定到其定義時所在的物件,箭頭函式可以取代 ES5 使用var self = this 或 .bind(this)的情況,它可以綁定 this 變數。但有時情況較特殊,要視情況而定,而不是每種情況都可以用箭頭函式來取代。
1 | var name = '全域阿婆' |
一般函式在建立時是在 window 下,所以在 window 下使用箭頭函式自然會指向 window,要確實將箭頭函式宣告在物件內部,這樣 this 才會指向該物件。
1 | var func = function () { |
注意:如果 不是 建立在物件內的函式,並不會影響箭頭函示的 this
6.縮寫的函式
不可以使用箭頭函式的情況
1.call、apply、bind 這三個方法,無法覆蓋箭頭函式中的this值
this 在 Arrow function 中是被綁定的,所以套用 call 的方法時是無法修改 this。
1 | let family = { |
2.不能用在建構式
由於 this 的是在物件下建立,所以箭頭函式不能像 function 一樣作為建構式的函式,如果嘗試使用此方法則會出現錯誤。
1 | const PhoneTemplate = (brand, modal, withCamera) => { |
3.DOM 事件監聽
如果是用在監聽 DOM 上一樣會指向 window,所以無法使用在此情境。
4.Prototype 中使用 this
一樣是 this 的問題,如果原型上新增一個箭頭函式,並嘗試使用 this 的話會指向全域。
1 | function PhoneTemplate (brand, modal, withCamera) { |
ps.物件函式綁定 this,過去我們在寫物件內的函式時,為了確保 this 能夠正確運作會先將它賦予在另一個變數上 (that, self, vm…)。
1 | var auntie = { |
箭頭函式本就會指向他所生成的物件上,所以可以不需要另外指向另一個物件。
1 | var auntie = { |
提升(Hoisting)
編譯器在編譯時期會先找出所有的變數並綁定所屬範疇,但不賦值,所以此刻變數所帶的值是 undefined;而在執行階段,JavaScript 引擎才會處理給值的事情。可以想成是把這些變數和函式「提升」到程式碼的最頂端。
變數與函式的拉升的不同之處在於,變數的拉升只有宣告部份,而函式的拉升是整個函式,因此函式在宣告前是可以執行的。
e.g.
變數
1 | console.log(a); // undefined |
編譯過程
1 | var a; // Hoisting,編譯時期確認 a 屬於全域範疇,但不賦值,所以此刻變數所帶的值是 undefined |
函式
以下例子,因為 Hoisting 效果,會將 calc 函式拉到執行時期的前面,所以可以正常使用
1 | // 函式陳述式 |
編譯過程
1 | function calc(x,y){ |
但若是使用函式表達式,就只有變數名稱被提升,並非整個函式提升,則會產生錯誤。
1 | // 函式表達式 |
編譯過程
1 | let calc; |
重複宣告
若函式和變數同名,則函式會優先;若同時有多個函式同名,則後面的會覆寫前面的宣告。
1 | foo(); // 1 |
1 | foo(); // 3 |
解構賦值
ES6 的新特性之一,用於提取陣列或物件中的資料。
用來賦值的等號左邊寫變數或是常數,右邊要寫上對應的數值,就像鏡子ㄧ樣左右對應,若沒有對應的值時,則會得到 undefined。
陣列
基本使用
const [a,b] = [1,2] //a=1,b=2
先宣告後指定
1 | let a,b |
略過
const [a,,b] = [1,2,3] //a=1,b=3
字串
1 | const str = "good"; |
其餘運算
const [a, ...b] = [1, 2, 3] //a=1, b=[2,3]
沒相對應的情況
const [, , , a, b] = [1, 2, 3] // a=undefined, b=undefined
物件
1 | let data = { |
直接對應相同名稱
let {name,height,sex} = data
基本用法
const { id:x } = {id:1} // x=1
屬性賦值
const { data1:data1,data2:data2 } = { data1:100,data2:200 } //data1=100,data2=200
屬性賦值縮寫
const { data1,data2 } = { data1:100,data2:200 }
// {data1} = {data1:data1}
字串模板 String Template
ES6 加入,使用 ${ variable_name }
即可代入變數,而不需再用 + 與雙/單引號拼湊字串。
1 | let name = 'john'; |
擴展運算子 Spread Operator
ES6 加入,以 … 表示,將陣列展開成個別數值,像展示陣列內的元素。
1 | let list = ['apple', 'boy', 'dog']; |
陣列的複製,備註:多維陣列或有複雜物件結構的情況時,是以淺拷貝(Shallow Copy)的方式進行複製,以下為範例。
1 | let list = ['apple', 'boy', 'cat']; |
陣列的合併。
1 | let list = ['apple', 'boy', 'cat']; |
當成參數,代入函式中。
1 | let student = ['john', 'boy']; |
其餘運算子 Rest Operator
以 … 表示,集合剩餘的數值並轉為陣列,可以想像是收集(多個元素至一個陣列)的功能。
1 | const concatenate = (...letters) => { |
ps.
其餘參數只能有一個,並且只能放在最後。
其餘參數在沒有傳入值的時候會是空陣列。
預設傳入參數
在 ES6 可為參數設定初始值。
原來的方式
1 | function callMe(phone) { |
現在可以寫成
1 | const callMe = (phone = '0911111111') => phone; |
參考:
https://pjchender.blogspot.com/2017/01/const.html
https://ithelp.ithome.com.tw/articles/10192146
https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/arrow_function.html
https://ithelp.ithome.com.tw/articles/10193313
https://ithelp.ithome.com.tw/articles/10191549
https://ithelp.ithome.com.tw/articles/10185780
https://cythilya.github.io/2018/10/20/hoisting/
https://wcc723.github.io/javascript/2017/12/21/javascript-es6-arrow-function/
https://wcc723.github.io/javascript/2017/12/14/javascript-arguments/
https://cythilya.github.io/2018/04/02/es6-top-features-you-must-know/