-
Before the Lab
- Watch WAT talk by Gary Bernhardt. The JavaScript part starts around 1:25
- Read Defining a Function through Arrow Functions
-
labs/L09
inside your ~/cs2613
git repositoryWe remember from earlier Eloquent JavaScript Chapter 1, and the WAT talk by Gary Bernhardt, that type coercion is an important part of evaluating JS expressions. This is sometimes useful
> 42 + 'is a big number'
But just as often a source of errors and confusion.
> "" + 1
> x=""
> x++
One operator where type coercion can be particularly surprising is the
standard equality test ==
. Not only does type coercion apply:
> "" == 0
> false == 0
> "" == 0
but special rules apply to "undefined" and "null"
> false == undefined
> undefined == null
> undefined == undefined
even though they would normally be considered falsy (considered false in boolean contexts).
> if (undefined) { console.log("truthy") } else { console.log("falsey") }
NaN
is another falsy value not ==
to the other falsy values, not even itself:
> NaN == undefined
> NaN == NaN
To avoid this twisty maze of "helpful" type coercion, you can use the "strict equality" checker ===
> "" === 0
> false === 0
> "" === 0
> false === undefined
> undefined === null
> undefined === undefined
Reference for this section is JavaScript functions Like Racket, JavaScript has two different ways of defining functions. The first way is by assigning an anonymous function to a variable.
(define square (lambda (x) (* x x)))
let square = x => x*x;
let square2 = function (x) {return x*x};
How is assignment (=
) in javascript different from binding (let
, define
) in Racket?
return
is mandatory. What does it mean?
what is the difference between the two kinds of anonymous functions in JavaScript? Hint: the first example is an arrow function.
The more compact way of defining functions in both Racket and
JavaScript combines the binding and creation of a function
/lambda
(define (square x) (* x x))
function square(x) { return x*x }
~/cs2613/labs/L09/loop-arith.js
, and fill
in the following definition for mult
using plus
and for
.const plus = (a,b) => {
for (let i=0; i < a; i++){
b++;
}
return b;
}
const mult = function (a,b) {
sum=0;
return sum;
}
node
REPL.In order to provide similar functionality to check-expect
in racket,
we will use the nodejs test
framework.
In order to use the test runner, we need to add two lines to the beginning of our javascript files.
const test=require("node:test");
const assert=require("assert");
We'll talk more about both const
and require
later.
A test is considered to pass if it does not throw an exception.
assert
provides several functions useful for this kind of test. We will
concentrate on
assert.strictEqual for now, which corresponds to the ===
test discussed above.
Here is a complete example with one passing test and one failing one
const test=require("node:test");
const assert=require("assert");
function add1(n) {
return n+1;
}
test('one plus one is two', (t)=>assert.strictEqual(add1(1),2));
test('one plus one is three', (t)=>assert.strictEqual(add1(1),3));
We will use an experimental feature of node v20 in order to make sure we have test coverage (analogous to how we set up DrRacket for visual test coverage).
Run our sample program from above with arguments to node
to see a test coverage report.
$ node --test --experimental-test-coverage sample.js
All going well, your output should contain something like
ℹ start of coverage report
ℹ ---------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lin…
ℹ ---------------------------------------------------------
ℹ sample.js | 100.00 | 100.00 | 100.00 |
ℹ ---------------------------------------------------------
ℹ all files | 100.00 | 100.00 | 100.00 |
ℹ ---------------------------------------------------------
ℹ end of coverage report
Add tests to your loop-arith.js
solution until you have
complete coverage.
Check your work for test coverage.
Make sure your tests pass as well as having coverage.