L-values and r-values
In programming, there are two kinds of expressions: l-values ("l" for left) and r-values ("r" for right). An l-value is something that can be assigned to while an r-value is something that we can assign to something else. For example, say we had the following HTML
<div id="my-div" style="background-color: blue;"></div>
and the following JS:
var myDiv = document.getElementById("my-div");
Then we could use myDiv.style.backgroundColor
as an l-value (i.e. assign something to it)
myDiv.style.backgroundColor = "black"; // myDiv.style.backgroundColor is an l-value
or as an r-value (assign it / pass it to something else):
var newDiv = document.createElement("div");
newDiv.style.backgroundColor = myDiv.style.backgroundColor // now both are "black"
When we have a "dot chain" like in the above myDiv.style.backgroundColor
, its value in an expression is equal to the value of the last link in the chain. So when we use myDiv.style.backgroundColor
in an expression, we are simply using the backgroundColor
. Assigning this to a new element, say newDiv
, does not create any sort of link between myDiv.style
and newDiv.style
.
Likewise, when you assign slates.render
to window.onload
, you're assigning the render
function only - there is no longer any connection to slates
.
You might wonder though... okay, but shouldn't this
be a link back to slates
? Well, think about it, you never actually assigned anything to this
in your render
function, right? You just used it. The language engine supplies the value of this
for you. Ordinary variables/properties in JS, e.g. slates
or slates.render
can be either l-values or r-values, depending on the context. However, the this
keyword is only valid as an r-value
. If we try to assign directly to the this
keyword, we will see an error like this:

The error says what we already know -- that this
is not a valid l-value. So where does it get its value from?
This brings us to a discussion of scope in JS and why this
is different from other variables in an important way.
Lexical scope (how variables work)
In JS, variables are lexically scoped, meaning that the value of a variable in a function call is determined not by the scope of the call, but rather by the scope of the function's definition.
var x = {name: "outer"};
function y() {
var x = {name: "inner"};
return function() {
console.log(x.name);
};
}
var z = y(); // z is a function here
z(); // logs "inner", not "outer" because JS is lexically scoped
In this example, we define a function y
which itself returns a function which gets assigned to z
. When z
executes, it does console.log(x.name)
, but the question is... which x
should it refer to? If JS were dynamically scoped, then we might expect it to print "outer", since this is the x
defined at the same "scope" as the variable z
. But it doesn't. It prints "inner" because JS is lexically scoped, meaning that x
is equal to the x
which was in scope at the time of definition.
Dynamic Scope (how the this
keyword works)
If the this
keyword was lexically scoped like other variables in JS, the code in your post would have worked. But it's not. While normal variables in JS are lexically scoped, the this
keyword is not lexically scoped. It has dynamic scope. Meaning that its value depends not on the context in which your function is defined, but rather the context in which your function executes. What context did you execute it in? The window object! If you imagine the load event being called like window.onload()
then this becomes more clear.
In fact, this is the behavior we want. Because if you had set a click event on a button, then you might do something like:
var inputBox = document.getElementById("my-input-box");
function myFunc() {
alert("Uh oh, you typed " + this.value);
}
inputBox.onchange = myFunc;
This is the same behavior. What's a little confusing is the fact that window
is also the default this
value. (More on that below).
With a few exceptions -- including the new
operator and the bind
, call
, and apply
methods -- the only way to pass a this
value into a function in JS is to call it on an object, like this:
object.method(); // this now refers to object within `method`
An analogy
Here's a real world analogy. Say that you have a ball. Define a method on the ball called kick
. The method is defined as move your leg to exert force on *it*
. The "it" in that phrase is much like this
in JavaScript. It's meaning is determined by context in which it is used, or in this case in which context the function is called. Without the ball, kick
still has meaning but the it
which it references is not defined. What I'm kicking is unknown.
When you pass a method to the window.onload handler like that, you're passing in the kick
function only, which on its own has no connection to the ball
. Only when we call it like ball.kick()
does kick
get a this
value of ball
. In real life, there is no "default" it value. We don't assume that it refers to, say, the earth if we're not sure. Unlike real life however, JS does have a default it
/this
value. That default value is the window
object. Trying to reason why the default is window
is probably futile - it's just an implementation decision that was made.
One solution
In your case, the easiest thing to do is to use the bind
function. The bind
function (introduced in the last version of JS) allows you to bind a certain this
value to a function, returning the new this
-bound function as its return value. So for instance, if you had a function like
Ball.prototype.kick = function() {
this.touch();
this.move();
console.log(this);
}
and you did
myBall = new Ball();
kickBall = ball.kick.bind(myBall); // returns a new function, doesn't affect the original ball.kick
Then you could call kickBall
without the normal ball.kick()
syntax and its this
value would be myBall
.
kickBall(); // myBall
window.setTimeout(myBall.kick, 1000); // window
A simpler solution
The simpler (and more common) way is to just assign a function which itself calls slates.render()
to window.onload
, like so:
window.onload = function() {
slates.render(); // this works because doing obj.method() passes obj as a this value to method
};
Further reading
Here's a better writeup of what this
means in JS: https://stackoverflow.com/a/3127440/2407870