I am giving a talk at the University of Pennsylvania’s PL Club next week, and was given the opportunity to assign a bit of homework to the audience. This blog post contains the content of that homework.
For this section, I recommend you open your browser’s Developer Tools to the Console, which is a REPL. All modern browsers have this feature. There, you can follow along with the examples.
It has some odd behavior due to implicit conversions:
There are two equality operators:
== performs implicit conversions,
=== does not.
Objects are essentially maps from strings to properties, and you can define new properties at any time:
Objects can inherit properties from a prototype object:
Arrays are objects, too:
Dog class, you define a function called
Dog and call it with
new. The result is an object that identifies as a
We can muck with
Dog later to confuse
In most traditional imperative programming languages, a program begins and ends in a single function (e.g.,
main()). If you pause the application at a debugger breakpoint, you can see a stack trace leading back to this main entrypoint.
Browser APIs expose functionality via events wherever possible: user input, network requests, timers, storage interfaces, etc.
For example, the following code subscribes to clicks on a button, and lets the user know if they clicked the button after 5 seconds:
For example, the following code will crash your browser tab. You cannot click the button on the page. The browser will ask you if you want to kill the tab:
Run this code (WARNING: crashes tab!)
Web developers need to consciously avoid this problem. If an application needs to process a large amount of data (or do something else that is CPU intensive), it needs to space processing across multiple events to keep the webpage responsive.
Events and Debugging
Events have many implications for debugging. If you pause the application at a debugger breakpoint, you will see a stack trace that leads back to the start of the current event, but you won’t see the set of events that preceded it.
In the following contrived example, the breakpoint in
bar will let you example a stack trace back to
foo, but will not say anything about
baz and its timer:
events-debugging.html in Chrome, and open developer tools before the error alert occurs. It will open the debugger at the debugger statement.
Events make it difficult to determine the origin of a bug. In this example, the “bug” occurs because
error to true. In an actual application, bugs may manifest many events in the future from the source of the bug.
If the bug is hard to reproduce, a developer will spend much of their time reproducing the bug and testing hypotheses by setting breakpoints in various locations (or by liberally inserting
console.log statements in the code).
As an example, the following code will display the content of
data when the user clicks the button. However,
data isn’t initialized until the timer fires, so if the user clicks the button prematurely, the webpage will display the message “undefined”:
The above example is contrived to keep these code snippets short, but these types of races are a common source of bugs!
Event scheduling-related races can be hard to find in more complicated web applications, and may only trigger in exceptional circumstances (e.g., a server is under heavy load, delaying requests).
To make the problem concrete, suppose that a web page uses CSS to make a
One might assume that if
isBlue(div) returns true the first time that it would also return true the second time. However, this is not true. The
backgroundColor changes while the script executes!
After reading through this document and trying some of the code samples, I hope you understand some of the challenges involved in writing and debugging web applications. Keep these challenges in mind during my talk.
If any of the examples or explanations are unclear or if you have questions, feel free to contact me.
Thanks for reading!