Function Bing Call Apply

Functions Bind,Call and Apply #

Overview #

call(), apply(), and bind() are JavaScript methods that allow you to control the this context of functions. They’re essential for function borrowing, method chaining, and controlling execution context.

The <code>call()</code> Method #

Syntax #

function.call(thisArg, arg1, arg2, ...)

Basic Example #

const person = {
  firstName: "John",
  lastName: "Doe",
  fullName: function () {
    return this.firstName + " " + this.lastName;
  },
};

const anotherPerson = {
  firstName: "Jane",
  lastName: "Smith",
};

// Using call to borrow the fullName method
console.log(person.fullName.call(anotherPerson)); // "Jane Smith"

Example with Arguments #

const calculator = {
  multiply: function (a, b) {
    return a * b * this.factor;
  },
};

const config = { factor: 10 };

// Using call with arguments
const result = calculator.multiply.call(config, 5, 3);
console.log(result); // 150 (5 * 3 * 10)

Practical Use Case - Function Borrowing #

const arrayLike = {
  0: "apple",
  1: "banana",
  2: "cherry",
  length: 3,
};

// Borrowing Array methods
const fruits = Array.prototype.slice.call(arrayLike);
console.log(fruits); // ['apple', 'banana', 'cherry']

// Converting NodeList to Array
const divs = document.querySelectorAll("div");
const divsArray = Array.prototype.slice.call(divs);

The <code>apply()</code> Method #

Syntax #

function.apply(thisArg, [argsArray])

Basic Example #

const numbers = [1, 2, 3, 4, 5];

// Finding maximum using Math.max with apply
const max = Math.max.apply(null, numbers);
console.log(max); // 5

// Modern alternative with spread operator
const maxModern = Math.max(...numbers);

Example with Context #

const greetings = {
  prefix: "Hello",
  greet: function (name, punctuation) {
    return `${this.prefix} ${name}${punctuation}`;
  },
};

const formalGreeting = { prefix: "Good morning" };

// Using apply with arguments array
const message = greetings.greet.apply(formalGreeting, ["Mr. Johnson", "."]);
console.log(message); // "Good morning Mr. Johnson."

Practical Use Case - Array Operations #

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

// Concatenating arrays using apply
arr1.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]

// Finding min/max in nested arrays
const nestedArray = [
  [1, 2],
  [3, 4],
  [5, 6],
];
const flattened = [].concat.apply([], nestedArray);
console.log(flattened); // [1, 2, 3, 4, 5, 6]

The <code>bind()</code> Method #

Syntax #

function.bind(thisArg, arg1, arg2, ...)

Basic Example #

const module = {
  x: 42,
  getX: function () {
    return this.x;
  },
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // undefined (this is not module)

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); // 42

Event Handling Example #

class Button {
  constructor(element) {
    this.element = element;
    this.clickCount = 0;

    // Binding ensures 'this' refers to the Button instance
    this.element.addEventListener("click", this.handleClick.bind(this));
  }

  handleClick() {
    this.clickCount++;
    console.log(`Button clicked ${this.clickCount} times`);
  }
}

const button = new Button(document.getElementById("myButton"));

Partial Application (Currying) #

function multiply(a, b, c) {
  return a * b * c;
}

// Creating a function with pre-filled arguments
const multiplyByTwo = multiply.bind(null, 2);
console.log(multiplyByTwo(3, 4)); // 24 (2 * 3 * 4)

const multiplyByTwoAndThree = multiply.bind(null, 2, 3);
console.log(multiplyByTwoAndThree(4)); // 24 (2 * 3 * 4)

Comparison Table #

MethodReturnsArgumentsUse Case
call()Executes immediatelyIndividual argumentsImmediate execution with specific context
apply()Executes immediatelyArray of argumentsImmediate execution with array arguments
bind()Returns new functionIndividual argumentsCreating bound functions for later use

Advanced Examples #

Method Chaining with Different Contexts #

const logger = {
  log: function (message) {
    console.log(`[${this.level}]: ${message}`);
    return this;
  },
};

const errorLogger = { level: "ERROR" };
const infoLogger = { level: "INFO" };

// Creating bound methods
const logError = logger.log.bind(errorLogger);
const logInfo = logger.log.bind(infoLogger);

logError("Something went wrong"); // [ERROR]: Something went wrong
logInfo("Process completed"); // [INFO]: Process completed

Function Composition #

const operations = {
  add: function (a, b) {
    return a + b;
  },
  multiply: function (a, b) {
    return a * b;
  },
  chain: function (value) {
    this.value = value;
    return this;
  },
  result: function () {
    return this.value;
  },
};

// Using bind for function composition
const calculator = {
  value: 0,
  add: operations.add.bind(operations),
  multiply: operations.multiply.bind(operations),
  chain: operations.chain.bind(operations),
  result: operations.result.bind(operations),
};

Polyfill Examples #

// Simple bind polyfill (for understanding)
if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      throw new TypeError(
        "Function.prototype.bind - what is trying to be bound is not callable",
      );
    }

    const aArgs = Array.prototype.slice.call(arguments, 1);
    const fToBind = this;
    const fBound = function () {
      return fToBind.apply(
        this instanceof fBound ? this : oThis,
        aArgs.concat(Array.prototype.slice.call(arguments)),
      );
    };

    fBound.prototype = Object.create(this.prototype);
    return fBound;
  };
}

Best Practices #

1. Use Arrow Functions for Lexical Binding #

class Timer {
  constructor() {
    this.seconds = 0;

    // Arrow function preserves 'this'
    setInterval(() => {
      this.seconds++;
      console.log(this.seconds);
    }, 1000);
  }
}

2. Avoid Overusing bind() in Render Methods #

// Bad - creates new function on every render
class BadComponent {
  render() {
    return <button onClick={this.handleClick.bind(this)}>Click</button>;
  }
}

// Good - bind in constructor
class GoodComponent {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

3. Performance Considerations #

// Bind once and reuse
const boundFunction = someFunction.bind(context);

// Rather than binding multiple times
// someFunction.bind(context)();
// someFunction.bind(context)();

Common Pitfalls #

1. Losing Context in Callbacks #

const obj = {
  name: "MyObject",
  delay: function () {
    // Wrong - 'this' will be undefined or window
    setTimeout(function () {
      console.log(this.name);
    }, 1000);

    // Correct - using bind
    setTimeout(
      function () {
        console.log(this.name);
      }.bind(this),
      1000,
    );

    // Modern solution - arrow function
    setTimeout(() => {
      console.log(this.name);
    }, 1000);
  },
};

2. Method References #

const obj = {
  value: 42,
  getValue: function () {
    return this.value;
  },
};

// Wrong - loses context
const getValue = obj.getValue;
console.log(getValue()); // undefined

// Correct - maintain context
const boundGetValue = obj.getValue.bind(obj);
console.log(boundGetValue()); // 42

Modern Alternatives #

Using Spread Operator Instead of Apply #

// Old way with apply
Math.max.apply(null, [1, 2, 3, 4, 5]);

// Modern way with spread
Math.max(...[1, 2, 3, 4, 5]);

Using Arrow Functions Instead of Bind #

// Using bind
const boundFunction = regularFunction.bind(this);

// Using arrow function (when appropriate)
const arrowFunction = (...args) => regularFunction.apply(this, args);

Summary #

  • call(): Invokes function immediately with specified this and individual arguments
  • apply(): Invokes function immediately with specified this and array of arguments
  • bind(): Returns new function with specified this and optional pre-filled arguments
  • All three methods are crucial for controlling function execution context
  • Modern JavaScript provides alternatives like arrow functions and spread operator
  • Choose the right method based on whether you need immediate execution or a bound function for later use