JavaScript: call()

Function.prototype.call()

call()
function func(args) {...}
func.call(obj, args)
call() allows an object's function to be called for a different object. It provides a new value this to the function.
Or it can be a generic object function altogether, created independent of any existing object, but for object use. In this case, the this keyword inside the function will refert to the global if it's not in strict mode.
Syntax
call()
call(obj)
call(obj, args)
Parameters
  • obj: The object the function is being called to. Refered as this from inside the function.
  • args: comma separated arguments for the function
Return value:
  • The result of calling the function with the specified this value and arguments.
call() vs apply()
call()
func.call(obj, arg1, arg2, ..., argN)
apply()
func.apply(obj, [arg1, arg2, ..., argN])
Similarities:
  • Both are for calling a function to operate on an object.
Differences:
  • apply(): Function's arguments are given as an array.
  • call(): Function's arguments are given as a comma separated list.

Using call() to chain constructors

Chaining constructors
Task: Chaining generic Product constructor with specialized constructors Food and Toy.
Generic Product object constructor
  • Defined with 2 parameters: name and price.
function Product(name, price) {
    this.name  = name;
    this.price = price;
}
Specialized Food and Toy object constructors
  • Invoke Product using call(), passing this, name, and price.
  • Product initializes the properties name and price, then the specialized constructor define the category.
function Food(name, price) {
  Product.call(this, name, price);
    this.categeory = "food";
}

function Toy(name, price) {
    Product.call(this, name, price);
    this.categeory = "toy";
}
Create objects with new keyword
const cheese = new Food("feta", 5);
const bread = new Food("muffin", 3);
const fun = new Toy("robot", 12);
const ocean = new Toy("figurine", 2);
Results
cheese;     // Food {name: 'feta', price: 5, category: 'food'}
bread;      // Food {name: "muffin", price: 3, category: 'food'}
fun;        // Toy {name: 'robot', price: 12, category: 'toy'}
ocean;      // Toy {name: 'figurine', price: 2, category: 'toy'}

Anonymous function and direct call()

Anonymous function and direct calling
call() can be used to invoke anonymous function directly after stating it.
(function() {...}).call(thisArg)
For each items in an object array
for (let i = 0; i < arr.length; i++) {
    (function() {...}).call(arr[i])
};
Don't forget keyword this
Task
Create anonymous function for printing content of each properties in an object array, with index numbering but starting from 1.
Basic way
const animals = [
    {species: "Dodo", name: "Dottie"},
    {species: "Flummel", name: "Op"},
    {species: "Flummel", name: "Ed"}
];

for (let i = 0; i < animals.length; i++) {    
    console.log(`#${i+1} ${animals[i].species}: ${animals[i].name}`);
};

----------------
#1 Dodo: Dottie
#2 Flummel: Op
#3 Flummel: Ed
----------------
Using function
const animals = [
    {species: "Dodo", name: "Dottie"},
    {species: "Flummel", name: "Op"},
    {species: "Flummel", name: "Ed"}
];

function print(arr, i) {
    console.log(`#${i+1} ${arr[i].species}: ${arr[i].name}`);
}

for (let i = 0; i < animals.length; i++) {
    print(animals, i)
};

----------------
#1 Dodo: Dottie
#2 Flummel: Op
#3 Flummel: Ed
----------------
Using anonymous function and direct call()
Working from the print function above
  • The this is animals[i]
  • Then the call: call(animals[i], i)
  • And inside the function: this.species and this.name
  • Parameters arr and i are not necessary
From this:
const animals = [
    {species: "Dodo", name: "Dottie"},
    {species: "Flummel", name: "Op"},
    {species: "Flummel", name: "Ed"}
];

for (let i = 0; i < animals.length; i++) {
    (function (arr, i) {
        console.log(`#${i+1} ${arr[i].species}: ${arr[i].name}`);
    }).call(animals[i], animals, i)
};

----------------
#1 Dodo: Dottie
#2 Flummel: Op
#3 Flummel: Ed
----------------
To this:
const animals = [
    {species: "Dodo", name: "Dottie"},
    {species: "Flummel", name: "Op"},
    {species: "Flummel", name: "Ed"}
];

for (let i = 0; i < animals.length; i++) {
    (function () {
        console.log(`#${i+1} ${this.species}: ${this.name}`);
    }).call(animals[i])
};

----------------
#1 Dodo: Dottie
#2 Flummel: Op
#3 Flummel: Ed
----------------

Invoke object functions and give context to "this"

Invoke object functions and give context to "this"
Functions can be made for use in object. Inside function, the object is refered as this. Function then could be invoke conveniently using call(object).
function func () { this.property... }

const obj = { property1: "value1", property2: "value2", ... }

func.call(obj)
Example
function dimension () {
    console.log(`The ${this.type} paper size is ${this.width} inch by ${this.length} inch.`);
};

const letter = { 
    type:   "letter", 
    width:  8.5, 
    length: 11 
};

dimension.call(letter);

// The letter paper size is 8.5 inch by 11 inch.

Blank call() to invoke global value

Blank call() to invoke global value
When thisArg is not given, global value is used. Because without context, this referes to the global object.
We can set a global values as default this.property values. This will be used when the thisArg is not given as an argument when calling.
function func () { this.property... }

const obj = { property1: "value1", property2: "value2", ... }

func.call(obj)
Task
Create welcome function that can send welcome message to name within an object, to all objects in an array (through a for function), and give default Guest for use when there's no specific object.
var name = "Guest";

function welcome() {
    console.log(`Welcome, ${this.name}!`);
};

const son = { name: "Dodo", age: 5 };

const children = [
    { name: "Dottie", age: 5 },
    { name: "Ed", age: 3 },
    { name: "Op", age: 2 }
]; 
One function for all
Just the one function can suit default, single object, and array of objects:
welcome.call();
// Welcome, Guest!

welcome.call(son);
// Welcome, Dodo!

children.forEach(el => welcome.call(el));
// Welcome, Dottie!
// Welcome, Ed!
// Welcome, Op!
Other outcomes:
welcome();
welcome(son);
welcome(children);
// Welcome, Guest!

welcome.call(children);
// Welcome, undefined!
Doesn't work on strict mode "use strict"
"use strict"
var name = "Guest";

function welcome() {
    console.log(`Welcome, ${this.name}!`);
};

welcome();
welcome.call();
// Uncaught TypeError...

References