jQuery 工具函式 (Utilities)

jQuery 提供了一系列實用的工具函式,這些函式不需要 DOM 元素,可以直接在 $jQuery 物件上呼叫,用於處理陣列、物件、類型判斷等常見任務。

陣列操作

$.each() - 迭代

遍歷陣列或物件的每個元素:

// 遍歷陣列
var fruits = ['蘋果', '香蕉', '橘子'];

$.each(fruits, function (index, value) {
  console.log(index + ': ' + value);
});
// 0: 蘋果
// 1: 香蕉
// 2: 橘子

// 遍歷物件
var person = { name: 'John', age: 30, city: 'Taipei' };

$.each(person, function (key, value) {
  console.log(key + ': ' + value);
});
// name: John
// age: 30
// city: Taipei

// 提前終止迭代(返回 false)
$.each(fruits, function (index, value) {
  if (value === '香蕉') {
    return false; // 停止迭代
  }
  console.log(value);
});
$.each() 是工具函式,而 .each() 是 jQuery 物件的方法。兩者功能類似但用途不同。

$.map() - 映射轉換

將陣列或物件的每個元素經過函式處理後,返回新陣列:

// 陣列映射
var numbers = [1, 2, 3, 4, 5];

var doubled = $.map(numbers, function (value, index) {
  return value * 2;
});
// [2, 4, 6, 8, 10]

// 可以返回 null 或 undefined 來排除元素
var filtered = $.map(numbers, function (value) {
  return value > 2 ? value : null;
});
// [3, 4, 5]

// 可以返回陣列來展開元素
var expanded = $.map(numbers, function (value) {
  return [value, value * 10];
});
// [1, 10, 2, 20, 3, 30, 4, 40, 5, 50]

// 物件映射
var person = { a: 1, b: 2, c: 3 };

var values = $.map(person, function (value, key) {
  return value * 2;
});
// [2, 4, 6]

$.grep() - 過濾陣列

根據條件過濾陣列元素:

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 取得大於 5 的數字
var large = $.grep(numbers, function (value) {
  return value > 5;
});
// [6, 7, 8, 9, 10]

// 第三個參數為 true 時,反轉過濾條件
var small = $.grep(
  numbers,
  function (value) {
    return value > 5;
  },
  true
);
// [1, 2, 3, 4, 5]

// 過濾物件陣列
var users = [
  { name: 'John', age: 25 },
  { name: 'Jane', age: 30 },
  { name: 'Bob', age: 20 },
];

var adults = $.grep(users, function (user) {
  return user.age >= 25;
});
// [{ name: 'John', age: 25 }, { name: 'Jane', age: 30 }]

$.merge() - 合併陣列

將兩個陣列合併為一個:

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];

var merged = $.merge(arr1, arr2);
// [1, 2, 3, 4, 5, 6]

// 注意:第一個陣列會被修改
console.log(arr1); // [1, 2, 3, 4, 5, 6]

// 如果不想修改原陣列,先複製一份
var merged = $.merge([], arr1);
$.merge(merged, arr2);

$.inArray() - 搜尋索引

在陣列中搜尋指定值,返回索引(找不到返回 -1):

var fruits = ['蘋果', '香蕉', '橘子'];

$.inArray('香蕉', fruits); // 1
$.inArray('葡萄', fruits); // -1

// 從指定索引開始搜尋
$.inArray('香蕉', fruits, 2); // -1(從索引 2 開始找不到)

// 用於判斷元素是否存在
if ($.inArray('香蕉', fruits) !== -1) {
  console.log('有香蕉');
}

$.makeArray() - 轉為陣列

將類陣列物件轉為真正的陣列:

// NodeList 轉為陣列
var divs = document.querySelectorAll('div');
var divsArray = $.makeArray(divs);

// jQuery 物件轉為陣列
var $items = $('.item');
var itemsArray = $.makeArray($items);

// arguments 轉為陣列
function example() {
  var args = $.makeArray(arguments);
  console.log(args);
}

$.unique() / $.uniqueSort() - 去除重複

去除 DOM 元素陣列中的重複元素(僅適用於 DOM 元素):

// 取得所有連結的父元素(可能重複)
var parents = $('a')
  .map(function () {
    return this.parentNode;
  })
  .get();

// 去除重複
var uniqueParents = $.uniqueSort(parents);
$.unique() 在 jQuery 3.0 已改名為 $.uniqueSort()。此方法僅適用於 DOM 元素陣列,不適用於字串或數字陣列。

物件操作

$.extend() - 合併物件

將一個或多個物件合併到目標物件:

// 基本合併
var defaults = { color: 'red', size: 10 };
var options = { size: 20, weight: 'bold' };

var result = $.extend({}, defaults, options);
// { color: 'red', size: 20, weight: 'bold' }

// 直接修改第一個物件
$.extend(defaults, options);
// defaults 變成 { color: 'red', size: 20, weight: 'bold' }

// 深層複製(第一個參數為 true)
var obj1 = { a: { b: 1 } };
var obj2 = { a: { c: 2 } };

// 淺層合併
$.extend({}, obj1, obj2);
// { a: { c: 2 } }  - obj1.a 被覆蓋

// 深層合併
$.extend(true, {}, obj1, obj2);
// { a: { b: 1, c: 2 } }  - 遞迴合併

$.extend() 常見用途

// 插件選項預設值
function myPlugin(options) {
  var defaults = {
    color: 'blue',
    size: 'medium',
    animate: true,
  };

  var settings = $.extend({}, defaults, options);

  // 使用 settings...
}

myPlugin({ color: 'red' });
// settings = { color: 'red', size: 'medium', animate: true }

$.param() - 物件轉查詢字串

將物件轉為 URL 查詢字串格式:

var params = { name: 'John', age: 30 };

$.param(params);
// "name=John&age=30"

// 處理陣列
var params = { colors: ['red', 'blue'] };

$.param(params);
// "colors%5B%5D=red&colors%5B%5D=blue"

// 使用傳統模式(不加 [])
$.param(params, true);
// "colors=red&colors=blue"

// 巢狀物件
var params = {
  user: {
    name: 'John',
    email: 'john@example.com',
  },
};

$.param(params);
// "user%5Bname%5D=John&user%5Bemail%5D=john%40example.com"

類型判斷

$.type() - 精確類型判斷

返回值的精確類型:

$.type(true); // "boolean"
$.type(123); // "number"
$.type('hello'); // "string"
$.type(function () {}); // "function"
$.type([]); // "array"
$.type({}); // "object"
$.type(null); // "null"
$.type(undefined); // "undefined"
$.type(new Date()); // "date"
$.type(/regex/); // "regexp"
$.type(Symbol()); // "symbol"

$.isArray() - 是否為陣列

$.isArray([1, 2, 3]); // true
$.isArray('hello'); // false
$.isArray({ length: 3 }); // false

$.isFunction() - 是否為函式

$.isFunction(function () {}); // true
$.isFunction('hello'); // false

$.isNumeric() - 是否為數值

判斷是否為有限的數值(含數字字串):

$.isNumeric(123); // true
$.isNumeric('123'); // true
$.isNumeric('123.45'); // true
$.isNumeric(''); // false
$.isNumeric('hello'); // false
$.isNumeric(NaN); // false
$.isNumeric(Infinity); // false

$.isPlainObject() - 是否為純物件

判斷是否為使用 {}new Object() 建立的物件:

$.isPlainObject({}); // true
$.isPlainObject({ a: 1 }); // true
$.isPlainObject(new Object()); // true
$.isPlainObject([]); // false
$.isPlainObject(new Date()); // false
$.isPlainObject(document.body); // false

$.isEmptyObject() - 是否為空物件

$.isEmptyObject({}); // true
$.isEmptyObject({ a: 1 }); // false
$.isEmptyObject([]); // true(陣列視為空物件)

$.isWindow() - 是否為 window 物件

$.isWindow(window); // true
$.isWindow({}); // false

字串處理

$.trim() - 去除首尾空白

$.trim('  hello world  '); // "hello world"
$.trim('\n\t hello \n'); // "hello"
在現代 JavaScript 中,建議直接使用原生的 String.prototype.trim() 方法。

HTML/JSON 解析

$.parseHTML() - 解析 HTML

將 HTML 字串解析為 DOM 元素陣列:

var html = '<div>Hello</div><span>World</span>';

var nodes = $.parseHTML(html);
// [div, span](DOM 元素陣列)

// 加入到頁面
$('#container').append(nodes);

// 第二個參數指定文件上下文
var nodes = $.parseHTML(html, document);

// 第三個參數允許包含 script 標籤(預設為 false)
var html = '<div>Hello</div><script>alert("hi")</script>';
var nodes = $.parseHTML(html, document, true); // 會包含 script

$.parseJSON() - 解析 JSON

var json = '{"name":"John","age":30}';
var obj = $.parseJSON(json);
// { name: "John", age: 30 }
$.parseJSON() 在 jQuery 3.0 已被棄用,請直接使用原生的 JSON.parse()

其他工具

$.noop - 空函式

一個什麼都不做的空函式,用於預設回呼:

// $.noop 等同於 function() {}

function doSomething(callback) {
  callback = callback || $.noop;
  // ...
  callback();
}

$.now() - 當前時間戳

var timestamp = $.now(); // 等同於 Date.now()
建議直接使用原生的 Date.now()

$.contains() - 檢查 DOM 包含關係

判斷一個 DOM 元素是否包含另一個:

var container = document.getElementById('container');
var child = document.getElementById('child');

$.contains(container, child); // true(如果 child 在 container 內)
$.contains(child, container); // false

$.proxy() - 綁定 this

設定函式執行時的 this 值:

var obj = {
  name: 'John',
  greet: function () {
    console.log('Hello, ' + this.name);
  },
};

// 直接呼叫
obj.greet(); // "Hello, John"

// 當作回呼時,this 會改變
$('#btn').on('click', obj.greet); // "Hello, undefined"

// 使用 $.proxy 綁定 this
$('#btn').on('click', $.proxy(obj.greet, obj)); // "Hello, John"

// 或指定方法名稱
$('#btn').on('click', $.proxy(obj, 'greet')); // "Hello, John"
在現代 JavaScript 中,建議使用原生的 Function.prototype.bind() 或箭頭函式。

$.globalEval() - 全域執行腳本

在全域範圍執行 JavaScript 程式碼:

$.globalEval('var globalVar = "hello";');
console.log(window.globalVar); // "hello"

實用範例

物件深拷貝

function deepClone(obj) {
  return $.extend(true, {}, obj);
}

var original = { a: { b: 1 } };
var clone = deepClone(original);

clone.a.b = 2;
console.log(original.a.b); // 1(原物件未被修改)

移除陣列中的空值

function removeEmpty(arr) {
  return $.grep(arr, function (value) {
    return value != null && value !== '';
  });
}

var arr = [1, '', null, 2, undefined, 3];
removeEmpty(arr); // [1, 2, 3]

物件轉為表單資料

function objectToFormData(obj) {
  var formData = new FormData();

  $.each(obj, function (key, value) {
    if (Array.isArray(value)) {
      $.each(value, function (i, v) {
        formData.append(key + '[]', v);
      });
    } else {
      formData.append(key, value);
    }
  });

  return formData;
}

節流函式

function throttle(fn, delay) {
  var lastCall = 0;

  return function () {
    var now = $.now();

    if (now - lastCall >= delay) {
      lastCall = now;
      return fn.apply(this, arguments);
    }
  };
}

// 使用
$(window).on(
  'scroll',
  throttle(function () {
    console.log('捲動');
  }, 100)
);