Tuesday, September 21, 2021

JavaScript 語言基礎快速介紹 (包含ECMAScript 6)


JavaScript語言基本例子

var usr = "danny", age = 18;

//ECMAScript 6, ES 6
const LED_PIN = 13; //宣告常數, 不能修改

//primitive type
//Boolean: true or false
//Number: 3.14
//String: "AAA"
//Null: null
//Undefined: undefined

//字串轉換成數自函式
Number("8.24") -> 8.24
Number("123abc") -> NaN

parseInt("8.24") -> 8
parseInt("123abc") -> 123

parseFloat("8.24") -> 8.24
var num = 0.1 * 0.2;
num.toPrecision(12) //精確度縮限制小數點12位

//嚴格相等運算子 ===
// 8 === "8"=> false
// 8 == "8" => true 

x = ( x === undefined) ? 0 : x;

//在函式內以var宣告的變數, 都是區域變數, 以外定義的變數都是全域變數
//每隔5秒執行一次
window.setInterval(function() {
    // do something
    }, 5000);

//Array
var she = ["AAA", "BBB"];
var she = new Array("AAA","BBB");
var she = new Array(3); //三個元素空白陣列
var she = [];

she.push("CCC"); //後面添加新元素
she.pop(); //刪除最後一個元素並傳回
she.unshift("DDD"); //在陣列前最前面加入新元素
she.shift(); //刪除並傳回第一個元素
she.splice(1,1); //在index=1的位置刪除一個元素
she.splice(1,1, "EEE", "FFF"); //在index=1的位置刪除一個元素,並加入兩個新元素

//for迴圈
for(var i=0; i<total; i++) {
}
she.forEach( function(val) {
});

//Object
var obj = {name:"Danny", age:18};
delete obj.name; // delete指令僅能刪除物件的屬性

for( var key in obj){
    var val = obj[key];
    console.log("attr:" + key + ",value:" + val); 
}

BOM ( Brower Object Model ) 與 DOM ( Document Object Model )


操作網頁物件

var n1 = document.getElementById("num1");

window.alert("");
window.confirm("");

location.href = "<http://swf.com.tw>"

事件處理程式



ES 6 (ECMAScript 6 or ECMAScript 2015)

let 區域化變數的存範圍

const常數宣告

Data Type 資料型別

for ... of 巡覽迴圈

for (variable of iterable) {
    statement
}
其中 variable 直接是值,而不是索引。

let iterable = [10, 20, 30];
for (let valueof iterable) {
    value += 1;
    console.log(value);
}

Template字串模板

模版字符串中,我們還可以透過 ${...} 這樣的方式,嵌入變量或任何的表達式:

let myName = "PJCHENder", numA = 4, numB = 7;
let content = `Hello, my name is ${myName}, my lucky number is ${2*(numA + numB)}`;
// "Hello, my name is PJCHENder, my lucky number is 22"
console.log(content);  

Arrow Function 箭頭函數

在ES6中, 箭頭函數的使用會把函數的參數放在前面的小括號()中,中間搭配 =>,原本函數執行的內容則放在最後的大括號{}中。

基本語法

(參數1, 參數2, …, 參數N) => { 陳述式; }

// 等相同(參數1, 參數2, …, 參數N) => { return 表示式; }

// 只有一個參數時,括號才能不加: (單一參數) => { 陳述式; } 單一參數 => { 陳述式; }

//若無參數,就一定要加括號: () => { statements }

(參數1, 參數2, …, 參數N) => 表示式; 

// 當函數會直接回傳值,可以省略箭頭後面的大括號

// 當函數會直接回傳物件,物件外需要使用小括號包起來

進階語法

// 用大括號將內容括起來,返回一個物件字面值表示法: params => ({foo: bar})

// 支援其餘參數預設參數 (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements }

// 也支援 parameter list 的解構 var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6

範例:

function area(p, r) {
    return p * r * r;
}
//箭頭函數
(p, r) => p * r * r
let pi = 3.14159
let radius = 10
const area = (p, r) => p * r * r
let result = `
    圓周率 pi 為 ${pi} 半徑為 ${radius}
    圓周長是 ${ 2 * pi * radius }
    圓面積是 ${ area(pi, radius)}
`
const Cicle = (p, r) => ({
    pi:pi,
    radius:r,
    area:p*r*r
})

展開語法

常用於複製一個物件,並為該物件新增一些屬性時,例如:

const mphone = {
  name: 'm phone',
  myear: '2018',
};
const iphone = {
  ...mphone,
  name: 'i phone',
  iyear: '2019',
};
可以注意到,在原本的 mPhone 就已經有 name 這個屬性,後來我們在iphone又添加同樣名為 name 的屬性時,就會把原本的 name 給覆蓋掉。換句話說,就是把原本的物件,解開來,再放進去新的物件裡面,同時還可以添加一些新的屬性。


另外,也可以用在複製陣列,並在新的陣列新增元素,像是這樣:

const mphone = ['phone1','phone1','phone1'];
const iphone = [...mphone, 'Oppp', 'Vivo'];

Rest Parameters 餘下參數

其餘參數(Rest parameters)的語法代表是"不確定的傳入參數值",例如最典型的就是下面這個加總的範例:

function sum(…numbers) {
  var result = 0;
  numbers.forEach(function (number) {
    result += number;
  });
  return result;
}
console.log(sum(1));// 1
console.log(sum(1, 2, 3, 4, 5));// 15

numbers參數,會把傳給它的"其餘"的所有值,轉換成一個數值陣列。

它可以把在解構賦值中沒有被取出來的物件屬性或是陣列元素都放到一個壓縮包內,例如:

const mphone = {
  name: 'm phone',
  pyear: '2018',
  feature: '3camera',
  status: 'good'
};
const {name, pyear, ...other} = mphone;
// other物件內含
// { feature: '3camera',
//    status: 'good'
// }

Object Literal物件參數

const Danny1 = {
    name: name,
    age: 18,
    showInfo: function(){
        return `${this.name} is ${this.age} years old!`
    }
}
// or
const Danny1 = {
    name,
    age: 18,
    showInfo(){
        return `${this.name} is ${this.age} years old!`
    }
}

Array物件常見的方法

// 相同的陣列
var people = [
  {
    name: 'Casper',
    like: '鍋燒意麵',
    age: 18
  },
  {
    name: 'Wang',
    like: '炒麵',
    age: 24
  },
  {
    name: 'Bobo',
    like: '蘿蔔泥',
    age: 1
  },
  {
    name: '滷蛋',
    like: '蘿蔔泥',
    age: 3
  }
];

every()

用來測試陣列中的所有元素是否"全部通過"所指定函數所給定的測試

const scores = [59,62,38,77,86]
const result = scores.every((score) => score >= 60)

some()

用來測試陣列中的所有元素中"任一個通過"所指定函數所給定的測試

const scores = [59,62,38,77,86]
const result = scores.find((score) => score >= 60)

forEach()

用來"逐一巡覽"陣列中所有的元素

const scores = [59,62,38,77,86]
scores.forEach((score) => {
    if(score >= 60){
        result.push(score)
    }
})

map()

用來"逐一巡覽"陣列中所有的元素,然後經過指定函數所給定的處理

const scores = [59,62,38,77,86]
scores.map((score) => score + 10)

reduce()

用來"逐一巡覽"陣列中所有的元素,累加所有元素後,將濃縮後(reduce)的加總值傳回。

const scores = [59,62,38,77,86]
let sum = 0
scores.map((score) => {
    sum += score
})
console.log(sum) //332

let ret = scores.reduce((sum, score) => {
  //console.log(sum); // 59, 121, 159, 236
  //console.log(score); // 62, 38, 77, 86
    return sum + score
})
console.log(ret) //332

slice()

依指定的 "起" "迄" 位置,切一塊新陣列物件出來,原本的陣列將不會被修改

let books = ['book 1','book 2','book 3','book 4','book 5']
console.log(books.slice(2)) //['book 2','book 3','book 4','book 5']
console.log(books.slice(2,4)) //['book 3','book 4']

splice()

依第一參數指定的"索引位置"來"增刪"現有陣列的元素。第二參數表示要刪除的元素"數量",第三參數以後表示要新增的元素。

let books = ['book 1','book 2','book 3','book 4','book 5']
//從索引位置2, 不刪, 加1個元素
console.log(books.splice(2,0,"book added"))
//從索引位置4, 刪2個元素, 加2個元素
console.log(books.slice(4,2,"new 1", "new 2"))
//從索引位置4, 刪所有元素
console.log(books.slice(4))

Function Chaining

const scores = [59,62,38,77,86]
let sum = 0
let result = scores
    .filter((score) => score >= 60)
    .reduce((sum, score) => sum + score)
console.log(result)

Async/await

async function 是不管怎樣都會回傳 Promise 的函式。例如:

const foo = async () => {
    return 1;
}

foo().then((res) => {
    console.log(res);
});

雖然我們的 foo 回傳的不是一個 Promise,但因為它是 async function 的關係,JS 會自動把它包成 Promise,所以可以使用 then,結果會得到 1。

而 await 則是可以等 Promise 執行完再執行下一行:

const demo = async () => {
    await logAsync('1 秒後會出現這句', 1000);
    await logAsync('再 1.5 秒後會出現這句', 1500);
    await logAsync('再 2 秒後會出現這句', 2000);
};

demo();

不過 await 必須在 async function 裡面才能使用。

而且 await 也能夠把 Promise 回傳的值接起來,通常我們在呼叫 API(例如執行 fetchaxios)的時候就很好用:

(async () => {
  const res = await fetch('API_URL');
  const data = await res.text();

  console.log(data);
})();

搭配 axios 更可以這樣使用:

((async () => {
    const { data } = await axios.get('API_URL');

    console.log(data);
})();

Promise

JavaScript ES6 Promise

簡單的 Promise 範例:

// 宣告 promise 建構式
let newPromise = new Promise((resolve, reject) => {
  // 傳入 resolve 與 reject,表示資料成功與失敗
  let ran = parseInt(Math.random() * 2); // 隨機成功或失敗
  console.log('Promise 開始')
  if (ran) {
    setTimeout(function(){
      // 3 秒時間後,透過 resolve 來表示完成
      resolve('3 秒時間(fulfilled)');
    }, 3000);
  } else {
    // 回傳失敗
    reject('失敗中的失敗(rejected)')
  }

});

newPromise.then((data)=> {
  // 成功訊息 (需要 3 秒)
  console.log(data);
}).catch((err)=> {
  // 失敗訊息 (立即)
  console.log(err)
});

執行 Promise 建構式時還會再帶入 resolve 與 reject 的 callback function。

  • resolve: 完成的 callback
  • reject: 失敗的 callback

Promise 的生命週期

  • pending: 等待中的初始狀態
  • fulfilled: 正確完成
  • rejected: 已拒絕,操作失敗

Module模組



// test.js
const scores = [59,62,38,77,86]
let sum = 0
let sum_func = scores.map((score) => {
    sum += score
})
let ret = scores.reduce((sum, score) => {
  //console.log(sum); // 59, 121, 159, 236
  //console.log(score); // 62, 38, 77, 86
    return sum + score
})

export {
    scores,
    sum,
    sum_func,
    ret as ReturnData
}
======================================
import * as Danny from "./test.js"
console.log(Danny.sum)

完全解析 JavaScript import、export

匯入 default export 並賦予名稱

預設匯出每個檔案僅會有一個,並且不會給予名稱,這種匯入方式會將預設匯出的模組引入,並且重新賦予一個變數名稱。

// export file
export default function() {
  console.log('這是一段函式');
}

// import file
import fn from './defaultModule.js';
fn(); // 直接執行函式

匯入 named export

具名的匯出方式,則需要使用解構的語法將特定的模組取出(命名需與匯出的名稱一致),並且只有被匯入的原始碼片段才能夠被執行。

// export file
export const obj = { name: 'obj'}; // 此段如果沒有被匯入,則無法運作
export function fn() {
  console.log('這是一段函式')
}

// import file
import { fn } from './module.js';
fn();
也可以透過解構同時匯入多個物件、變數、函式等等。

// import file
import { fn, obj } from './module.js';
fn();
console.log(obj)

重新命名

具名匯出的物件、變數本身就帶有固定的名稱,如果要避免與當前的作用域產生衝突,則可以使用 as(alias) 來重新命名匯入的名稱。

// export file
export const obj = { name: 'obj'};
export function fn() {
  console.log('這是一段函式')
}

// import file
import { fn as newFn } from './module.js';
newFn();

同時匯入預設、具名

匯出時可同時存在兩種形式,因此匯入時也同樣支援。以下片段來說,在匯入時前者是帶入 default export,逗點後方則是帶入 named export

import defaultExport, * as name from "module-name";


No comments: