Skip to content

Instantly share code, notes, and snippets.

@0bie
Last active September 21, 2022 17:50
Show Gist options
  • Save 0bie/334e4aeef1962aff527946bc1103bd35 to your computer and use it in GitHub Desktop.
Save 0bie/334e4aeef1962aff527946bc1103bd35 to your computer and use it in GitHub Desktop.
JS functions
Functions
  • When a function is invoked on or through an object, that object is the invocation context or this value for the function.

  • Functions designed to initialize a newly created object are called constructors

  • JS functions can be nested within other functions and they have access to any variables that are in scope where they're defined. (closures)

  • In JS functions are first class objects because they can be passed as an argument to another function, returned from another function and also assigned as a value to a variable or property (methods)

  • Higher order functions are functions that accept a function, and/or return a function

// function declaration
function identifier(param1, param2, ...paramN) {}

function printprops(o) {
  /**
   * o = @type {object}
   */
  for(var p in o) {
    console.log(p + ': ' + o[p] + '\n');
  }
}

function distance(x1, y1, x2, y2) {
  /**
   * x1 = @type {number}
   * x2 = @type {number}
   * y1 = @type {number}
   * y2 = @type {number}
   */
  var dx = x2 - x1;
  var dy = y2 - y1;
  return Math.sqrt(dx * dx + dy * dy);
}

function factorial(n) {
  /**
   * n = @type {number}
   */
  if (n <= 1) return 1;
  return n * factorial(n - 1);
}

// function expression
var square = function(n) {
  /**
   * n = @type {number}
   */
  return n * n;
};

var f = function fact(n) {
  /**
   * n = @type {number}
   */
  if (n <= 1) {
    return 1;
  }
  return n * fact(n - 1);
};

data.sort(function(a, b){
  /**
   * a = @type {number || string}
   * b = @type {number || string}
   */
  return a - b;
});

var tensquared = (function(n) {
  /**
   * n = @type {number}
   */
  return n * n;
}(10));

var fivesquared = (function(n) {
  /**
   * n = @type {number}
   */
  return n * n;
})(5);
  • Variable declarations are hoisted, but assignments to those variables are not hoisted, so functions defined with expressions can't be invoked before they're defined.

  • Functions with no return value are sometimes called procedures

  • Functions can be invoked in four ways:

    • as functions ()
    • as methods obj.method()
    • as constructors new FOO()
    • call() and apply()
  • A constructor invocation creates a new, empty object that inherits from the prototype property of the constructor.

  • This newly created object is used as the invocation context, so the constructor function can refer to it with the this keyword

  • call() and apply() allow you to explicitly specify the this value for the invocation, which means you can invoke any function as a method of any object, even if it is not actually a method of that object.

  • Functions are executed using the scope chain that was in effect when they were defined.

  • Closures capture the local variable (and parameter) bindings of the outer function within which they are defined.

function counter() {
  var n = 0;
  return {
    count: function() {return n++;},
    reset: function() {n = 0}
  };
};

var c = counter(), d = counter(); // Initialize two counter objects with different private vars (`n`)
c.count() // => 0
d.count() // => 0
c.reset() // reset() and count() share state (`n`)
c.count() // => 0
d.count() // => 1
Methods
  • call() and apply() allow you to indirectly invoke a function as if it were a method of some other object.

  • The first argument passed is the object on which the function is to be invoked; this argument is the invocation context and becomes the value of the this keyword within the body of the function

  • Any arguments to call() after the first invocation context argument are the values that are passed to the function that is invoked. E.g f.call(o, 1, 2);

  • This also applies to apply() except that the arguments to be passed to the function are specified as an array E.g f.apply(o, [1, 2]);

  • The primary purpose of bind() is to bind a function to an object.

  • bind() returns a new function

function f(y) {return this.x + y;};
var o = {x: 1};
var g = f.bind(o);
g(2) // => 3
  • bind() also performs partial application. This means that any arguments you pass to bind() after the first are bound along with the this value. This is also called currying.

  • Currying basically means to use the bind() method to pass arguments to a function before it is invoked. Presetting arguments of a function E.g

var sum = function(x, y) {return x + y;};
var succ = sum.bind(null, 1); // bind `this` and `x`
succ(2); // => 3

function f(y, z) {return this.x + y + z};
var g = f.bind({x: 1}, 2); // bind `this` and `y`
g(3); // => 6: this.x is bound to 1, y is bound to 2 and z is 3

function multiply(number) {
  'use strict';
  return this * number;
}
var double = multiply.bind(2);
double(3) // => 6
double(10) // => 20
  • bind() returns a new function that is supposed to be invoked later with a pre-configured this

  • apply() and .call() invoke the function right away.

  • The function constructor (new Function()) doesn't use lexical scoping, instead they are always compiled as if they were top level functions.

  • A higher order function is a function that operates on functions, taking one or more functions as arguments and returning a new function.

this
  • In JS this is the current execution context of a function

  • this is undefined in a function invocation in strict mode

  • In a function invocation (E.g foo()) this returns the window object

  • this is the newly created object in a constructor invocation (using the new keyword)

var numbers = {
  numberA: 5,
  numberB: 10,
  sum: function() {
    console.log(this === numbers);
    function calculate() {
      console.log(this === numbers);
      return this.numberA + this.numberB;
    }
    return calculate.call(this); // or calculate.call(numbers)
  }
};

numbers.sum();

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;
  this.logInfo = function() {
    console.log(this === myCat);
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  };
}

var myCat = new Animal('Cat', 4);
setTimeout(myCat.logInfo.bind(myCat), 1000);

// Use arrow function to preserve the context of `this` within a nested function
var numbers = {
  numberA: 5,
  numberB: 10,
  sum: function() {
    console.log(this === numbers);
    const calculate = () => {
      console.log(this === numbers);
      return this.numberA + this.numberB;
    }
    return calculate();
  }
}

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;
  this.logInfo = () => {
    console.log(this === myCat);
    console.log('The ' + this.type + ' has ' + this.legs + ' legs');
  };
}

var myCat = new Animal('Cat', 4);
setTimeout(myCat.logInfo, 1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment