Skip to content

Instantly share code, notes, and snippets.

@stevecass
Last active February 13, 2016 17:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevecass/15c688f895383924172a to your computer and use it in GitHub Desktop.
Save stevecass/15c688f895383924172a to your computer and use it in GitHub Desktop.
Some notes on Javascript
//variables declared in global scope become properties of the window in browser environments
var a = 123;
console.log('a', window.a);
// functions introduce scope. A variable declared inside a function is not visible outside
function f() {
var b = 456;
console.log('b', b);
// inside the function we can still see the global a. We don't need "window." - it's implied
console.log('a', a);
}
f();
// But outside function f we can't see "b"
//console.log(b); here will produce an error
// Javascript functions have access to two special variables - "this" and "arguments"
// "this" depends on the function execution context - how the function is called
// Contrast with ruby where "self" depends on where the function (method) is written
// "arguments" is the array of arguments passed to the function, which may be empty
function g() {
console.log('this', this);
console.log('arguments', arguments);
}
g();
// We can also call .call() on any expression that represents a function.
// Calling g(); is equivalent to
g.call();
// Using .call() allows us to explicitly specify "this" for the call
g.call(document); // calls g with its "this" explicitly set to the current document
//After the argument to set "this," call accepts a list of arguments
g.call(document, 1,2,3); // will log the document then [1, 2, 3]
// .apply is the same as .call, except that we pass the arguments in an array rather than a list
g.apply(document, [1,2,3]); // is equivalent to g.call(document, 1,2,3); - use whichever is more convenient
// We can use call and apply without using an explicit this by passing null as the first argument.
// Null here effectively means "use the default this for the current context"
g.call(null, 1,2,3); // will log window then 1,2,3
// bind gives us a copy of a function, optionally with "this" and/or one or more of its arguments pre-defined.
// For example
boundG = g.bind(null, 1);
boundG(); // boundG() will log window as the value of this and [1] as the value of arguments
secondBoundG = g.bind(document, 1, 2);
secondBoundG(66); // this call will see document for "this" and [1, 2, 66] as its arguments
// The behavior of "this" can be unexpected if we are used to a language called ruby.
// For example
var obj = {
handler: function() {
console.log('this', this);
}
};
obj.handler(); // logs "obj" as this - much as we might expect
// BUT
var hCopy = obj.handler;
hCopy(); // logs "window" not "obj"
// The fact that "this" in handler was === obj in the first example is ONLY because we called it using obj.handler();
// This often bites us in event handlers. We might have
document.addEventListener('click', obj.handler );
// We might expect that clicking the document will log "obj" for this but it doesn't - it logs "document"
// The browser has called our event handing function with "this" set to document.
// Contrast
document.addEventListener('click', function() {
console.log('this in click handler', this);
obj.handler(); // will log obj as as this
});
// The second example does what we might have expected - because we explicity call our
// function with obj.handler() it is called with "this" set to obj. The browser calls the
// containing anyonymous function with "this" set to document.
// Another (usually more useful) way to achieve the same effect is to use bind
document.addEventListener('click', obj.handler.bind(obj));
// This will call a copy of handler with "this" set to obj when a click happens.
// We are passing a bound function to the addEventListener function
// You may have noticed by now that functions in JS behave a lot like data. We
// can pass functions as arguments to other functions and assign them to variables.
//This might look weird but it works
function leave() {
console.log('Bye');
}
function eat() {
console.log('Yum Yum you cook well.');
}
function showUp() {
console.log("I'm here");
}
var fnArray = [showUp, eat, leave];
for (var i=0; i < fnArray.length; i++) {
var fn = fnArray[i];
fn();
// fn.call() would also work here of course
// as would (fnArray[i]).call() or (fnArray[i])();
}
// As well as accepting functions as arguments, functions can return other functions
// as their return value. That's what "bind" was doing above.
// Another example of using bind to make a new function
function multiply(a, b) {
return a * b;
}
var triple = multiply.bind(null, 3);
console.log('triple 5 is ', triple(5));
// The function triple is returned by the call to bind.
// The function triple calls multiply with the first argument set to 3.
// I could write my own function that return other functions, e.g.
var makeMultiplier = function(x) {
return function(y) {
return x * y;
};
};
var quadrupler = makeMultiplier(4);
console.log('quadruple 5 is', quadrupler(5));
// here makeMultiplier is a function that returns another function.
// The returned function returns the product of its argument (y) and the
// argument that was given to makeMultiplier (x) to produce the function quadrupler itself.
// We say the inner function "closes over" the value of x. This construct is known as a closure.
// To use the MDN definition: Closures are functions that refer to independent (free) variables.
// In other words, the function defined in the closure 'remembers' the environment in which it was created.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment