We previously used jQuery’s getJSON method to request data from twitter’s server - just give getJSON a URL and it requests data for you. Our endpoint URL contained two parameters:
We included a callback parameter to make use of the JSONP hack. Otherwise, getJSON requests the data we’re after using XMLHttpRequest - a very sophisticated JavaScript object that’s more powerful but also more law abiding than JSONP.
XMLHttpRequest is subject to the same-origin policy. For security reasons, browsers block data requests made to third-party domains (i.e., domains other than the domain hosting the web page). So, if a page originates from Server X it shouldn’t make a request to Server Y. As Cross Origin Resource Sharing (CORS) limits the security issues that were once present, XMLHttpRequest may soon be the default for cross-domain API requests, but for now we still need JSONP to get over this security hurdle.
The JSONP hack uses the script tag’s security pass. Surprisingly the script tag is not restricted by the same-origin policy, so it can request and execute JavaScript from any server. This is a great benefit in our era of information sharing across the internet.
If we were to write our own version of getJSON, then it might go something like this:
varmyGetJSON=function(endpoint,callback){// Create a script tag.varscript=document.createElement("script");// Create a unique random string of characters to be used as a// function name. We don't want to clash with any existing// function names.varfunctionName=Math.random().toString(36).substring(7);// Set the value of the endpoint's callback parameter to functionName:// "http://search.twitter.com/search.json?q=cats&callback=?"// becomes// "http://search.twitter.com/search.json?q=cats&callback=randomString"endpoint=endpoint.replace("callback=?","callback="+functionName);// Set the script tag's src attribute to the endpoint. script.src=endpoint;// Dynamically create and attach to the window object// a new method with the same name as the callback parameter.window[functionName]=function(data){// NB DATA PASSED TO THE myGetJSON CALLBACKcallback(data);};// Append script tag to webpage bodydocument.getElementsByTagName("head")[0].appendChild(script);};// TO TEST:myGetJSON("http://search.twitter.com/search.json?q=cats&callback=?",function(data){console.log(data);});
As soon as the script tag is appended to the body, the browser calls the endpoint. A JSONP savvy API will look for the callback parameter in the endpoint. That is the indicator the client is expecting a JSONP response. The server responds with an invocation of the dynamically created method (named the value of the callback parameter), passing the JSON object:
My challenge to myself this weekend was to tackle my first API - twitter’s search API. I’ll break down my process into five blog posts: (1) fetching and displaying tweets, (2) templating with Handlebars, (3) adding a search field to find tweets, (4) using JavaScript’s timer - setInterval - to update tweets, and (5) using regular expressions to create links in tweets.
I wanted to get to the heart of things, without getting lost in too much information. These posts are about taking some big (for me anyway) first steps. I chose twitter’s search API because it doesn’t require any authentication. I’m using jQuery’s getJSON method.
STARTING AT THE ENDPOINT
getJSON always requests a JSON object for us to play with. It takes two parameters: an endpoint or resource URL, and a callback function. The endpoint / resource URL refers to a URL that points to a web service that returns data. There’s no website at the end of this URL, but rather data - the JSON object we’re after. This is what the getJSON method looks like:
123
$.getJSON(url,function(data){...})
Looking at the first parameter, twitter’s search API documentation gives us the following Resource URL:
http://search.twitter.com/search.format
It took me a while to twig, but obviously we need to add the correct format - .json - and build our resource URL from there. Starting at the beginning, a resource URL is generally built from the following parts:
The domain
The path (follows the forward slash)
The format, e.g., .html (requests markup) and .json (requests JSON)
? (separates the parameters from the rest of the URL)
The parameters (name-value pairs separated by an ampersand, &)
Twitter’s resource URL has one required parameter - your search query, named q - but, since we want to use the JSONP hack when we make our request, we also need to add a callback parameter. At this point, we probably don’t need to worry too much about what the JSONP hack is; just understand it’s a clever way for us to get around the same-origin policy. Here’s our resource URL for querying twitter’s search API for tweets that include the word cats:
The second parameter in our getJSON method is a callback function. A callback function is so named because, at some convenient time, e.g., when it’s finished speaking to an API, the function you’re passing it to will call it (back). Callbacks are used widely in JavaScript. The callback passed to getJSON should not be confused with the callback parameter in our resource URL (see above), which is used by the API service as part of the JSONP hack.
The request made by getJSON happens asynchronously, meaning the programme continues immediately as getJSON, well, gets JSON. When there is a response from the server, the getJSON callback function is invoked with a JSON object being passed as the argument. We receive this data inside the callback, and it’s there that we have to deal with it. We can either send it to another function, or do something with it there and then. In the following example, we’re iterating over the data.results array of tweets, and prepending the text of each tweet to a list:
123456789
$.getJSON('http://search.twitter.com/search.json?q=cats&callback=?',function(data){// NB Another example of a callback: jQuery's each() method takes// an array and iterates over that array, invoking the callback// function for each item...$.each(data.results,function(i,tweet){$('ul#tweets').prepend("<li>"+tweet.text+"</li>");});});
Twitter currently provides the following example of the returned data (note, we have drilled into the data object to access the information we want):
{"completed_in":0.031,"max_id":122078461840982016,"max_id_str":"122078461840982016","next_page":"?page=2&max_id=122078461840982016&q=blue%20angels&rpp=5","page":1,"query":"blue+angels","refresh_url":"?since_id=122078461840982016&q=blue%20angels","results":[{"created_at":"Thu, 06 Oct 2011 19:36:17 +0000","entities":{"urls":[{"url":"http://t.co/L9JXJ2ee","expanded_url":"http://bit.ly/q9fyz9","display_url":"bit.ly/q9fyz9","indices":[37,57]}]},"from_user":"SFist","from_user_id":14093707,"from_user_id_str":"14093707","geo":null,"id":122032448266698752,"id_str":"122032448266698752","iso_language_code":"en","metadata":{"recent_retweets":3,"result_type":"popular"},"profile_image_url":"http://a3.twimg.com/profile_images/51584619/SFist07_normal.jpg","source":"<a href="http://twitter.com/tweetbutton" rel="nofollow">Tweet Button</a>","text":"Reminder: Blue Angels practice today http://t.co/L9JXJ2ee","to_user_id":null,"to_user_id_str":null},// ...],"results_per_page":5,"since_id":0,"since_id_str":"0"}
Last year I wrote a post on JavaScript closures. Another newbie programmer read that post and emailed to say, yes, that’s nice, but when practically do you use them… fast forward a few months, and I’m finally beginning to grok the beauty of closures myself.
This happened precisely because I started building - then rebuilding - my first app. I have restructured things more than once, in my attempt to investigate and wrap my brain around popular ways of organising code. Along the way, I have discovered the importance of good building blocks, as well as how to use closure to keep the guts of a program tucked away.
Here’s some code for capturing a value and building a list:
12345678910111213141516171819202122232425262728
$(document).ready(function(){// Define list here so available to all methodsvar$list=$('#itemsList');// When form submitted...$('form#listRandomItemsForm').on('submit',function(event){// Get valuevar$input=$(this).find('input[type=text]'),value=$input.val().trim();// Add to list if there is an input valueif(value){addToItemsList(value);};// Reset text input field to blank$input.val('').focus();event.preventDefault();});varaddToItemsList=function(value){var$li=$('<li />').text(value);$list.prepend($li);};});
This is some pretty trivial code, but we’re going to refactor it anyway, to quickly illustrate the usefulness of two common coding practices. In our examples, we are careful to keep variables out of that most messy of intersections, the global scope - a best practice achieved by all the various coding patterns available to us.
The Building Blocks For Adaptive Code
Plans have a habit of straying from the sketched path. When this happens to your application, change in one area should come easily enough, without unpredictable knock-on effects jumping up elsewhere. Such forward compatibility is greatly more achievable if we break things down into clearly defined packages of reusable code at every step and level of our program. In the following example of the object literal pattern, we define single-purpose atomic parts - the methods - in a containing object literal that groups related units of behaviour:
varListApp={init:function(){// Define list as a property of ListApp, so available to all methodsthis.$list=$('#itemsList');// When form submitted...$('#listRandomItemsForm').on('submit',function(event){varvalue=ListApp.getValue(this);// Add to list if there is an input valueif(value){ListApp.addToItemsList(value);};event.preventDefault();});},getValue:function(form){var$input=$(form).find('input[type=text]');value=$input.val().trim();// Reset text input field to blank$input.val('').focus();returnvalue;},addToItemsList:function(value){var$li=$('<li />').text(value);this.$list.prepend($li);}};// Initialise ListApp when DOM ready $(document).ready(function(){ListApp.init();});
The final example shows how we can use closure to prevent misadventure when a developer interacts with our program. Only an object, which acts as a public API (used by other parts of our application, or even other applications), is returned from a self-invoking function. The returned object is automatically assigned to ListApp, but everything else in the closure is removed from general reach, so the guts of our program are protected. Here’s the code:
varListApp=function(){// Define list here so available to all methodsvar$list;varinit=function(){// Cache list$list=$('#itemsList');// When form submitted...$('#listRandomItemsForm').on('submit',function(event){varvalue=getValue(this);// Add to list if there is an input valueif(value){addToItemsList(value);};event.preventDefault();});};vargetValue=function(form){var$input=$(form).find('input[type=text]'),value=$input.val().trim();// Reset text input field to blank$input.val('').focus();returnvalue;};varaddToItemsList=function(value){var$li=$('<li />').text(value);$list.prepend($li);};// OUR PUBLIC APIreturn{init:init};}();$(document).ready(ListApp.init);
The above is an example of the module pattern popularised by JavaScript hero Douglas Crockford. Check out one of his lectures (starts 20 minutes in, at hopefully the relevant bit).
This post presents the JavaScript methods for retrieving element nodes from the DOM: getElementById, getElementsByClassName, getElementsByTagName, getElementsByName, querySelector, and querySelectorAll.
JavaScript evolves. The changes spur browsers to become all the better to support it, and developers to figure out how best to apply it. Here, the Selectors API - querySelector and querySelectorAll - is the newer kid on the block. It lets developers pin down elements using CSS selectors.
You can check out browser support for both older and newer methods here. Let’s take a look at each method in turn, starting with the trusty old getElementById…
getElementById
If you know the id of the element you want, the document object’s getElementById is the quickest method in your toolbox. It returns a single element (or else null), e.g.:
// Returns the div and its contentsdocument.getElementById('stockings');
querySelector
querySelector also returns a single element, but it takes one or more (potentially complicated) CSS selectors, e.g.:
12345
// Returns the div and its contents (as above)document.querySelector('#stockings');// Returns the list element containing 'Harmonica'document.querySelector('#stockings ul:nth-child(2) li:not(.edible)');
querySelector returns the first matching element it finds; if there is no match, it returns null. You can call it on the document, or on any element, e.g.:
12345678
// Caches second stockingvarstockingB=document.querySelector('#stocking ul:nth-child(2)');// Returns the list element containing 'Harmonica' (as above)stockingB.querySelector('li:not(.edible)');// Returns the list element containing 'Strawberry bootlaces'stockingB.querySelector('li.edible');
getElementsBy…
The getElementsBy… methods each return a livenodeList of elements matching the passed argument, e.g.:
123456789
// Returns a nodeList of all the elements with class 'edible'document.getElementsByClassName('edible');// Returns a nodeList of uls / stockingsdocument.getElementsByTagName('ul');// Returns a nodeList containing the one element with name// attribute 'socks'document.getElementsByName('socks');
A live nodeList is an up-to-date collection of elements - an array-like object that updates on the fly (as the DOM changes).
You can call getElementsByClassName and getElementsByTagName on the document, or on any element, whereas getElementsByName is a method of the document only. An empty nodeList is returned if no elements match your query.
querySelectorAll
Like querySelector, querySelectorAll supports multiple selectors (separated by commas), and you can call it on the document, or on any element, e.g.:
12
// Returns an array of elements matching the CSS selectorsdocument.querySelectorAll('li.edible, [name=socks]')
It returns a static nodeList - a snapshot of how things were when the nodeList was created. This means, if the DOM should change, the result of your query may no longer be up-to-date, so you will probably want to re-query the DOM as and when you need to.
querySelectorAll is a little sluggish compared to methods that return a dynamic nodeList but, once you have it, manipulating a static nodeList might be relatively quick (ref). Most of the time, though, nobody will be the wiser if you do trade a little efficiency for more simplicity, e.g.:
1234567891011
// getElementsByTagNamevaritems=document.getElementsByTagName('li');varinedible=[];for(vari=0;i<items.length;i+=1){if(items[i].getAttribute('class')!=='edible'){inedible.push(items[i]);}}// vs. querySelectorAllvarinedible=document.querySelectorAll('li:not(.edible)');
There are two ways to register an event listener on the li elements:
Once
As many times as the list is long
We can register an event listener once, on a common ancestor - the ul element would do nicely here - and listen out for events from there. Or, we can register a listener on eachli element.
Basically, we have a choice between less work and more work. Bubbling gives us this choice; an event bubbles up the family tree - all the way to the window object - and any of the elements along the way may be instructed to listen out for it.
ONCE AND ONLY ONCE
Once and Only Once is a core principal of software development. An explosion of event listeners in a web page has a cost - a measurable performance loss if lists are long enough / sites are big enough. To keep things peppy (and site visitors happy), it seems we must keep our event listeners to a minimum. Once and Only Once also improves programmer performance.
This is because fewer event listeners means clearer code that’s easier to maintain. We typically set our event listeners as soon as the Document Object Model (DOM) has loaded. If we do things right, we don’t need to worry about attaching event listeners to li elements created on the fly after this point. Here’s a silly example to illustrate:
Apples
Pears
Bananas
Thanks to bubbling, we can listen for click events on newly created grapesli elements the future proof way. Our event listener is set on the ul element, so no worries…
1234567
varlist=document.getElementById('fruits');list.addEventListener('click',function(event){if(event.target.innerHTML==='Grapes (CLICK ME!)'){alert('I love Grapes!');}},false);
TELL MY PARENTS (AND ANCESTORS)
There may be multiple scripts listening out for events on the same DOM element; if so, we don’t want to be rude and stop up the bubbling action (by calling stopPropagation() on the event object). Telling the parents / ancestors is generally important. What we’ll invariably want to do, though, is prevent the browser from performing its default action. This is how we stop our form (above) from loading a new page whenever Add Grapes is pressed:
Notice we pass false as the final parameter when calling addEventListener. This parameter, which takes a boolean value, decides whether we’re using bubbling (false), or trickling / capturing (true). Trickling / capturing is bubbling in reverse (top-down). Bubbling makes more sense and is supported by all modern browsers (pre-IE9 browsers don’t support trickling / capturing).
What follows is a brief overview of event handlers in JavaScript. It’s not meant to be a thorough overview; the aim is to provide a beginner’s look at each event model, so we can understand when we might choose one over another.
It’s now pretty standard to keep document content (markup) and document presentation (CSS) in separate files, but this wasn’t always the case. Prior to the rise of CSS, designers and coders relied on less elegant solutions, e.g., contorting content into place using tables-inside-tables-inside-tables. For web browsers, finding content was like hunting for the smallest matryoshka doll.
It makes sense to organise like with like, so I was surprised to learn you can also muddy content with JavaScript behaviour: The original (and out-of-date) way to register an event handler - a function that’s executed when an event occurs - is to write it inline as an attribute of the HTML element, e.g.:
123
// As an HTML attribute
<inputtype="button"value="Hit Me"onclick="alert('This feels wrong!');">
This string of JavaScript code - written directly into the HTML tag above - adds an onclick property to a button, so an alert pops up when the button is clicked. This is a simple way to get interactivity into a web page, and web browsers still support this mix of content and behaviour just fine. But should we use it?
If you have only a small website, and you’re only binding to a handful of events, then maintaining things won’t present too much of a problem. That’s the argument for JavaScript in your markup. Now, here are two arguments against using this technique:
It relies on old tricks.
Keeping code in one place - rather than littering content with little bits of it - makes it easier to maintain and update.
Keeping JavaScript out of your markup is especially important if a site is large. Most programmers have better things to do than go looking for code hardwired into the content. And new and returning (perhaps after maternity leave, or an extended caravanning holiday) programmers need to get up-to-speed swiftly, a task made easier if the narrative of the program tells the full story, no pages bits missing. Here’s our JavaScript again, but this time it’s not riding an HTML element:
1234567
// Take Two - Click event on a button elementwindow.onload=function(){varbutton2=document.getElementById("button2");button2.onclick=function(){alert("This feels better!");};};
Otherwise, not much else has changed: We’re still setting an onclick property to a button.
Of course, things do change - we advance quickly into the future - and we want our sites and applications to keep up. Along the way, things get more sophisticated and demands change, so stay awake! One thing worth knowing about today’s premier event model - the W3C standard event model - is a single event can trigger multiple responses / event handlers, e.g.:
123456789101112
// NB Won't work for older versions of Explorer (pre-IE9)window.onload=function(){varbutton3=document.getElementById("button3");button3.addEventListener("click",function(){alert("I'm thinking...");},false);button3.addEventListener("click",function(){alert("I like it!");},false);};
You can achieve the same double alert without addEventListener, but in this case one response / event handler must do it all, e.g.:
12345
varbutton=document.getElementById("button");button.onclick=function(){alert("I'm thinking...");alert("I like it!");};
It follows that addEventListener allows you to bind a handler to an event from multiple places in your web app (without overwriting handlers set elsewhere). A web app will often have multiple JavaScript programs running at the same time; programmers working within a particular program can fearlessly add event handlers without consulting other programs.
An addEventListener function must take three parameters: event type (full list here), event handler, and finally a boolean value (almost always false). Really, really modern browsers don’t require the third parameter - if it’s not passed it’s default is false - but you should include it for the benefit of the masses. I’ll write a post about this final parameter next, since it concerns bubbling - a simple feature with big implications.
IE9 supports addEventListener, but we include the similar method attachEvent for older versions of Explorer (IE5 and later):
123456789101112
window.onload=function(){varbutton4=document.getElementById("button4");varhandlerA=function(){alert("I'm groovy!");};varhandlerB=function(){alert("I wish I was groovy!");};if(button4.addEventListener){button4.addEventListener("click",handlerA,false);}elseif(button4.attachEvent){// NB Takes two parametersbutton4.attachEvent("onclick",handlerB);}};
A quick note about the final example: We’ve declared our handler functions first, passing them by reference. From the code I’ve seen, this seems to be a common practice, to help keep code readable when more is going on.
I’ve recently been reading ‘JavaScript: The Good Parts,’ by Douglas Crockford. This concise book defines a subset of core JavaScript that’s robust and powerful: The Good Parts. Appendix B identifies The Bad Parts, and sets our competitors - the function statement and the function expression - apart.
ROUND ONE - LOOKS (CAN BE DECEIVING)
Function Statement
Our competitors may look alike, but the function statement has two distinguishing characteristics: the declarative keyword function always comes first, and you must name it. Here’s what it looks like:
12
// What we seefunctionfuncName(){}
Pull back the curtain, though, and the function statement expands into a variable with a function value:
12
// Under the hoodvarfuncName=functionfuncName(){}
Function Expression
Absent the mark of the function statement, you have the function expression. The function expression is optionally named (but normally anonymous), e.g.:
12345
// Stores ref. to anonymous function in a variable varfuncRef=function(){};// Stores ref. to named function in a variable varfuncRef=functionfuncName(){};
WINNER: Function Expression. Crockford’s manual argues the case for clear code. The function expression is clearly recognisable as what it really is (a variable with a function value).
ROUND TWO - (UNPREDICTABLE) BEHAVIOUR
Function Expression
Variables are subject to a thing called hoisting. Irrespective of their apparent place in the flow of things, their var part is removed and hauled to the top of a containing (whether function or global) scope, and initalised with undefined:
123456
// What we seevarfuncRef=function(){};// Under the hoodvarfuncRef=undefined;funcRef=function(){};
Function Statement
The function statement - while just shorthand for a var statement with a function value - is treated differently: the whole lot is hoisted. For this reason, you can call a function statement before you have declared it in your code:
12345678910
console.log(statement());// I am a function statement.console.log(expression());// TypeError...functionstatement(){return"I am a function statement.";}varexpression=function(){return"I am a function expression.";}
WINNER: Function Expression. Hoisting is already a behind-the-scenes behaviour that can cause head scratching. The particular behaviour of the function statement can lead to more furious head scratching.
ROUND THREE - SUPER POWERS
Function Statement
The function statement is widely used and performs just fine, but it has an obvious limitation: You can’t immediately invoke it.
Function Expression
The function expression is more flexible, e.g.:
1234
// Executed immediately(function(){alert("I am not a function statement.");}());
Parentheses are required to nudge function out of statement position (can’t immediately invoke), and into expression position (can immediately invoke). Crockford recommends grouping the entire invocation inside parentheses for clarity - what’s important here is the product of the invoked function - otherwise, Crockford argues, “you’ve got these things hanging outside of it looking like dog balls.”
WINNER: Function Expression.
FINAL SCORE: Function Expression Wins 3 - 0
CONCLUSION - I HEART A GOOD MANUAL
It turns out the function statement came first; the function expression was added to JavaScript later. The result is two very similar ways of defining JavaScript functions. Logically, the addition was intended to improve the language, and, in this case, it would seem this end was achieved.
If you haven’t already seen it, here’s a particularly entertaining speech (2011) Crockford gave on his approach to JavaScript:
In JavaScript, local variables tend to exist for as long as the function block they’re defined in is executing. The thing about nested functions is they can be used to exceed that life expectancy tremendously.
If you’re unfamiliar with JavaScript’s execution context, we discuss it here. Each time a function is called / activated, JavaScript creates a new execution context with a new special object - the call / activation object - to store the function’s arguments and local variables. This is how JavaScript creates the degrees of separation called scope.
Scope determines access to and the visibility of code. In JavaScript, code has either global scope - everything has access to it - or local / function scope.
Location, Location, Location
When code exists within a function it has local scope and is hidden from the code contained without. Working in the other direction, local code has access to the code in its outer scope.
This long reach is achieved because a function’s execution context has associated with it a scope chain - a string of one or (because a function can call a function) more activation object(s) stretching back to the global object. If JavaScript cannot find what it’s looking for in the executing function’s activation object, it checks the next object in the scope chain and so on, until, if it has not already found the missing value, it arrives at the global object and checks there.
Most of the time, local scopes exist only temporarily; once a function has exited, its activation object is no longer accessible to in-scope code, so JavaScript reclaims the memory it uses. But, if a reference to a nested function is caught in another scope, it takes with it access to the environment in which it was defined. This phenomenon is known as a closure:
JavaScript functions are a combination of code to be executed and the scope in which to execute them. This combination of code and scope is known as a closure in the computer science literature. All JavaScript functions are closures. These closures are only interesting, however… when a nested function is exported outside the scope in which it is defined. When a nested function is used in this way, it is often explicitly called a closure.
JavaScript, The Definitive Guide, 8.8.4, by David Flanagan
Example - Bob Rules
Bob is my niece. She’s happy to share her corn snacks one at a time, but, and this is crucial if you don’t want to cross Bob, nobody else gets direct access to the packet, so it must absolutely not be left lying about in the public interface. Here’s Bob’s code:
12345678910111213141516171819202122
functionpacketCornSnacks(){varcornSnackCount=3;functionbobShares(){if(cornSnackCount>0){cornSnackCount-=1;return"One corn snack for you!";}else{return"Oops, my bag is empty!";}}returnbobShares;}// Save ref to nested function (bobShares) in globalvargetSnacks=packetCornSnacks();// Invoke bobShares...alert(getSnacks());// One corn snack for you!alert(getSnacks());// One corn snack for you!alert(getSnacks());// One corn snack for you!alert(getSnacks());// Oops, my bag is empty!
If you run this code, you’ll ‘get’ one corn snack until Bob’s bag is empty. Nobody can go in and change the value of cornSnackCount - i.e., nab all the virtual corn snacks on the sly - because cornSnackCount is hidden inside a function. Bob is happy!
Happy But Confused
It mightn’t be immediately obvious how we achieve this state of corn snack affairs, so let’s talk it through…
In the above example, the outer function (packetCornSnacks) returns its inner function (bobShares), and the code at the bottom saves a reference to this nested function in the global environment, changing the normal flow of things. The nested function is created when its outer function is invoked, and it contains an inherent reference to where it was defined. So, even when the outer function has finished executing, a reference to its activation object remains in the global scope. The variable cornSnackCount persists in this object.
The code at the very bottom calls bobShares multiple times. Each time it is called, the function is the same, but its scope is changed because the value of cornSnackCount defined in the outer function is decremented over invocations, until Bob’s bag is empty.
Conclusion
getSnacks is a closure of the interesting variety. Bob’s packet of corn snacks can’t be interfered with, but its value is remembered across invocations of bobShares.
This has been a particularly tricky topic to approach as a newbie coder, and I’d welcome any pointers that could provide further insight.
The value of JavaScript’s this keyword can seem puzzling. This post aims to make it a little clearer.
Variables and properties in JavaScript are equivalent:
They are both assigned the same way, they are used the same way in JavaScript expressions, and so on. Is there really any fundamental difference between the variable i and the property i of an object o? The answer is no. Variables in JavaScript are fundamentally the same as object properties.
It continues that global JavaScript variables - top-level code, so not part of a function - are just properties of the global object. The global object is created automatically when the JavaScript interpreter fires up. If you’re coding for the browser, the Window object is the global object.
The special keyword this crops up in methods, and its value varies depending on how it is invoked. This is perhaps best understood when illustrated.
Illustrate this
1. this object we’re in:
123456789101112131415161718
//In methods of the global window objectalert(this);// [object Window](function(){alert(this);})();// [object Window]//In method of a local objectvara={object:"A",declareObject:function(){alert(this.object);alert(this);alert(this===a);}};a.declareObject();// A, [object Object], true
2. this has no fixed abode:
12345678910111213141516
vara={object:"A",declareObject:function(){alert(this.object);alert(this===a);alert(this===b);}};varb={object:"B"};b.declareObject=a.declareObject;//When called, method now belongs to object bb.declareObject();// B, false, true
So, this refers to the object that called the method.
3. Nested objects can obfuscate which object this is, but we can handle it:
12345678910111213141516171819
varmyObject="Apple";vara={b:{c:function(){console.log("B is for "+this.myObject+"!")},myObject:"Banana"}};//var abc points to method cvarabc=a.b.c;//When called, same as saying window.abc()abc();// B is for Apple!//var ab points to object bvarab=a.b;ab.c();// B is for Banana!
4. this works just the same in a constructor:
12345678910111213
//NB: Functions are objects too!functionLetter(letter){this.object=letter;this.declareObject=function(){alert(this.object);};}vara=newLetter("A");varb=newLetter("B");a.declareObject();// Ab.declareObject();// B
The special keyword new creates a new object and calls on the constructor in the context of the new object. As expected, the value of this is the new object.
5. this works just the same when handling events: Click me
123456789
// <a href="#" id="link">Click me</a>varlink=document.getElementById("link");link.onclick=function(){this.innerHTML="I've been clicked!";returnfalse;};
JavaScript can respond to events happening in the browser. In the above example, we use document.getElementById to return a link element (an object with its own collection of properties). This has the built-in property onclick. When someone clicks on the link, link.onclick calls the event handler (an anonymous function).
In other words, our event handler operates on the object through which it was invoked: our link element.
6. You can override the default this, e.g.:
123456789101112131415
vara={object:"A",declareObject:function(){alert(this.object);alert(this===a);alert(this===b);}};varb={object:"B"};a.declareObject();// A, true, falsea.declareObject.call(b);// B, false, true
The value of this becomes the object passed to the call method.
Conclusion
Rebecca Murphey probably sums it up best:
In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked.
This is a bit of a pick-me-up post. While I didn’t exactly cruise through learning the JavaScript basics (I’ll revisit some more of my first JavaScript problems on the blog soon), my first introduction to jQuery is going less smoothly.
I recently finished Codecademy’s JavaScript Fundamentals track - my high point so far - only to hit a wall immediately after, on Codecademy’s jQuery track. The exercises haven’t felt clear, and without feeling there’s a reasonable hope of getting it, it’s been hard to stick with it.
Of course, there are bound to be times when learning is even more frustratingly difficult than normal doesn’t come as easy. My point is not just to alert you to my own personal protracted moments of I CAN’T DO IT despair, but to share some ideas about keeping spirits and the momentum up:
Just code: If I’ve been quiet for a couple of days, Codecademy send me a ‘keep going’ email, which I think is very considerate indeed. Take a coding hiatus, and time can set in and spread. I don’t think it’s necessary to code every day, but doing at least a little bit most days feels a good habit to adopt to keep things moving.
Solve it first: Personally, I find if I take a break when I’m feeling defeated, all sorts of procrastination happens before I come back to the obstacle again. My advice is to try to pause after rather than before solving a problem.
Look elsewhere: If you don’t get something, you’re not stupid (note to self!). I find the worst thing to do is worry about finding things too difficult; ever heard the saying what you brood over will hatch? Look / keep a look out for an explanation that makes more sense, and be content to build knowledge one little revelation at a time.
Blog: Not for everyone, but this is an important one for me. I genuinely hope my discoveries can help other people make their discoveries, but if nothing else writing this blog helps me! A bit like being at school, I try harder if I think somebody might be watching. And writing things down forces me to look at a topic or problem much more closely than I might have otherwise.
Most importantly, though, a blog is like a water hole for people on a similar journey. The blog gives me a space to ‘meet’ other rookie and experienced programmers and share and hear ideas. The feedback from the folk who comment and email makes home study much less solitary.
So, I’m still learning to code - just about. How are you doing?
Procrastination in progress: Just five more minutes!