2021年4月16日 星期五

p5.js 學習筆記 (五) : Javascript 語法摘要

熟悉前幾篇的 p5.js 基本繪圖技巧後便可進一步來學習繪製動態圖形. 之前我們都只是呼叫 p5.js 的內建函式來繪圖, 完全不需要用到 Javascript 語法來控制, 但對於較複雜或動態的圖形時則需要用到 Javascript 的判斷與迴圈等語法功能, 本篇摘要整理 Javascript 語法, 做為學習 p5.js 動態圖形繪製前複習之用. 

本系列之前的文章參考 : 


以下測試使用之參考文件 : 

https://p5js.org/reference/ (教學文件)
https://editor.p5js.org (線上編輯器)

Javascript 教學文件參考 :



1. 基本語法 :

Javascript 語法摘要整理如下表 : 


 語法 說明 範例
 註解不會被執行的說明文字 //這是單行註解
 /*這是多行註解
    繼續註解
    註解結束*/
 識別字有分大小寫, 只能用字母, 底線, 前號, 與數字, 但首字元不能是數字 var c2c;
 var $2a;
 var _foo$;
 基本型態數值 (整數, 浮點數)
字串 (單雙括號均可)
布林 (true/false)
 var i=2; var a=3.14159; var a=6.626e-34;
 var a="foo"; var a='bar'; alert(a.length)  //字串長度
 var a="Hello" + "world";  //字串用 + 串接
 var a=true; var a=false;
 基本物件使用 new 運算子建立之
物件 (有 prototype). 

 var obj=new Object();           //Object 物件
 var s=new String("123");      //字串物件
 var b=new Boolean("true");  //布林物件
 var n=new Number("123");  //數值物件
 var arr=new Array(3);           //陣列物件
 var r=new RegExp("[Jj]avascript", "g");   //正規式物件
 var date=new Date();           //日期物件
 var f=new Function("a", "b", "return a+b");   //函式物件
 也可用對應之包裹器函式建立物件 (去掉 new 即可)
 全域屬性
 與方法
不須建立物件即可使用 infinity : 無限大  
 NaN : 非數值  
 undefined : 未定義
 isNaN() : 檢查 a 是否為非數值 (true/false)
 isFinite(a) : 檢查 a 是否為有限值 (true/false)
 運算子算術運算子
關係運算子
邏輯運算子
位元運算子
指定運算子
條件運算子 
 +,  -,  *,  / (四則運算), % (餘數), ++ (遞增),  -- (遞減)
 >,  <,  <=,   >=,  ==(值),  !=,  === (值與型),  !=== (值或型)
 && (且),  || (或),  ! (非)
 & 且,  | 或, ^ XOR, ~ 非, << 左移,  >> 右移,  >>> 有號右移
 =,  +=,  -=,  *=,  /=,  %=, &=,  |=,  <<=, >>=,  >>>=
 r=b? x: y  (若 b 為 true 則 r=b 否則 r=y)
 判斷if (條件式1) {
    敘述1;
    }
else if (條件式2) {
    敘述2;
    }
else {
    敘述4;
    }
條件式為 true 時執行
(0, none, undefined, 空字串, NaN 為 false)
 //判斷成績等級 A~E
 var score=parseFloat(prompt("請輸入成績:"));
 if (score < 60) {var grade="E";}
 else if (score < 70 && score >= 60) {var grade="D";}
 else if (score < 80 && score >= 70) {var grade="C";}
 else if (score < 90 && score >= 80) {var grade="B";}
 else {var grade="A";}

 分支switch (運算式) {
  case 值1 : {
         敘述1;
         break;
         }
  case 值2 : {
         敘述2;
         break;
         }
  default : {
         預設敘述;
          }
   }
運算式的值可以是 :
1. 數值 2. 字串 3. 布林
當 case 均不符時執行 default, 每個 case 必須以 break 結束.
  var today=new Date();
  var day=today.getDay();
  switch (day) : {
    case 0: {var weekday="週日";  break;}
    case 1: {var weekday="週一";  break;}
    case 2: {var weekday="週二";  break;}
    case 3: {var weekday="週三";  break;}
    case 4: {var weekday="週四";  break;}
    case 5: {var weekday="週五";  break;}
    case 6: {var weekday="週六";  break;}
    default: {var weekday="錯誤!";}
    }
  
 迴圈//for 迴圈
for (初始; 條件; 更新) {
  敘述;
  }
for (鍵 in 物件) {
  敘述;
  }
for (索引 in 陣列) {
  敘述;
  }
for (元素 of 陣列) {
  敘述;
  }
//while 迴圈
while (條件式) {
    敘述; //至少執行0次
    } 
//do 迴圈
do {
    敘述; //至少執行1次
    }
while (條件式);
  //for 迴圈 : 計算 1+2+3+...+10
  var total=0;
  for (var i=0; i<=10; i++) {
    total += i;
    }
  //走訪物件項目
  var obj={'a': 1, 'b': 2, 'c':3};
  for (k in obj) {     //for in 迭代鍵
     var v=obj[k];
     console.log(k + ":" + v);
     }
  //走訪陣列元素   //for in 迭代索引  
  var arr=[1, 2, 3];
  for (idx in arr) {
     console.log(arr[idx]);
     }
  //走訪陣列元素   //for of 迭代元素 (只能用在陣列/字串)
  var arr=[1, 2, 3];
  for (e of arr) {
     console.log(e);
     }
  //while 迴圈  : 計算 1+2+3+...+10
  var total=0;
  var i=0;
  while (i <= 10) {
     total += i;
     ++i;
     }
  //do 迴圈 : 計算 1+2+3+...+10
  var total=0;
  var i=0; 
  do {
    total += i;
    ++i;
    }
  while (i <= 10);  
 函式 function 名稱([參數]) {
   敘述;
   [return 傳回值;]
   }
 參數=原生型態 : 傳值
 參數=物件/陣列 : 傳址
 function sum(a, b) {
    return a+b;
    }
 var s=sum(1, 2);
 物件 {鍵1:值1,  //屬性
  鍵2:值2,  //屬性
  ...
  function:{  //方法
     運算式;  
     }
  }
 obj={a:1, b:2, c: 3}
 var obj={
   firstName:'foo', //屬性
   lastName:'bar', //屬性
   getFullName: function() { //方法
   return this.firstName + ' ' + this.lastName;
   };
 陣列arr.length 陣列長度
建立陣列可在中括號 [ ] 內放以逗號隔開之元素 (literal 法), 或用 new Array(n) 建立 n 個元素之空陣列, 但若傳入 1 個以上整數, 這些整數卻是元素. 
new Array() 只能建立一維陣列, 二維陣列必須使用迴圈. 
 //一般陣列
 var arr=['a', 'b' ,'c'];   //建立一個 3 元素之陣列
 var arr=new Array(); //建立不定長度之空陣列
 var arr=new Array(3);    //建立一個長度為 3 的空陣列
 arr[0]='a'; //新增元素
 arr[1]='b'; //新增元素
 arr[2]='c'; //新增元素
 var arr=new Array(1, 2, 3);   //建立內容 [1, 2, 3] 之陣列
 var arr=new Array('a', 'b','c');  //建立一個陣列 ['a','b','c']
 //二維陣列
 var arr=[[1, 2], [3, 4]]    //建立 2*2 陣列
 var arr=new Array(2);
 for (var i=0; i<2; i++) {   //建立 2*2 空陣列
   arr[i]=new Array(3);
   } 
 arr[0][0]=1; arr[0][1]=2; arr[1][0]=3; arr[1][1]=4;
 //關聯式陣列
 var fruit={"apple":"蘋果", "papaya":"木瓜"};
 alert(fruit["apple"]);  //蘋果


條件運算子有兩種寫法, 一是運算結果指派給單一變數的, 例如 :

r=(a >= 60) ? "pass" : "fail"

另外一種是問號後面有兩個運算式的, 例如 : 

(a >= 60) ? r="pass" : r="fail"

for in 可用在陣列與物件, 迭代的對象是陣列索引或物件之鍵 (key, 屬性); for of 迴圈只能用在陣列, 迭代的對象是陣列元素. 陣列的 forEach() 方法也可以用來走訪陣列, 迭代之對象為元素. 


2. 輸出入函式 :

Javascript 用來輸出資料的方式有下列四種 (可用來除錯或輸出) :


 Javascript 輸出方式 說明
 alert(str) 在輸出視窗中顯示變數 str 之值
 document.write(str)  在網頁內容 (body) 輸出變數 str 之值
 document.getElementByID(myID).innerHTML(str) 將 id=myID 的網頁元素內容以 str 取代
 console.log(str) 在瀏覽器主控台輸出變數 str 之值


從使用者取得資料的輸入方式有兩種 :


 Javascript 輸入方式 說明
 prompt(msg [, default]) 彈出輸入視窗, msg=提示訊息, default=預設值, 傳回值為字串型態
 confirm(msg)  彈出確認視窗, msg=詢問訊息, 傳回值為 true (確定)/false (取消)


注意, prompt() 的傳回值是字串, 在進行計算前應該呼叫下列函式進行轉換 :


 型態轉換函式 說明
 parseInt(s) 將傳入參數轉成整數, 傳回整數或 NaN, 例如 parseInt("3.14")
 parseFloat(s) 將傳入參數轉成浮點數, 傳回浮點數或 NaN, 例如 parseFloat("3.14")


3. 字串操作 :

Javascript 內建包裹函式 String() 可將其他物件轉成字串物件, 其 length 屬性表示字串長度 (字元數), 常用的方法如下表 :


 字串物件的方法 說明
 toLowerCase() 將字串中的英文字母轉成小寫
 toUpperCase() 將字串中的英文字母轉成大寫
 charAt(index) 傳回字串索引 index (0 起始) 之字元
 charCodeAt(index) 傳回字串索引 index (0 起始) 之字元的 unicode 編碼
 fromCharCode(c) 傳回 unicode 編碼為 c 的字元, 例如 "".fromCharCode(65) 傳回 'A'
 indexOf(str [, index]) 傳回從 index 開始找尋子字串 str 第一次出現的位置, 未找到傳回 -1
 lastIndexOf(str) 傳回從字串尾端向前找尋子字串第一次出現之位置, 未找到傳回 -1
 match(str) 從頭開始搜尋子字串 str, 找到傳回 str, 否則傳回 null
 search(str) 與 indexOf() 功能相同, 但 search() 可傳入正規式, indexOf() 不可
 replace(str1, str2) 將字串中的子字串 str1 全部以 str2 取代, 傳回結果字串
 split(str) 以子字串 str 分割字串, 成功傳回陣列, 失敗傳回字串本身
 substr(index,length) 從索引 index 開始取出長度 length 的子字串傳回
 substring(a, b) 從字串索引位置 a 開始至 b-1 取出子字串傳回 (a, b 不可為負)
 slice(a, b) 從字串索引位置 a 開始至 b-1 取出子字串傳回 (a, b 可為負)
 concat(str) 將子字串 str 串接到字串後面之後傳回
 repeat(n) 將字串重複 n 次後傳回 (ECMA 6)


注意, 用 split() 以跳行為界拆分字串時可用 split("\n"), split("\n\r")  或 split("%0D%0A"). 

另外還有如下與網頁文字格式相關的字串物件方法 (有些在 HTML5 已不支援) :


 字串物件的方法 說明 HTML5
 anchor() 傳回錨點標籤 :  <a>字串</a> O
 big() 傳回放大字型文字標籤 : <big>字串</big>  X
 blink() 傳回閃爍文字標籤 : <blink>字串</blink> X
 bold() 傳回粗體文字標籤 : <b>字串</b> O
 fixed() 傳回打字機格式文字標籤 <tt>字串</tt> X
 fontcolor(color) 傳回字型文字標籤 : <font color="color">字串</font> X
 fontsize(size) 傳回字型文字標籤 : <font size="size">字串</font> X
 italics() 傳回斜體文字標籤 : <i>字串</i> O
 link(url) 傳回超連結標籤 : <a href="url">字串</a> O
 small() 傳回縮小字型文字標籤 : <small>字串</small> O
 strike() 傳回刪除文字標籤 : <strike>字串</strike> X
 sub() 傳回上標文字標籤 : <sub>字串</sub> O
 sup() 傳回下標文字標籤 : <sup>字串</sup> O


4. 陣列操作 :

Javascript 最常用的資料型態為陣列, 其 length 屬性表示元素之個數, 陣列物件之方法如下 :


 陣列物件的方法 說明
 push(e) 將元素 e 放入陣列尾端 (新增元素)
 pop() 取出陣列最後一個元素傳回並從陣列中刪除該元素
 shift() 取出陣列第一個元素傳回並從陣列中刪除該元素, 其餘元素往前移
 unshift(e1, e2, ...) 將元素 e1, e2, ... 依序放入陣列最前面 (從索引 0), 原來元素往後挪
 slice(a, b) 將陣列索引 a 至索引 b-1 之元素 (不含索引 b) 組成子陣列傳回
 splice(a, n) 將陣列索引 a 開始的 n 個元素刪除, 其餘元素往前移
 join(str) 將陣列全部元素以 str 字串串接後傳回 (字串)
 toString() 將陣列全部元素以逗號 "," 串接後傳回, 等於 join(",")
 sort([f]) 將陣列全部元素依字串值 (或比較函式 f 之傳回值) 遞增排序
 reverse([f]) 將陣列全部元素依字串值 (或比較函式 f 之傳回值) 遞減排序
 indexOf(e) 傳回元素 e 的索引, 若 e 不存在傳回 -1
 forEach(function(e){}) 透過匿名函式走訪陣列元素


5. 數學函式 :

Math 物件是 Javascript 較特殊的內建物件, 它沒有建構函式, 而是直接用 Math 調用其屬性與方法 (Math 類似 Java 中的靜態類別), 其屬性定義了常用之數學常數如下表 : 


 Math 物件之屬性 說明
 Math.E 自然指數 e=2.718281828459045
 Math.LN2 2 的自然對數=0.6931471805599453
 Math.LN10 10 的自然對數=2.302585092994046
 Math.LOG2E e 以 2 為底之對數=1.4426950408889633
 Math.LOG10E e 以 10 為底之對數=0.4342944819032518
 Math.PI 圓周率=3.141592653589793
 Math.SQRP1_2 1/2 的平方根=0.7071067811865476
 Math.SQRT2 2 的平方根=1.4142135623730951


Math 物件的方法 :

 Math 物件的方法 說明
 Math.max(a, b) 傳回 a, b 中較大者
 Math.min(a, b) 傳回 a, b 中較小者
 Math.abs(a) 傳回 a 之絕對值
 Math.pow(a, b) 傳回 a 的 b 次方
 Math.sqrt(a) 傳回 a 的平方根
 Math.round(a) 傳回 a 的四捨五入到整數
 Math.ceil(a) 傳回 a 的無條件進位到整數
 Math.floor(a) 傳回 a 的無條件捨去到整數
 Math.sin(a) 傳回 a 的正弦值
 Math.cos(a) 傳回 a 的餘弦值
 Math.tan(a) 傳回 a 的正切值
 Math.asin(a) 傳回 a 的反正弦值
 Math.acos(a) 傳回 a 的反餘弦值
 Math.atan(a) 傳回 a 的反正切值
 Math.exp(a) 傳回 a 的自然指數值
 Math.log(a) 傳回 a 的自然對數值 (以 e 為底)
 Math.random() 傳回 0~1 的隨機數 (精度 : 小數點後 16 位)


常用的數學技巧 : 

常用對數 (以 10 為底) =Math.LOG10E * Math.log(a)
0~100 之亂數=Math.floor(Math.random() * 100)
四捨五入到小數點後一位=Math.round(a * 10)/10
四捨五入到小數點後兩位=Math.round(a * 100)/100   
四捨五入到小數點後三位=Math.round(a * 1000)/1000
求商=Math.floor(a/b) 


6. 正規表達式 :

Javascript 的正規式有兩種表示法 :
  • 斜線表示法 : 正規式用前後斜線包圍, 例如 var reg=/Javascript/i;
  • 建構式表示法 : 呼叫內建函式 var reg=RegExp("Javascript", "i"), 
其中 i 為比對方式, 共有三種 :
  • i : 不分大小寫
  • g : 全部比對
  • ig 或 gi : 不分大小寫全部比對
正規表示法定義如下之特殊字元用來表示特定之字元與其排列方式 : 


 特殊字元 說明 範例
 . 比對換行 \n 以外的任何字元 a.b 匹配 a1b, acb 
 ^ 比對字串的開始位置 ^ab 匹配 abc, 不匹配 cab
 $ 比對字串的結束位置 ab$ 匹配 cab, 不匹配 abc
 * 比對前一個字元或分組出現 0 次以上 ab* 匹配 a, ab, abb, abbb
 + 比對前一個字元或分組出現 1 次以上 ab+ 匹配 ab, abb, abbb
 ? 比對前一個字元或分組出現 0 或 1 次 ab? 匹配 a, ab, 不匹配 abb
 {m} 比對前一個字元或分組剛好出現 m 次 ab{2} 匹配 abb, 不匹配 abbb
 {m,} 比對前一個字元或分組至少出現 m 次 ab{2,} 匹配 abb, abbb, 不匹配 ab
 {m, n} 比對前一個字元或分組出現 m~n 次 ab{2, 3} 匹配 abb, abbb, 不匹配 abbbb
 \ 跳脫後面的特殊字元, 以一般字元處理 a\+ 匹配 a+bbb
 [] 匹配括弧內的任一字元 [abc] 匹配 12a34, 7cb54a
 [-] 匹配 - 前後範圍內之任一字元 [a-z] 匹配任一小寫字母
 [^] 匹配不是 ^ 後面所列舉之任一字元 [^a-d] 匹配任一不是 a, b, c, d 之字元
 () 匹配括號內正規式者為一群組 a(.+)b 匹配 a2b, 不匹配 ab
 \d 匹配數字, 等於 [0-9] [\d] 匹配任一數字字元
 \D 匹配非數字, 等於 [^0-9] [\D] 匹配任一非數字字元如 W, a, ...
 \w 匹配字母, 數字, 底線, 等於 [a-zA-Z0-9_] \w 匹配 a, A, 0, _
 \W 匹配非 \w, 等於 [^a-zA-Z0-9_] \W 匹配 !, @, + 等
 \s 匹配空白字元, 等於 [\f\r\n \t\f] (含空格) [a\sb]+ 匹配 a bc 中的 a b
 \S 匹配非空白字元, 等於 [^\f\r\n \t\f] [a\S]+ 匹配 a bc 中的 abc
 \b 匹配英文字的邊界 \bm\w 匹配 monday 之 mo
 \B 匹配非英文字的邊界 \w\Bm 匹配 mommy 之 mm 
 \cX 匹配控制字元 X \cM 匹配 Cntl+M
 \xhexXX 匹配 16 進制 ASCII 編碼 XX 之字元 \xhex41 匹配字元 A
 \ooctalOO 匹配 8 進制 ASCII 編碼 OO 之字元 \ooctal81 匹配字元 A
 \n 匹配換行字元 
 \r 匹配回車字元 (carriage return) 
 \t 匹配定位字元 (Tab) 
 \v 匹配垂直定位字元 
 \f 匹配換頁字元 (form feed) 


這些特殊字元中 ^ 與 ? 都各有兩種不同用法, 容易混淆 :
  • 正規式的多重量詞 * 或 + 預設是貪婪比對, 亦即會比對出最多字元, 但只要在重複量詞 * 或 + 後面添加? 即可改成懶惰比對, 即匹配字元越少越好.
  • ^ 字符放在中括號內表示 Not (非, 否定) 意思, 但在中括號外則表示字串的開始位置.
正規式的搜尋比對可用 RegExp 物件或 String 物件的方法, 但取代則需使用 String 物件之 replace() 方法 :


 RegExp 物件方法 說明
 reg.test(str) 用 RegExp 物件 reg 比對字串 str, 比對成功傳回 true, 失敗傳回 false
 reg.exec(str) 用 RegExp 物件 reg 比對字串 str, 將匹配的所有子字串以陣列傳回

 String 物件方法 說明
 str.search(reg) 用 RegExp 物件 reg 比對字串 str, 傳第一次出現位置之索引 (否則 -1)
 str.match(reg) 用 RegExp 物件 reg 比對字串, 將匹配的所有子字串以陣列傳回
 str.replace(reg, str1) 用 RegExp 物件 reg 比對字串, 將匹配的部分以 str1 取代


沒有留言 :