EthicsComputerCourses
Join training +91 90532 10052

Utility Functions

jQuery · Lesson 22 of 36 Advanced 5 min read Updated

Besides working with elements, jQuery has a set of utility functions that are called directly on $, such as $.each() and $.extend(). They handled arrays, objects and promises in the days before JavaScript had good built-in tools for these jobs.

Many utilities have now been removed from jQuery 4 because JavaScript itself does the job, and the rest have clear modern equivalents. In this lesson you will learn what the common utilities do so you can read old code, and which built-in feature to use instead.

Looping and transforming arrays

jQueryWhat it doesModern JavaScript
$.each(arr, (i, v) => ...)loop; return false stops the loopfor...of, arr.forEach((v, i) => ...)
$.each(obj, (key, v) => ...)loop over an object's own propertiesfor (const [key, v] of Object.entries(obj))
$.map(arr, (v, i) => ...)map; drops null/undefined, flattens returned arraysarr.flatMap(...) or map + filter
$.grep(arr, fn)keep items where fn returns truearr.filter(fn)
$.grep(arr, fn, true)the opposite: keep items where fn is falsearr.filter((v) => !fn(v))
$.inArray(v, arr)index of v, or -1arr.indexOf(v), arr.includes(v)
$.merge(a, b)append b into a (changes a)a.push(...b) or [...a, ...b]
$.makeArray(list)turn an array-like into an arrayArray.from(list), [...list]

Two details trip people up. The callback order is (index, value) for $.each but (value, index) for $.map and $.grep. And $.map quietly removes null and undefined results and flattens any array you return, which is why it is closer to flatMap() than to map().

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Array utilities</title>
</head>
<body>
  <h3>Unit test marks, Class 9-A</h3>
  <pre id="out"></pre>

  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    const marks = { Aarav: 72, Priya: 91, Kabir: 28, Meera: 85, Rohan: 31 };
    const lines = [];

    // $.each over an object: (key, value); return false would stop early
    $.each(marks, function (name, score) {
      lines.push("jQuery each:  " + name + " scored " + score);
    });

    // Modern: Object.entries with for...of
    for (const [name, score] of Object.entries(marks)) {
      if (name === "Kabir") break;                 // stop early, like return false
      lines.push("for...of:     " + name + " scored " + score);
    }

    const scores = Object.values(marks);
    lines.push("");
    lines.push("$.grep pass:       " + $.grep(scores, (s) => s >= 33).join(", "));
    lines.push("filter pass:       " + scores.filter((s) => s >= 33).join(", "));
    lines.push("$.grep fail:       " + $.grep(scores, (s) => s >= 33, true).join(", "));

    // $.map drops null and flattens arrays
    const grace = $.map(scores, (s) => (s < 33 ? [s, s + 5] : null));
    lines.push("$.map grace marks: " + grace.join(", "));
    lines.push("flatMap version:   " + scores.flatMap((s) => (s < 33 ? [s, s + 5] : [])).join(", "));

    lines.push("$.inArray(85):     " + $.inArray(85, scores) + "   indexOf: " + scores.indexOf(85));
    $("#out").text(lines.join("\n"));
  </script>
</body>
</html>

Merging objects with $.extend()

$.extend(target, a, b) copies the properties of a and b into target and returns it. Passing true first makes a deep merge, which merges nested objects instead of replacing them. Plugins use it to combine default settings with the user's options.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>$.extend</title>
</head>
<body>
  <pre id="out"></pre>
  <script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
  <script>
    const defaults = { theme: "light", currency: "INR", alerts: { sms: true, email: true } };
    const userOptions = { theme: "dark", alerts: { email: false } };

    const shallow = $.extend({}, defaults, userOptions);       // alerts replaced as a whole
    const deep = $.extend(true, {}, defaults, userOptions);    // alerts merged key by key
    const spread = { ...defaults, ...userOptions };            // modern shallow merge
    const modernDeep = { ...defaults, ...userOptions, alerts: { ...defaults.alerts, ...userOptions.alerts } };

    $("#out").text(
      "shallow $.extend: " + JSON.stringify(shallow) + "\n" +
      "deep $.extend:    " + JSON.stringify(deep) + "\n" +
      "spread:           " + JSON.stringify(spread) + "\n" +
      "spread, nested:   " + JSON.stringify(modernDeep) + "\n" +
      "defaults intact:  " + JSON.stringify(defaults)
    );
  </script>
</body>
</html>

The shallow merge loses sms: true, because the whole alerts object from the user replaces the default one. The {} first argument matters: without it, $.extend(defaults, userOptions) would change defaults itself. To make an independent deep copy of an object (not a merge), modern JavaScript has structuredClone(obj).

Checking types

jQuery 4 removed most of its type-checking helpers. If old code uses them, it will now throw "is not a function":

Removed in jQuery 4Use instead
$.isArray(x)Array.isArray(x)
$.isFunction(x)typeof x === "function"
$.isNumeric(x)Number.isFinite(x) for numbers; for text, write a small check of your own
$.type(x)typeof, Array.isArray(), instanceof
$.trim(s)s.trim()
$.parseJSON(s)JSON.parse(s)
$.now()Date.now()
$.unique(arr)$.uniqueSort(arr) for DOM elements; [...new Set(arr)] for values

These still exist in jQuery 4: $.isPlainObject(x) (true for {} literals only, false for arrays and dates), $.isEmptyObject(obj) (modern: Object.keys(obj).length === 0), $.contains(parent, child) (modern: parent.contains(child)) and $.noop, an empty function used as a default callback.

$.proxy(fn, context) also still exists but has been deprecated since jQuery 3.3. Use fn.bind(context) or an arrow function.

Deferred objects and promises

Before JavaScript had promises, jQuery had Deferred objects. A Deferred is a promise that you resolve or reject yourself. AJAX requests and .promise() on animations use them inside. $.when() combines several, like Promise.all().

JavaScript
// jQuery Deferred
function waitFor(ms) {
  const dfd = $.Deferred();
  setTimeout(() => dfd.resolve(ms), ms);
  return dfd.promise();          // hide resolve/reject from callers
}
waitFor(500).done((ms) => console.log("waited", ms));

// Modern: Promise.withResolvers() (ES2024), or simply new Promise
function waitForModern(ms) {
  const { promise, resolve } = Promise.withResolvers();
  setTimeout(() => resolve(ms), ms);
  return promise;
}
console.log("waited", await waitForModern(500));

Since jQuery 3.0, Deferreds follow the standard promise rules, so await and .then() work on them. The slim build of jQuery 4 no longer includes Deferred at all, a sign of where things are heading.

Common mistakes

  • Mixing up callback orders. $.each is (index, value); $.map, $.grep and native array methods are (value, index).
  • Using return false in forEach to stop. It does nothing there; use for...of with break.
  • Forgetting the empty target in $.extend(). $.extend(defaults, options) changes defaults for everyone.
  • Expecting a shallow merge to keep nested defaults. Use $.extend(true, ...) or merge the nested object yourself.
  • Calling removed helpers after upgrading. $.trim, $.isArray and friends no longer exist in jQuery 4.

Key points

  • $.each, $.map, $.grep and $.inArray map to for...of/forEach, flatMap/map, filter and indexOf/includes.
  • $.extend({}, defaults, options) merges shallowly; true first merges deeply; spread syntax is the modern shallow merge.
  • jQuery 4 removed $.trim, $.isArray, $.isFunction, $.isNumeric, $.type, $.parseJSON, $.now and more; use built-in JavaScript.
  • $.proxy is deprecated; use bind() or arrow functions.
  • Deferreds are jQuery's older promises; Promise, Promise.withResolvers() and Promise.all() replace them.

Exercise

You inherit this old code: var names = $.map(students, function (s) { return $.trim(s.name) || null; }); if ($.isArray(names) && $.inArray("Isha", names) !== -1) { console.log("Isha found"); }. It breaks on jQuery 4. Rewrite it with modern JavaScript only (no jQuery), keeping the same behaviour, including dropping empty names. Test it with an array of four students where one name is just spaces.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Modernised utilities</title>
</head>
<body>
  <pre id="out"></pre>
  <script>
    const students = [{ name: " Isha " }, { name: "Farhan" }, { name: "   " }, { name: "Ananya" }];

    const names = students.map((s) => s.name.trim()).filter((name) => name !== "");
    const lines = ["names: " + JSON.stringify(names)];
    if (Array.isArray(names) && names.includes("Isha")) {
      lines.push("Isha found");
    }
    document.querySelector("#out").textContent = lines.join("\n");
  </script>
</body>
</html>

trim() replaces $.trim, map plus filter replaces $.map returning null, and includes() replaces the $.inArray(...) !== -1 check.

Quick Quiz

1. What does $.map([1, 2, 3], (n) => (n === 2 ? null : [n, n])) return?

2. Which call merges options into defaults without changing defaults?

3. Old code calls $.trim(text). What should it become in jQuery 4?

4. What replaces $.proxy(fn, obj)?

Learn jQuery with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call