These are notes I made while watching this video by
David Aragon from jQuery conf 2014.
He was kind enough to share his slides here.
I have copied some of his examples form the slides to provide context for my notes,
some of them have been slightly modified. Any errors, are likely my own. Caveat Lector.
JS uses functional scope not block scope like C or Scala, e.g. the if(){}
'block' is
not its own scope
var episode_1 = "Winter Is Coming";
if (true) {
// This will overwrite the first episode 1 because it is not in its own scope
var episode_1 = "The Kingsroad";
}
console.log(episode_1);
// "The Kingsroad"
but a function(){}
block is
var episode_1 = "Winter Is Coming";
(function() {
var episode_2 = "The Kingsroad";
})();
console.log(episode_2);
// Error: episode_2 is not defined
Variables in a function can leak to the global scope if var is missing.
This can be undesirable behavior because it can lead to naming collisions in the
global object( window
in the browser )
function foo () {
// The following variable will be in the global scope
bar = "this variable will be in the global scope"
}
console.log( bar );
// This is useful for making sure your variables are not leaking into the global scope.
Object.keys(window);
Lexical scoping can be inferred visually such as
var a = {
prop1 :'Dat scope!',
prop8 : function () {
console.log(this);
}
}
a.prop8() // Object {prop1: "Dat scope!", prop8: function}
Context in javascript uses dynamic scoping.
"If a function is a sentence, the context is the subject of that sentence. It's who the function is about".
-- David Aragon
- Call a method *on* an object
var user = {
handle: "@davidmaragon",
alertName: function() {
alert(this.handle);
}
};
user.alertName(); // @davidmaragon
- Call a function and pass
this
in, with .call() or .apply()
apply
is similar tocall
except it accepts arguments as an array.
var user = {
name: "makenova",
greet: function( name ) {
console.log( 'Hello ' + name + ', my name is, ' + this.name );
}
};
user.greet.call(user, 'andrew'); // Hello andrew, my name is, makenova
user.greet.apply(user, ['andrew']); // Hello andrew, my name is, makenova
- Use
new
to create a brand new function context
// This example assumes that the user object in the previous one is available
function User(handle) {
this.handle = handle;
}
var andrew = new User("@AndrewWK");
// andrew === {handle: "AndrewWK"}
user.greet(andrew.handle); // Hello @AndrewWK, my name is, makenova
- .bind() methods exist ($.proxy, _.bind, native .bind())
var alertName = function() {
alert(this.handle);
}
var andrew = {handle: "@AndrewWK"};
$.proxy(alertName, andrew)(); // alerts "@AndrewWK"
A performance comparison of the vendor and native bind metod can be found at the
following jsPerf
spoiler, avoid using bind.
Callbacks create a new closure and a new scope so you have to
$('button').on('click', function() {
// without storing the scope of the onClick callback in a variable, it will
// be unavailable in the setTimeout callback. Test it!!
var _this = this;
setTimeout(function() {
$(_this).addClass('clicked'); // All better
}, 1000);
});
let
gives a variable block scope.
// EcmaScript 6
if (true) {
let local_var = "I'm local to the if block!";
}
console.log(local_var); // Error: undefined local_var
Arrow functions pass the parent context to the child, that will allow you to convert this:
$('button').click(function() {
var _this = this;
setTimeout(function() {
// lookup _this from parent scope
$(_this).addClass('clicked');
}, 1000);
});
to this. Notice that it is unnecessary to write out function
is also gone
$('button').click(function() {
setTimeout(() => {
$(this).addClass('clicked');
}, 1000);
});