Image default
Feature

12 common JavaScript questions answered

JavaScript is one of the most flexible web design tools around, as our top examples of JavaScript article shows. But even with the biggest of tools, there's always more to learn and discover.

Here are 12 questions (and answers) about JavaScript that have left many developers stumped – even experienced JavaScript coders. 

On this page we run through common questions and answers, including those often trotted out in job interviews. On the next page, we take a more in-depth look at two more complex areas: how to use JavaScript to improve site performance, and how to future-proof JavaScript code.

01. What is prototypical inheritance and how useful is it?

In JavaScript, almost everything is an object. Every object has a prototype, from which it inherits values and behaviours. If an object doesn't include a property that's being requested, JS will look for it inside its prototype instead. It carries on up the chain of prototypes until it finds a match, or returns an error.

This is useful when making objects that share the same values and operations. By having these live on a prototype, only one copy of them needs to exist, making projects memory efficient.

Prototypes can be added to objects on creation using Object.create(), or after with Object.setPrototypeOf(). ES2015 has a 'class' keyword that, when used with 'extends', will use that value as its prototype.

02. How can JavaScript be used to improve accessibility on the web?

It is common for modern web accessibility tools to be able to handle JavaScript and dynamic content. It can be used to help these technologies as long as it is used as an enhancement rather than required to function.

A common way to assist users is to provide useful focus management. A calendar, for example, should be able to cycle through days using the arrow keys, with up and down keys skipping a week at a time. It is just a case of listening for keyboard events while focus is within that component.

Important data changing as a result of JavaScript, for example form feedback, should also be announced for screen readers. Often this is achieved by marking up a container as a live region.

03. What is event bubbling and how is it different to event capturing?

Javascript event bubbling graphic

Event delegation is a technique that uses event bubbling to its advantage. By adding a listener to a parent element, developers are alerted to events for any of its children

Both event capturing and bubbling are both part of a process called 'event propagation', where browsers respond to events happening on the page. Older browsers used to do one or the other, but nowadays they all do both. 

The first phase – the capturing phase – occurs as soon as an event happens. It starts at the topmost level, which is either 'document' or 'window' depending on the event. From there it goes through and whatever lives inside it until it reaches the element the event occurred within.

The second phase – the bubbling phase – then happens. It repeats the process but in reverse, beginning with the element the event was triggered by and 'bubbling' to the topmost element. When adding event listeners, this is the default.

04. How does event delegation improve code on sites with lots of interactive elements?

Websites often have lots of dynamic content regularly changing on the page. If these elements need to be interactive, these would need some kind of event listener picking up those interactions. If each element required its own listener, this would clutter up code and increase what the browser needs to keep track of.

Event delegation is a technique that uses event bubbling to its advantage. By adding a listener to a parent element, developers are alerted to events for any of its children.

Inside the event callback, the original target of that event will always be the 'target', which can be used to decide what to do next. For example, a data attribute could hold an ID to reference an object property.

05. What are closures and how can they be useful in organising code?

Functions in JavaScript use what is called the 'lexical environment', meaning it has access to variables defined outside, but those defined inside can only be accessed within.

Calling outer() will show 'I love Web Designer!', but if 'shout' or 'x' are referenced outside of outer(), both will be undefined. A closure is a combination of a function alongside its lexical environment. In this case, the closure is the 'outer' function.

These are useful when creating multiple components, as anything declared inside will not affect others. They can be used to create private functions and variables in a similar way to other object-oriented languages like Python. The module pattern uses closures extensively to provide structured ways for modules to interact. 

06. What does 'use strict' mean at the top of a block of code?

ES5 introduced an optional variant of JavaScript called 'strict mode'. In strict mode, quirks of earlier versions would throw errors rather than result in unintended behaviour.

Above we are accessing a variable that doesn't exist. Outside of strict mode, this would instead add myVar to the global scope, which if we are not careful could completely overwrite functionality defined earlier in the script. In strict mode, this throws an error and halts any destruction. ES2015 modules are in strict mode by default, but in closures created with functions, 'use strict' can be applied at a function level as well as an entire file. 

07. What does the term 'hoisting' mean in reference to JavaScript?

JavaScript is unique in that it does not need compiling before being distributed. A browser will compile scripts as it finds them and make notes of any functions and variables that are declared within. 

The browser then makes a second pass to execute the code, knowing where these functions and variables apply. As a block is executed, its function and variable declarations are 'hoisted' to the top of the block.

In this example, we are able to use the 'welcome' function as it is hoisted up to the top of the script.

08. What is the difference between an arrow function and a regular function?

ES2015 provided lots of changes and one to make an quick impact was arrow functions.

The key difference, despite being shorter to write, is that arrow functions do not create their own value for 'this'. They will instead use the value of the enclosing block. In the above example, this would log out 1, 2, 3, etc once every second. With a regular function, this.x would be undefined, so it would log NaN. The body of an arrow function is assumed to be the return value from it. This makes them useful for promises, where values are passed through. Regular functions must explicitly return a value, or will return undefined.

09. Where should I use the 'let' and 'const' keywords instead of 'var'?

Another fundamental change with ES2015 was the introduction of 'let' and 'const' as alternative ways to define variables. Variables declared in this way are limited to the block they were defined in. This provides more certainty that values created inside different blocks won't interfere with code outside.

If the value of the variable will not change, use 'const' instead of 'let'. Errors are thrown when attempting to redefine a constant. Objects and array variables can still change internally, but are not completely replaced.

Both 'let' and 'const' do not get hoisted like 'var', so cannot be referenced before they are initialised. Between the start of the block and the initialisation is known as the 'temporal dead zone' and can often be a source of confusion.

10. What is functional programming and how is it different?

Functional programming graphic

Functional programming provides a different way of thinking 

It is an alternative way of creating programs by passing application state exclusively through functions. By avoiding side effects, it's possible to develop code that's easy to understand.

Traditionally, JavaScript projects are built with an object-oriented structure. Information about the current state of the program is held within objects, which get updated as the page changes.

Functional programming provides a different way of thinking. While languages like F# have used it for a while now, ES2015 brought important methods to JavaScript that open this up to the web.

All work must be done inside 'pure' functions. These are functions that are not affected by any data outside the scope of that function. In other words, if the same values are supplied to the function, the same result will always be returned.

This also means that there can be no shared state between functions. Any state within an application that needs to be used should be passed as a parameter to a function, rather than accessed directly from it. 

Finally, code should avoid changing values after they are made. For example, each change of an object should return a copy of that object with the value changed. This is to avoid side effects, which can cause bugs and make code harder to test.

Next page: How can I use JS to improve the performance of my site? and How can I future-proof my JavaScript code?

11. How can I use JS to improve the performance of my site?

At a time where the majority of web browsing is done on a phone or a tablet, performance is critical. Not everybody has the latest device, and each delay and stutter could cost a customer. Thankfully, there are plenty of ways to use JavaScript to stop this happening.

Keep it passive

Jagged scrolling is a clear sign something is up. In some cases, the browser is being forced to wait because of listeners applied to the page. Events such as 'wheel' or 'touchmove' are able to cancel scrolling, so the page has to wait until the event has completed before the default scrolling behaviour can begin. 

This can cause jerky and inconsistent scrolling, which makes for a poor user experience.

To get around this, pass an object as the third parameter when adding an event listener. By marking the event as passive, the browser can assume scrolling will not be affected, so it can start immediately. 

This third parameter replaces the 'useCapture' option in older browsers, so it is important to use feature detection when making use of this type of listener. To intentionally disable scrolling, applying 'touch-action: none' in CSS will help in more browsers.

Throttling events

Events like scrolling and resizing fire as quickly as they can to make sure whatever is listening can stay up to date. If something resource intensive is happening on each event, this can quickly grind a page to a halt.

Debouncing is a technique that throttles how often the callback to one of these events is called. The implementation of a debounce function and how often the function gets called will vary by project, but reducing the events to five times a second, for example, will see an instant improvement on the page.

Focus on the viewport

A common use of the scroll event is to detect when an element comes into view on the page. Even with debouncing, calling getBoundingClientRect() requires the browser to reanalyse the layout of the entire page. There is a new browser API called IntersectionObserver, which reports on the visibility of observed elements by calling a function whenever they enter or exit the viewport. For infinite scrolling sites, this can be used as a flag to remove or recycle older views.

IntersectionObserver is available in all the latest browsers except Safari. It's worth using this API and falling back to older techniques, as the difference is vastly noticeable.

Separate expensive work

When working with large datasets or processing big files such as images, JavaScript can quickly lock up the browser window. All the work is getting performed on a single thread, so if that thread is busy the interface cannot update.

If you know a process is going to take a long time to run, it is a good idea to put it inside a web worker. These are scripts that run on a separate thread, which leaves the user interface running smoothly. Scripts can talk to each other through a special message method. Web workers don’t have access to the DOM and some properties on the window object, so these messages can be used to pass the necessary information.

12. How can I future-proof my JavaScript code?

One of JavaScript's core principles is that it always tries to avoid breaking changes where possible. The vast majority of code written today will still function a decade later, even in this ever-changing industry.

Just because a block of code runs, however, does not mean it's future-proof. Will your codebase still make sense a few years later?

Avoid spaghetti code

When first creating a project, it can be tempting to write everything together. While that makes it explicit what each block of code is doing, it also means all behaviour is coupled together as well. If another area of the project needs that functionality, it will either be copied or rewritten. If bugs are found or a new feature is added, each version will need updating individually, which can be a time-consuming process.

By keeping code modular, functionality can be included where it's needed. Once it's been written, any changes are instantly available. Techniques such as the module pattern can be implemented without affecting the rest of the project, which makes them easier to implement.

Be framework agnostic

Many modern frameworks, like React or Polymer, encourage developers to keep things modular through the creation of components. Each has its own way of communicating between other parts of the project. 

What happens when the next best framework arrives? Switching – or even just updating – frameworks can be a laborious task. Finding new ways for these old components to work together can eat up precious time.

Where possible, use native JavaScript features to achieve results. This way, when changing frameworks these points of friction are minimised. For example, use objects to handle data manipulation before it gets handed to a component.

This also helps when writing universal JavaScript. By avoiding using browser-based APIs where possible, code can be reused both in the browser and on the server, inside Node. 

Clean up

Once modules are written, keep them clean. Anybody reading through them should understand its functionality to speed up debugging.

Self-documenting code is a powerful tool to future-proof your code. Using descriptive names for variables in iterators, for example, can be clearer to read than generic names like 'i'.

Most importantly, be consistent. By sticking to a uniform style, all code becomes readable. Use a style guide to define how code should look and use tools like ESLint to enforce it.

Work at scale

Being readable also extends to project structure. Without one, things can get overwhelming very quickly.

When first starting out, having files in one directory keeps things simple. When scripts import modules, there is no confusion as to where they are located.

As projects age, large collections of files can get lost in the masses. Keep a structure that scales well, such as storing all modules that deal with users in a 'users' directory. Ultimately, the best structure will depend on the project. For a single-page app, keeping model, view and controller logic separate is a must.

Keep things testable

laptop on table with Jest website opened

Periodically run tests with tools like Jest to ensure all is working well

Frameworks like React encourage creating small, reusable components. Even with a scalable project structure, it can be hard to be sure they all still work correctly. By writing tests, projects can be deployed with confidence.

Unit tests will work at a module level. Tools like Mocha and Jest let developers define an expected output for a given input. Periodically running these tests can make sure there are no side effects.

Modules need to be written in a way that means they can be tested in isolation. This means having as few external dependencies as possible and not relying on global state.

There's far more to project testing, such as integration and functional testing. These should cover as much of a project as possible to keep code working long into the future.

Language of tomorrow

screenshot of Babel website

Make sure your code works in older browsers by using a transpiler like Babel

The best way to future-proof code, however, is by writing it in the syntax of the future. While this might sound like a step into the unknown, there are tools that make it as easy as possible.

Babel is a transpiler, which is a tool that converts one form of JavaScript to another. This is used to turn modern code into formats older browsers and environments can understand.

ES2015 brought lots to JavaScript that can help write clean code, such as arrow functions, promises and native modules. The latest standard – ES2017 – brings even more convenience through async functions Given the right presets, Babel can help convert all these into code to use today.

Eventually, projects will be able to skip the transpilation step altogether. But for now, they are a must to keep code future-proofed.

This article originally appeared in Web Designer issue 265. Subscribe here.

To improve your web design skills further, head to our conference Generate and experience a packed schedule of industry-leading speakers, workshops and valuable networking opportunities. Don't miss it! Get your Generate ticket now.

Related articles:

Related posts

33 stunning iOS app icon designs

KingInExile

Minimalist Graphic Design: 10 Examples & Tips

KingInExile

15 great web design podcasts

KingInExile

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More