timoxley/functional-javascript-workshop

Function_Call Explanation

TheGreatAbyss opened this issue · 0 comments

I've always felt that the author of this course invested a lot of time in the excellent exercises themselves, but not enough effort into the explanations. Below is an explanation of the function_call problem that I wrote out for myself, and hopefully can help some others too.
(Note I did this all in a Jupyter notebook [using ijs] which I find a great learning tool )
(Double note that BERNIE is the name of the dog in my avatar)

"use strict";

function myFunction(passed1) {
  console.log('called my function')
  console.log(passed1)
};

// Inside myFunction.call, the value of this is myFunction itself

myFunction.call() // => called my function



// The function test below only works if the this of the function is set.  In order to use it you need to use call
// or apply to set the this to the object to run on

test_obj = {a:10, b:15}
function test() {
    return this.a +this.b
}
test.call(test_obj) // => 25

// These are equivalent and nothing is returned because Function.prototype isn't a function that does anything
Function.prototype.call()  // => undefined
Function.prototype.call(myFunction)  // => undefined

// Call is a function that applies the original function to an object or function passed in as the this,
// And then passes in the variables afterwards.

// What this is saying is that as stated above the first call would call Function.prototype 
// (which doesn't do anything)
// The second call is taking the first call function and then calling it.  

// The first argument to the second call function sets the the this of the first call function.
// So now the first call function is equivalent to: myFunction.call(this, arg1, arg2 etc..)
// But in order to use the first call function we still have to pass it a this, and then the arguments

// So the first argument (myFunction) is the this argument of the 2nd call function 
// that sets the "this environment" of the 1st call function
// The second argument (this), is passed to the 1st call function as it's first argument (the thisArg of the 1st call)
// The 3rd argument is passed as the 2nd argument to the 1st call function which is the first argument that gets
// passed to the function

Function.prototype.call.call(myFunction, this, "BERNIE") 
 // => called my function
// => BERNIE

// Similar to the above but using bind instead.  
// bind also takes the same arguments as call (thisArg, arg1, arg2 etc), but returns a partial function instead of
// executing it

// The this arg of bind sets the "this Environment" of call, 
// What is returned is a partial implementation of call that is still expecting 
// it's normal arguments: (thisArg, arg1, arg2, ...)
// But its "this environment" is now set to myFunction

testfunc = Function.prototype.call.bind(myFunction)
testfunc(this, "BERNIE")
// => called my function
// => BERNIE

// Could also be written like this

testfunc = Function.prototype.call.bind(myFunction, this)
testfunc("BERNIE")
// => called my function
// => BERNIE


// So this is the same as the above but using the slice method on Array.prototype
// So the call method is now a partial method that is still expecting (thisArg, arg1, arg2..)
// But it's thisenvironment has been set to the Array.prototype.slice method
// So you can therefore pass it any array.

slice = Function.prototype.call.bind(Array.prototype.slice)
slice([1,2,3,4],1,3) // => [2,3]

// The same as below where call sets the thisenvironment of the slice function to the array [1,2,3,4]


Array.prototype.slice.call([1,2,3,4],1,3 )
// => [ 2, 3 ]

// This doesn't work because slice is not bound to any actuall array
Array.prototype.slice([1,2,3,4,5], 1,3)
// => []