# javeScript 重要的
https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/var_const_naming.html
https://ithelp.ithome.com.tw/users/20065504/ironman/1259
六角 z

## 通知
https://www.youtube.com/watch?v=Bm0JjR4kP8w&ab_channel=WebDevSimplified
## iife
https://www.youtube.com/shorts/WAqvLSgR6_U
應用
## 剪貼簿
https://www.youtube.com/watch?v=1D5evO70wWY&list=PLSfH3ojgWsQr9bPuWl7kYHYy5kqhzMzNw&index=7&ab_channel=Acadea.io
有原生地可以用
## 無限滾動效果載入
https://www.youtube.com/watch?v=bc1bld3yuxQ&ab_channel=Acadea.io
## Debounce and Throttle
如果他每次input change打api 那會效能太開
不如抓 一秒後的現在的value去打

https://www.youtube.com/watch?v=cjIswDCKgu0&ab_channel=WebDevSimplified
## import()
超重要
https://ithelp.ithome.com.tw/articles/10250847
https://medium.com/unalai/%E8%AA%8D%E8%AD%98-dynamic-import-3a6f75da2fc9
## 很好的基本介紹
https://fireship.io/courses/javascript/concepts-console/
## callback
非同步也是callback一種
他會
把你不同步的丟在task queue裡面
圖片裡面的web API 指的是ajax DOM SetTimeout之類的 是瀏覽器提供的

## function 詳細
必看
https://www.youtube.com/watch?v=gigtS_5KOqo
## 子找父指定名稱 DOM操作
https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
## string取第幾個字
'apple'
取他第二個就[2]就可以了

## ES6 中如果希望「陣列(Array)」的元素不會重複,可以使用 Set;如果是希望物件(Object)的鍵不會重複,則可以使用 Map。
https://pjchender.dev/javascript/js-set/
## object vs array
https://stackoverflow.com/questions/17295056/array-vs-object-efficiency-in-javascript
可以看到
短的用array
多的 用object或 裡面很多空的
無排序的
你也可以看演算法two Sun那邊 他也是用object
## ES6模塊
一個寫千行會太大
所以要分開

但這樣全域變數會影響到
所以要用模塊概念

### const let
不過,在JavaScript語言中,常數指是的"不能再次指定值"(not re-assigned),而不是"完全無法改變值"(immutable),這兩個是有差異的概念。在之後的內容中會說明,物件類型的常數內容屬性是可以改變的,而在這個情況下常數會是一個常數指標。基本上,JavaScript語言中並沒有immutable(不可改變的)的特性。
宣告了一個常數,代表這個識別名稱的參照(reference)是唯讀的(read-only),並不代表這個參照指定到的值是不可改變的(immutable)。這是在講什麼?這是在講如果你宣告的常數是一個物件或陣列類型,像這種參照類型的值,裡面的值是可以作改變的。像下面的例子都是合法的使用:
```
const a = []
a[0] = 1
const b = {}
b.foo = 123
```
**所以對於物件、陣列、函式來說,使用const常數來宣告就可以,除非你有需要再指定這個陣列或物件的參照。**
註: 既然可以用const來宣告物件與陣列,用全英文字元全大寫來命名常數,也已經不需要,像一般的正常命名變數就可以了,一般用小駝峰(camelCase)命名法,例如myBook、userName這樣。
### flasy 與預設
Douglas Crockford是在JavaScript界相當知名的大師級人物,他主張使用"truthy"與"falsy"來描述資料類型的值。也就是說,像上面說講的那些會轉換為布林值的false值的資料類型的值,通稱為"falsy"(字典裡是沒這個字詞,意思是"false的"),你可以把它當成是"false家族成員"。
"falsy"包含了0, -0, null, NaN, undefined, 空白字串(''),當然也一定包含了false值
"falsy"的概念在JavaScript的邏輯運算,以及布林值中都是很重要的概念。之前已經看到一個邏輯運算符 - 邏輯反相(Logic NOT)(!)的運用,還有兩個邏輯運算符,也很常用到:
邏輯與(Logical AND)(&&)
邏輯或(Logical OR)(||)
邏輯與(&&)與邏輯或(||)的運算,正常的使用情況下,並沒有什麼太大的問題,範例如下:
```
console.log(true && false) //false
console.log(true || false) //true
```
只不過,因為JavaScript採用了與其他程式語言不同的邏輯運算的回傳值設計,我們把這兩個運算符稱為"短路求值(Short-circuit)"的運算符。實際上JavaScript中,在經過邏輯與(&&)與邏輯或(||)的運算後,它的回傳值是 - 最後的值(Last value),並不是像在常見的Java、C++、PHP程式語言中的是布林值。
因此,短路求值運算變成JavaScript中一種常被使用的特性,尤其是邏輯或(||)。那這與之前所說的"falsy"特性有何關係?關於邏輯或(||)運算在JavaScript語言中的可以這樣說明:
邏輯或(Logical OR)(||)運算符在運算時,如果當第1個運算子為"falsy"時,則回傳第2個運算子。否則,將會回傳第1個運算子。
也就是說像下面這樣的程式碼,基本上它的回傳值都不是布林值,而是其他的資料類型的值:
```
console.log('foo' || 'bar') // 'foo'
console.log(false || 'bar') // 'bar'
```
"falsy"擴大了這種運算的範圍,像下面的程式碼的回傳情況:
```
console.log( 0 || '' || 5 || 'bar') //5
console.log(false || null || '' || 0 || NaN || 'Hello' || undefined) //'Hello'
```
而出現了一種常見的在程式碼中簡短寫法,這稱為"指定預設值"的寫法,就是使用邏輯或(Logical OR)(||)運算符,範例如下:
`let a = value || 5 //5`是預設值
不過,這樣的指定預設值的寫法,並不能完全精確判斷value屬於哪一種資料類型與值的方式,在某些情況下會出現意外,例如你可能認為value=0也是合法的值,但value=0時,a會被短路求值成5。所以,只要當value是"falsy"時,變數a就會被指定為預設值5。所以在使用時還是需要特別注意,不然你會得到出乎意料的結果。
### const 預設
這個像我們之前說過的,用邏輯或運算符(||)來指定變數/常數預設值的語句。不過如果是單純的判斷某個值是否存在,然後設定它為預設值,用邏輯或運算符(||)是比較好的作法,例如:
```
const foo = a ? a : b
//相當於
const foo = a || b
```
### 轉整數要注意
如果是ES6之後的2、8進位定義格式的字串,需要用Number()方法才能轉為10進位,parseInt是無法正確轉換為整數的。範例如下:
### switch注意
判斷時有多個case情況而執行同一個語句時,會使用像下面這個範例的語法,這個語法結構也是很常見的用法,例如:
```
const fruit = '芒果'
switch (fruit)
{
case '芭樂':
case '香蕉':
console.log(fruit, '是四季都出產的水果')
break
case '西瓜':
case '荔枝':
case '芒果':
default:
console.log(fruit, '是只有夏季出產的水果')
break
}
```
### for in
**for廻圈語句完全可以取代for...in語句**
for...in語句主要是搭配物件類型使用的,不要使用在陣列資料類型。它是用於迭代物件的可列舉的屬性值(enumerable properties),而且是任意順序的。因為陣列資料在進行迭代時,順序是很重要的,所以陣列資料類型不會用這個語句,而且陣列資料類型本身就有太多好用的迭代方法。
### for of
for...of語句是新的ES6語句,可以用於可迭代物件上,取出其中的值,可迭代物件包含陣列、字串、Map物件、Set物件等等。簡單的範例如下:
```
//用於陣列
let aArray = [1, 2, 3]
for (let value of aArray) {
console.log(value)
}
//用於字串
let aString = "abcd";
for (let value of aString) {
console.log(value);
}
```
### break與continue
break(中斷)與continue(繼續)是用於迴圈區塊中語句的控制,在switch語句中也有看過break這個關鍵字的用法,是一個跳出switch語句的語法。
break是"中斷整個迴圈語句"的意思,可以中斷整個迴圈,也就是"跳出"迴圈區塊的語句,如果是在巢狀迴圈時是跳出最近的一層。break通常會用在迴圈語句中的if判斷語句裡,例如下面這個例子:
```
let count = 0
while (count < 10) {
console.log(count)
//count的值為6時將會跳出廻圈
if(count === 6) break
count++
}
```
continue是"繼續下一次迴圈語句"的意思,它會忽略在continue之下的語句,直接跳到下一次的重覆執行語句的最開頭。因為continue有"隱含的goto到迴圈的最開頭程式碼",它算是一個在JavaScript語言中的壞特性,它會讓你的判斷情況(condition)整個無效化,也可以破壞整體的迴圈語句執行結構,容易被濫用出現不被預期的結果,結論是能不用就不要使用。一個簡單的範例如下:
```
let count = 0
let a = 0
while (count < 10) {
console.log('count = ', count)
console.log('a = ', a)
count++
//count的值大於為6時將會不再遞增a變數的值
if(count > 6) continue
a++
}
```
註: 雖然JavaScript語言中並沒有goto語法,但迴圈語句中的continue搭配labeled語句,相當於其他程式語言中的goto語法。goto語法幾乎在所有的程式語言中,都是惡名昭彰的一種語法結構。
#### for 要注意的語法
用於判斷情況的運算,應該要儘可能避免,先作完運算在迴圈外部或或for迴圈的第一個表達式中,效率可以比較好些。
//較差的寫法
```
for (let i=0 ; i < aArray.length ; i++) {
//statment
}
//較好的寫法
for (let i=0, len = aArray.length ; i < len; i++) {
//statment
}
//另一種寫法,這種寫法腦袋要很清楚才看得懂
for (let i = aArray.length; i--;){
//statment
}
//較差的寫法
let i = 0
while (i < aArray.length) {
//statment
i++
}
//較好的寫法
let i = 0
let len = aArray.length
while ( i < len) {
//statment
i++
}
```
註: 上面有一說法是在陣列資料類型的迴圈中的第三表達式(更新用表達式)中,使用i--會比i++效率更佳。實際上在現在的瀏覽器上這兩種的執行速度是根本一樣,沒什麼差異。
## 物件傳質 傳參考
https://www.youtube.com/watch?v=y1odVMpi6dU
重點 傳參考的被改
原本的也會被改
因為物件是存在記憶體的
### 不固定傳入參數(Variadic)與其餘(rest)參數
像下面這個範例中,原先sum函式中,定義了要有三個傳入參數,但如果真正在呼叫函式時傳入的參數值(arguments)並沒有的情況下,或是多出來的時候,會發生什麼情況?
前面有說到,沒有預設值的時候會視為undefined值,而多出來的情況,是會被直接略過。有的時候需要一種能夠"不固定傳入參數"的機制,在各種函式應用時,才能比較方便。
```
function sum(x, y, z) {
return x+y+z
}
console.log(sum(1, 2, 3)) //6
console.log(sum(1, 2)) //NaN
console.log(sum(1, 2, 3, 4)) //6
console.log(sum('1', '2', '3')) //123
console.log(sum('1', '2')) //12undefined
console.log(sum('1', '2', '3', '4')) //123
```
雖然上一節有說過的,有個隱藏的arguments物件,它可以獲取到所有傳入的參數值,然後用類似陣列的方式來使用,但它的設計相當怪異(有一說是設計錯誤),使用時要注意很多例外的情況,加上使用前根本也不需要定義,很容易造成程式碼閱讀上的困難。另外,在一些測試報告中,使用arguments物件本身比有直接使用具有名稱傳入參數慢了數倍。所以結論是,arguments物件的是不建議使用它的。
那麼,有沒有其他的機制可以讓程式設計師能處理不固定的傳入參數?
在ES6中加入了其餘參數(rest parameters)的新作法,它使用省略符號(ellipsis)(...)加在傳入參數名稱前面,其餘參數的傳入值是一個標準的陣列值,以下是一個範例:
```
function sum(...value) {
let total = 0
for (let i = 0 ; i< value.length; i++){
total += value[i]
}
return total
}
console.log(sum(1, 2, 3)) //6
console.log(sum(1, 2)) //3
console.log(sum(1, 2, 3, 4)) //10
console.log(sum('1', '2', '3')) //123
console.log(sum('1', '2')) //12
console.log(sum('1', '2', '3', '4')) //1234
```
如果要寫得更漂亮、簡潔的的語法,直接使用Array(陣列)本身的好用方法,像下面這樣把原本的範例重寫一下:
```
function sum(...value) {
return value.reduce((prev, curr) => prev + curr )
}
```
註: reduce(歸納)是陣列的方法之一,它可以用來作"累加"
其餘參數只是扮演好參數的角色,代表不確定的其他參數名稱,所以如果一個函式中的參數值有其他的確定傳入參數名稱,其餘參數名稱應該要寫在最後一個位子,而且一個函式只能有一個其餘參數名稱:
function(a, b, ...theArgs) {
// ...
}
其餘參數與arguments物件的幾個簡單比較:
其餘參數只是代表其餘的傳入參數值,而arguments物件是代表所有傳入的參數值
其餘參數的傳入值是一個標準陣列,可以使用所有的陣列方法。而arguments物件是"偽"陣列的物件類型,不能使用陣列的大部份內建方法
其餘參數需要定義才能使用,arguments物件不需要定義即可使用,它是隱藏機制
#### 回調(callback)
回調(callback)是一種特別的函式結構,也因為JavaScript具有"高階函式(Higher-order function)"的特性,意思是說在函式中可以用另一個函式當作傳入參數值,最後也可以回傳函式。
一般而言,函式使用回傳值(return)作為最後的執行語句。但回調並不是,回調結構首先會定義一個函式類型的傳入參數,在此函式的最後執行語句,即是呼叫這個函式傳入參數,這個函式傳入參數,通常我們稱它為回調(callback)函式。回調函式經常使用匿名函式的語法,直接寫在函式的傳入參數中
```
function showMessage(greeting, name, callback) {
console.log('you call showMessage')
callback(greeting, name)
}
showMessage('Hello!', 'Eddy', function(param1, param2) {
console.log(param1 + ' ' + param2)
})
```
由於回調函式是一個函式類型,通常會在使用它的函式中,作一個基本檢查,以免造成程式錯誤,本章節的最前面有說明過了,函式的typeof回傳值是'function',以下為改寫過的程式碼,改寫過後不論是不是有傳入回調函式,都可以正常運作,也就是回調函式變成是一個選項:
```
unction showMessage(greeting, name, callback) {
console.log('you call showMessage')
if (callback && typeof(callback) === 'function') {
callback(greeting, name)
}
}
```
回調(callback)提供了使用此函式的開發者一種彈性的機制,讓程式開發者可以自行定義在此函式的最後完成時,要如何進行下一步,這通常是在具有執行流程的數個函式的組合情況。實際上,回調函式實現了JavaScript中非同步(asynchronously)的執行流程,這使得原本只能從頭到底(top-to-bottom)的程式碼,可以在同時間執行多次與多種不同的程式碼。在實際應用情況時,回調結構在JavaScript程式中大量的被使用,它也變成一種很明顯的特色,例如以下的應用中很常見:
HTML中的DOM事件
AJAX
動畫
Node.js
#### 提升(Hoisting)
簡單的來說,提升是JavaScript語言中的一種執行階段時的特性,也是一種隱性機制。不過,沒先定義與指定值就使用,這絕對是個壞習慣是吧?變數/常數沒指定好就使用,結果一定是不是你要的。
var、let和const會被提升其定義,但指定的值不會一併提升上去,像下面這樣的程式碼:
```
console.log(x) //undefined
var x = 5
console.log(y) //undefined
let y = 5
```
最後的結果出乎意料,竟然只是沒指定值的undefined,而不是程式錯誤。實際上這程式碼裡的變數被提升(Hoisting)了,相當於:
```
var x
console.log(x)
x = 5
let y
console.log(y)
y = 5
```
函式定義也會被提升,而且它變成可以先呼叫再定義,也就是整個函式定義內容都會被提升到程式碼最前面。不過這對程式設計師來說是合理的,在很多程式語言中都可以這樣作:
f
```
oo() //可執行
function foo(){
console.log('Hello')
}
```
不過使用匿名函式的指定值方式(函式表達式, FE),就不會有整個函式定義都被提升的情況,只有變數名稱被提升,這與上面的變數宣告方式的結果一致:
```
foo() //錯誤: foo is not a function
let foo = function(){
console.log('Hello')
}
```
結論如下:
所有的定義(var, let, const, function, function*, class)都會被提升
使用函式定義時,在函式區塊中的這些定義也會被提升到該區塊的最前面
當函式與變數/常數同名稱而提升時,函式的優先程度高於變數/常數。
遵守好的風格習慣可以避免掉變數提升的問題
## 展開運算符與其餘運算符
展開運算符(Spread Operator)與其餘運算符(Rest Operator)是ES6中的其中兩種新特性,雖然這兩種特性的符號是一模一樣的,都是(...)三個點,但使用的情況與意義不同。我們常常在文字敘述或聊天時,這個(...)常用來代表了"無言"、"無窮的想像"或"還有其他更多的"的意思。
簡單摘要一下這個語法的內容:
符號都是三個點(...)
都與陣列有關
一個是展開陣列中的值,一個是集合其餘的值成為陣列
註: 三個點符號(...),比較正式的英文字詞是Ellipsis,翻成中文是"省略"符號的意思,不過它有各種形式(全形或半形),也有超過三個點的情況,所以一般要說得明白只有三個點的情況,會用"three dots"與"dot-dot-dot"反而更為明確。
註: 目前看到的名詞上並沒有統一的但都是指這種語法。在ES6標準上的用語是SpreadElement與rest parameter,並沒有集合成為一個章節,而是內容散落在各章節中。在MDN上用Spread operator與Spread syntax的名詞(標題是syntax,網址列上是operator),以及Rest operator與Rest parameters(標題是parameters,但連結是operator)。
註: 用於物件上的類似語法並不是ES6中的特性,它是正在制定中的新語法標準,不過在React與Redux中很常見,能透過babel編譯,這稱為Object Rest/Spread Properties
展開運算符(Spread Operator)
展開運算符是把一個陣列展開成個別的值的速寫語法,它只會在陣列字面定義與函式呼叫時使用
展開運算符(Spread Operator)是把一個陣列展開(expand)成個別值,這個運算符後面必定接著一個陣列。最常見的是用來組合(連接)陣列,對應的陣列方法是concat,以下是一個簡單的範例:
```
const params = [ "hello", true, 7 ]
const other = [ 1, 2, ...params ] // [ 1, 2, "hello", true, 7 ]
```
展開運算符可以作陣列的淺拷貝,當然陣列的淺拷貝有很多種方式,這是新的一種語法,也是目前語法上最簡單的一種:
```
const arr = [1,2,3]
const arr2 = [...arr]
```
`arr2.push(4) //不會影響到arr`
註: 淺拷貝(shallow-copy)對於陣列中的陣列值(多維陣列),或是有複雜的物件值情況時,是只會拷貝參照值而已。
註: 上述的展開運算符在陣列字面中使用時,並沒有限制位置,或是在個數。像const arr = [...a, 1, ...b]這樣的語法都是可以的。
你也可以用來把某個陣列展開,然後傳入函式作為傳入參數值,例如下面這個一個加總函式的範例:
```
function sum(a, b, c) {
return a + b + c
}
const args = [1, 2, 3]
sum(…args) // 6
```
對照ES5中的相容語法,則是用apply函式,它的第二個參數也是使用陣列,以下是用ES5語法與上面相同結果的範例程式:
```
function sum(a, b, c) {
return a + b + c;
}
var args = [1, 2, 3];
sum.apply(undefined, args) ;// 6
```
展開運算符還有一個特別的功能,就是把可迭代(iterable)或與陣列相似(Array-like)的物件轉變為陣列,在JavaScript語言中內建的可迭代(iterable)物件有String、Array、TypedArray、Map與Set物件,而與陣列相似(Array-like)的物件指的是函式中的隱藏物件"arguments"。下面的範例是轉變字串為字元陣列:
```
const aString = "foo"
const chars = [ ...aString ] // [ "f", "o", "o" ]
```
下面的範例是把函式中的隱藏偽物件"arguments"轉成真正的陣列物件:
```
function aFunc(x){
console.log(arguments)
console.log(Array.isArray(arguments))
//轉為真正的陣列
const arr = [...arguments]
console.log(arr)
console.log(Array.isArray(arr))
}
aFunc(1)
```
其餘運算符(Rest Operator)
其餘運算符是收集其餘的(剩餘的)這些值,轉變成一個陣列。它會用在函式定義時的傳入參數識別名定義(其餘參數, Rest parameters),以及解構賦值時
會用其餘運算符(Rest Operator)的主要意義是因為它會用在兩個地方,一個是比較常提及的在函式定義中的傳入參數定義中,稱之為其餘參數(Rest parameters)。另一種情況是用在解構賦值時。
其餘參數(Rest parameters)
就像在電影葉問中的台詞:"我要打十個",其餘參數可以讓你一次打剩下的全部,不過葉問會變成一個陣列。
既然是一個參數的語法,當然就是用在函式的傳入參數定義。其餘參數代表是將"不確定的傳入參數值們"在函式中轉變成為一個陣列來進行運算。例如下面這個加總的範例:
```
function sum(…numbers) {
const result = 0
numbers.forEach(function (number) {
result += number
})
return result
}
sum(1) // 1
sum(1, 2, 3, 4, 5) // 15
```
特別注意: 其餘參數在傳入參數定義中,必定是位於最後一位,並且在參數中只能有一個其餘參數。
其餘參數的值在沒有傳入實際值時,會變為一個空陣列,而不是undefined,以下的範例可以看到這個結果:
```
function aFunc(x, ...y){
console.log('x =', x, ', y = ' , y)
}
aFunc(1,2,3) //x = 1, y = [2, 3]
aFunc() //x = undefined, y = []
```
其餘參數的設計有一個很明確的用途,就是要取代函式中那個隱藏"偽陣列"物件arguments,arguments雖然會包含了所有的函式傳入參數,但它是個類似陣列的物件卻沒有大部份陣列方法,它不太像是個隱藏的密技,比較像是隱藏的陷阱,很容易造成誤解或混亂,完全不建議你使用arguments這個東西。
其餘參數的值是一個真正的陣列,而且它需要在傳入參數宣告才能使用,至少在程式碼閱讀性上勝出太多了。
解構賦值(destructuring)時
解構賦值在另一獨立章節會講得更詳細,這裡只是要說明其餘運算符的另一個使用情況。解構賦值也是一個ES6中的新特性。
解構賦值是用在"陣列指定陣列"或是"物件指定物件"的情況下,這個時候會根據陣列原本的結構,以類似"鏡子"對映樣式(pattern)來進行賦值。聽起來很玄但用起來很簡單,這是一種為了讓陣列與物件指定值時更方便所設計的一種語法。例如以下的範例:
```
const [x, y, z] = [1, 2, 3]
console.log(x) //1
```
像這個例子就是最簡單的陣列解構賦值的範例,x當然會被指定為1,y與z你應該用腳底板也想得到是被指定了什麼值。
當使用其餘運算符之後,就可以用像其餘參數的類似概念來進行解構賦值,例如以下的範例:
```
const [x, ...y] = [1, 2, 3]
console.log(x) //1
console.log(y) //[2,3]
```
當右邊的值與左邊數量不相等時,"鏡子對映的樣式"就會有些沒對到,用了其餘運算符的那個識別名稱,就會變成空陣列。就會像下面這個例子一樣:
```
const [x, y, ...z] = [1]
console.log(x) //1
console.log(y) //undefined
console.log(z) //[]
```
在函式傳入參數中作解構賦值,這個例子會的確也是一種解構賦值的語法,而且加了上一節的函式中的其餘參數的用法。例子出自MDN的這裡:
```
function f(...[a, b, c]) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
```
你可以回頭再看一下"其餘參數"的使用情況,是不是與解構賦值時很相似。
特別注意: 在使用解構賦值時一樣只能用一個其餘運算符,位置也只能放在最後一個。
ES7+標準的其餘屬性(Rest Properties)與展開屬性(Spread Properties)
上面都只有談到與陣列搭配使用,但你可能會看到在物件上也會使用類似語法與符號(...),尤其是在React與Redux中。這些都是還在制定中的ES7之後草案標準,稱為其餘屬性(Rest Properties)與展開屬性(Spread Properties)。例如下面這樣的範例,來自這裡:
```
// Rest Properties
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }
console.log(x) // 1
console.log(y) // 2
console.log(z) // { a: 3, b: 4 }
// Spread Properties
let n = { x, y, ...z }
console.log(n) // { x: 1, y: 2, a: 3, b: 4 }
```
有些新式的框架或函式庫中已經開始使用了,babel轉換工具可以支援轉換這些語法,但使用時要注意要額外加裝babel-plugin-transform-object-rest-spread外掛。
撰寫風格建議
不要使用函式中的arguments,總是使用其餘參數語法來取代它。(Airbnb 7.6, Google 5.5.5.2, eslint: prefer-rest-params).
不要在展開運算符與其餘運算符後面有空格,也就是與後面的識別名稱(傳入參數名稱、陣列名稱)之間要緊接著。(Google 5.2.5/5.5.5.2, eslint: rest-spread-spacing)
用展開運算符的語法來作拷貝陣列。(Airbnb 4.3)
用展開運算符的語法來取代函式中的apply的語法,作不定個數傳入參數的函式呼叫。(Airbnb 7.14, eslint: prefer-spread)
用展開運算符的語法來取代slice與concat方法的語法。(Google 5.2.5)
優先使用物件展開運算符(object spread operator)的語法取代Object.assign,來作物件的淺拷貝。(Airbnb 3.8) (ES7+標準)
結論
## 解構
https://www.youtube.com/watch?v=UgEaJBz3bjY
可用來重新命名

或嵌套

解構賦值
解構賦值(Destructuring Assignment)是一個在ES6的新特性,用於提取(extract)陣列或物件中的資料,這是一種對原本語法在使用上的改進,過去要作這件事可能需要使用迴圈或迭代的語句才行,新語法可以讓程式碼在撰寫時更為簡短與提高閱讀性。
解構賦值的解說只有一小段英文:
The destructuring assignment syntax is a JavaScript expression that makes it possible to extract data from arrays or objects using a syntax that mirrors the construction of array and object literals.
這句後面的mirrors the construction of array and object literals,代表這個語法的使用方式 - 如同"鏡子"一般,對映出陣列或物件字面的結構。也就是一種樣式(pattern)對映的語法。
解構賦值如果你能抓得住它的基本概念,就可以很容易理解與使用。不過與ES6其他的特性配合時,會顯得複雜比較難理解。
在使用時有以下幾種常見的情況:
從陣列解構賦值
從物件解構賦值(或是從混用物件或陣列)
非物件或非陣列解構賦值
解構賦值時給定預設值
搭配函式的傳入參數使用
destructuring: 變性、破壞性。使用"解構"是對照de-字頭有"脫離"、"去除"的意思。
assignment: 賦值、指派。賦值通常指的是程式中使用等號(=)運算符的語句。
從陣列解構賦值(Array destructuring)
從陣列解構賦值沒太多學問,唯一比較特別的是可以用其餘運算符(Rest Operator)的語法,既然是其餘運算符,最後就會把其餘的對應值集合成一個陣列之中。下面是幾個幾個範例:
//基本用法
const [a, b] = [1, 2] //a=1, b=2
//先宣告後指定值,要用let才行
let a, b
[a, b] = [1, 2]
// 略過某些值
const [a, , b] = [1, 2, 3] // a=1, b=3
// 其餘運算
const [a, ...b] = [1, 2, 3] //a=1, b=[2,3]
// 失敗保護
const [, , , a, b] = [1, 2, 3] // a=undefined, b=undefined
// 交換值
const a = 1, b = 2;
[b, a] = [a, b] //a=2, b=1
// 多維複雜陣列
const [a, [b, [c, d]]] = [1, [2, [[[3, 4], 5], 6]]]
// 字串
const str = "hello";
const [a, b, c, d, e] = str
用法就是這麼簡單,用來賦值的等號符號(=)左邊按照你寫的變數或常數樣式,然後在右邊寫上要對映數值,就像之前說的"鏡子"般的對應。當沒有對應的值時,就會得到undefined。
從物件解構賦值(Object destructuring)
物件除了有使用特別的字面符號,也就是花括號({})來定義,其中也會包含屬性。按照基本的原則,也是用像"鏡子"般的樣式對應,一樣看範例就很容易理解:
// 基本用法
const { user: x } = { user: 5 } // x=5
// 失敗保護(Fail-safe)
const { user: x } = { user2: 5 } //x=undefined
// 賦予新的變數名稱
const { prop: x, prop2: y } = { prop: 5, prop2: 10 } // x=5, y=10
// 屬性賦值語法
const { prop: prop, prop2: prop2 } = { prop: 5, prop2: 10 } //prop = 5, prop2=10
// 相當於上一行的簡短語法(Short-hand syntax)
const { prop, prop2 } = { prop: 5, prop2: 10 } //prop = 5, prop2=10
// ES7+的物件屬性其餘運算符
const {a, b, ...rest} = {a:1, b:2, c:3, d:4} //a=1, b=2, rest={c:3, d:4}
下面的語法是個有陷阱的語法,這是錯誤的示範:
// 錯誤的示範:
let a, b
{ a, b } = {a: 1, b: 2}
註: 這個語法如果使用const來宣告常數是根本不能使用,只能用let來宣告變數。而且eslint檢查工具一定會回報語法錯誤。
因為在Javascript語言中,雖然使用花括號符號({})是物件的宣告符號,但這個符號用在程式敘述中,也就是前面沒有let、const、var這些宣告字詞時,則是代表程式碼的區塊(block)。在外面再加上括號符號(())就可以改正,括號符號(())有表達式運算的功能,正確的寫法如下:
let a, b
({ a, b } = {a: 1, b: 2}) //a=1, b=2
註: 在大部份情況,你應該是在定義常數或變數時,就進行解構賦值。
複雜的物件或混合陣列到物件,如果你能記住之前說的鏡子樣式對映基本原則,其實也很容易就能理解:
// 混用物件與陣列
const {prop: x, prop2: [, y]} = {prop: 5, prop2: [10, 100]}
console.log(x, y) // => 5 100
// 複雜多層次的物件
const {
prop: x,
prop2: {
prop2: {
nested: [ , , b]
}
}
} = { prop: "Hello", prop2: { prop2: { nested: ["a", "b", "c"]}}}
console.log(x, b) // => Hello c
從非陣列或非物件解構賦值
從其他的資料類型進行陣列或物件解構,一開始是null或undefined這兩種時,你會得到錯誤:
const [a] = undefined
const {b} = null
//TypeError: Invalid attempt to destructure non-iterable instance
如果是從其他的原始資料類型布林、數字、字串等作物件解構,則會得到undefined值。
const {a} = false
const {b} = 10
const {c} = 'hello'
console.log(a, b, c) // undefined undefined undefined
從其他的原始資料類型布林、數字、字串等作陣列解構的話,只有字串類型可以解構出字元,在上面的例子有看到這種情況,其他也是得到undefined值:
const [a] = false
const [b] = 10
const [c] = 'hello' //c="h"
console.log( a, b, c)
註: 字串資料類型的值只能用在陣列的解構賦值,無法用在物件的解構賦值。
以上會有出現這樣的結果,是當一個值要被進行解構時,它會先被轉成物件(或陣列),因為null或undefined無法轉成物件(或陣列),所以必定產生錯誤,這是第一階段。下一個階段如果這個值轉換的物件(或陣列),沒有附帶對應的迭代器(Iterator)就無法被成功解構賦值,所以最後回傳undefined。
解構賦值時的預設值
在等號左邊的樣式(pattern)中是可以給定預設值的,作為如果沒有賦到值時(對應的值不存在)的預設數值。
const [missing = true] = []
console.log(missing)
// true
const { message: msg = 'Something went wrong' } = {}
console.log(msg)
// Something went wrong
const { x = 3 } = {}
console.log(x)
// 3
要作一個簡單的陷阱題滿簡單的,你可以試看看下面這個範例中到底是賦到了什麼值:
const { a ='hello' } = 'hello'
const [ b ='hello' ] = 'hello'
console.log( a, b)
在函式傳入參數定義中使用
在函式傳入參數定義中也可以使用解構賦值,因為函式的傳入參數本身也有自己的預設值設定語法,這也是ES6的一個特性,所以使用上會容易與解構賦值自己的預設值設定搞混。這地方會產生不少陷阱。
一個簡單的解構賦值用在函式的參數裡,這是正常情況的語法:
function func({a, b}) {
return a + b
}
func({a: 1, b: 2}) // 3
當你用上了預設值的機制,而且前面的a有預設值,後面的b就沒有,這時候因為沒有賦到值時,都會是undefined值,任何數字加上undefined都會變成NaN,也就是非數字的意思:
function func({a = 3, b}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({b: 2}) // 5
func({a: 1}) // NaN
func({}) // NaN
func() // Cannot read property 'a' of undefined
當a與b兩個都有預設值時,NaN的情況不存在:
function func({a = 3, b = 5}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({a: 1}) // 6
func({b: 2}) // 5
func({}) // 8
func() // Cannot read property 'a' of undefined
實際上函式傳入參數它自己也可以加預設值,但這情況會讓最後一種func()呼叫時與func({})相同結果:
function func({a = 3, b = 5} = {}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({a: 1}) // 6
func({b: 2}) // 5
func({}) // 8
func() // 8
另一種情況是在函式傳入參數的預設值中給了另一套預設值,這只會在func()時發揮它的作用:
function func({a = 3, b = 5} = {a: 7, b: 11}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({a: 1}) // 6
func({b: 2}) // 5
func({}) // 8
func() // 18
你可以觀察一下,當對某個變數賦值時你給他null或void 0,到底是用預設值還是沒有值,這個範例的g()函式是個對照組:
function func({a = 1, b = 2} = {a: 1, b: 2}) {
return a + b
}
func({a: 3, b: 5}) // 8
func({a: 3}) // 5
func({b: 5}) // 6
func({a: null}) // 2
func({b: null}) // 1
func({a: void 0}) // 3
func({b: void 0}) // 3
func({}) // 3
func() // 3
function g(a = 1, b = 2) {
return a + b
}
g(3, 5) // 8
g(3) // 5
g(5) // 7
g(void 0, 5) // 6
g(null, 5) // 5
g() // 3
註:所以在函式傳入參數中作解構賦值時,給定null值時會導致預設值無用,請記住這一點。當數字運算時,null相當於0。
實例應用
迭代物件中的屬性值
這個範例用了for...of語法。出自Destructuring assignment:
const people = [
{
name: 'Mike Smith',
family: {
mother: 'Jane Smith',
father: 'Harry Smith',
sister: 'Samantha Smith'
},
age: 35
},
{
name: 'Tom Jones',
family: {
mother: 'Norah Jones',
father: 'Richard Jones',
brother: 'Howard Jones'
},
age: 25
}
];
for (let {name: n, family: { father: f } } of people) {
console.log('Name: ' + n + ', Father: ' + f)
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
結合預設值與其餘參數
這個範例混用了一些ES6的語法,出自Several demos and usages for ES6 destructuring.:
// 結合其他ES6特性
const ajax = function ({ url = 'localhost', port: p = 80}, ...data) {
console.log('Url:', url, 'Port:', p, 'Rest:', data)
}
ajax({ url: 'someHost' }, 'additional', 'data', 'hello')
// => Url: someHost Port: 80 Rest: [ 'additional', 'data', 'hello' ]
ajax({ }, 'additional', 'data', 'hello')
// => Url: localhost Port: 80 Rest: [ 'additional', 'data', 'hello' ]
_.pluck
這個例子相當於Underscore.js函式庫中的_.pluck,把深層的屬性值往上拉出來。
var users = [
{ user: "Name1" },
{ user: "Name2" },
{ user: "Name2" },
{ user: "Name3" }
]
var names = users.map( ({ user }) => user )
console.log(names)
// => [ 'Name1', 'Name2', 'Name2', 'Name3' ]
React Native的解構賦值
這個是React Native的一個教學,裡面有用了解構賦值的語法。出自React Native Tutorial: Building Apps with JavaScript:
var React = require('react-native')
var {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
ActivityIndicatorIOS,
Image,
Component
} = React
**loops**

**變數互換**

**正規表達式**

## ES6 的map
https://ithelp.ithome.com.tw/articles/10191607
類似 物件 但key只能string 所以有map
並且限定 key=>value 只能唯一
且沒有順序
Map
Map跟物件非常相近,當我們需要鍵值時常會用到物件
但物件有幾個缺點
物件原型特性,可能使我們對應到不需要的東西
沒有簡單的方法查詢一個物件裡有多找東西
鍵必須是字串或符號,無法將物件設為鍵
物件無法保證特性順序
Map能夠解決這些缺點,尤其是能夠將物件設為鍵的特性更是好用
## ES6 的 set
https://ithelp.ithome.com.tw/articles/10191607
Set類似Array, 但其中元素的值都會是唯一不重複的
Set甚至可以搭配Array使用,例如將Array的值unique
## 浮點數問題 以後使用小數點要注意
最好要判斷一下

## 檢查程式碼速度
console.time
console.timeEnd去判斷

## 預設值用 ??去取代
|| 不能解決0的問題 0是false的意思
所以用 ??解決預設


## 超實用 判斷這個存不存在存在就往下執行
大概像 a?.b 意思a存在就執行a.b 不存在就undefind
巢狀判斷簡短
整個程式碼長這個

原本

之後 用?去判斷前面有沒有

物件取array
注意要 .[]取

## 「傳值」或「傳址」?
https://ithelp.ithome.com.tw/articles/10191057
所以我說那個 JavaScript 是「傳值」或「傳址」呢?
在大多數的情況下,基本型別是「傳值」,而物件型別會是「傳址」的方式,但凡事都有例外。
我們來看看下面這個例子:
```
var coin1 = { value: 10 };
function changeValue(obj) {
obj = { value: 123 };
}
changeValue(coin1);
console.log(coin1); // ?
```
猜猜看,經過` changeValue(coin1)` 操作後的 coin1 會是什麼?
答案仍是` { value: 10 }` 。
剛剛說過,物件型別會是「傳址」的方式來更新資料,那應該會是 `{ value: 123 } `才對,為什麼依然不變?
事實上,JavaScript 不屬於單純的傳值或傳址。
更準確一點來說,JavaScript 應該屬於透過 pass by sharing (還沒找到合適的中文翻譯) 來傳遞資料。
「傳值」或「傳址」對大多數的開發者來說應該都不陌生,那麼「pass by sharing」又是什麼呢?
Pass by sharing
「Pass by sharing」的特點在於,當 function 的參數,如 function changeValue(obj){ ... } 中的 obj 被重新賦值的時候,外部變數的內容是不會被影響的。
```
var coin1 = { value: 10 };
function changeValue(obj) {
obj = { value: 123 };
}
changeValue(coin1);
console.log(coin1); // 此時 coin1 仍是 { value: 10 }
```
如果不是重新賦值的情況,則又會回到大家所熟悉的狀況:
```
var coin1 = { value: 10 };
function changeValue(obj) {
// 僅更新 obj.value,並未重新賦值
obj.value = 123;
}
changeValue(coin1);
console.log(coin1); // 此時 coin1 則會變成 { value: 123 }
```
## ES6 縮寫
https://wcc723.github.io/javascript/2017/12/23/javascript-short-hand/
有宣告的變數 帶入 物件 解決重複問題

只要寫一次 會把宣告的key value自動拿來用

**物件縮寫**
function 這個詞彙如果使用在物件內,也可以省略 :function,省略後的語意是沒有變化的,並沒有轉而使用箭頭函式。
**變數作為物件屬性**
過去變數會使用 xxx. 來定義,而前者本身就是一個字串,無法再轉為變數使用,現在可以直接在宣告變數時使用 [],在 [] 內則是變數,當然也可以搭配 Template String 使用。
```
let prop = 'Ming';
let value = '小明';
let teamMember = {
[prop]: value,
[`${prop}_invert`]: value.split("").reverse().join("")
}
console.log(teamMember);
// { Ming: "小明", Ming_invert: "明小" }
```**
## 非常非常重要 script引入時間

head加上defer
優點 跑到需要的會去執行 比較好
body下面
缺點 到最下面才會讀取
```
## 对象扩展
链判断操作符(?.):是否存在对象属性(不存在返回undefined且不再往下执行)
对象属性:obj?.prop、obj?.[expr]
函数调用:func?.(...args)
空判断操作符(??):是否值为undefined或null,是则使用默认值
作者:JowayYoung
链接:https://juejin.cn/post/6844903959283367950
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
## Even
https://ithelp.ithome.com.tw/articles/10192015
**阻擋事件冒泡傳遞 event.stopPropagation()**
```
<label class="lbl">
Label <input type="checkbox" name="chkbox">
</label>
```
label會log兩次
所以要讓它裡面的ckeckbox不能冒泡
當然用this就解決了,這是範例
```
// label
var lbl = document.querySelector('.lbl');
// chkbox
var chkbox = document.querySelector('#chkbox');
lbl.addEventListener('click', function (e) {
console.log('lbl click');
}, false);
// 阻擋 chkbox 的事件冒泡
chkbox.addEventListener('click', function (e) {
e.stopPropagation();
}, false);
```
e.target 其實是「觸發事件的元素」,而 this 指的是「觸發事件的目標」元素,也就是 event.currentTarget。
當然,如果在不考慮事件傳遞的情況下,this 實質上就等同於 e.target 了。
## 事件
https://ithelp.ithome.com.tw/articles/10192175
**load 事件**:
註冊在 window 物件上,指的是網頁資源 (包括CSS、JS、圖片等) 全數載入完畢後觸發。
如果是 img 元素的 load 事件,則表示是此圖片載入完畢後觸發。
**unload** 、 **beforeunload** 事件:
與 load 事件相反,unload 與 beforeunload 事件分別會在離開頁面或重新整理時觸發,而 beforeunload 會跳出對話框詢問使用者是否要離開目前頁面。
**error** 事件:
error 事件會在 document 或是圖片載入錯誤時觸發。
值得一提的是,由於維護性的考量,大多事件的註冊我會強烈建議使用「非侵入式 JavaScript」的寫法,另外寫在` <script>` 標記,只有 error 事件最適合以 on-event handler 的寫法來處理:
`<img src="image.jpg" onerror="this.src='default.jpg'">`
**注音搜尋事件**
請看文章有點難
## 涵式深入
https://ithelp.ithome.com.tw/articles/10192368
涵式是物件
涵式參數太多情況請用物件去放
不然太多一個一個要對照去填參數
ex
`addPerson('Kuro', 'Hsu', '0987654321', 'kurotanshi@gmail.com', 'male', null, 'Taipei City');`
用這種
```
var people = {
firstName: 'Kuro',
lastName: 'Hsu',
phone: '0987654321',
email: 'kurotanshi@gmail.com',
gender: 'male',
address: 'Taipei City'
};
// 最後把 people 物件作為參數傳入 addPerson
addPerson(people);
```
**注意
參數傳進去,有更動的話不會影響到外面的,他是會複製一份的概念**
**但在使用 function 傳遞參數時,要小心 「Pass by sharing」帶來的誤解(像上面的情況)(不知道pass by sharing去看上面)。**
## callback
可看布魯斯
和
https://ithelp.ithome.com.tw/articles/10192739
主要是涵式再呼叫另外一個涵式用參數的情況呼叫
這樣會波動拳
## this
* this 是 JavaScript 的一個關鍵字。
* this 是 function 執行時,自動生成的一個內部物件。
* 隨著 function 執行場合的不同,this 所指向的值,也會有所不同。
* 在大多數的情況下, this 代表的就是呼叫 function 的物件 (Owner Object of the function)。
### .bind()
```
var obj = {
x: 123
};
var func = function () {
console.log(this.x);
};
func(); // undefined
func.bind(obj)(); // 123
```
**這裡你可以想像成某個 function 在執行的時候,「暫時」把它掛在某個物件下,以便透過 this 去取得該物件的 Context。**
### .call() 與 .apply()
基本上 .call() 或是 .apply() 都是去呼叫執行這個 function ,並將這個 function 的 context 替換成第一個參數帶入的物件。 換句話說,就是強制指定某個物件作為該 function 執行時的 this。
**差別**
```
function func( arg1, arg2, ... ){
// do something
}
func.call( context, arg1, arg2, ... );
func.apply( context, [ arg1, arg2, ... ]);
```
**.call() 傳入參數的方式是由「逗點」隔開,而 .apply() 則是傳入整個陣列作為參數,除此之外沒有明顯的差別。**
**總結**
* 這個 function 的呼叫,是透過 new 進行的嗎? 如果是,那 this 就是被建構出來的物件。
* 這個 function 是以 .call() 或 .apply() 的方式呼叫的嗎? 或是 function 透過 .bind() 指定? 如果是,那 this 就是被指定的物件。
* 這個 function 被呼叫時,是否存在於某個物件? 如果是,那 this 就是那個物件。
* 如果沒有滿足以上條件,則此 function 裡的 this 就一定是全域物件,在嚴格模式下則是 undefined。
###### tags: `javaScript`