2019年12月31日 星期二

jQuery 的 Ajax 捷徑函數測試

過去兩周終於把 Ajax 的低階 Ajax 函數 $.ajax() 以及工具函數 (utilities) 的測試筆記寫完, 特別是對 $.ajax() 這個低階 Ajax 函數進行了完整的測試, 對於 url 與 data 的參數攜帶方式與 type 屬性值如何影響 Ajax 引擎發出的 HTTP 請求以及後端要如何捕捉參數有了清楚的了解, 參考 :

測試 jQuery 的 Ajax 函數 $.ajax()

結論摘要如下 :
  • type 屬性值 GET/POST 決定了 HTTP 要求是 GET/POST.
  • 若 type 是 GET, 不論參數是放在 url 查詢字串還是 data 屬性中, 一律轉成查詢字串附在 URL 中傳遞, 後端須用 $_GET[] 讀取.
  • 若 type 是 POST, 則放在 url 屬性中的參數以查詢字串附在 URL 中傳遞, 後端須以 $_GET[] 讀取; 放在 data 的參數不論是查詢字串或物件形式, 一律放在 HTTP 的 BODY 中傳遞, 後端須以 $_POST[] 讀取.
雖然 PHP 還提供了一個 $_REQUEST[] 函數可用來捕捉所有前端傳遞之參數, 無需理會前端是用 URL 查詢字串或 HTTP BODY 方式傳送, 但這個測試對於了解 jQuery 的 Ajax 操作還是很有用.

本篇繼續測試與 Ajax 操作相關的 jQuery 工具函數如下 :
  • $.get() 函數
  • $.post() 函數
  • $.getJSON() 函數
  • $.getScript() 函數
其實這些函數是 jQuery 以 $.ajax() 為基礎封裝的捷徑 (shortcuts) 函數, 設定方式較低階的 $.ajax() 簡單. 本篇測試其實是上一篇 jQuery 工具程式測試的延續, 因為篇幅太長切割為兩部分, 參考 :

jQuery 的工具函數測試

在測試這些 Ajax 捷徑函數之前, 先來測試 Ajax 的輔助函數與方法.


一. Ajax 輔助函數與方法 :

為了配合 Ajax 操作方便傳遞表單參數, jQuery 提供輔助函數如下表 :


 Ajax 輔助函數與方法 說明
 $.param(obj/array [,traditional]) 將物件或陣列序列化為查詢字串, traditonal 預設 false
 serialize() 將表單元件陣列元素之鍵值序列化為查詢字串
 serializeArray() 將表單元件陣列元素之鍵值轉成陣列


其中 $.param() 為工具函數, 是 jQuery 名稱空間內的頂層函數, 可直接用 $. 直接呼叫, 而 serialize() 與 serializeArray() 則是表單 form 物件或表單控制項物件陣列的方法, 必須在這些物件上才可呼叫. 在 Ajax 操作中最常用的是 serialize() 方法, 用來從表單產生 HTTP 請求所需之查詢字串, 它事實上是用 $.param() 為基礎打造的.


1. $.param() 函數 :

此工具函數會將傳入之物件或陣列轉成查詢字串, 其介面如下 :

$.param(object/array [, traditional])

第一個參數為要轉換之 Javascript/jQuery 物件或陣列, 第二個參數為備選之布林值, 用來設定是否要進行傳統之淺層序列化, 預設為 false. 傳回值為 "k1=v1&k3=v2 ..." 的格式化查詢字串, 例如 :


範例 1 : $.param() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var obj1={user:"tony",pwd:"123"};
        document.write('obj1={user:"tony",pwd:"123"}<br>');
        var qstr=$.param(obj1);
        document.write(qstr + '<br>');
        var obj2={a:1,b:{b1:2,b2:3},c:[4,5,6]};
        document.write('obj2={a:1,b:{b1:2,b2:3},c:[4,5,6]}<br>');
        var qstr=$.param(obj2);
        document.write(qstr + '<br>');
        });
    </script>
  </body>
</html>

此例用兩個物件 obj1 與 obj2 來測試 $.param(), 其中 obj1 是簡單的鍵值對物鍵, 而 obj2 則是具有兩層結構且有一個屬性值為陣列, 結果如下 :

obj1={user:"tony",pwd:"123"}
user=tony&pwd=123
obj2={a:1,b:{b1:2,b2:3},c:[4,5,6]}
a=1&b%5Bb1%5D=2&b%5Bb2%5D=3&c%5B%5D=4&c%5B%5D=5&c%5B%5D=6

可見簡單物件 obj1 經過 $.param() 轉換後就是單純的 k=v&k=v 格式的查詢字串, 而多層或含有陣列值的 obj2 則在轉出的查詢字串中含有 %5B (表示字元 '[') 與 %5D (表示字元 ']') 這些經 URL 編碼後的查詢字串, 這是用來表示物件結構的編碼字元, 所以 obj2 的輸出可以翻譯為 :

a=1&b[b1]=2&b[b2]=3&c[]=4&c[]=5&c[]=6

亦即, 深層物件會以 [屬性] 標示其鍵, 而陣列值則用空的 [] 表示其鍵. 不過實務上很少會用到深層物件結構會以陣列為值, $.param() 通常都用來從簡單的表單或其控制項陣列來轉換成查詢字串, 例如 :


範例 2 : $.param() 測試 (控制項陣列) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <form>
     <label for="user">帳號 : </label>
     <input type="text" id="user" name="user"><br>
     <label for="pwd">密碼 : </label>
     <input type="text" id="pwd" name="pwd">
     <button id="login">登入</button>
    </form>
    <div id="result"></div>
    <script>
      $(function(){
        $("#login").click(function(){
          var qstr=$.param($("input"));   //傳入 input 控制項物件陣列
          $("#result").html(qstr);
          return false;
          });
        });
    </script>
  </body>
</html>

此例以選擇器 $("input") 選取全部 input 控制項物件陣列, 傳入 $.param() 後, 這些元件的 name 與 value 會被組成查詢字串傳回來. 結果如下 :




注意, 控制項必須有 name 屬性, $.param() 以 name 作為查詢字串的 key, 如果只有 id 卻沒有 name, 則查詢字串將沒有 key (即變數名稱). 另外, 此利只是要測試 $.param() 功能並在網頁中顯示轉換結果, 並沒有要真的提交表單, 因此 click 事件結尾處要用 return false 阻止預設之提交動作與事件提升, 否則網頁會因提交重載而無法看到結果.

如果控制項含有 input, select, 與 textarea 等控制項, 則需要用複合選擇器來選取它們, 例如 :


範例 3 : $.param() 測試 (多種控制項) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <form>
      年收入 :
      <input type="radio" name="income">50 萬以下
      <input type="radio" name="income" checked>50~100 萬
      <input type="radio" name="income">100以上<br>
      投資經驗 :
      <input type="checkbox" name="experience1" checked>基金
      <input type="checkbox" name="experience1" checked>股票
      <input type="checkbox" name="experience1">選擇權
      <input type="checkbox" name="experience1">期貨<br>
      投資屬性 :
      <select name="risk">
        <option value="positive" selected>積極型</option>
        <option value="robust">穩健型</option>
        <option value="conservative">保守型</option>
      </select><br>
      備註 :
      <textarea name="remark" rows="5" cols="25">預設值</textarea><br>   
      <button id="ok">確定</button>
    </form>
    <div id="result"></div>
    <script>
      $(function(){
        $("#ok").click(function(){
          var qstr=$.param($("input,select,textarea"));
          $("#result").html(qstr);
          return false;
          });
        });
    </script>
  </body>
</html>

此例包含 input 的 radio, checkbox, select, 以及 textarea 等控制項, 故選擇器必須包含 input, select, 與 textarea, 結果如下 :



income=on&income=on&income=on&experience1=on&experience1=on&experience1=on&experience1=on&risk=positive&remark=test

結果每一個 radio 與 checkbox 選項都傳出 on, 這樣根本無法判別, 於是就有了下面以表單 form 物件的 serialize() 方法了.


2. serialize() 方法 :

上面的 $.param() 函數需以選擇器收集要轉換為查詢字串的控制項元件, 實務上提交表單是以表單為基礎, 因此 jQuery 在表單物件上用 $.param() 為基礎實作了 serialize() 方法, 可以將表單內之有效的控制項 (有 name 且不是 disabled 者) 全部轉成查詢字串, 其介面如下 :

serialize()

此方法無傳入參數, 傳回值為"k1=v1&k3=v2 ..." 的格式化查詢字串, 例如 :


範例 3 : .serialize() 方法測試 (表單) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <form id="login_form">
     <label for="user">帳號 : </label>
     <input type="text" id="user" name="user"><br>
     <label for="pwd">密碼 : </label>
     <input type="text" id="pwd" name="pwd">
     <button id="login">登入</button>
    </form>
    <div id="result"></div>
    <script>
      $(function(){
        $("#login").click(function(){
          var qstr=$("#login_form").serialize();
          $("#result").html(qstr);
          return false;
          });
        });
    </script>
  </body>
</html>

此例與上面使用 $.param() 函數之結果相同, 但做法改為用選擇器選取整個表單物件, 再呼叫其 serialize() 方法, 傳回格式化查詢字串, 結果如下 :




用這方法的好處是不需要用不同選擇器選取 input, select, 與 textarea 等不同控制項, 再傳給 $.param() 處理, 只要針對表單元件即可. 但同樣要注意的是, 表單內的每一個控制項都必須有 name 屬性.


3. serializeArray() 方法 :

此函數與 serialize() 方法一樣是表單物件之方法, 用來將表單內控制項要傳遞的變數轉成物件陣列, 其介面如下 :

serializeArray()

此函數無參數, 傳回值為物件陣列, 例如 :


範例 4 : .serializeArray() 方法測試 (表單) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <form id="login_form">
     <label for="user">帳號 : </label>
     <input type="text" id="user" name="user"><br>
     <label for="pwd">密碼 : </label>
     <input type="text" id="pwd" name="pwd">
     <button id="login">登入</button>
    </form>
    <div id="result"></div>
    <script>
      $(function(){
        $("#login").click(function(){
          var arr=[];    //儲存輸出訊息用
          var SA=$("#login_form").serializeArray();   //轉成物件陣列
          $.each(SA, function(idx, obj){       //迭代物件陣列
            arr.push("[" + idx + "]=" + obj);
            $.each(obj, function(k, v){            //迭代物件屬性
              arr.push(" " + k + ":" + v);
              });         
            });
          var qstr=$.param(SA);    //將物件陣列序列化
          arr.push(qstr);
          $("#result").html(arr.join("<br>"));
          return false;
          });
        });
    </script>
  </body>
</html>

此例取得表單物件後呼叫 serializeArray() 傳回控制項 name:value 組成之物件陣列, 故第一層迴圈迭代此陣列, 其元素就是控制項的鍵值物件, 因為有兩個控制項, 所以為陣列有兩個元素. 第二層迴圈迭代每一個元素 (即鍵值物件) 之屬性. 只要將 serializeArray() 傳回之物件陣列傳給 $.param() 函數就可以得到格式化的查詢字串了, 結果如下:




可見第一個物件是 user 控制項, 第二個物件是 pwd 控制項.


二. Ajax 捷徑函數 : 

jQuery 的 Ajax 捷徑函數如下表, 它們都會傳回一個 jqXHR 物件 :


 jQuery Ajax 捷徑工具函數 說明
 $.get(url [,data] [,success] [,dataType]) 指定 url 向伺服器發出 GET 請求
 $.post(url [,data] [,success] [,dataType]) 指定 url 向伺服器發出 POST 請求
 $.getJSON(url [,data] [,callback]) 指定 url 向伺服器發出 POST 請求 JSON 資料
 $.getScript(url [,callback]) 指定 url 向伺服器發出 POST 請求 Script 程式


其中 data 為要傳遞給後端伺服器之參數資料, 可以是 Javascript 物件或查詢字串, data 若為物件, 則其屬性值會以 URI 編碼成查詢參數. dataType 指定以何種格式解析 HTTP 回應本體 (body) 中的資料, 可用 html, txt, xml, json, jsonp, script 等格式. 參數 success 為收到 HTTP 回應狀態為成功時財呼叫的回呼函數 (失敗就不呼叫), 此回呼函數有三個參數 :

function(data, textStatus, jqXHR) {}

其中參數 data 為伺服器的回應資料 (html, txt, xml, json, script 等物件), 參數 textStatus 為回應狀態字串 (success, error, notmodified, timeout 等), 參數 jqXHR 為一個物件. 這是一個 XMLHttpRequest 物件的子集.

這些快捷函數都是由低階的 $.ajax() 再包裝而成, 關於 $.ajax() 參考 :

測試 jQuery 的 Ajax 函數 $.ajax()


1. $.get() 函數 :

此函數會以 GET 方法向伺服器發出非同步請求, 其介面如下 :

$.get(url [,data] [,callback] [,dataType])

此函數相當於下列 $.ajax() 的封裝 :

$.ajax({
  type: "GET",
  url: url,
  data: data,
  success: success,
  dataType: dataType
  });

由於 type 已經設為 GET, 因此 HTTP 要求為 GET 方法, 所以不論參數放在  url 中或以參數物件放在 data 屬性中, Ajax 引擎都會把參數轉成查詢字串放在 URL 後面, 因此後端 PHP 程式都要用 $_GET[] 函數捕捉, 例如 :


範例 5 : $.get() 帶 URL 查詢字串載入 PHP 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="get" value="get">
  <script> 
    $(function(){
      $("#get").click(function() {
        $.get({
          url: "test.php",
          dataType: "html",
          data: "user=tony&pwd=123",
          success: function(data) {
            $("#result").html(data);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例將全部參數以字串形式放在 data 屬性中, 結果如下 :





若將參數分散在 URL 查詢字串與 data 屬性的參數物件中, 結果也跟上面範例 5 完全一樣, 都要用 $_GET[] 函數捕捉, 例如 :


範例 6 : $.get() 帶 URL 查詢字串與參數物件載入 PHP 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="get" value="get">
  <script> 
    $(function(){
      $("#get").click(function() {
        $.get({
          url: "test.php?user=tony",
          dataType: "html",
          data: {pwd: 123},
          success: function(data) {
            $("#result").html(data);
            }     
          });
        });
      });
  </script>
  </body>
</html>

即使將參數完全以 data 屬性參數物件形式, 結果參數也是轉成查詢字串 :


範例 7 : $.get() 以 data 參數物件請求 PHP 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="get" value="get">
  <script> 
    $(function(){
      $("#get").click(function() {
        $.get({
          url: "test.php",
          dataType: "html",
          data: {user: "tony", pwd: 123},
          success: function(data) {
            $("#result").html(data);
            }     
          });
        });
      });
  </script>
  </body>
</html>

綜合以上測試, $.get() 函數不管參數以哪種形式表達, 都會被轉成 URL 查詢字串傳送, 所以後端都要用 $_GET[] 捕捉.

下面範例使用 $.get() 向後端請求 test_xml.php, 此程式會回應一個 XML 格式資料 :

<?php
//test.xml.php
header("Content-Type: text/xml");
$user=$_REQUEST["user"];           
$pwd=$_REQUEST["pwd"];
$response="<?xml version='1.0' ?>".
          "<result>".
          "<user>".$user."</user>".
          "<pwd>".$pwd."</pwd>".
          "</result>";
echo $response;
?>

此程式會將捕捉到的參數以 XML 格式包裝後回應給客戶端.


範例 8 : 用 $.get() 請求 XML 資料 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script>
    $(function(){
      $("#login").click(function(){
        $.get({
          url: "test_xml.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=$(data).find("user").text();
            var pwd=$(data).find("pwd").text();
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            }
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例使用表單物件的 serialize() 方法將控制項的 name:value 序列化為查詢字串, $.get() 函數會將其附在 URL 字串後面送出 GET 請求, 收到 XML 回應資料 data 後將其轉成 jQuery 物件, 方便使用 find() 方法找出 XML 節點元素, 結果如下 :




比 XML 還要輕巧的 JSON 現在已成為資料交換的新寵兒, 為了透過 $.get() 取得後端的 JSON 資料, 需準備一個後端程式 tes_json.php 程式如下 :

<?php
//test_json.php
header("Content-Type: application/json");
$user=$_REQUEST["user"];           
$pwd=$_REQUEST["pwd"];
$response=array(
  'user' => 'tony',
  'pwd' => '123'
  );
echo json_encode($response);
?>

此程式利用 json_encode() 將陣列轉換成如下 JSON 資料 :

{"user":"tony","pwd":"123"}


範例 9 : 用 $.get() 請求 JSON 資料 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script>
    $(function(){
      $("#login").click(function(){
        $.get({
          url: "test_json.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=data.user;
            var pwd=data.pwd;
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            }
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例收到後端回應 {"user":"tony","pwd":"123"} 後, 會傳入 success 屬性的回呼函數參數 data, 這樣就可以直接用物件屬性 data.user 與 data.pwd 取得回應資料之值, 不需要從 data 回應中撈資料, 這是 JSON 比 XML 方便的地方. 結果如下 :




綜合以上 $.get() 的測試可知此函數會以 GET 方法發出 HTTP 請求, 不論參數是帶在 url 查詢字串還是 data 參數物件上, 它都會將參數全部轉成查詢字串附在網址後面送出, 摘要如下表 :


 url 屬性 data 屬性 HTTP 請求 後端 PHP 讀取參數
 test.php?a=1&b=2 無 GET $_GET["a"] 與 $_GET["b"]
 test.php?a=1 {b: 2} GET $_GET["a"] 與 $_GET["b"]
 test.php {a:1, b: 2} GET $_GET["a"] 與 $_GET["b"]
 test.php a=1&b=2 GET $_GET["a"] 與 $_GET["b"]


GET 是一種等冪操作 (idempotent), 意思是同樣的 GET 請求重複操作會得到一樣的結果, 通常用來從伺服器取得資料; 而 POST 則是非等冪操作 (non-idempotent), 傳送給伺服器的參數通常用來改變後端伺服器狀態, 例如資料庫內容異動等, 故資料庫的 CRUD 操作應該用 POST.


2. $.post() 函數 :

此函數會以 POST 方法向伺服器發出非同步請求, 其介面如下 :

$.post(url [,data] [,callback] [,dataType])

此函數相當於下列 $.ajax() 的封裝 :

$.ajax({
  type: "POST",
  url: url,
  data: data,
  success: success,
  dataType: dataType
  });

由於 type 已經設為 POST, 因此 HTTP 要求為 POST 方法, 所以不論參數放在  url 中或以參數物件放在 data 屬性中, Ajax 引擎都會把參數放在 HTTP 本體 (body) 內傳送, 因此後端 PHP 程式都要用 $_POST[] 函數捕捉, 例如 :


範例 10 : $.post() 帶 URL 查詢字串載入 PHP 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="post" value="post">
  <script> 
    $(function(){
      $("#post").click(function() {
        $.post({
          url: "test.php",
          dataType: "html",
          data: "user=tony&pwd=123",
          success: function(data) {
            $("#result").html(data);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例用 $.post() 載入一個 htm 網頁, 參數以查詢字串方式放在 data 屬性中傳送, 結果如下 :





可見雖然 data 屬性值是查詢字串, 還是會被放在 HTTP 本體 (body) 中傳送, 因此後端要用 $_POST[] 捕捉. 如果參數一部分放 URL 字串, 另一部分放在 data 字串, 則 $.post() 會怎麼傳送變數呢? 參考


範例 11 : $.post() 帶查詢字串與參數物件載入 PHP 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="post" value="post">
  <script> 
    $(function(){
      $("#post").click(function() {
        $.post({
          url: "test.php?user=tony",
          dataType: "html",
          data: "pwd=123",
          success: function(data) {
            $("#result").html(data);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例將兩個參數一個放 URL 查詢字串, 一個放在 data 查詢字串, 結果如下 :





可見使用 $.post() 發出 Ajax 請求時, 雖然 HTTP 請求方法是 POST, 但放在 url 屬性中的參數會以查詢字串方式附在 URL 中送出, 而放在 data 屬性中的字串參數則放在 HTTP 本體中傳送.

下面則是全部參數以物件形式放在 data 屬性中的範例 :


範例 12 : $.post() 以 data 參數物件載入 PHP 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="post" value="post">
  <script> 
    $(function(){
      $("#post").click(function() {
        $.post({
          url: "test.php",
          dataType: "html",
          data: {user: "tony", pwd: 123},
          success: function(data) {
            $("#result").html(data);
            }     
          });
        });
      });
  </script>
  </body>
</html>

結果如下 :





可見若 data 屬性值為物件, 則 $.post() 會將這些參數都放在 HTTP 本體中傳送.

綜合以上 $.post() 的測試可知, 此函數會以 POST 方法發出 HTTP 請求, 帶在 url 屬性中的參數會以查詢字串附在網址後面送出, 而帶在 data 屬性中的參數, 不論是字串或物件形式, 都會放在 HTTP 本體中送出, 摘要如下表 :


 url 屬性 data 屬性 HTTP 請求 後端 PHP 讀取參數
 test.php?a=1&b=2 無 POST $_GET["a"] 與 $_GET["b"]
 test.php?a=1 {b: 2} POST $_GET["a"] 與 $_POST["b"]
 test.php {a:1, b: 2} POST $_POST["a"] 與 $_POST["b"]
 test.php a=1&b=2 POST $_POST["a"] 與 $_POST["b"]


下面為以 $.post() 取得 XML 回應資料之範例, 後端 PHP 程式仍然是上面 $.get() 所用的 test_xml.php.


範例 13 : 用 $.post() 請求 XML 資料 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script>
    $(function(){
      $("#login").click(function(){
        $.post({
          url: "test_xml.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=$(data).find("user").text();
            var pwd=$(data).find("pwd").text();
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            }
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例先將回應之 XML 字串用 $() 工廠函數轉成 jQuery 物件, 以便用 find() 函數找尋 XML 節點元素. 此程式與上面範例 8 之差別只是將 $.get() 改為 $.post() 而已, 結果一樣得到下列 XML 回應 :

<?xml version='1.0' ?><result><user>tony</user><pwd>123</pwd></result>

下面為以 $.post() 取得 JSON 回應資料之範例, 後端 PHP 程式仍然是上面 $.get() 所用的 test_json.php.


範例 14 : 用 $.post() 請求 JSON 資料 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script>
    $(function(){
      $("#login").click(function(){
        $.post({
          url: "test_json.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=data.user;
            var pwd=data.pwd;
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            }
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此程式與上面範例 9 差別只是將 $.get() 換成 $.post() 而已, 結果完全一樣, 都會得到如下 JSON 回應 :

{"user":"tony","pwd":"123"}

除了可用 $.get() 或 $.post() 取得後端 JSON 回應資料, jQuery 還提供了 JSON 專用的工具函數 $.getJSOM().


3. $.getJSON() 函數 :

此函數會以 POST 方法向伺服器發出非同步請求, 其介面如下 :

$.getJSON(url [,data] [,callback] [,dataType])

此函數相當於下列 $.ajax() 的封裝 :

$.ajax({
  type: "POST",
  url: url,
  data: data,
  success: success,
  dataType: "json"
  });

由於 type 已經設為 POST, 因此 HTTP 要求為 POST 方法, 所以不論參數放在  url 中或以參數物件放在 data 屬性中, Ajax 引擎都會把參數放在 HTTP 本體 (body) 內傳送, 因此後端 PHP 程式都要用 $_POST[] 函數捕捉, 例如 :


範例 15 : 用 $.getJSON() 請求 JSON 資料 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script>
    $(function(){
      $("#login").click(function(){
        $.getJSON({
          url: "test_json.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=data.user;
            var pwd=data.pwd;
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            }
          });
        return false;
        });
      });
  </script>
  </body>
</html>

結果如下 :




可見雖然名稱為 $.getJSON(), 但發出的 HTTP 請求卻是 POST.


4. $.getScript() 函數 :

此函數會以 POST 方法向伺服器發出非同步請求, 其介面如下 :

$.getScript(url [,data] [,callback])

此函數相當於下列 $.ajax() 的封裝 :

$.ajax({
  type: "script",
  url: url,
  success: success,
  });

為了這個測試, 準備了一個 Javascript 程式 test_script.js 如下 :

alert("ok");

利用 $.getScript() 載入 test_script.js 後就會立即執行.


範例 16 : $.getScript() 載入 Javascript [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form>
   <button id="load">載入 Script</button>
   <div id="result"></div>
  </form>
  <script>
    $(function(){
      $("#load").click(function(){
        $.getScript({
          url: "test_script.js",
          dataType: "script",
          success: function() {
            $("#result").html("Ajax 請求成功!<br>");
            }
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例在載入成功後會先執行 Javascript 然後再執行回呼函數, 結果如下 :




參考 :

jquery getScript動態載入JS方法改進詳解

除了用 jQuery 工具函數進行 Ajax 操作外, jQuery 還有一個 DOM 元素物件的 load() 方法可進行 Ajax 存取, 用來直接將 Ajax 回應資料插入 DOM 元素的內容中, 參考 :

測試 jQuery 的 Ajax 方法 load()

OK! 終於在 2019 年的年尾將 jQuery 的 Ajax 全部測試完畢了, 完工!

泰正點購買拖把

今天收到國泰簡訊說泰正點點數將於年底歸零, 以前我沒時間管這個, 都讓它歸零, 今年想說看看有甚麼可買, 發現 2600 點可折抵 150 元, 就買了一組拖把, 原價 699, 折抵後 549 :




歐萊禮好書 : TinyML

今天在歐萊禮網站看到下面這本 12 月剛出版的新書 :

# TinyML: Machine Learning with TensorFlow Lite on Arduino and Ultra-Low-Power Microcontrollers


Source : Oreilly


不得了, 機器學習也能用在 Arduino 上了嗎? 仔細看目錄與序的說明, 原來 TinyML 是用在嵌入式設備上的小型機器學習架構, Google 推出了 TensorFlow 的微小版 TensorFlow Lite for Microcontrollers 框架, 是專門設計給一般微控器的機器學習框架, 可以在微控器上訓練語音辨識模型等. IoT 有了 AI 加持就變成 AIoT 啦!

我找到此書早期試讀版的一篇很棒的讀書心得 :

TinyML一書早期釋出版本閱讀心得

Adafruit 有一些展示影片 :

https://www.youtube.com/watch?v=Mogt8NSNvVM




https://www.youtube.com/watch?v=cn9PEDX_qLk




這些 TinyML 應用看起來都是在效能較好的嵌入式設備上跑, 我有點懷疑 Arduino 這種金魚腦處理器真的跑得動 TinyML 嗎?

擴充 jQuery 的工具函數

之前測試過 jQuery 內建的工具函數, 例如 $.each(), $.grep() 等好用函數, 但這些函數的功能並不能滿足所有的應用, jQuery 提供了兩種方法可讓我們自行擴充工具函數. 此篇測試參考了下面兩本書中的範例 :

超詳解 jQuery 開發範例大全 (陳佳新譯, 悅知文化) 10-2-2 節
打造 jQuery 網頁風暴 (張子秋, 佳魁) 10.5 節


在之前的 jQuery 測試中, 如果要在網頁中顯示執行結果有下列方法 :
  1. 使用 alert() 函數
  2. 使用 document.write() 直接覆蓋 body 原內容
  3. 使用 console.log() 輸出到瀏覽器的控制台 
  4. 在網頁中設置一個 div 或 p 元素, 以選擇器選取後呼叫 html() 輸出
第一種方法會彈出是顯示, 資料多時要捲動視窗不方便; 第二種方法每一次呼叫 document.write() 都會覆蓋前一次呼叫輸出之內容, 必須將輸出字串串接後一次輸出; 第三種方法則需要在網頁中額外放一個元素.

下面用擴充工具函數方式只要在網頁中添加程式碼即可呼叫 $.print() 直接於網頁中輸出訊息.


1. 利用 Javascript 本身的擴充功能 :

只要在網頁中加入下列擴充函數即可 :

$.print=function(msg){$("<div></div>").html(msg).appendTo('body');};

此程式先建立一個空的 div 元素, 將其內容設為要顯示的資料後呼叫 appendTo() 將 div 插入到 body 元素後面.

例如 :


範例 1 : 擴充 Javascript 函數來輸出訊息 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <input type="button" id="Hello" value="Hello">
  <script> 
    $(function(){
      $.print=function(msg){$("<div></div>").html(msg).appendTo('body');};
      $("#Hello").click(function(){
        $.print("Hello World");
        });
      });
  </script>
  </body>
</html>

此例按下 Hello 按鈕就會呼叫 $.print() 函數於網頁中輸出訊息, 結果如下 :




2. 使用 $.extend() 工具函數擴充 : 

jQuery 本身有一個 $.extend() 工具函數可用來擴充自訂的工具函數 :

$.extend({functionName: function() {}});

以上面在網頁中輸出訊息為例, 擴充函數如下 :

      $.extend({print: function(msg){
        $("<div></div>").html(msg).appendTo('body');
        }});

此處 functionName 為 print, 表示呼叫時要用 $.print(), 上面的範例可以改寫為如下程式 :


範例 2 : 使用 $extend() 擴充工具函數來輸出訊息 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <input type="button" id="Hello" value="Hello">
  <script> 
    $(function(){
      $.extend({print: function(msg){
        $("<div></div>").html(msg).appendTo('body');
        }});
      $("#Hello").click(function(){
        $.print("Hello World");
        });
      });
  </script>
  </body>
</html>

結果與上面範例 1 相同.

2019年12月30日 星期一

2019 年第 52 周記事

終於迎來 2019 最後一周, 一年前的這時候在做啥? 相信沒幾個人能記得起來吧? 哈哈! 這下就是寫流水帳的好處了, 倒帶到去年的最後一周, 發現原來那時的我正在 :
  • 學習 Julia
  • 買了 PyTorch 的 NLP 新書 (抱歉, 到現在還沒時間看)
  • 看韓劇 "我的大叔"
  • 剛結束正修科大電子系的六堂基本電學協同教學
要不是有花時間在做紀錄, 不管經歷甚麼, 學了甚麼, 憂傷還是歡樂, 一個月之後都漸漸淡出我的記憶舞台, 一年之後沒人追問或提醒全然不記得曾經發生過這些事. 所謂的人生都是建築在記憶之上, 沒有記憶或失去記憶都是極其可怕之事. 不過還好有 blogger 當我的事件紀錄器, 以及那個因為擔心失去記憶而勤於紀錄的自己.

過去兩周都在忙著整理 jQuery 筆記, 主要集中在 Ajax 操作相關. 在學過 jQuery 十年後回頭複習, 與現今流行的 Reat, Angular, 與 Vue 相比, jQuery 似乎顯得老氣橫秋. 雖然我也一時好奇學了點 Vue, 覺得還真的很好用, 但是心裡一直有個疑問, jQuery 能辦到的事情, 為什麼還需要 Vue 呢? 下面這篇文章寫得很棒, 幾乎完全回答我的問題 :

脫離資料分散的問題,從 jQuery 換到 Vue.js 

原來是要解決資料統整問題, 主要原因是 jQuery 沒有資料流觀念. 但作者也說了, 如果只是在寫中小型專案或資料統整不是很重要, 用 jQuery 能更快速完成任務, 因為用 Vue 的話需先花較多時間做資料規劃. 對啊! 如果 jQuery 就能達成專案目的, 為何要改用 Vue 呢? 老狗只要玩好老把戲就可以了, 不需要一味追求新技術. 雖然對 Vue 很感興趣, 但目前時間寶貴, 所以 Vue 等有空再來學吧!

本周終於把長達 32 集的 "意外發現的一天" 看完了, 結局是河路在一年後殷端午上大學時回來了, 但好奇怪, 為什麼高三時河路會突然消失呢? 這部戲在場景與暗處不斷切換, 有時都會弄不清楚, 總之對河路好時是在暗處, 對白經好時就是在場景.

這部戲金惠允的演技大爆發, 我真的會被她豐富的表情笑死, 特別是裝可愛的時候習慣兩手向耳際梳頭髮. 她之前在天空之城出演明星高中資優生姜藝瑞的表現就很吸睛, 今天看維基資料才發現原來她也在隧道中演出過華陽連續殺人案唯一倖存者, 那時是客串沒注意.

# 意外發現的一天 OST3 SONDIA - « 첫사랑 初戀 »  Sondia (孫敏京)




週日經過種子行買了許多菜苗 :
  1. 羅蔓 *30
  2. 日本蘿蔓 *30
  3. 波菜 *30
  4. 香菜 *20
下午滷菜與煮湯都 ok 後從兩點種到四點才把這 110 棵菜苗種完, 另外阿運伯母給的地瓜葉剩下的藤也沒浪費, 也種在蔥的旁邊 :





鄉下的山葉迅光 125 老舊機車本周汰換成光陽 Cute100, 老闆周三就載來了, 價格 46000 含稅, 舊機車淘汰補助 5000 元另外申請後再匯入帳戶 :




由於此車比迅光小, 原先的洗衣籃有點擠不進腳踏板空間, 昨天去市場時順路去上元買了一個剛好合身的, 但高度太低去全聯買的東西放不下, 回來後還是把原來的大洗衣籃塞進去, 新買的就真的拿來當洗衣籃.

自 2005 年 8 月上線的公司工作日誌系統在 2019 年最後一周因為 JLOG5 資料庫已滿 2GB 而停擺, 我剛好又在忙工作而沒時間將其升級為 JLOG6, 雖然工作上造成一些不便, 但似乎一個禮拜過去也還好, 我都直接列印工作單恢復紙本作業. 這個起源於我的碩士論文, 已連續運轉 15 年的老舊系統也應該功成身退了吧!

2019年12月29日 星期日

好書 : Advances in Financial Machine Learning

今天在亞馬遜看到 Wiley 出版的機器學習應用在財務工程 (Fintech) 的好書, 看目錄說明覺得非常值得一讀, 且目前特價才 12.5 美元. 不過我想看的書太多了, 先記下來 :

# Amazon : Advances in Financial Machine Learning (US$ 50 => 12.5)
Advances in Financial Machine Learning (Wiley, 2018)


Source : Wiley


這本國內有進, 但價格貴了好幾倍 :

# 博客來 : Advances in Financial Machine Learning $1750
# 天瓏 : Advances in Financial Machine Learning $1568

此書以 Python 為實作語言 (太棒了), 前面介紹資料科學作為鋪路, 中間是 ML 模型與演算法, 後面是金融特徵擷取與投資策略, 有介紹夏農的 (熵, 音同滴) 理論, 看來是理論與實作兼具的好書, 特別是每章有習題, 可當財務工程教科書用.

最近在學 AWS, 或許以後可把 Fintech 佈署在 AWS 上跑, 參考 :

在 AWS 上免費使用 Machine Learning

2019年12月28日 星期六

喵星族二號報到

因水某同事家的母貓生了兩隻小貓要送養, 水某說想養其中一隻黃色毛的, 要不然會被賣給寵物店, 所以周四晚上就開車去載回來, 一回來打開籠子馬上一溜煙鑽進椅子底下躲起來喵喵叫, 可能它也知道家裡還有另一隻貓, 更加不敢出來.

小咪也是馬上採取警戒姿態, 以前進客廳都大搖大擺, 還前後拉腿, 一副爽貓樣, 但現在不同, 躡手躡腳還不說, 眼睛還四處搜尋, 小小咪待過的地方還聞來聞去, 確認對方的氣味. 小小咪躲在椅子下兩天, 今天終於敢走出來了 :




小咪這幾天緊張兮兮, 看來神經緊繃, 昨晚還不敢去菁菁房間睡 (因小小咪待過), 溜進我房間躲在雜衣堆中睡覺, 演出咪咪版 "威利在哪裡", 在黑衣堆中沒仔細看還找不到哩 :




很好奇兩隻貓要怎麼從陌生情況打開新關係? 雖然還不確定小小咪的性別, 但我打算叫它 "奶茶妹", 菁菁則堅持叫它 "兩萬", 因為聽說寵物店買要花兩萬, 我覺得 "奶茶妹" 不是比較好聽嗎?

jQuery 的工具函數測試

jQuery 的優點除了提供好用的選擇器可用來操控網頁內容外, 還提供了許多工具函數 (utilities) 可用來操作 DOM 物件以外的 Javascript 物件, 不僅可簡化 Javascript 程式碼, 也彌補了 Javascript 內建函數之不足, 其主要功能有 :
  • 陣列/物件操作
  • 字串處理
  • Ajax 請求
  • 表單請求參數處理
這些工具函數都是定義於 jQuery 名稱空間 (name space) 中的頂層函數, 並不是某個 DOM 元素的 jQuery 物件之方法, 因此這些函數是用 $ 物件直接呼叫, 它們被用來操作 Javascript 物件而不是 DOM 物件. 操作 DOM 元素的功能通常被設計為 jQuery 物件之方法.

jQuery 說明文件參考 :

https://jquery.com/
https://www.jquery123.com/


一. 陣列與物件處理 : 

jQuery 工具函數中與陣列或物件處理相關之函數如下表 :


 jQuery 陣列/物件工具函數 說明
 $.each(array/object, callback) 迭代陣列元素或物件屬性給回呼函數, 傳回新物件或陣列
 $.grep(array, callback[, invert]) 以回呼函數過濾陣列, 以通過之元素組成新陣列傳回
 $.map(array, callback) 迭代陣列元素至回呼函數, 以傳回值組成新陣列傳回
 $.makeArray(object) 將物件轉成陣列後傳回
 $.merge(array1, array2) 將兩個陣列合併後傳回
 $.unique(array) 去除陣列內重複之元素後傳回新陣列
 $.inArray(value, array) 檢查 value 是否為陣列 array 之元素 (true/false)
 $.isArray(param) 檢查參數是否為 Javascript 陣列
 $.isPlainObject(param) 檢查參數為用 {} 或 new Object() 建立之物件


1. $.each() 函數 :

此函數是一個通用的迭代 (iteration) 函數, 可以同時迭代陣列, 類陣列 (例如代表函數引數的 arguments 物件), 或物件. 其介面如下 :

$.each(collection, callback)

第一個參數為欲迭代的對象容器, 包含陣列, 類陣列, 與物件. 這些容器由不定個數的元素組成, 為了存取這些容器的元素, Javascript 提供了下列迴圈或方法來迭代這些容器的元素 :
  • for 迴圈 : 用來迭代字串, 陣列或類陣列物件之元素
  • for in 迴圈 : 用來迭代字串, 陣列元素或物件屬性
  • for of 迴圈 : 用來迭代字串, 陣列元素或物件屬性
  • forEach 方法 : 用來迭代陣列元素 
其中 forEach 與 for of 比較新, forEach 是 ECMAScript 5 (ES5) 才加入的功能 (只能用在陣列), 而 for of 則是為了修補 for in 之不足而在 ECMAScript 2015 (ES6) 才加入的功能, 有些較舊的瀏覽器並不支援 (例如 IE8 與 Firefox 1.8),  jQuery 的 $.each() 函數不僅可同時用在陣列與物件, 而且解決了跨瀏覽器與版本問題.


範例 1 : Javascript 的迭代迴圈 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var arr=["a", "b", "c"];
        var obj={name:"tony", gender:"male", age:18};
        document.write('arr=["a", "b", "c"]<br>');     
        var str="Hello World!";
        document.write('arr=["a", "b", "c"]<br>');
        document.write('obj={name:"tony", gender:"male", age:18}<br>');   
        //測試物件的 for in 迴圈
        document.write('for (var attr in obj) : ');
        for (var attr in obj){            //用在物件時, 迭代對象為屬性 (鍵)
          document.write(attr + ":" + obj[attr] + ", ");
          }
        document.write("<br>");
        //測試陣列的 for in 迴圈
        document.write('for (var idx in arr) : ');
        for (var idx in arr){             //用在陣列時, 迭代對象為索引
          document.write(arr[idx] + ", ");
          }
        document.write("<br>");
        //測試字串的 for in 迴圈
        document.write('for (var idx in str) : ');
        for (var idx in str){             //用在字串時, 迭代對象為索引
          document.write(str[idx] + ", ");
          }
        document.write("<br>");
        //測試陣列的 for of 迴圈
        document.write('for (var ele of arr) : ');
        for (var ele of arr){                //用在陣列時,  迭代對象為元素
          document.write(ele + ", ");
          }
        document.write("<br>");
        //測試陣列的 forEach() 方法
        document.write('arr.forEach : ');
        //測試字串的 for of 迴圈
        document.write('for (var ele of str) : ');
        for (var ele of str){                //用在字串時, 迭代對象為元素 (字元)
          document.write(ele + ", ");
          }
        document.write("<br>");
        arr.forEach(function(ele) {     //只能用在陣列, 迭代對象為元素
          document.write(ele + ",");
          });
        document.write("<br>");
        });
    </script>
  </body>
</html>

結果如下 :




for in 迴圈不僅可用在陣列, 也可以用在物件. 對於陣列而言, for (var idx in arr) 中迭代的對象是索引 idx, 故取得陣列元素要用 arr[idx]. 但對於物件而言, for (var attr in obj) 中迭代的是物件的鍵 (屬性), 但其值仍要用 obj[attr] 存取, 用 obj.attr 會得到 undefined. 與 for in 不同的是, for (var ele of arr) 只能用在字串或陣列的迭代, 不可用在物件, 且迭代的對象是字串之字元或陣列之元素而非索引, 這是 for in 與 for of 最大的不同. forEach() 方法與 for of 迴圈很相似, 都只能用在陣列, 且迭代的對象都是元素, 參考 :

JavaScript中for of和for in的差別

jQuery 的 $.each() 函數可同時迭代陣列與物件 (但不能迭代字串), 其介面如下 :

$.each(array/object, function(key/index, value))

第一參數為要迭代的對象, 可以傳入陣列或物件. 第二參數為回呼函數, 其傳入參數有兩個, 第一個是陣列的索引或物件的屬性, 第二個是值.


範例 2 : $.each() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var arr=["a", "b", "c"];
        var obj={name:"tony", gender:"male", age:18};
        document.write('arr=["a", "b", "c"]<br>');
        document.write('obj={name:"tony", gender:"male", age:18}<br>');   
        document.write('str="Hello World!"<br>');
        //測試陣列的 $.each() 迴圈
        document.write('$.each(arr, callback) : ');
        $.each(arr, function(idx, value){
          document.write("arr[" + idx + "]=" + value + ", ");
          });
        document.write("<br>");
        //測試物件的 $.each() 迴圈
        document.write('$.each(obj, callback) : ');
        $.each(obj, function(key, value){
          document.write("obj." + key + "=" + value + ", ");
          });
        document.write("<br>");
        //測試物件的 $.each() 迴圈
        document.write('$.each(document.location, callback) : ');
        $.each(document.location, function(key, value){
          document.write("document.location." + key + "=" + value + ", ");
          });
        document.write("<br>");
        });
    </script>
  </body>
</html>

此例分別迭代拜訪了陣列元素與物件成員, 注意傳入回呼函數的第一參數在陣列為其索引, 在物件為其屬性. 結果如下 :

arr=["a", "b", "c"]
obj={name:"tony", gender:"male", age:18}
str="Hello World!"
$.each(arr, callback) : arr[0]=a, arr[1]=b, arr[2]=c,
$.each(obj, callback) : obj.name=tony, obj.gender=male, obj.age=18,
$.each(document.location, callback) : document.location.href=http://tony1966.xyz/test/jquerytest/jquerytest_utilities_each.htm, document.location.ancestorOrigins=[object DOMStringList], document.location.origin=http://tony1966.xyz, document.location.protocol=http:, document.location.host=tony1966.xyz, document.location.hostname=tony1966.xyz, document.location.port=, document.location.pathname=/test/jquerytest/jquerytest_utilities_each.htm, document.location.search=, document.location.hash=, document.location.assign=function assign() { [native code] }, document.location.reload=function reload() { [native code] }, document.location.toString=function toString() { [native code] }, document.location.replace=function replace() { [native code] },

注意, $.each() 不能用來迭代字串, 要用 for in 或 for of 迴圈才可以.


2. $.grep() 函數 :

此函數用來過濾陣列元素, 陣列元素會逐一迭代傳入回呼函數中, 回呼函數中的條件式若傳回 true 則此元素會被傳回給新陣列, 否則會被丟棄, 傳回值為過濾後之新陣列, 其介面如下 :

$.grep(array, function(value [, index]) [, invert])

第一個參數為欲過濾的陣列, 第二個參數為回呼函數, 可傳入兩個參數, 第一個是必要的被迭代之陣列元素 value, 第二個是備選的索引 index. $.each() 的第三個參數為備選之布林值, 預設為 false, 若設為 true, 則過濾規則會相反, 即符合條件的被丟棄, 不符條件的被傳回給新陣列.


範例 3 : $.grep() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var arr=[34,78,99,84,56,22,100];
        document.write('arr=[34,78,99,84,56,22,100]<br>');
        var narr=$.grep(arr, function(value){   //過濾元素值
          return value >= 60;
          });
        document.write("value >= 60 : " + narr.join() + "<br>");
        narr=$.grep(arr, function(value){    //反向過濾
          return value >= 60;
          }, true);
        document.write("value >= 60 (invert) : " + narr.join() + "<br>");
        narr=$.grep(arr, function(value, index){    //過濾索引
          return index > 4;
          });
        document.write("index > 4 : " + narr.join() + "<br>");
        });
    </script>
  </body>
</html>

此例使用 $.grep() 對同一個成績陣列過濾, 但條件不同, 第一個過濾分數大於等於 60 分者; 第二個則是使用 invert 參數進行反向過濾 (低於 60 分者), 第三個是過濾陣列索引.

執行結果如下 :

arr=[34,78,99,84,56,22,100]
value >= 60 : 78,99,84,100
value >= 60 (invert) : 34,56,22
index > 4 : 22,100

對於較複雜的過濾條件可在比對條件中使用正規表達式, 例如美國的郵遞區號 (ZIP) 格式為 10 個字元, 前面五個數字後面四個數字中間用 dash 串接, 例如 22162-1010, 其正規表達式為 /^\d{5}(-\d{4})?$/, 若要從一堆 ZIP 號碼中挑出正確的號碼可用字串的 match() 方法 :

return value.match(/^\d{5}(-\d{4})?$/) != null


範例 3 : $.grep() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var arr=["12345-6789","234567-123","8888a-5432"];
        document.write('arr=["12345-6789","234567-123","8888a-5432"]<br>');
        var narr=$.grep(arr, function(value){
          return value.match(/^\d{5}(-\d{4})?$/) != null;
          });
        document.write("valid ZIP: " + narr.join() + "<br>");
        var narr=$.grep(arr, function(value){
          return value.match(/^\d{5}(-\d{4})?$/) != null;
          }, true);
        document.write("invalid ZIP: " + narr.join() + "<br>");
        });
    </script>
  </body>
</html>

結果如下 :

arr=["12345-6789","234567-123","8888a-5432"]
valid ZIP: 12345-6789
invalid ZIP: 234567-123,8888a-5432


3. $.map() 函數 :

此函數用來將陣列或物件透過迭代運算轉換 (映射) 成另一個陣列, 其介面如下 :

$.map(array/object, function(value [, index/attr])

第一個參數為待轉換之陣列或物件, 第二個參數為回呼函數, 它也可傳入兩個參數, 第一個是陣列元素或物件之值 (必要), 第二個是可有可無的陣列索引或物件的屬性, 回呼函數的傳回值會被收集起來成為轉換後的新陣列, 但有一些特殊的回傳值要注意 :
  • 如果傳回 null 或 undefined 就不會被收集, 這樣轉換後的新陣列或物件就會比原來的小 (失去對映).
  • 如果回傳值不是純量 (scaler), 而是向量 (即一個陣列), 則這些向量會全部串接成一個陣列. 
例如 :


範例 4 : $.map(陣列) 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var score=[34,78,99,84,56,22,100];
        document.write('arr=[34,78,99,84,56,22,100]<br>');
        //傳回全部且不及格加 5 分
        var result1=$.map(score, function(value){
          return value < 60 ? value + 5 : value;
          });
        document.write("result1[]: " + result1.join() + "<br>");
        //只傳回及格者並加 5 分
        var result2=$.map(score, function(value){
          return value > 60 ? value + 5 : null;      //null 濾掉不及格者
          });
        document.write("result2[]: " + result2.join() + "<br>");
        //傳回陣列元素與索引
        var result3=$.map(score, function(value, index){
          return value > 60 ? [value, index] : null;
          });
        document.write("result3[]: " + result3.join() + "<br>");
        //轉換字串陣列
        var data=["2","4.5","23%","10","@!","22"];
        document.write('data=["2","4.5","23%","10","@!","22"]<br>');
        var result4=$.map(data, function(value){
          var num=new Number(value);
          return isNaN(num) ? undefined : num;     //undefined 濾掉非數字
          });
        document.write("result4[]: " + result4.join() + "<br>");
        });
    </script>
  </body>
</html>

此例成績陣列 score 利用回呼函數進行轉換, 只要沒有傳回 null 或 undefined則新舊陣列長度一樣, 否則轉換後新陣列較小. 陣列 data 用來示範如何濾掉非數字元素, 結果如下 :

arr=[34,78,99,84,56,22,100]
result1[]: 39,78,99,84,61,27,100
result2[]: 83,104,89,105
result3[]: 78,1,99,2,84,3,100,6
data=["2","4.5","23%","10","@!","22"]
result4[]: 2,4.5,10,22

$.map() 也可以傳入物件進行轉換, 但傳回值為一陣列, 例如 :


範例 5 : $.map(物件) 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var obj1={math:57,chinese:95,english:78,art:45};
        document.write('obj1={math:57,chinese:95,english:78,art:45}<br>');
        var result1=$.map(obj1, function(value, attr){
          return value < 60 ? attr : null;    //傳回及格科目
          });
        document.write("value < 60 : " + result1 + "<br>");
        document.write($.isArray(result1) + "<br>");
        var obj2={a:1,b:[2,3],c:{c1:4,c3:5}};
        document.write('obj2={a:1,b:[2,3],c:{c1:4,c3:5}}<br>');
        var result2=$.map(obj2, function(value, attr){
          return value;   //直接傳回屬性值
          });
        document.write("value : " + result2 + "<br>");
        document.write($.isArray(result2) + "<br>");
        });
    </script>
  </body>
</html>

此例傳入 $.map() 的是兩個物件, obj1 為成績單, 回呼函數判斷屬性值 (成績) 是否及格, 是傳回科目 attr (屬性), 否則傳回 null (不收集), 因此 $.map() 收集到的轉換後之陣列為及格科目所組成之陣列. obj2 是較複雜的物件, 屬性 b 之值為陣列, 傳回時會與其他元素串接, 屬性 c 為一物件, $.map() 只會迭代第一層屬性, 不會往下迭代, 因此傳回 [Object Object], 結果如下 :

obj1={math:57,chinese:95,english:78,art:45}
value < 60 : math,art
true
obj2={a:1,b:[2,3],c:{c1:4,c3:5}}
value : 1,2,3,[object Object]
$.isArray(result2): true
$.isArray(result2[3]): false
$.isPlainObject(result2[3]): true

此處使用了 $.isArray() 與 $.isPlainObject() 來檢驗回呼函數傳回值, 可見屬性 c 之值為物件被直接傳回去, 因此 result2 陣列的索引 3 為一個物件.


4. $.makeArray() 函數 :

Javascript 或 jQuery 許多函數或物件方法 (例如工廠函數 $) 的傳回值是類陣列物件 (例如函數引數 arguments), 並非原生陣列, 因此無法使用陣列的 push(), pop(), 或 reverse() 等方法. $.makeArray() 函數可用來將這些陣列式物件轉換成真正的 Javascript 陣列, 其介面如下 :

$.makeArray(object)

傳入參數只有一個, 即要轉換的物件, 傳回值為一個原生單純的 Javascript 陣列. 例如 :


範例 6 : $.makeArray() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var obj={a:1, b:2, c:3};
        document.write('obj={a:1, b:2, c:3}<br>');
        document.write('arr=$.makeArray(obj)<br>');
        var arr=$.makeArray(obj);
        document.write("$.isArray(arr): " + $.isArray(arr) + "<br>");
        document.write("arr.length : " + arr.length + "<br>");
        document.write('$.each(arr, callback) : ');
        $.each(arr, function(idx, value){
          document.write("arr[" + idx + "]=" + value + ", ");
          });
        document.write("arr[]: " + arr.join() + "<br>");
        document.write("$.isPlainObject(arr[0]): " + $.isPlainObject(arr[0]) + "<br>");
        document.write("for (var attr in arr[0]): ");
        for (var attr in arr[0]){       
          document.write(attr + ":" + arr[0][attr] + ", ");
          }
        });
    </script>
  </body>
</html>

結果如下 :

obj={a:1, b:2, c:3}
arr=$.makeArray(obj)
$.isArray(arr): true 
arr.length : 1 
$.each(arr, callback) : arr[0]=[object Object], arr[]: [object Object]
$.isPlainObject(arr[0]): true
for (var attr in arr[0]): a:1, b:2, c:3

此例用 $.makeArray() 將一個簡單的物件 obj={a:1, b:2, c:3} 轉成陣列, 可見其作法是將物件內容打包為一個陣列元素 (用 $.isArray 檢查為 true), 所以轉換後得到的 arr 陣列只有一個元素, 其值用 $.isPlainObject() 去檢查可知為一物件.

下面範例則是用 $.makeArray() 將 DOM 元素的 jQuery 物件轉成陣列, 這樣就可以呼叫陣列的 push(), pop(), reverse() 等方法操作元素 :


範例 7 : $.makeArray() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <ul id="result">
      <li>a</li>
      <li>b</li>
      <li>c</li>
      <li>d</li>
      <li>e</li>
    </ul>
    <button id="reverse">reverse</button>
    <button id="push">push</button>
    <button id="pop">pop</button>
    <button id="sort">sort</button><br><br>
    <div id="info"></div>
    <script>
      $(function(){
        var i=1;
        $("#reverse").click(function(){
          var obj=$("li");
          var arr=$.makeArray(obj);
          arr.reverse();           //陣列反轉
          var info=[];
          info.push('obj=$("li")<br>');
          info.push('arr=$.makeArray(obj)<br>');
          info.push("$.isArray(arr): " + $.isArray(arr) + "<br>");
          info.push("arr.length : " + arr.length + "<br>");
          $("#info").html(info.join(""));
          $("#result").html($(arr));
          });
        $("#push").click(function(){
          $("#result").append("<li>" + (i++) + "</li>");
          var obj=$("li");
          var arr=$.makeArray(obj);
          var info=[];
          info.push('obj=$("li")<br>');
          info.push('arr=$.makeArray(obj)<br>');
          info.push("$.isArray(arr): " + $.isArray(arr) + "<br>");
          info.push("arr.length : " + arr.length + "<br>");
          $("#info").html(info.join(""));
          $("#result").html($(arr));
          });
        $("#pop").click(function(){
          var obj=$("li");
          var arr=$.makeArray(obj);
          arr.pop();      //刪除陣列最後一個元素
          var info=[];
          info.push('obj=$("li")<br>');
          info.push('arr=$.makeArray(obj)<br>');
          info.push("$.isArray(arr): " + $.isArray(arr) + "<br>");
          info.push("arr.length : " + arr.length + "<br>");
          $("#info").html(info.join(""));
          $("#result").html($(arr));
          });
        $("#sort").click(function(){
          var obj=$("li");
          var arr=$.makeArray(obj);
          arr.sort();
          var info=[];
          info.push('obj=$("li")<br>');
          info.push('arr=$.makeArray(obj)<br>');
          info.push("$.isArray(arr): " + $.isArray(arr) + "<br>");
          info.push("arr.length : " + arr.length + "<br>");
          $("#info").html(info.join(""));
          $("#result").html($(arr));
          });
        });
    </script>
  </body>
</html>

此例中網頁預設已有 a, b, c 清單, 按 reverse 按鈕會先用選擇器取得全部 li 元素物件, 透過 $.makeArray() 將此物件集合轉成陣列後, 呼叫陣列的 reverse() 方法讓清單元素反轉順序, 再將結果用 $() 工廠函數轉回 DOM 之jQuery 物件覆蓋原來的清單更新網頁. 同理, 按 pop 會從 arr 陣列中去除最後一個元素. 但 push 部分若用 arr.push("<li></li>") 會出現錯誤, 故暫時用 append() 先新增清單, 還沒用到 push() 函數. 結果如下 :




但 sort() 似乎無效, 這可能是其元素為物件之故.


5. $.merge() 函數 :

此函數用來合併兩個陣列, 其介面如下 :

$.merge(array1, array2)

此函數有兩個必要參數, 即要合併的陣列, 合併是將第二個陣列合併到第一個陣列, 然後將第一個陣列傳回. 此合併操作 array1 會改變, 但 array2 則不變, 例如 :


範例 8 : $.merge() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var arr1=[1,2,3];
        var arr2=[4,5,6];
        document.write('arr1=[1,2,3]<br>');
        document.write('arr2=[4,5,6]<br>');
        var arr1_bak=$.merge([], arr1);     //備份 arr1
        document.write('arr1_bak[]: ');
        $.each(arr1_bak, function(value){
          document.write(value + ", ");
          });
        document.write("<br>");
        var arr3=$.merge(arr1, arr2);   //將 arr2 合併到 arr1 並傳回給 arr3
        document.write('arr3[]: ');
        $.each(arr3, function(value){
          document.write(value + ", ");
          });
        document.write("<br>");
        document.write('arr1[]: ');
        $.each(arr1, function(value){   //檢查 arr1 內容
          document.write(value + ", ");
          });
        document.write("<br>");
        });
    </script>
  </body>
</html>

由於第一個陣列為結果陣列會被改變, 因此如果有保留需要可先用空陣列 [] 與 arr1 先合併, 將 arr1 備份到 arr1_bak, 結果如下 :

arr1=[1,2,3]
arr2=[4,5,6]
arr1_bak[]: 0, 1, 2,
arr3[]: 0, 1, 2, 3, 4, 5,
arr1[]: 0, 1, 2, 3, 4, 5,

可見合併後 arr1 與 arr3 是一樣的. 事實上可以將複製陣列結果當作 $.merge() 的第一參數, 例如 :

var arr3=$.merge($.merge([], arr1), arr2);

這樣就不需要用到 arr1_bak 了, 而且 arr1 與 arr2 都不變.


6. $.unique() 函數 :

此函數用來剔除 DOM 元素陣列中的重複元素, 其介面如下 :

$.unique(array)

傳回值為剔除重複 DOM 元素經排序過的陣列. 注意, 此函數只能用於 DOM元素陣列, 不能用在一般陣列, 否則會出現非預期結果, 此函數在 jQuery 3 已經被棄用, 改為 $.uniqueSort(), 但 $.unique() 仍作為其別名而可繼續使用.


範例 9 : $.unique(一般陣列) 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        var arr=[9,3,4,3,7,3];
        document.write('arr=[9,3,4,3,7,3]<br>');
        var arr1=$.unique(arr);
        document.write('arr1[]: ');
        $.each(arr1, function(index, value){
          document.write(value + ", ");
          });
        });
    </script>
  </body>
</html>

結果如下 :

arr=[9,3,4,3,7,3]
arr1[]: 9, 3, 4, 3, 7, 3,

可見 $.unique() 對一般陣列無效, 必須傳入 DOM 元素陣列, 例如 :


範例 10 : $.unique(DOM 元素陣列) 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <div class="red">a</div>
    <div class="blue">b</div>
    <div class="blue">c</div>
    <div class="green">d</div>
    <div class="red">e</div>
    <p id="result"></p>
    <script>
      $(function(){
        var arr=[];
        //取得 div 元素陣列 div0
        var div0=$("div").get();
        arr.push('div0=$("div").get() : ');
        $.each(div0, function(k, v){
          arr.push($(v).html() + ',');
          });
        arr.push('<br>');
        //取得 class=blue 之元素陣列 div1
        var div1=$(".blue").get();
        arr.push('div1=$(".blue").get() : ');
        $.each(div1, function(k, v){
          arr.push($(v).html() + ',');
          });
        arr.push('<br>');
        //串接元素陣列 div0 與 div1 傳回 div2
        var div2=div0.concat(div1);
        arr.push('div2=div0.concat(div1) : ');
        $.each(div2, function(k, v){
          arr.push($(v).html() + ',');
          });
        arr.push('<br>');
        //去除 div2 中的重複元素傳回 div3
        var div3=$.unique(div2);
        arr.push('div3=$.unique(div2) : ');
        $.each(div3, function(k, v){
          arr.push($(v).html() + ',');
          });
        $("#result").html(arr.join(""));
        });
    </script>
  </body>
</html>

結果如下 :

a
b
c
d
e
div0=$("div").get() : a,b,c,d,e,
div1=$(".blue").get() : b,c,
div2=div0.concat(div1) : a,b,c,d,e,b,c,
div3=$.unique(div2) : a,b,c,d,e,

可見串接後重複的 class=blue 元素 b,c 被 unique() 刪除了.


7. $.inArray() 函數 :

此函數用來陣列中是否含有特定元素, 其介面如下 :

$.inArray(value, array [, fromIndex])

第一個參數為要在陣列中搜尋之值, 第二個參數是要搜尋之陣列, 第三個參數是要搜尋之索引起點, 預設為 0 (即從頭開始搜尋). 此函數會傳回搜尋到的第一個元素索引, 若未找到則傳回 -1, 這與 Javascript 搜尋子字串的 indexOf() 方法功能類似, 例如 :


範例 11 : $.inArray() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <div id="result"></div>
    <script>
      $(function(){
        var arr=[];
        var fruits=["apple", "grape", "guava"];
        arr.push('var arr=["apple", "grape", "guava"]');
        var idx=$.inArray("grape", fruits);
        arr.push('$.inArray("grape", fruits): ' + idx);
        var idx=$.inArray("pineapple", fruits);
        arr.push('$.inArray("pineapple", fruits): ' + idx);
        var idx=$.inArray("guava", fruits);
        arr.push('$.inArray("guava", fruits): ' + idx);
        $("#result").html(arr.join("<br>"));
        });
    </script>
  </body>
</html>

結果如下 :




8. $.isArray() 函數 :

此函數用來檢測傳入參數是否為陣列, 只有用 [] 或 new Array() 建立的陣列才會傳回 true, 其介面如下 :

$.isArray(param)


範例 12 : $.isArray() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <div id="result"></div>
    <script>
      $(function(){
        var arr=[];
        var b=$.isArray([]);
        arr.push('$.isArray([]): ' + b);
        b=$.isArray(new Array("a", "b"));
        arr.push('$.isArray(new Array("a", "b")): ' + b);
        b=$.isArray({});
        arr.push('$.isArray({}): ' + b);
        b=$.isArray($);
        arr.push('$.isArray($): ' + b);
        b=$.isArray("Hello World!");
        arr.push('$.isArray("Hello World!"): ' + b);
        $("#result").html(arr.join("<br>"));
        });
    </script>
  </body>
</html>

結果如下 :




9. $.isPlainObject() 函數 :

此函數用來檢測傳入參數是否為普通物件, 即用 {} 或 new Object() 所建立之物件, 其介面如下 :

$.isPlainObject(param)

參數可以是任何型態資料 (例如 $ 或 jQuery 物件), 但只有傳入普通物件時才會傳回 true, 例如 :


範例 13 : $.isPlainObject() 測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <div id="result"></div>
    <script>
      $(function(){
        var arr=[];
        var b=$.isPlainObject({});
        arr.push('$.isPlainObject({}): ' + b);
        b=$.isPlainObject(new Object());
        arr.push('$.isPlainObject(new Object()): ' + b);
        b=$.isPlainObject("Hello World!");
        arr.push('$.isPlainObject("Hello World!"): ' + b);
        b=$.isPlainObject(document.location);
        arr.push('$.isPlainObject(document.location): ' + b);
        $("#result").html(arr.join("<br>"));
        });
    </script>
  </body>
</html>

結果如下 :




此例主要在測試 $.isPlainObject() 函數只有用 {} 與 new Object() 建立的普通物件才會傳回 true, 否則傳回 false.


二. 字串處理 :

jQuery 的字串工具函數有如下四個 :


 jQuery 字串工具函數 說明
 $.trim(string) 去除傳入字串前後的空白字元, 傳回結果字串, 原字串不變
 $.parseHTML(string) 剖析 JSON 格式字串, 傳回 Javascript 物件
 $.parseXML(string) 剖析 XML 格式字串, 傳回 XML 文件
 $.parseJSON(string) 剖析 JSON 格式字串, 傳回 Javascript 物件 


這些函數主要用來彌補 Javascript 內建的字串函數之不足. 其中 $.parseJSON() 在 jQuery 3.0 已被廢止, 剖析 JSON 可用 Javascript 的 JSON.parse().


1. $.trim() 函數 :

許多語言例如 PHP 或 Python 內建的字串處理函數都有刪除前後空白字元的功能, 但 Javascript 卻沒有, jQuery 的 $.trim() 彌補了這個缺憾. 此函數常用於前端表單驗證, 用來去除文字欄位輸入字串頭尾的空白字元 (white space), 包括空格, 換行, Tab 等字元 (即正規表示法中的 /s 字元), 避免傳送至後端時出現錯誤 (例如帳密多了一個空白導致登入失敗).

$.trim() 只有一個參數, 即要進行修剪的字串 :

$.trim(str)

它會傳回修剪後的字串, 原字串內容不變, 例如 :


範例 14 : $.trim() 函數測試 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <input type="text" id="origin">
    <button id="btn">$.trim()</button>
    <input type="text" id="trimed">
    <div id="len"></div>
  <script> 
    $(function(){
      $("#btn").click(function(){
        var origin=$("#origin").val();
        var trimed=$.trim(origin);
        $("#trimed").val(trimed);
        var out="輸入長度:" + origin.length + " 輸出長度:" + trimed.length;
        $("#len").html(out);
        });
      });
  </script>
  </body>
</html>

此例有兩個文字欄位, 左邊輸入頭尾有空格的字串後, 按 $.trim() 鈕會將頭尾空格去除後顯示在右方文字欄位中, 並於下方顯示各自長度, 結果如下 :




可見頭尾空白被去除後, 字串長度變短了.


2. $.parseHTML() 函數

此函數用來剖析 HTML 字串, 結果傳回 DOM 元素節點陣列, 其介面為 :

$.parseHTML(htmlString [, context] [, keepScripts])

第一參數 htmlString 為待剖析之 HTML 字串, 第二參數 context (備選) 用來指定要在網頁中何處建立元素, 預設是在 document 元素上, 第二參數 keepScripts 為布林值, 設定是否要保持其中的指令碼, 為了安全預設為 false.


範例 15 : $.parseHTML() 函數測 (不含 script) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <p id="result"></p>
    <script>
      $(function(){
        var arr=[];
        var str="Hello, <b><u>W</u>orld</b>";
        var html=$.parseHTML(str);
        arr.push('str="Hello, <b><u>W</u>orld</b>');
        $.each(html, function(i, v){
          arr.push('<li>' + v.nodeName + '</li>');
          });
        $("#result").html("<ul>" + arr.join("") + "</ul>");
        });
    </script>
  </body>
</html>

此例用 $.parseHTML() 剖析了一個簡單的 HTML 字串 "Hello, <b><u>W</u>orld</b>", 其傳回值為 HTML 的 DOM 元素物件陣列, 每一個元素代表一個 DOM 節點, 然後用 $.each() 來迭代遍歷此物件陣列, 於迴圈中顯示節點名稱 (nodeName 屬性), 為了顯示剖析結果, 使用一個空陣列 arr 來儲存每一個節點 (使用 li 清單), 最後用 join() 串接頭尾加上 ul 清單元素, 結果如下 :




可見 $.parseHTML() 只剖析了第一層元素而已, 它不會自動往下剖析, 所以只看到 text 與 b 元素, 而看不到 u 元素.

如果 HTML 字串中包含了 Javascript 程式碼, 則 $.parseHTML() 因安全考量預設不會對 script 元素進行解析, 若要剖析可將 keepScripts 參數設為 true, 如下面範例所示 :


範例 16 : $.parseHTML() 函數測試 (含 script) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <p id="result"></p>
    <script>
      $(function(){
        var arr=[];
        var str="<b>Hello, World</b><script>alert('ok');<\/script>";
        var html=$.parseHTML(str, document, true);
        arr.push("<b>Hello, World</b><script>alert('ok');<\/script><br>");
        $.each(html, function(i, v){
          arr.push('<li>' + v.nodeName + '</li>');
          });
        $("#result").html("<ul>" + arr.join("") + "</ul>");
        });
    </script>
  </body>
</html>

此例將 $.parseHTML() 的備選第三參數設為 true 即啟動對 script 元素之剖析 (第二參數用預設 document), 結果如下 :




可見解析出 script 元素節點了. 參考:

jQuery.parseHTML() 函式詳解


3. $.parseXML() 函數 : 

此函數用來剖析 XML 字串, 其介面如下 :

$.parseXML(xmlString)

傳入參數為一個 XML 格式字串, 傳回值為一 XML 文件物件.


範例 17 : $.parseXML() 函數測試 [看原始碼]


"<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta class="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    str="<?xml version='1.0' ?><result><user>tony</user><pwd>123</pwd></result>";
    <p id="result"></p>
    <script>
      $(function(){
        var arr=[];
        var str="<?xml version='1.0' ?><result><user>tony</user><pwd>123</pwd></result>";
        var xmldoc=$.parseXML(str);
        var xml=$(xmldoc);                //將 XML 文件物件轉成 jQuery 物件
        var user=xml.find("user");     //取得 XML 節點物件
        var pwd=xml.find("pwd");     //取得 XML 節點物件
        arr.push("user=" + user.text());      //取得 XML 節點文字內容
        arr.push("pwd=" + pwd.text());     //取得 XML 節點文字內容
        $("#result").html(arr.join("<br>"));
        });
    </script>
  </body>
</html>"

此例使用 $.parseXML() 剖析一個簡單的 XML 字串, 然後用 $() 工廠函數將傳回的 XML 文件物件轉成 jQuery 物件, 以便利用 jQuery 的 find() 方法來找尋 XML 節點物件, 找到後再呼叫 XML 節點物件的 text() 方法取出其節點內容, 結果如下 :




$.parseXML() 只是剖析字串後加上一堆 XML 文件的屬性與方法而已, 其實直接將上例中的 str 傳給工廠函數 $() 也可以得到同樣結果, 如果只是要取得 XML 字串中特定節點之內容並不需要經過 $.parseXML() 剖析, 直接轉成 jQuery 物件即可, 例如用 Ajax 取得遠端 XML 資料時即是如此, 參考 :

測試 jQuery 的 Ajax 函數 $.ajax()

與 Ajax 操作相關的工具函數除了上面這篇所測試的 $.ajax() 外, 還有 $.get(), $.post, $.getJSON(), $.getScript(), 以及 $.serialize() 與 $.params() 等函數, 參考 :

# jQuery 的高階 Ajax 函數測試