Background
Methods
- Time
- 10 minutes
- Activity
- Demo
We can use JavaScript objects in a simple way by e.g. naming conventions for functions.
function timePlus(time1, time2) {
let mins = (time1.mins + time2.mins) % 60;
let hours = time1.hours + time2.hours + Math.floor((time1.mins+time2.mins)/60);
return {'hours': hours, 'mins': mins};
}
console.log(timePlus({hours: 10, mins:30}, {hours: 17, mins:47}));
If we want to have "time" objects with a "plus" method, one way is to
add that method manually. In order for that to work, we need to make
use of the JavaScript variable this. When the function method
is
called as object.method()
, the special variable this
is defined to
mean the current object. We can use this
to wrap our existing
function as a method.
function maketime(hours, mins){
let plus=function (other) {
let raw=timePlus(this,other);
return ______________________________
};
return { 'hours': hours, 'mins': mins, 'plus': plus};
}
let A=maketime(10, 30);
let B=maketime(17, 47);
let C=A.plus(B);
console.log(C);
Protypes
- Time
- 15 minutes
- Activity
- Individual work
Adding methods manually to newly created objects is a bit confusing and verbose, so JavaScript has the notion of a 'prototype',
let protoTime = {
plus: function(other) {
let raw=timePlus(this,other);
return timeNew(raw.hours, raw.mins);
}
};
In the following code, the method plus
is inherited from the
prototype, but the fields hours
and minutes
are not.
Fill in the blanks to get the following code working.
function timeNew(hours, mins) {
let obj=Object.create(protoTime);
_______________;
_____________;
return obj;
}
D=timeNew(21,42);
E=timeNew(17,37);
F=D.plus(E);
console.log(F);
The benefit is not huge here, but you can see how it would help if there were many methods, all creating new objects.
Classes (are really about prototypes)
- Time
- 5 minutes
- Activity
- Demo
In practice it's not usual to call object.create directly with the
prototype object, but rather to use new
keyword, along with the
prototype
property of a constructor.
function Time(hours, mins){
this.hours=hours;
this.mins=mins;
}
Time.prototype.plus=function (other) {
let raw=timePlus(this,other);
return ______________________________;
}
G = new Time(20,59);
H = new Time(11,11);
I=G.plus(H);
console.log(I);
Finally, ES2015 provides the class
keyword to concisely generate
constructors and prototypes.
class Time2 {
constructor(hours, mins){
this.hours=hours;
this.mins=mins;
};
plus(other) {
let raw=timePlus(this,other);
______________________________________;
}
}
Time2;
J= new Time2(5,30);
K= new Time2(11,55);
L=J.plus(K);
It's important to keep in mind that this is just convenience syntax for the same prototype based system.
JavaScript Arrays
- Time
- 20 minutes
- Activity
- Exercise from book
JavaScript supports arrays. They are actually implemented as objects, which leads to some slightly surprising behaviour:
> x=[]
> x[10]=1
> x
> x["10"] === x[10]
Follow the (first part of the)
Sum of a Range Exercise
to write a range
function that passes the following tests.
describe("range", function () {
it("empty", function () {
expect(array.range(2,1)).toEqual([]);
});
it("single", function () {
expect(array.range(2,2)).toEqual([2]);
});
it("multiple", function () {
expect(array.range(42,50)).toEqual([42,43,44,45,46,47,48,49,50]);
});
});
Arrays Continued
- Time
- 20 minutes
- Activity
- Exercise from book
Follow the (second part of the)
Sum of a Range Exercise
to write a sum
function that passes the following tests.
describe("sum", function () {
it("empty", function () {
expect(array.sum([])).toBe(0);
});
it("single", function () {
expect(array.sum([2])).toBe(2);
});
it("multiple", function () {
expect(array.sum(array.range(1,10))).toBe(55);
});
});
Array methods
- Time
- 10 minutes
- Activity
- Demo
It turns out that JavaScript arrays have some useful higher order methods, i.e. methods that take functions as arguments.
map
andfilter
behave similary to Racket list functions of the same name. It turns out that most places one would use lists in Racket, one uses arrays in Javascript.reduce
is similar tofoldl
. We used the related Racket macrofor/fold
in Lab 5. In Eloquent JavaScript Chapter 5, the author implimentsaverage
using reduce.
function average (array) {
return array.reduce((a,b) => , 0)/array.length;
}
console.log(average([1,2,3]));
forEach
gives a way to iterate over a list with an arbitrary action (with side effects), without fussing with loop indices and bounds. Note that this is different thanmap
in that it doesn't construct a list.
function average2 (array) {
let sum=0;
array.forEach( (a) => );
return sum/array.length;
}
Recursion in Javascript
- Time
- 20 Minutes
- Activity
- Individual
- start a new file
rec-arith.js
with the following content:
function plus(a,b) {
if (a === 0) {
return b;
} else {
return plus(a-1, b+1);
}
}
function mult(a,b) {
if (a===0) {
return 0;
} else {
}
}
Fill in the recursive definition of
mult
usingplus
One solution for the previous question would be to translate the following racket solution:
(define (mult a b)
(cond
[(zero? a) 0]
[else (plus (mult (sub1 a) b) b)]))
Make a copy from Lab 9 of your file
loop-arith.spec.js
(or use mine) to make a test suite for your new file. This should only mean changing therequire
line.Make a copy from Lab 9 of your file
loop-arith.js
(or use mine). Make a second test suite for the two modules to test them against each other. You will need tworequire
statements.