Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

312
Views
How context in the call() method works

For a series of exercises that are recreating Underscore.js, I am trying to understand how the call() method is working under the hood.

I do understand how the call() method works in the example below.

let person = {
    firstName:"John",
    lastName: "Doe",
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
}
let myObject = {
    firstName:"Mary",
    lastName: "Doe",
}
person.fullName.call(myObject);  // Will return "Mary Doe"

However, I am having a hard time to understand the idea of context as in iteratee.call(context, collection[i], i, collection).

Here is the exercise I am working on:

// _.each(collection, iteratee, [context])
// Iterates over a collection of elements (i.e. array or object),
// yielding each in turn to an iteratee function, that is called with three arguments:
// (element, index|key, collection;), and bound to the context if one is passed.
// Returns the collection for chaining.

_.each = function (collection, iteratee, context) {
  if (Array.isArray(collection)) {
    for (let i = 0; i < collection.length; i++) {
      iteratee.call(context, collection[i], i, collection);
    }
  } else if (collection !== null) {
    Object.entries(collection).map(([key, value]) => {
      iteratee.call(context, value, key, collection);
    });
  }

  return collection;
};

Thanks in advance for your help.

about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

Many years ago it was common to use the word "context" to refer to the value that this should have during a function call. (It's not a good term and has fallen out of favor.) What that _.each definition is doing is accepting an optional parameter, context, and using that to set what this is when calling the iteratee function that was passed in so that this within the iteratee call is whatever context is. (If context isn't provided, it will be undefined, and this within iteratee will either be undefined [in strict mode] or the global object [in loose mode].)

For example, suppose you have an object with a method and you want to call that method for each entry in an array using _.each. You can do that like this:

const obj = {
    id: "some ID",
    method(value) {
        console.log(`this.id = ${this.id}, value = ${value}`);
    }
};
_.each([1, 2, 3], obj.method, obj);

Live Example:

"use strict";

const _ = {};
_.each = function (collection, iteratee, context) {
  if (Array.isArray(collection)) {
    for (let i = 0; i < collection.length; i++) {
      iteratee.call(context, collection[i], i, collection);
    }
  } else if (collection !== null) {
    Object.entries(collection).map(([key, value]) => {
      iteratee.call(context, value, key, collection);
    });
  }

  return collection;
};


const obj = {
    id: "some ID",
    method(value) {
        console.log(`this.id = ${this.id}, value = ${value}`);
    }
};
_.each([1, 2, 3], obj.method, obj);

If you didn't provide obj as the third parameter, this.id in obj.method wouldn't work correctly because this wouldn't be obj, it would be either undefined (strict mode) or the global object. For instance (strict mode):

"use strict";

const _ = {};
_.each = function (collection, iteratee, context) {
  if (Array.isArray(collection)) {
    for (let i = 0; i < collection.length; i++) {
      iteratee.call(context, collection[i], i, collection);
    }
  } else if (collection !== null) {
    Object.entries(collection).map(([key, value]) => {
      iteratee.call(context, value, key, collection);
    });
  }

  return collection;
};


const obj = {
    id: "some ID",
    method(value) {
        console.log(`this.id = ${this.id}, value = ${value}`);
    }
};
_.each([1, 2, 3], obj.method); // <== Note no third argument

The built-in array method forEach also has this parameter (called thisArg), as do many other built-in array methods.

But these days, it seems to me it's more common to just use a wrapper arrow function instead:

_.each([1, 2, 3], value => obj.method(value));
about 4 years ago · Juan Pablo Isaza Report

0

You can think of this as an implicit parameter of all functions. To illustrate, the following function will throw an error because there is no declared variable banana:

function fruit() {
    console.log(banana);
}

fruit();

But the following function is fine because this is an implicitly declared parameter:

'use strict';

function fruit() {
    console.log(this);
}

fruit();

You cannot pass this directly as an argument, because then you would have to redeclare the name:

function broken(this) {
    console.log(this.name);
}

broken({name: 'Ann'});

The only valid ways to pass a this argument to a function are to either call it as a method, or use call or apply:

// the following lines are equivalent
anObject.aMethod(argument1, argument2);
aMethod.call(anObject, argument1, argument2);
aMethod.apply(anObject, [argument1, argument2]); 

In all cases, aMethod will see anObject as its this argument. However, the first case only works if aMethod is a property of anObject, while the others always work. The only difference between call and apply is that the latter takes all arguments after this in a single array.

Now to get to the line that confused you:

iteratee.call(context, collection[i], i, collection)

iteratee is just any function, and context is just a variable that will be supplied as its implicit this argument. The context variable could have had any other name, such as thisArg or object.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!