一個JS學習者的日常

前言

這是參考鐵人賽的一個JS學習者的日常所得來的,就只是記錄一下

day01陣列相關

我們會用很多的陣列處理方法,forEach,map,reduce,filter

測試資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var people = [
{
name: 'Jim',
nickname: 'Hamburger',
age: 28
},
{
name: 'Andy',
nickname: 'Spaghetti',
age: 27
},
{
name: 'Kevin',
nickname: 'Curry',
age: 27
},
{
name: 'Arel',
like: 'Sushi',
age: 27
}
];

過了一年,所有人都長了一歲怎麼辦?要在每一個age上面加一歲

先將資料簡化成更單純的樣子

1
2
3
4
5
6
7
8
9
10
11
var age = [28, 27, 27, 27]
age.forEach(function(item,index){
console.log(item,index);
});
age.forEach((item,index)=>{
console.log(item,index);
});
// 28 0
// 27 1
// 27 2
// 27 3

forEach 只把資料丟給內部的callback function去進行處理

map 把資料丟給內部的callback function去進行處理,並回組一組陣列資料

1
2
3
4
5
6
7
8
9
10
var result=age.map(function (item, index) {
item = item + index
return item;
});
console.log(result);
result= age.map((item,index)=>{
item= item+index;
return item;
});
console.log(result);

reduce抓取初始值與下一個值,並回傳一個結果值

1
2
3
4
5
6
7
8
9
10
11
12
var rsreduce = age.reduce(function (item, item2) {
item = item + item2;
return item;
});
// 28 + 27 + 27 + 27 = 109
console.log(rsreduce);
rsreduce = age.reduce((item, item2) => {
item = item + item2;
return item;
});
// 28 + 27 + 27 + 27 = 109
console.log(rsreduce);

filter回傳Boolesn值true或false判斷引人處理後是否為要回傳的值,最後回一組陣列

1
2
3
4
5
6
7
8
9
10
11
//filter
var rsfilter = age.filter(function (item) {
if (item < 28)
return true;
});
console.log(rsfilter);
rsfilter = age.filter((item) => {
if (item < 28)
return true;
});
console.log(rsfilter);

但回到原本的資料,如何把所有的年齡加1

1
2
3
4
people.forEach((item) => {
item.age += 1;
});
console.log(people);

day02

一個物件資料, 想要複製樣的格式給下一個,並修改結果被更動到了原本的人資料,這是因為是指同一個位置

1
2
3
4
5
6
7
8
var Jim = {
favMovie: "LaLaLand",
favBook: "Thid Hitchhiker's Guide to the Galaxy"
}
var Albert = Jim;
Albert.favMovie = "Fight Club";
console.log(Jim.favMovie);
//Fight Club

使用Object.assign

1
2
3
4
5
6
7
8
9
//使用Object.assign
var Jim = {
favMovie: "LaLaLand",
favBook: "Thid Hitchhiker's Guide to the Galaxy"
}
var Albert = Object.assign({}, Jim);
Albert.favMovie = "back to the future";
console.log(Jim.favMovie)
//LaLaLand

const 指定常數,不可改變,但是是陣列時內容是可以改變

1
2
3
4
5
const PI=3.14159;
PI=1.141;//有錯誤,不可再指定
//Assignment to constant variable. 
// at ​day02.js:21:0​
// at ​​​Object.<anonymous>​​​
1
2
3
4
5
6
7
8
const arr=[1,2,3]
arr.push(4)
console.log(arr);
arr[3]=999;
console.log(arr)
//
arr2=[10,11,12,13]
arr= arr2;//有錯誤,不可再指定

函式沒有傳入參數,卻有這個變數可以用?回傳的這是啥

1
2
3
4
5
6
7
function hello() {
console.log(arguments.length);
console.log(arguments);
}
hello('echoooo');
//1 ​​​​​at ​​​arguments.length
//{ [Iterator] 0: 'echoooo', [Symbol(Symbol.iterator)]: [λ: values] } at ​​​arguments​​​

this是“這”的意思嗎?this到底指到哪裏?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
sayHi: "How ar you?",
sayHello: function () {
console.log(this.sayHi);
}
}

function sayHelo(obj){
console.log(obj);
obj.sayHello();
}

sayHelo(obj);
// { sayHi: 'How ar you?', sayHello: [λ: sayHello] }  at ​​​obj​​​
// How ar you? ​​​​​at ​​​this.sayHi

day03基本型別

有六種基本型別

  • string
  • number
  • boolean
  • null
  • undefined
  • object

物件的宣告方式為兩種

物件宣告第一種

1
2
3
4
5
6
7
//物件宣告第一種
var myObj = {
name: "123",
age: 18
}
console.log(myObj.name)
console.log(myObj.age)

物件宣告第二種

1
2
3
4
5
var curObj= new Object();
curObj.name="456";
curObj.age=28;
console.log(curObj.name)
console.log(curObj.age)

物件就像是一個群集,包含了資料跟處理資料的方法,他可能長成這樣

1
2
3
4
5
6
7
8
var introduction={
name:'Jim',
favFood:'Sushi',
breakIce:function(){
console.log(`Hello I am ${this.name},My favFood is ${this.favFood}`)
}
}
introduction.breakIce();

我們有了一個績件後如何呼叫與存取有兩種方式為特性存取property access與鍵值存取key access

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var firstName = 'Hu';
var ages = {
FirstName: 'Hu',
'firstName': 'Hu',
'Hu Jim': 28,
'Hu Koa': 60
}
//特性存取(property access)
console.log(ages.FirstName);
console.log(ages.firstName);

//鍵值存取(key access)
console.log(ages[firstName + ' Jim']);
//加入一個新的
ages[firstName + ' Ge'] = 30;
console.log(ages);

物件的複製

Shallow Clone

1
2
3
4
5
6
7
8
9
10
11
12
var firstName = 'Hu';

var ages = {
'Hu Jim': 28,
'Hu Koa': 60
}
//Shallow Clone
var agesNextYear = ages;
agesNextYear[firstName + ' Jim'] = 29;
console.log('Shallow Clone ages:' + ages[firstName + ' Jim']);
console.log('Shallow Clone agesNextYear' + agesNextYear[firstName + ' Jim']);

Deep Clone

1
2
3
4
5
6
7
8
9
10
11
12
//Deep Clone
var firstName = 'Hu';

var ages = {
'Hu Jim': 28,
'Hu Koa': 60
}
var agesNextYear= Object.assign({},ages);
agesNextYear[firstName + ' Jim'] = 29;
console.log('Deep Clone ages:' + ages[firstName + ' Jim']);
console.log('Deep Clone agesNextYear' + agesNextYear[firstName + ' Jim']);

day04 callback相關

1
2
3
4
5
6
7
function example(msg, callback) {
callback(msg)
}
example('hello callback', function (sayHi) {
console.log(sayHi)
})
//結果 hello callback

什麼是callback,專有名詞來說就是一種高階函式的用法,可以把函式當作變數傳遞,當然函數也可以返回函數,而這樣做我們可以在需要的時候再去使用它或者解決非同步阻塞的問題。在處理陣列時我們也使用同樣的方法來處理我們的陣列資料。

1
2
3
4
5
6
7
var total = [1, 2, 3, 4, 5]
var res = total.reduce((initvalue, nextitem) => {
initvalue += nextitem
return initvalue;
});
console.log(res);
// 結果 15

在撰寫邏輯上,我們可以直接的方式撰寫,變很快速理解,但直正使用的理由是,當我們事件與服務要求次數變多,要達到不阻塞I/O或多緒執行的 益時,更需要思考我們撰寫程式的邏輯與方式並非單純直覺的以單線作為考量

day05 API

當我們做一個會員登人系統,接到API後,拿到一個像這樣子的一筆JSON資料

1
2
3
$.get('https://randomuser.me/api/',function(result){
console.log(result)
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"firstName": "Hu",
"lastName": "Jim",
"sex": "male",
"age": 18,
"address":
{
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"
},
"phoneNumber":
[
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}

什麼又是JSON

通常我們會拿到一個物件或陣列格式,包含物件或陣列的資料。這樣的結構我們稱為JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"results": [
{"firstCol1": "data"},
{"firstCol2": "data"},
{"firstCol3": "data"},
{"firstCol4": "data"},
{"firstCol5": [
{"SecCol1": "data"},
{"SecCol2": "data"},
{"SecCol3": "data"},
{"SecCol4": "data"}
]
}
]
}

看起來是物件或陣列,但在網路上傳遞資料的方式,是以字串的方式去處理的,所以我們必須使用JSON.stringify轉成字串或JSON.parse去轉回物件或陣列。

再往回看一點點,我們到底在程式上要如何去接一個 API,可以使用 JS 原生的功能或者用套件,針對接 API 功能去處理,這裡提供五個例子,包含原始 XMLHttpRequest、jQuery、axios、superAgent、fetch。

接API的五種方式

day06同步和非同步

當你程式的某部份現在(now)立即執行而另外一部分要在之後(later)執行時,會發生什麼事,在這個now和loater之間有個間隙(gap)存在,期間你的程式並有積極執行

什麼是非同步?非同步包括,網路連線要求之類的I/O(例如AJAX,DOM事件處理,動畫流程處理,計時器)

1
2
3
4
5
console.log("a");
setTimeout(() => {
console.log("b");
}, 3000);
console.log("c");

我們可以看到執行結果A->B->C,但是我們要處理的狀態越來越多,結構越來越雜的時候,callback也許就不夠用山,事件的交錯與不確定資料處理的期間,不斷增加開發中理解與撰寫上的困難,再進一步學習中,處理相關的問題Promise是其中一個選項

day07多判斷switch case

當我們需要多個判斷的時候,使用swith case的小細節

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let fruit = 'apple';
switch (fruit) {
case 'banana':
console.log('It is a banana');
break;
case 'peach':
console.log('It is a peach');
break;
case 'apple':
console.log('It is a apple');
break;
default:
console.log('I don\'t know what is it');
break;
}

在switch case中如果要中斷,要加入break這關鍵字不然會一直執行下去。

day08 ES6常用語法

ES6它的全名是ECMAScript6。便是我們所學的JS的語言核心。從第五版ES5到第六版ES6日漸普遍,而ES7也蓄勢待發

  1. 解構賦值

    說明:我們可以將一個陣列以的形式,一次展開或者一次打包起來

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var arr = [1, 2, 3, 4, 5];
    //展開
    console.log(...arr);
    //結果 1 2 3 4 5

    function showArr(num1, num2, ...num3) {
    console.log(num1);
    console.log(num2);
    console.log(num3);
    }
    //打包
    showArr(...arr);
    //結果
    // 1
    // 2
    // [ 3, 4, 5 ]
  2. 模板字變量和多行字符串

    說明:可以在``(注意此為鍵盤左上方``符號非’’單引號)之間包含字串,邺以${}包𨭎變數,甚至換行也可以。

    1
    2
    3
    4
    5
    6
    7
    8
    function sayMyName(firstName, lastName) {
    console.log(`Hollo My Name
    is ${firstName} ${lastName}
    `);
    }
    sayMyName("tom", "tang");
    //結果 Hollo My Name 
    // is tom tang 
  3. 箭頭函數

    說明:寫法(引數)=>{函式內容}

    1
    2
    3
    4
    5
    6
    7
    8
    var arr = [1, 2, 3, 4, 5]

    var res=arr.reduce((total,items)=>{
    total +=items;
    return total;
    });
    console.log(res);
    //結果 15
  4. let 宣告

    說明:JS本來是以function去分辨解析變名稱所使用的範圍,意思是在一各物綿內具有一個或往更上層找或是全域等等,也就是大家常說的scope,有別於其他語言可能以大括號**{}作為範圍的選擇,所以當使用var在物件宣告的時候,一樣會變宣告成往上找到最上層fucnton的範圍。而如果使用let,更會改以{}**去判斷scope

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var test ="test";

    if(test=="test"){
    var num5=10;
    let num6=20;
    }
    console.log(num5);
    console.log(num6);
    //結果 10
    // num6 is not defined 

5.預設參數

說明:一般來 說來如果引數與對應的參數不符合時,就會出錯。

1
2
3
4
5
6
7
8
9
10
11
function add(num1=1){
return num1+num2;
}
add(10);
//結果 num2 is not defined 
function add(num1=1,num2=1){
return num1+num2;
}
res=add(10);
console.log(res);
//結果 11

其它還未使用到的:

Module的import export功能、OOP的class寫法

補充:

當我們所使用的語法。瀏覽器可能過舊或不支援,本身無法被解析的時候,我們便需要使用polyfill,便是那麼一段程式碼,好讓新的語法也能解析。而事實上自已也可以針對語法的格式去寫出要的功能,但你必須對語言背後的行為邏輯有正確的理解

day09閉包Closure

1
2
3
4
5
6
7
8
9
function saveMoney(newSaving = 0) {
var myAccount = 20000;
return function () {
return myAccount + newSaving;
}
}
total = saveMoney(1000);
console.log(total());
//結果 21000

第一次看到這個寫法覺得很𫋵奇,卻不知道它就是所謂鼎鼎大名的閉包(Closure)

什麼是閉包?看code,是由兩個函式搆成的而且互為表裡。

JS用function去界定變數名稱的作用範圍,當我們它一固函式用另一個函式包起來,便會形成一個封閉的空間,而裡面的變數名稱,也只在範圍內可以使用,我們稱無private變數。使用它來達到隱藏資訊的效果。

再看一個累加的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
function accumulation(number=0){
return function(){
console.log(number++);
}
}
var total= accumulation();
total();
total();
total();
//結果
//0
//1
//2

為什麼number會一直累加上去,而因為每次執行而開始重新計算,這更是閉包達到的效果。藉由雙層函式的架構將number變數存在記憶體當中,並藉由accumulation去執行裡面的匿名函式去改變值。

閉包博大精深,網路上有很多不同的資訊,但基本的function產生作用域用雙層function去達到封閉作用空間,是我對閉包基本認知。

day10強制轉型

1
2
3
4
5
6
7
8
9
10
conditionA = "";
if (conditionA) {
console.log("hello");
}
//無結果
conditionB = "hello";
if (conditionB) {
console.log("hello");
}
//結果 hello

在修件判斷中,為什麼conditionA為什麼不會通過條件判斷?

在條件判斷中,我們需要得到一個true或false才能去判斷接下來要不要執行𧟕面的程式,但如果裡面不是一個單純的比較,是一個物件或者其他東西會發生什麼事情?

從JS的強制轉型搆起,看以下程式碼

1
2
3
4
5
6
7
8
var a=42;
var b=a+"";//隱含的強制轉型
var c= String(a);//明確的強制轉型

console.log(b);
console.log(c);
//42 ​​​​​at ​​​b​​​
//42 ​​​​​at ​​​c​​​

當我們的操作不符合正常狀況時,JS語言會幫我們進一步處理。回到我們條件判斷,我們應該給予一個Boolean值或一個得出Boolean值的比較,但如果是其他的東西,JS會幫我們進行強制轉型而要變為Boolean的true或fals就是用所謂的Truthy值或Falsy值去判斷。那我們現在給的東西是哪一種值呢?

其㲒可以很簡單,除了以下Falsy的,都是Truthy

  • undefined

  • null

  • false

  • +0、-0 以及NaN

  • “”

最上面的例子,其實常常用在判斷是否為空值時的判斷,我們也可以寫成以下,去判斷資料是否為空。

1
2
3
4
5
6
7
conditionA = "";

if (!conditionA) {
console.log("目前沒有值");
}
// 結果
//目前沒有值 ​​​​​

另外使用三元運算式將我們原本的程式改寫成更簡潔的狀態。

語法:

判斷式?如果符合:如果不符合

以下改寫

1
2
3
4
5
6
7
conditionA="";
!conditionA ? console.log("目前沒有值"):console.log(conditionA);
//結果 目前沒有值
//
conditionA="Hello";
!conditionA ? console.log("目前沒有值"):console.log(conditionA);
//結果 Hello

day11鏈式函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var $ = function (Person) {
return {
breakTheIce: function (message) {
console.log(message + " My name is " + Person.name);
return this;
},
introduce: function () {
console.log("I'm a " + Person.intro + " Do you want to program with me?");
return this;
}
}
}

var Jim = {
name: "Jim Hu",
intro: "Programmer!"
}
$(Jim).breakTheIce("Hi").introduce();

day12修改網頁背景

把 google 首頁背景變成黃色的三種方式。

  1. 利用開發者模式功能

在開法者模式下選擇 HTML tag 的部分,
然後在後面style欄位填上 background: yellow

  1. 利用 JS 原生語法

利用 JavaScript 原生語法, getElementsByTagName 抓到
body 選項,再加上 style 改變顏色

1
2
var x = document.getElementsByTagName("body");
x[0].style.background = 'yellow'
  1. 利用 jQuery 函式庫

先創建一個包含引入函式庫的 標籤,再來抓取 標頭,將標籤附加上去。

1
2
3
4
5
6
var el = document.createElement('script');
el.src = "https://code.jquery.com/jquery-3.2.1.js"
document.getElementsByTagName('head')[0].appendChild(el);

//jQuery語法
$("body").css("background-color", "yellow");

day13網頁重繪的流程

要如何決定今天我們要製作一個移動的動畫,要用 translate 還是去改變 position 呢?
那我們就要了解一下重排(reflow)跟重繪(repaint)

五個步驟:

  1. HTML 轉換成 DOM
  2. CSS 轉換成 CSSOM
  3. 將兩個東西結合,生成一刻渲染樹 (包含每個節點的視覺訊息)
  4. 生成佈局(layout),即將所有渲染入的節點進行平面合成
  5. 將佈局繪製(paint)在平面上

而大致上網頁的變動都不斷在重複 4. 跟 5. 的動作,而效能選擇上的差異便在降低這兩項事情的發生。

需要重排跟重繪的狀況分為

  1. 修改DOM
  2. 修改樣式表
  3. 觸發事件

避免重新渲染,我們可以看看 CSStrigger 這個網站對於 transform 跟 position 的差異。就可以知道哪個選擇上比較合適。

參考資料:
阮一峰的网络日志
CSStrigger

day14網頁事件

事件表

事件 行為 例子
onchange 當元素改變 selector
onclick 當點下 HTML 元素 點擊後送出資料或選擇該元素
onmouseover 當滑鼠通過 HTML 元素 提示效果,通常會搭配 onclick
onmouseout 當滑鼠離開 HTML 元素
onkeydown 當按下鍵盤 抓取值顯示
onload 當瀏覽器載入頁面時後 串接 API 更改資料列

其實寫法很簡單,都是在 tag 裡面
加上你要執行的 function 名稱

1
<element event='some JavaScript'>

在例子中我們通常很難感受到效果,直到實際去操作。而 W3School 提供很多有趣的例子跟實驗平台,可以快速地以實踐為學習。推薦一開始從此下手。
學習連結

day16亂數的取法

思考:

如何取40~80間(不包含80)的亂數

我們用Math.random()的語法,可以得到一個0~1之間穴包含1的亂數

1
2
3
let res=Math.random();
console.log(res);
//0.8273783802602397

取亂數的方法:

  1. 將Math.random結果換算成取1~*並去掉小數點

  2. 用取餘數得到想要的亂數範圍

  3. 如果不是從0開始,加上要從多少開始數字

1
2
let res = Math.floor(Math.random() * 100) % 40 + 40;
console.log(res);

逐一拆解便為

  1. Math.random()*100 //可取0~99之間的數字
  2. Math.floor(Math.random()*100) //去掉小數點
  3. Math.floor(Math.random()*100)%40 //可取0~39之間的餘數
  4. Math.floor(Math.random()*100)%40+40//可霰40~79之間的餘數

day17畫圓

stackblitz
用Canvas畫圓,並做成一個簡單的小動畫,想要畫的圓是同心圓,並搭配Fibonacci數列,讓圓圈大小更自然的變化, 在HTML的碼

1
<canvas id="canvas" width="1000px" height="1000px"></canvas>
  1. 畫同心圓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Import stylesheets
import './style.css';
// 畫圓功能
var circle= function (x, y, radius) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2*Math.PI, false);
ctx.stroke();
};
// Write Javascript code!
const appDiv = document.getElementById('app');
//appDiv.innerHTML = `<h1>JS Starter</h1>`;
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
ctx.lineWidth = 4;

ctx.strokeStyle = "Red";
circle(200,150,10);

ctx.strokeStyle = "orange";
circle(200, 150, 20);

ctx.strokeStyle = "yellow";
circle(200, 150, 30);

ctx.strokeStyle = "Green";
circle(200, 150, 50);

ctx.strokeStyle = "Blue";
circle(200, 150, 80);

ctx.strokeStyle = "Purple";
circle(200, 150, 130);
  1. 用for loop 優化程式

    因為位置是重複的,所以可以試著把同樣的東西,用陣列與迴圈整理出來

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // Import stylesheets
    import './style.css';
    // Write Javascript code!
    const appDiv = document.getElementById('app');
    // 畫圓功能
    var circle= function (x, y, radius) {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2*Math.PI, false);
    ctx.stroke();
    };

    let canvas = document.getElementById("canvas");
    let ctx = canvas.getContext("2d");
    ctx.lineWidth = 4;

    let styles = ["Red","orange","yellow","Green","Blue","Purple","Gray"]
    let size = [10,20,30,50,80,130,210]
    for(let i=0;i<7;i++){
    ctx.strokeStyle = styles[i];
    circle(200,150,size[i]);
    }
  2. 用setInterval反覆畫圖,將其變成動畫

    因為是動態的,所以不會一開始就色所有的線都畫出來,並且會一直執行畫線的動作,所以利用setInterval並改變半徑大小畫出不同的圓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Import stylesheets
import './style.css';
// Write Javascript code!
const appDiv = document.getElementById('app');
// 畫圓功能
var circle= function (x, y, radius) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2*Math.PI, false);
ctx.stroke();
};

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
ctx.lineWidth = 4;



let styles = ["Red", "orange","yellow", "Green", "Blue", "Purple", "Gray"];
let size = [10, 20, 30, 50, 80, 130, 210];
let styleClear = "white"
let i = 0

setInterval(function(){
ctx.lineWidth = 4;
ctx.strokeStyle = styles[i];
circle(200, 150, size[i]);
ctx.lineWidth = 5;
ctx.strokeStyle = styleClear;
circle(200, 150, size[i-3]);

i += 1;
if ( i > 9 ) {
i = 0
}
}, 1000);

day18錯誤處理

通常錯誤處理的格式為try,catch,throw.

一個字典查詢的功能,這裡用一個陣列來看看如何處理錯誤。當我們有找到值的時候,會回傳這個值,如果沒有就會進行錯誤處理。

1
2
3
4
5
6
7
8
9
10
11
12
function checkDictionary(key) {
let words = {
'apple': "蘋果",
'banana': "香蕉",
'peach': "桃子"
};
if (words[key]) {
return words[key];
} else {
throw key;
}
}

if(words[key]) 的寫法我們從if(wordd[key] != null) 簡化而來,因為null是falsy物件,所以當沒有值時結果會與判斷式寫法相等。

1
2
3
4
5
6
7
try {
let res = checkDictionary("apple");
console.log(res);
} catch (e) {
console.log("We don\'t know this world: " + e);
}
//蘋果

用try去執行一個function並在裡面加入流程控制,除了正常的功能外,如有錯誤也用throw指令,直接跳往catch進行進一步的處理,傳遞參數可以是任何型態 ,由catch去接收。而當在try中直接發生錯誤或我們沒有使用throw時,發生錯誤不會執行catch裡的功能,所以我們可能無法掌握錯誤的中斷之處。

所以有try catch的語句一定記得要有throw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function checkDictionary(key) {

let words = {
'apple': "蘋果",
'banana': "香蕉",
'peach': "桃子"
};
if (words[key]) {
return words[key];
}
else {
throw key; //有try catch 一定要有 不然會undefie
}
}

try {
let res = checkDictionary("mongo");
console.log(res);
} catch (e) {
console.log("We don\'t know this world: " + e);
}

day19 chrome 工具

在chrome在按下F12按下時,會出現debug的工具,有可以設定的選項。

在Performance的部分可以記錄效能,可以勾選記憶體使用以燭幕擷取的選項,它提供數字,畫面甚至圖表資料,讓我們進一步分析程式效能

day20 時間的精度

一直以來都以為要用 setTimeout 或 setInterval 來製作動畫,但 setTimeout 最快也只能到 10 毫秒,可能造成畫面遺失的問題,而我們就可以用 requestAnimationFrame 這個方法。 一起來看一下 MicroSoft Developer Network 提供的這個例子,在裡面學到不少東西。

說明:
window.performance.now 高精準的時間戳,可以取到一毫秒的千分之一
elm.style.left = ((lpos += 3) % 600) + “px”; 利用累加方式調整矩形位置,並用餘數值來限定範圍
requestId = window.requestAFrame(render) 計算 requestAFrame 執行的次數, requestAFrame 的回傳值會從 1 開始持續往上累加

stackblitz

index.html

1
2
3
4
5
<body >
<div id="animated">Hello there.</div>
<button id="btnstart" >Start</button>
<button id="bnstop" >Stop</button>
</body>

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Import stylesheets
import './style.css';
//多瀏覽器的支援
window.cancelAFrame = (function () {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
function (id) {
window.clearTimeout(id);
};
})();

var requestId = 0;
var startime = 0;
var lpos = 0;
var elm;


function init() {
console.log("init");
elm = document.getElementById("animated");

}
function render() {
elm.style.left = ((lpos += 3) % 600) + "px";
requestId = window.requestAFrame(render);
}

function start() {
console.log("start");
if (window.performance.now) {
startime = window.performance.now();
} else {
startime = Date.now();
}
requestId = window.requestAFrame(render);
}
function stop() {
console.log("stop");
if (requestId)
window.cancelAFrame(requestId);
}

// handle multiple browsers for requestAnimationFrame()
window.requestAFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
// if all else fails, use setTimeout
function (callback) {
return window.setTimeout(callback, 1000 / 60); // shoot for 60 fps
};
})();

document.body.addEventListener("load", init(), false);
document.getElementById("btnstart").addEventListener('click', start);
document.getElementById("bnstop").addEventListener("click", stop);

day21 DOM

W3School介紹HTMLDOM時,有一個觀念會被一直強調

DOM是一個樹狀模型中,所有東西都是結點(node)。所有結點分為ACDET

  1. docment根(document)
  2. HTML元素(element)
  3. 屬性(attribute)
  4. 文字(text)
  5. 註解(comment)

當HTML文件被載入瀏覽器的時候,就會產生一個document物件做為根節點。而document物件是所有節點的擁有者,並提供屬性跟方法去操控其它結點。

Elemnt object
NodeList object(集合,有序)
Att object
NamedNodeMap objcet(集合,沒有順序)
Style object

思考:
Attr Object 和 Style Object 在哪?結果發現自己總是把CSS 調整的 style 跟 HTML 本身的 attribute 搞混。Attribute 是跟 HTML 標籤一起出現的,例如我們使用 placeholder 去提示要輸入的訊息。而屬性分為兩種,content(HTML)跟 IDL (JavaScript),也可以自定屬性。補充資料。內容為更多屬性介紹,作者:PJ

完整的 attribute 列表

說明:
當我們開始操控 JS ,我們會使用 document 的方法,去抓取節點。然後用串接(chain)的方式,進行之後動作。下面的例子,我們選取到的不是單一元素,因為有多個 P tag,行程所謂的 Node list ,再藉由 item 去選取到要針對的元素。

1
document.getElementsByTagName("P").item(0).innerHTML;

所以排下來的順序大概是這樣的:
Document > Element + NodeList > Attr + NamedNodeMap + style + DOM Events > style

day22程式邏輯

上LeetCode去練習程式邏輯,人家所謂的bug就是思考上的缺陷,有時候𤆧法能解,但是思考角度不對,其實就會在測試中出現問題。

下面的例子是給定一個陣列,真一個目標,找尋是否陣列裡有一對元素,相加等於目標數字。

1
2
3
Given nums = [2,7,11,15] ,target =9,
Because nums[0] + num[1] = 2+7 =9
return [0,1]

在這個例子中,當如果輸入兩個不同數字,結果正確。但如果兩個數字相同,就會出現錯誤。

1
2
3
4
5
6
7
8
9
10
11
12
13
var nums = [3,3]
var target = 6

var twoSum = function(nums, target) {
for (var i = 1; i < target ; i++)
if (nums.indexOf(i) + 1 && nums.indexOf(target - i) + 1) {
return [nums.indexOf(i), nums.indexOf(target - i)]
} else {
if( i == target - 1) {
return console.log("Sorry")
}
}
};

而正確的作法應該要用兩個迴圈。以下為pseudocode

1
2
3
4
5
6
7
8
9
10
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] == target - nums[i]) {
return new int[] { i, j };
}
}
}
throw new IllegalArgumentException("No two sum solution");
}

day23 Html的ID

當你在HTML標籤裡宣告ID的時候,其實就同時在JS裡宣告一僤全域變數。

說明:

用不同抓取element的方式(除ID之外)去更改tag裡面的內容,最後用createElement製造一個按鈕,然後用addEventListener去監聽按下的事件來執行。

stackblitz

1
2
3
4
<h1>Hello</h1>
<h2>My Friend</h2>
<h3 class="lion" >My Class</h3>
<h4 id="king" >My ID</h4>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Import stylesheets
import './style.css';

var btn =document.createElement("button");
var example = document.getElementsByTagName("h1");
var example2 = document.getElementsByTagName("h2");
var example3 = document.getElementsByClassName("lion");

btn.innerHTML="按鈕比大顆";
btn.style.width="100px";
btn.style.height="100px";
btn.style.background="yellow";
btn.style.fontSize="25px";
document.body.appendChild(btn);

btn.addEventListener("click",function(e){
example[0].innerHTML="Ha Ha";
example2[0].innerHTML="My My";
example3[0].innerHTML="Hi Hi";
//殺出一個程咬金
king.innerHTML ="Bye Bye";
})

可以看到最後一行,其實是直接用==ID名稱==,沒有getElementById,去串接innerHTML屬性來指派值,卻成功了

day24 html屬性

釐清一下HTML屬性與JS的關系,如何去叫用HTML裡面的屬性。我們可以把每個HTML tag 想成是一個物件,擁有自已的屬性,並且以element.propertyName的方式叫用。

stackblitz
以下例子,去判斷屬性名稱為特定姓名的話,將placeholder裡面的值修改html

1
2
3
4
5
6
<input type = "json" name="Jason" placeholder="一">
<input type = "json" name="John" placeholder="二">
<input type = "json" name="Peter" placeholder="三">
<input type = "json" name="Jim" placeholder="四">
<input type = "json" name="Amy" placeholder="五">
<button class="changePlaceholder">Action</button>

js的cdoe

1
2
3
4
5
6
7
document.getElementsByClassName("changePlaceholder")[0].addEventListener('click', function () {
document.querySelectorAll("INPUT").forEach(function (node) {
if (node.name == 'Jim') {
node.placehlder = 'Jim';
}
});
});

但是在HTML裡面他不是一個真實的物件,不是你自已加上名稱就會擁有,所以當我們要自訂屬緎的時候,必須符合它的規則。把資料屬緎在HTML tag裡寫成data-protertyName,並在JS中用element.dataset.propertyName取值

說明:

自訂一個屬性,當按下按金,秀出屬性內容

html的cdoe

1
2
<h1 data-hint="insert Data" name="Json">My data</h1>
<button class="showData">Action</button>

在jscode

1
2
3
4
document.getElementsByClassName("showData")[0].addEventListener('click', function () {
var myData = document.getElementsByTagName("H1")[0].dataset;
console.log(myData);
});

day25 抓取輸入

當熟悉抓element裡面的值之後,就可以把輸入振解成“監聽事件”觸發加上“抓取值”。input就是一個盒子,當事事件觸發的時候,動手去拿裡面的東西。然後清空裡 的東西,才不會盒子看起來髒髒的。

stackblitz

說明:
最簡易的用兩個HTML tag,包括input與button(甚至只用一個input, 按enter觸發,但記得考慮手機可能會不方便觸發輸入)

1
2
3
<h1>有沒有頻果的產品</h1>
<input class="insert" type="text" placeholder="現在搜尋" >
<button class="showData">Action</button>

說明:
input的初始設定placeholder是靠左,且字是與邊框貼齊的,所以我們會使用text-align與padding去調整文字位置與空出邊空的距離。

1
2
3
4
5
6
insert{
margin: 10px;
padding-right: 5px;
height: 15px;
text-align: right;
}

說明:

這裡用一個array來暫時記錄apple產品包含的資料,實際資料也可以是接api的資料庫、瀏覽器的localstrage或cookie。於是當我們輸入的時候,用includes去搜尋陣列,回傳true or false,來判斷是否有這個值。

1
2
3
4
5
6
7
8
9
10
11
var apples = ['手機', '電腦', '滑鼠', '支付', '電視', '音響'];
document.getElementsByClassName("showData")[0].addEventListener("click", function () {
var insert = document.getElementsByClassName("insert")[0];
var result = apples.includes(insert.value);
if (result) {
console.log("有喔有喔");
} else {
console.log("等你發明");
}
insert.value="";
});

day26置換class網頁效能的方法一

提高網頁效能的方法一用JS置換class。
我們做一個checkbox,可以使用加上class的方式,來呈現checked效果。

stackblitz

說明:
我們用sprite的方式,來切換”部分”背景圖片,製造圖片切換的效果。而不需要重新載入一個圖片進行切換。

1
2
<h1>戳我戳我</h1>
<div class="sampleClass" ></div>
1
2
3
4
5
6
7
8
9
10
.sampleClass {
width:300px;
height:330px;
background:url('https://image.freepik.com/free-vector/check-and-cross-signs-paint-design_1102-228.jpg') no-repeat;
background-position: 100% 50%;
background-size:cover;
}
.active{
background-position: 0% 50%;
}

說明:
使用className這個屬性,抓出元素的class名稱字串,再去針對字串處理加減class來達到效果。而下面的程式碼用includes去判斷是否是checked的狀態,也就是有加上active的class,如果有就切換回原本的名稱,如果沒有就加上active。

1
2
3
4
5
6
7
8
9
var checkBar = document.getElementsByTagName("div")[0];
checkBar.addEventListener("click", () => {
//console.log("checkBar.className="+checkBar.className);
if (checkBar.className.includes("active")) {
checkBar.className = "sampleClass";
} else {
checkBar.className += " active";
}
});

day27置換css屬性網頁效能的方法二

用cssText來置換屬性。每當點擊橘色盒子的時候,盒子就會向左偏移。並做一個按鈕可以讓盒子回到原來位置。

stackblitz

說明:
我們把原本的rectangle.style.left = left + 'px';
置換成rectangle.style.sytle.cssText+=left: ${left}px;來避免reflow

小提醒:
因為cssText是直接複寫css內容,所以我們用+=累加的方式,將要修改的樣式字串加到後面。

HTML code:

1
2
<div class="move where">點我向右走</dvi>
<button class="reset">reset</button>

CSS code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.move {
width:100px;
line-height:100px;
text-align: center;
background:orange;
position:absolute;
top:50%;
transform: translateY(-50%);
cursor:pointer;
}
.where{
left:10px;
}
.reset{
border-radius:50px;
font-weight:bold;
background:black;
color:white;
}

JS code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Import stylesheets
import './style.css';

var left = 10;
var top = 10;

var rectangle = document.getElementsByClassName("where")[0];
var reset = document.getElementsByClassName("reset")[0];

rectangle.addEventListener("click", function () {
left += 10;
rectangle.style.cssText = `left:${left}px`;
//rectangle.style.left= left+'px';
});

reset.addEventListener("click", () => {
rectangle.style.cssText = "left:10px";
left = 10;
});

day28處理時間

javascript如何處理時間

stackblitz

html

1
2
<h1 class="time"></h1>
<button class="transTime">現在是幾年</button>

javascript

1
2
3
4
5
6
7
8
9
var ele = document.getelementsByClassName("time")[0];
var trans = document.getElementsByClassName("transTime")[0];
var now = new Date().getFullYear()
ele.innerHTML= now;
trans.addEventListener("click",function(){
now = now - 1911;
ele.innerHTML = '民 '+now;
trans.disabled = 'disabled';
});

day29 一些小技巧

總要進步,總能更好,保持學習。

以下一個清除陣列的小技巧

1
2
3
4
var list = [1, 2, 3, 4];
list.length = 0;
console.log(list);
//[]

下一步,JS框架。
思考:為什麼要用框架?
如果只是寫JS的一些小東西,可能不需要用到框架。但當專案的規模越來越大,更需要有效的管理程式碼,考慮擴展與降低重複問題。很多東西是一體的兩面,框架用得好可以幫助我們產生更好的成果與增加可維謢性,用得不好可能綁手綁腳,本末導致。除了規模上的總則,選擇框架也會考慮技術的成熟度,社群的支援度,甚至是學習曲線。而下面是始一個Vue.js的例子。

說明:

兩個大括號中間是渲染的文字,然後把資料分離到JS裡面。data的部分就是儲存文字資料的地方。所以當我們之後要改變資料內容。就不需要直接改html畫面的檔案,而是去調整JS裡的資料內容,

1
2
3
<div id="app">
{{ text }} Nice to meet Vue.
</div>
1
2
3
4
5
6
new Vue({
el: '#app',
data: {
text: 'Hello World!'
}
});
參考資料

一個 JS 學習者的日常 共 30 篇

https://rxjs-dev.firebaseapp.com/api