Intermediate JavaScript
For the Modern Web
What It Is
- Learning about modern browser JavaScript and how to apply crucial native concepts
- Learning a bit of jQuery and covering some "best practices"
- Understanding JavaScript's events and unique flavor of "object-oriented" programming
And Isn't
- Learning legacy browsers' JS quirks
- Learning about JavaScript outside of the web browser (eg. Node.js)
- Learning about bleeding edge/future versions of JavaScript (eg. ES6) that are not well supported.
- Learning about any specific JS application framework (eg. Angular)
This Workshop
Who Am I?
- Web developer at Turner Broadcasting
- Worked on SI.com (2011-2013)
- Currently working:
- NCAA.com
- March Madness Live
- Been writing JavaScript for a long time
- Seen the language evolve over the years
- It's one of my favorite languages
- Like it or not, it is the primary language of the web
- There are now languages that compile to JS if you hate writing JS
Introduce yourselves!
- What's your name?
- Where are you from?
- Is there a particular web application or site that you would like to build?
Let's get started!
Timers
Let's warm up with something pretty easy!
Timers
- While a webpage is open, a JavaScript thread for it is running the entire time.
- If nothing is being executed, the thread will idle.
- Timing and interactions is a very important dimension of frontend development.
- The concept of timers helps introduce these concepts.
Timers
- Timers allow you to schedule actions to occur at some point in the future.
- When setting a timer, you provide a function to be called after a certain amount of time passes.
- This is referred to as a callback function.
- JavaScript's standard unit of time is milliseconds.
- It's safe to assume that any function/API concerned with time is expecting you to represent time in ms
- Why?
- Precision
- LCD monitor can draw a new frame every 8-16ms
- CPUs are fast enough to perform multiple operations even in that short span of time
Timers
-
JavaScript provides just two functions for setting timers.
- setTimeout(): waits N seconds and then executes the callback function once and only once.
- setInterval(): waits N seconds and then executes the function, waits another N seconds, executes that function again, and so forth until either the page is closed or some other part of the code explicitly turns the timer off (more on this later).
What are timers used for?
- Timers are used to create any kind of JS-based animation.
- Remember that the unit of time in JavaScript is milliseconds?
- Within jQuery, JS animation is achieved by simply setting a timer with a short interval (say, every 200ms) to change a CSS property many times over.
- Timers are also used within dynamic applications to "poll" (ie. make continuous requests) a remote data source (this is AJAX, by the way!)
- Think sitting on Twitter.com and after enough time passes, new tweets are available by clicking a link
Exercise: Using Timers
Closures
Closure = Function = Scope
-
Short for "enclosure"
-
Closures are all about scope (or context) control
-
They allow you to avoid the use of global variables.
-
Why are global variables undesirable?
-
In the browser, all of the JavaScripts running on a page share the same global scope.
-
That global scope is the named the "window" object
-
Your *.js file is probably not going to be the only one on the page
-
If you have 10 scripts, and they all use a global variable called "config", they will conflict and the value of "config" will be difficult to pinpoint at any given time during page life.
-
- It is always best to avoid using any more globals than absolutely necessary.
-
Closures allow your JavaScript to use ZERO globals, if so desired.
- There are valid use cases for global variables though.
Closures Are Functions
-
To create a closure, you simply write a function body.
-
Inside the function body you have created a new scope.
-
Scope, simply put, is all of the references (variables, function names) that are available for use.
-
This inner scope can access the outer scope references.
-
Functions are considered "first-class citizens" in JavaScript.
-
They can be passed around and assigned to variables just like any other type (like a number or string).
-
This is an extremely important language feature!
-
-
Within any function, variables defined using the "var" keyword will be private to the scope of that function (ie. not accessible to the outer scope)
-
Since a closure is quite literally a function, this is how closures control scope.
-
If you've ever used the "var" keyword inside a function, you've technically used the power of closures already!
IIFE
-
So since closures are functions, how can one avoid using 0 global references within a *.js file? Won't your closure function need at least 1 reference - for itself?
-
There is a nifty trick called IIFE which stands for "immediately invoked function expression" (you can just refer to it as a self-executing function though for brevity)
-
This is simply a closure that executes itself immediately.
-
Remember how closures are functions?
-
If you're just using standard functions, that function would need to be called from somewhere in the *.js file (or by another script, since the global scope is all shared)
-
This is not so with IIFEs!
Exercise: Closures and IIFE Walk-through
Closure Scope Is Exposable
-
All of the global scope is available inside of the closure. (Otherwise you couldn't use the "window" object without having passing it in your function as an argument each time).
-
By default, everything inside the closure is private unless you are referencing global scope.
-
If you need access to functions or other types that are "private" inside of your closure scope, there are ways of exposing them as "public"
-
This enables a simple namespacing mechanism to be introduced to the language
-
It also means we can write safe, modular JavaScript code that can't conflict with other code sharing the same memory and global scope even if we have no idea what that other code does.
Exercise: Simple name-space pattern using a closure
Events
DOM Events
- Like timers, DOM events further demonstrate that every webpage's JavaScript is "living": it will run until the user closes their browser/tab!
- Oftentimes, JS code is intended to be executed at some point after the DOM has been created.
- Allows you to listen for events that occur throughout the lifespan of a webpage.
- The simplest example is a user clicking their mouse on something.
- When you tell JavaScript to listen for some type of event, you must also provide a function to be called when that event occurs.
- This is another example of a callback function!
Exercise: "Global" Click Event
DOM Events
- JavaScript can listen to the entire page (by attaching event listeners to the "window" object as we just saw), or it can listen to individual elements within the DOM.
- Remember, each page has one global scope for all of the JavaScript on that page.
- It's unlikely that you "own" all of the JS running on a given page.
- Being able to listen to events only on certain elements that you are concerned with is one way of keeping your JS from interfering with the entire thread.
- In the case of a click listener, the click you are listening for most likely corresponds to a specific element.
- JavaScript's event system was designed to handle this.
Exercise: DOM-Specific Click Event
Other Common Events
- Aside from clicks, JavaScript has a variety of other useful events that you can listen for.
- These include, but are not limited to:
- movement of the mouse in or out of certain elements
- scrolling of the page
- keystrokes
- Since the appearance of non-mouse and keyboard devices running web browsers, a variety of new event types have been added over the years.
- Event types are not part of the language-spec so browser vendors can choose to add them at will.
- Early versions of Android and iOS mobile browsers had discrepancies w/r/t touch events
Bubbling and Capture
- http://www.quirksmode.org/js/events_order.html (the figures here illustrate these concepts well)
- If you set a listener on the HTML "<body>" for clicks, it receives the clicks from any element within itself (any child element)
- Same behavior demonstrated setting listener on "window"
- Bubbling can be very useful and tends to be easier to understand than capture
- Imagine you have an HTML <table> with potentially thousands of cells...
- Bubbling occurs naturally (by default, capture is off)
- UNLESS someone has coded their event listener specifically to prevent bubbling of the events received
Exercise: Event Bubbling and Capture
Managing Listeners
- Like timers, listeners may need to be managed by your code.
- When writing a listener, consider if it will always be useful to a page?
- For example, if I am listening on the button to a dialogue which can be dismissed (taking the button along with it), then that listener is still listening but it's listening for... nothing.
- That is technically considered a memory leak and can be harmful to the client's performance.
- On the other hand, if you want to listen for clicks on an ever-present navigation element, then you will never need to turn that listener off.
- Unlike timers, you can accidentally turn off listeners set by other scripts inadvertently.
The 'this' keyword
What the heck is 'this'?
- Anyone who has worked on much JavaScript has probably encountered and subsequently been confused by the concept of the "this" keyword within JavaScript.
- It's even confusing to talk about this JavaScript concept of "this"
- In JavaScript, "this" is a reserved keyword that is automatically managed by the language.
- It is typically found in constructor/factory functions used to make object instances (with the "new" keyword).
- It may seem simple (don't worry if it doesn't), but there are many potential pitfalls in the world of JavaScript OO.
- Some discourage the use of the keyword altogether due to its confusing nature. Still worthwhile concept to learn if you want to truly understand JavaScript.
- It's in the language, people use it whether it's a good idea or not!
"this"
- In the global context, "this" is simply a reference to "window"
- This is because "window" is the global object in browsers.
- It would never be used this way in practice.
- "this" is meant to be used within function contexts, and this is where things begin to get tricky.
- There are numerous ways to use functions within JavaScript, and "this" keyword doesn't necessarily behave the same way depending on how and where a function is written and used.
- Thinking back to exercise #2, where an object literal was being used as a rudimentary "namespace" that produced a single global, the "this" reference inside any member functions of that object would refer to that object itself.
Exercise 3: Introducing 'this'
- Things get hairier though when you have functions nested within other functions.
-
Function nesting is extremely common in JavaScript for a variety of reasons
- Closures are functions
- Functions can accept functions as arguments
- JavaScript's asynchronous nature promotes the usage of callbacks which means functions get passed around constantly.
- Having a firm grasp on the "this" keyword will let you be extremely precise as you move through different scopes.
- In exercise #3, we saw that in a simple global function call, "this" was simply set to the global scope (window).
- Another way of saying is that "this" gets set to the parent scope of the function in which it is used.
-
In constructor functions, when the "new" keyword is used to create an object instance, "this" refers to the object it will be used to construct (ie. to itself)
Function context
Exercise #4: "this" and constructor functions
"this" and event callbacks
- Inside the callback to an event listener, what does the value of "this" get assigned to?
- "this" is assigned the value of the element that the listener is set on.
- What if you still needed to access something from the "this" reference to the scope that contains the listener function itself?
- A very common situation given the prevalence of nesting and passing functions...
- You may see variables with names like "self" or "outer" or "that" that are being assigned the value of "this" in a particular part of the scope.
- They're most commonly used in callback functions (such as to a DOM event).
-
Although "this" itself is a reserved keyword and cannot be set, it is otherwise no different than any other variable reference such as a reference to the parent scope.
- Recall that "window" is the name of the reference to the global scope
- The outermost parent of a function is not always the most desirable context to have "this" keyword assigned to.
- Since "this" is always assigned to the most immediate context, it may be useful to "capture" the keywords value in one context and use it in another.
- May seem silly but it actually happens quite frequently in practice
- It has become popular to assign the reference of "this" to a variable.
var self = this; ???
Exercise #5: self = this
Object{} prototypes
Object.prototype
- "Inheritance" in JS is implemented using prototypes
- Think back to JS constructor functions that we use to create the equivalent of a class?
- Each function has a prototype property (empty by default)
- Attaching methods and properties the "prototype" of a function makes those methods and properties available to every instance of it.
- In this way, you can even "prototype" your own custom methods onto JavaScript's native types like String if you like
Exercise: Intro to Prototypes
jQuery
A friendlier face for JS
jQuery, then and now
- Used to be considered more or less vital to writing a modern website using JS for things like animations and AJAXing data to the page
- These days, you don't need jQuery to do as much but it is still a nicety.
- More consistent "API" than native JS itself
- jQuery falls back on native JS functions as soon as possible so there is virtually no performance hit if you prefer to use it.
- Still a life-saver for complicated DOM traversing
jQuery is a fairly large library
- Can't cover everything
- What did you guys want to learn about the most?
- Basics
- Animations and other effects
- DOM traversal and manipulation
- AJAX
- Events
- Other
Intermediate JavaScript
By Matt Stills
Intermediate JavaScript
General Assembly workshop presentation
- 220