---
title: Bitovi Academy - Functional Utilities
description: Create some of jQuery’s functional utility methods.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us) 
    - [ Functional Utilities ](https://www.bitovi.com/academy/learn-dom-jquery/functional-utilities.html?hsLang=en-us)
    - [ Finding Elements ](https://www.bitovi.com/academy/learn-dom-jquery/finding-elements.html?hsLang=en-us)
    - [ Traversing Elements ](https://www.bitovi.com/academy/learn-dom-jquery/traversing-elements.html?hsLang=en-us)
    - [ Attributes and Properties ](https://www.bitovi.com/academy/learn-dom-jquery/attributes-and-properties.html?hsLang=en-us)
    - [ Layout ](https://www.bitovi.com/academy/learn-dom-jquery/layout.html?hsLang=en-us)
    - [ Events Part 1 ](https://www.bitovi.com/academy/learn-dom-jquery/events-part-1.html?hsLang=en-us)
    - [ Events Part 2 ](https://www.bitovi.com/academy/learn-dom-jquery/events-part-2.html?hsLang=en-us)
    - [ Fn Plugins ](https://www.bitovi.com/academy/learn-dom-jquery/fn-plugins.html?hsLang=en-us)
    - [ Tabs Widget ](https://www.bitovi.com/academy/learn-dom-jquery/tabs-widget.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Functional Utilities `page`

Create some of jQuery’s functional utility methods.

## Overview

We will learn about:

- Extending objects by building `$.extend`
- Type checking by building `$.isArray` and `$.isArrayLike`
- Iterating objects and arrays by building `$.each`
- Binding functions to a particular context by building `$.proxy`

## Video

## Slides

## Setup

Run the following example in CodePen:

```
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css" />
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<script type="module">
  (function () {
    $ = function (selector) {};

    $.extend = function (target, object) {};

    // Static methods
    $.extend($, {
      isArray: function (obj) {},
      isArrayLike: function (obj) {},
      each: function (collection, cb) {},
      makeArray: function (arr) {},
      proxy: function (fn, context) {},
    });

    $.extend($.prototype, {
      // These will be added later.
    });
  })();
</script>
```

Each exercise builds on the previous exercise. There is a completed solution at the end of this page.

## Exercise: `$.extend( target, source ) -> target`

### The problem

[jQuery.extend](https://api.jquery.com/jquery.extend/) merges the contents of a `source` object onto the `target` object.

Click to see test code

```
QUnit.test("$.extend", function () {
  var target = { first: "Justin" },
    object = { last: "Meyer" };

  var result = $.extend(target, object);

  equal(result, target, "target and result are equal");
  deepEqual(
    result,
    { first: "Justin", last: "Meyer" },
    "properties added correctly"
  );
});
```

> **PRO TIP**: Use [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) in a modern app.

### What you need to know

- Loop through an object’s enumerable properties with a [for-in loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in):
  
  ```
  var obj = { foo: "bar", zed: "ted" };
  for (var prop in obj) {
    console.log(prop); // Logs "foo" then "zed"
  }
  ```

- Read a property with a string using the `[]` [member operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors).
  
  ```
  var obj = { foo: "bar" };
  var prop = "foo";
  console.log(obj[prop]); // Logs "bar"
  ```

- Assign a property with a string using the `[]` member operator:
  
  ```
  var obj = {};
  var prop = "foo";
  obj[prop] = "bar";
  console.log(obj.prop); // Logs "bar"
  ```

- Use [Object.prototype.hasOwnProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) to detect if an object has the specified property as it’s own property:
  
  ```
  var obj = { foo: "bar" };
  console.log(obj.hasOwnProperty("foo")); // Logs true
  ```

### The solution

Click to see the solution

```
$.extend = function (target, object) {
  for (var prop in object) {
    if (object.hasOwnProperty(prop)) {
      target[prop] = object[prop];
    }
  }
  return target;
};
```

## Exercise: `$.isArray( obj ) -> boolean`

### The problem

[jQuery.isArray](http://api.jquery.com/jquery.isarray/) determines whether the argument is an array.

Click to see test code

```
QUnit.test("$.isArray", function () {
  equal($.isArray([]), true, "An array is an array");
  equal($.isArray(arguments), false, "Arguments are not an array");

  var iframe = document.createElement("iframe");
  document.body.appendChild(iframe);

  var IframeArray = iframe.contentWindow.Array;

  equal(
    $.isArray(new IframeArray()),
    true,
    "Arrays from other iframes are Arrays"
  );

  document.body.removeChild(iframe);
});
```

> **PRO TIP**: Use [Array.isArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) in a modern app.

### What you need to know

- [Object.prototype.toString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString) called on built-in types returns the object class name:
  
  ```
  console.log(Object.prototype.toString.call(new Date()));
  // Logs "[object Date]"
  ```

### The solution

Click to see the solution

```
    isArray: function(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    },
```

## Exercise: `$.isArrayLike(obj) -> boolean`

### The problem

jQuery uses an internal `isArrayLike` method to detect if an object looks like an array. A value is array-like if:

- it is an object.
- the object has a number length property: 
    - the length property is 0, or
    - the length is greater than 0 and there is a `length - 1` property.

Click to see test code

```
QUnit.test("$.isArrayLike", function () {
  equal($.isArrayLike([]), true, "An array is array like");
  equal($.isArrayLike(arguments), true, "Arguments is array like");
  equal($.isArrayLike({ length: 0 }), true, "length: 0 is array like");
  equal(
    $.isArrayLike({ length: 5, 4: undefined }),
    true,
    "length > 0 and has property is array like"
  );

  equal($.isArrayLike(null), false, "Null is not array like");
  equal($.isArrayLike({}), false, "Plain object is not array like");
  equal($.isArrayLike({ length: -1 }), false, "length: -1 is not array like");
});
```

### What you need to know

- The [in operator ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) returns `true` if the specified property is in the specified object or its prototype chain.
  
  ```
  var obj = { foo: "bar" };
  console.log("foo" in obj); // Logs true
  ```

### The solution

Click to see the solution

```
    isArrayLike: function(obj) {
      return obj &&
        typeof obj === "object" &&
        (   obj.length === 0 ||
            typeof obj.length === "number" &&
            obj.length > 0 &&
            obj.length - 1 in obj );
    },
```

## Exercise: `$.each( obj , cb(index, value) ) -> obj`

### The problem

[jQuery.each](http://api.jquery.com/jquery.each/) loops through objects and arrays, calling the `cb` callback for each value.

```
import "https://unpkg.com/jquery@3/dist/jquery.js";

var collection = ["a", "b"];
$.each(collection, function (index, item) {
  console.log(item + " is at index " + index);
  // logs "a is at 0"
  //      "b is at 1"
});

collection = { foo: "bar", zed: "ted" };
res = $.each(collection, function (prop, value) {
  console.log("prop: " + prop + ", value: " + value);
  // logs "prop: foo, value: bar"
  //      "prop: zed, value: ted"
});
```

Click to see test code

```
QUnit.test("$.each", function () {
  expect(9);
  var collection = ["a", "b"];
  var res = $.each(collection, function (index, value) {
    if (index === 0) equal(value, "a");
    else if (index === 1) equal(value, "b");
    else ok(false, "called back with a bad index");
  });
  equal(collection, res);

  collection = { foo: "bar", zed: "ted" };
  res = $.each(collection, function (prop, value) {
    if (prop === "foo") equal(value, "bar");
    else if (prop === "zed") equal(value, "ted");
    else ok(false, "called back with a bad index");
  });
  equal(collection, res);

  collection = { 0: "a", 1: "b", length: 2 };
  res = $.each(collection, function (index, value) {
    if (index === 0) equal(value, "a");
    else if (index === 1) equal(value, "b");
    else ok(false, "called back with a bad index");
  });
  equal(collection, res);
});
```

> Use [forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) on arrays in apps.

### What you need to know

- Use a [for statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration#for_statement) to loop through something array-like:
  
  ```
  var items = ["a", "b", "c"];
  for (var i = 0; i < items.length; i++) {
    console.log(items[i]); // logs "a", "b", "c"
  }
  ```

### The solution

Click to see the solution

```
    each: function(collection, cb) {
      if ($.isArrayLike(collection)) {
        for (var i = 0; i < collection.length; i++) {
          if (cb.call(this, i, collection[i]) === false) {
            break;
          }
        }
      } else {
        for (var prop in collection) {
          if (collection.hasOwnProperty(prop)) {
            if (cb.call(this, prop, collection[prop]) === false) {
              break;
            }
          }
        }
      }
      return collection;
    },
```

## Exercise: `$.makeArray`

### The problem

[jQuery.makeArray](https://api.jquery.com/jQuery.makeArray/) converts an array-like object into a true JavaScript array. For example, it can make arrays of the following:

```
$.makeArray(document.body.childNodes);

$.makeArray(document.getElementsByTagName("*"));

$.makeArray(arguments);

$.makeArray($("li"));
```

Click to see test code

```
QUnit.test("$.makeArray", function () {
  var childNodes = document.body.childNodes;

  ok(!$.isArray(childNodes), "node lists are not arrays");

  var childArray = $.makeArray(childNodes);

  ok($.isArray(childArray), "made an array");

  equal(childArray.length, childNodes.length, "lengths are the same");

  for (var i = 0; i < childArray.length; i++) {
    equal(childArray[i], childNodes[i], "array index " + i + " is equal.");
  }
});
```

### What you need to know

You already know everything you need to know. You can do it!

> In modern apps, use [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) instead of `jQuery.makeArray`.

### The solution

Click to see the solution

```
    makeArray: function(arr) {
      if ($.isArray(arr)) {
        return arr;
      }
      var array = [];
      $.each(arr, function(i, item) {
        array[i] = item;
      });
      return array;
    },
```

## Exercise: `$.proxy`

### The problem

[jQuery.proxy](https://api.jquery.com/jQuery.proxy/) takes a function and returns a new one that will always have a particular context.

The following logs `"undefined says woof"` instead of `"fido says woof"`:

```
var dog = {
  nickname: "fido",
  speak: function () {
    console.log(this.nickname + " says woof");
  },
};

setTimeout(dog.speak, 500);
```

`$.proxy` fixes this:

```
import "https://unpkg.com/jquery@3/dist/jquery.js";

var dog = {
  nickname: "fido",
  speak: function () {
    console.log(this.nickname + " says woof");
  },
};

setTimeout($.proxy(dog.speak, dog), 500);
```

`$.proxy` can pass arguments too:

```
import "https://unpkg.com/jquery@3/dist/jquery.js";

var dog = {
  nickname: "fido",
  speak: function (word) {
    console.log(this.nickname + " says " + word);
  },
};

var dogSpeak = $.proxy(dog.speak, dog);
dogSpeak("ruff"); // Logs 'fido says ruff'
```

Click to see the test code

```
QUnit.test("$.proxy", function () {
  var dog = {
    name: "fido",
    speak: function (words) {
      return this.name + " says " + words;
    },
  };

  var speakProxy = $.proxy(dog.speak, dog);

  equal(speakProxy("woof!"), "fido says woof!");
});
```

> **PRO TIP**: Use [Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind) or [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) instead of `$.proxy`.

### What you need to know

- Use [Function.prototype.apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) to call a function with a specified `this` and arguments:
  
  ```
  var cat = { name: "sparky" };
  
  var dog = {
    name: "fido",
    speak() {
      console.log(this.name + "says woof");
    },
  };
  
  dog.speak.apply(cat, []); // Logs "sparky says woof"
  ```

### The solution

Click to see the solution

```
    proxy: function(fn, context) {
      return function() {
        return fn.apply(context, arguments);
      };
    },
```

## Completed Solution

Click to see completed solution

```
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css" />
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<script type="module">
  (function () {
    $ = function (selector) {};

    $.extend = function (target, object) {
      for (var prop in object) {
        if (object.hasOwnProperty(prop)) {
          target[prop] = object[prop];
        }
      }
      return target;
    };

    // Static methods
    $.extend($, {
      isArray: function (obj) {
        return Object.prototype.toString.call(obj) === "[object Array]";
      },
      isArrayLike: function (obj) {
        return (
          obj &&
          typeof obj === "object" &&
          (obj.length === 0 ||
            (typeof obj.length === "number" &&
              obj.length > 0 &&
              obj.length - 1 in obj))
        );
      },
      each: function (collection, cb) {
        if ($.isArrayLike(collection)) {
          for (var i = 0; i < collection.length; i++) {
            if (cb.call(this, i, collection[i]) === false) {
              break;
            }
          }
        } else {
          for (var prop in collection) {
            if (collection.hasOwnProperty(prop)) {
              if (cb.call(this, prop, collection[prop]) === false) {
                break;
              }
            }
          }
        }
        return collection;
      },
      makeArray: function (arr) {
        if ($.isArray(arr)) {
          return arr;
        }
        var array = [];
        $.each(arr, function (i, item) {
          array[i] = item;
        });
        return array;
      },
      proxy: function (fn, context) {
        return function () {
          return fn.apply(context, arguments);
        };
      },
    });

    $.extend($.prototype, {
      // These will be added later.
    });
  })();
</script>
```

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-dom-jquery/functional-utilities.html",
          "description": "Create some of jQuery’s functional utility methods. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```