JavaScript
History
- Originally designed as a language to run in the browser to
- Add nice UI effects
- Reduce the need for page reloads (e.g., by performing form validation locally).
- 1995: Netscape creates LiveScript (quickly changed to JavaScript)
- Microsoft introduces VBScript and then Jscript
- 1996: Netscape proposes ECMAScript specification as a standard
- ECMA: European Computer Manufacturers Association
- https://www.ecma-international.org
- 1997: First version of ECMA-262
- 2015: ES6 – later called ES2015
- Introduced many of the “modern” features (classes, imports, () => notation, etc.)
- New (minor) version every year since https://www.w3schools.com/js/js_versions.asp
- Current version is 10th Edition (also called ES2019)
- Has capability to modify the web page (i.e., the DOM), thereby giving web apps a more native feel. (More on that later.)
Basics
- Similar to Java/C/C++
- I will focus on the key features – especially those that are different from most programming languages.
- JavaScript is Dynamically Typed (as opposed to Java/C/C++ which are statically typed)
- What does it mean for a language to be dynamically typed?
- Variable types aren’t declared.
- What are the pros and cons?
- Pro: Can leverage polymorphism without elaborate inheritance/interface structures.
- Con: Compiler can’t identify when you’ve tried to call a method that doesn’t exist.
- See
dynamicTyping.js
in the course sample code. - Pro: This lack of typing makes functional programming less cumbersome because we don’t have to jump through hoops to specify the type of the “lambda” parameter.
- Con: Again, mistakes aren’t caught until runtime.
- What does it mean for a language to be dynamically typed?
Key features / differences
- See
keyFeatures.js
- Template strings
- You can put code inside the
${}
. It doesn’t have to just be a variable.
- You can put code inside the
- Uses “Java-style” comments (both
//
and/* ... */
) - Integers and floating point are the same type. (Try
typeof(1)
andtypeof(3.14159)
); - All objects behave like associative arrays.
==
vs===
==
converts both parameters to the same type before comparison (called coercion)===
does not coerce parameters. If the parameters are different types, the result isfalse
.- Use
===
unless you have a good reason to use==
. - When you use
==
add a comment explaining why. - See
keyFeatures.js
null
andundefined
are different.null == undefined
istrue
: Both convert tofalse
null === undefined
isfalse
: They are different types.- Be sure to check for both
null
andundefined
.
- Nullish Coalescing Operator (??) https://www.w3schools.com/js/js_2019.asp#mark_nullish_coalescing
let
vsvar
- Variables declared using
let
have block scope (are local to the block) - Variables declared using
var
are visible throughout the function (are not local to the block.) - Use
let
unless you have a good reason to usevar
. - When you use
var
, and a comment explaining why.
- Variables declared using
- Some methods are asynchronous. (See
asynchronous.js
) - Deconstructing arrays
- Deconstructing objects
- Use a linter (e.g.,
JSHint
) https://jshint.com - Modules: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
Express
npm init
(accept defaults)npm install express --save
npm install ejs --save
(skip if you prefer to use Pug)-
Create
index.js
with the following content:const express = require('express') const app = express() const port = 3000 app.get('/', (req, res) => res.send('Hello World!')) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
- Launch
node index.js
- visit
localhost:3000
- visit
- Create a directory named
views
- By default, templates go here.
- Convention vs. configuration.
- If you want to change it:
app.set('views', './your_preferred_location')
-
Create a file in
views
namedhello.ejs
(orhello.pug
, if you prefer)<p>Hello, there <%= name %></p> <p>Not a bad first page.</p>
- Add
app.get('/', (req, res) => res.render('hello.ejs', { name: "Bob" }));
toindex.js
- You can also set a default template engine:
app.set('view engine', 'ejs')
- Doing so allows you to leave off the
.ejs
- You can also set a default template engine:
-
Launch the server and visit the root page
- Add a route
- print the
req
object. - print just
req.query
- Use
req.query
set a variable in the template
- print the
- Show multiplication table and processing of POST
MVC (Model, View, Controller)
- This is a general software development design pattern. It’s not just for web apps
- Model
- Represents the data / objects
- Access the database
- Provides the business logic
- View
- Renders the web page
- Controller:
- Ties model and view together
- Basic workflow
- Receives input / action (e.g., HTTP request)
- Calls appropriate model (runs the correct business logic)
- Uses the results of the model’s actions to update view accordingly.
- Look at
Toy.js
- Primarily a container for a DB record
- class methods represent DB accesses
- The
module.exports =
line is a module system called CommonJS- CommonJS was designed by Node to prevent namespace pollution (in other words, to avoid name collisions when importing multiple modules)
module.exports
is an object contains the exported elements of a module.- Sometimes it is the single exported object
- (In the case of
express
, the module returns a function that creates the server object.)
- (In the case of
- Sometimes it is an object that maps names to the several “public” elements of a module.
- Sometimes it is the single exported object
- Node provides a
require
method to load the code and return theexports
object- The
Toy
module only returns one thing: TheToy
class.
- The
- The most recent versions of JavaScript use new
import
andexport
statements.- Node supports these statements on an “experimental” basis.
- The routing in
index.js
maps each web request (combination of method and path) to a method on a controller. - That controller method
- Accesses the model
- Renders a view
- Step through the following
- Index
- Show
- Create
- Update
- Point out the use of partials.
- Point out the use of the Array “find” method.
Convention over configuration
- By convention, Express uses pug.
- If you choose to use pug, you don’t need to type anything.
- If you choose to go against convention (e.g., use EJS), you need to add a line of configuration.
-
Similarly, by convention, the EJS templates go in a folder named
views
; but you can explicitly configure it. - Notice that the set of routes in
index.js
follows a patternGET /modelName
=>modelNameController#index
GET /modelName/:id
=>modelNameController#show
- etc.
- If we agree to use this pattern for all models, we can easily write a function to automatically generate these routes.
- We could also have this routing function automatically render a conventionally-named view.
- We could even have a program write an outline of our model, controller, and all of our views.
- Rails (and many other frameworks) do all of this.
- They have steep learning curves; but, allow you to be very expressive/efficient once you become comfortable.
Testing / Jest
- Discuss the addition of
isValid()
to the Model. - Add Jest to the project https://jestjs.io
npm install jest --save
- edit the “test” line of
packages.json
to say"test": "jest"
- Add a file
test/Toy.test.js
and add some test to it. - run `npm run test
- Notice the use of “!this.name” to check for a missing name (including an empty string.)
- Notice that
!this.price
doesn’t work for checking missing values, because it will also disallow0.0
- Explain
parseFloat(this.price) != this.price
- Demonstrate
.only
- Demonstrate
toBeTruthy
andtoBeFalsy
- Be sure to watch tests fail. Watch what happens if we
- Make a mistake in
create
and - Make a mistake in the test (by using a previous description).
- Make a mistake in
- Review the
describe
/it
syntax inToy.test.js
- Run
npm run test test/Toy.test.js
- Notice how the tests read as a sentence and describe the expected behavior (hence the
describe
block)
- Run
- To run tests by name
- run
npm run test -- -t '#create'
- The
--
causes the-t '#create'
to be passed to jest instead of consumed by npm. - We need single quotes around
#create
because#
has special meaning to the shell.
- run
Mocking
- Look at
ToyController.test.js
- To verify the behavior or this controller, we primarily need to verify how it interacts with the
request
andresponse
objects - Setting up these objects (and avoiding any pitfalls) would be time consuming and difficult, so we use mock objects.
- We “mock” the response object for use in the
#index
method by simply creating an object with arender
method. - This mocked render method, doesn’t actually do anything, it just keeps track of how it is used (how many times it is called, what parameters are passed, etc.)
- This tracking is done by
jest.fn
- explain the test
renders the index view
- explain the
#show
tests - explain the
#new
tests- Explain how
Toy
and its constructor are mocked
- Explain how
- To verify the behavior or this controller, we primarily need to verify how it interacts with the
Databases / Asynchronous programming / Promises
- You can use the
sqlite
package to access a simple, file-based SQL database. - Challenge: DB access is asynchronous.
- Examine
sqliteWithCallbacks/whyCallbacks.js
- examine
SqliteToyDB.js
- Notice that
all
andfind
need to take a callback method. - They should really take a second callback for errors.
- Notice that
- This example isn’t too bad; but, if we had a sequence of database accesses, you would need nested callbacks, which can get confusing and hard to maintain.
- Promises provide a means of organizing chains of callbacks.
- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises for an example of the “pyramid of doom”
- Examine
sqliteWithPromises
- Note: At this point, the difference is small.
- Async / Await
- We can use new keywords
async
andawait
to avoid callbacks at all. - If a method returns a promise, then you can just say
await
to instead obtain theresolve
value. - Any method that uses
await
must be declared asasync
. async
methods implicitly return promises, so you may need toawait
an async method.- Show updated MVC
- Point out that when updating, we need to use
function()
instead of()=>
to have access tothis
.
- Point out that when updating, we need to use
- We can use new keywords