You are going to continue refactoring code that was presented in the first and second form. Again, what you are given is one of several possible solutions to the previous exercises.
You can also look at JS Learner Forms documentation for other forms.
You will be working in the jsforms-source/3_third-form.js file.
You are going to start by refactoring the greet
function.
Here are the steps you will take to refactor the greet
function to ensure you change the shape without changing the behavior.
- Create function called
isTypeOf
- Replace
typeof
check in greet function withisTypeOf
- Create function called
eitherOnType
- Replace ternary in greet function with
eitherOnType
Create a function called isTypeOf
with the two parameters of type
and value
. You are not exporting this function. This function checks the type of the value
and returns a Boolean true if the value
is of the given type
.
Hints
This will use typeOf
to do the check.
Code
Example
function isTypeOf(?, ?) {
return typeOf(?) === ?;
}
Now you will modify the greet
function to use the isTypeOf
function instead of the typeOf
function.
Hints
You will need to replace not only the call to typeOf
but also the comparison to the result.
Code
Example
function greet(greeting) {
return isTypeOf(?, ?) ? greeting + '!' : 'Hello!';
}
It has a function called eitherOnType
with parameters type
, testValue
, defaultValue
-- return testValue
if it matches type
, otherwise return defaultValue
Create a the function eitherOnType
, you will not export this function. The eitherOnType
function takes two parameters testValue
and type
. It then compares the testValue
's type. If the testValue
has the same type as the one given, it returns the testValue
. If the testValue
has a different type then it returns the defaultValue
.
Hints
You will want to use the new isTypeOf
function.
Code
Example 1
function eitherOnType(?, ?) {
if (isTypeOf(?, ?)) {
return testValue;
}
return defaultValue;
}
Example 2
function eitherOnType(?, ?) {
return isTypeOf(?, ?) ? testValue : defaultValue;
}
Modify the greet
function so that you replace the trinary operator with the eitherOnType
function.
Hints
The trinary operator has effectively moved to the eitherOnType
method. The one gotcha to worry about is the pesty !
. If you look closely, you can see that it is added to both sides of the trinary. Maybe you can add it instead to the result of the eitherOnType
function.
Code
Example
function greet(greeting) {
return eitherOnType(?, ?, ?) + '!';
}
You will refactor the sum
function.
Here are the steps you will take to refactor the sum
method so that you can change its shape without changing its behavior.
- Replace forEach with reduce -- assign calculated sum to "result" variable
- Remove wrapping function from reduce -- pass add function directly to reduce
- Remove assignment and return calculated result directly to caller
KEEP THE TESTS PASSING!
Modify the sum
function to use the Array.prototype.reduce
method in place of the Array.prototype.forEach
method.
Hints
The Array.prototype.reduce
method iterates over an array and performs an accumulation of each value. It can take a function with two parameters previous
and current
. The previous
value represents the results of the function being applied to the previous value. The current
value represents the current value in the array. What ever this function returns becomes the previous
value for the next iteration.
In short you can use this similarly to the forEach
method but without the need for the accumulator variable.
Code
Example 1
function sum(nums) {
let result = nums.reduce(function (previous, current) {
return add(?, ?);
});
return result;
}
Example 2
function sum(nums) {
let result = nums.reduce((previous, current) => add(?, ?));
return result;
}
Modify the sum
function to no longer use a function expression and instead just directly use the add
function.
Hints
The reduce
method takes a function with two parameters, add
takes two parameters.
Code
Example
function sum(nums) {
let result = nums.reduce(add);
return result;
}
Modify the sum
function to just return the result instead of assigning it to a variable.
Hints
Get rid of the result
variable and just return the result of the call to reduce.
Code
Example
function sum(nums) {
return nums.reduce(add);
}
You will refactor the buildVector
function.
You will learn how to add readonly properties to an object. You will be modifying the Vector
object.
The Vector
object's internal points
array should be read only. This is accomplished by using a property getter.
Hints
The way you add a getter to an object defined without the class
keyword is by using the Object.defineProperty
. Also, to ensure non-write ability.
Object.defineProperty(obj, key, {
get: () => value
});
Code
Example
function Vector(points) {
Object.defineProperty(this, 'points', {
get: () => points;
});
}
Now you will modify the Vector
function to return a copy of the array passed into it. This will isolate the Vector
object from changes that happen outside of the object.
Hints
Maybe the Array.prototype.slice
method might be useful.
Code
Example
function Vector(points) {
let pts = ?.slice();
Object.defineProperty(this, 'points', {
get: () => pts;
});
}
Modify the Vector
method to make the copy of the points
array read only.
Hints
It might help to use the Object.freeze method.
Code
Example
function Vector(points) {
let pts = Object.freeze(?.slice());
Object.defineProperty(this, 'points', {
get: () => pts;
});
}
You will be modifying the Vector
function to have better type checking. This will include type validation and throwing of errors.
Modify the Vector
function to throw an error if the points
parameter is not a type of an array.
Hints
To test that something is an array or not, you cannot use the typeOf
function as this function will return "Object"
. What you need to do is check the constructor property to see if the constructor is the Array
function. Also make sure you throw
a new Error
object.
Code
Example
function Vector(points) {
if (?.constructor !== Array) {
throw new Error('Expected an array');
}
let pts = points.slice();
Object.defineProperty(this, 'points', {
get: () => pts;
});
}
Modify the Vector
function to check the type of the first element in the array to ensure it is a number. Then throw
a new Error
if it isn't.
Hints
You might be able to use the isTypeOf
function here.
Code
Example
function Vector(points) {
if (points.constructor !== Array) {
throw new Error('Expected an array');
}
if (!isTypeOf(?, ?[0])) {
throw new Error('Expected all values to be numbers');
}
let pts = points.slice();
Object.defineProperty(this, 'points', {
get: () => pts;
});
}
Modify the Vector
function to check the type of all elements in the array to ensure that they are all numbers. Then throw
a new Error
if any of them are not.
Hints
You might be able to use the Array.prototype.filter
method or the Array.prototype.reduce
method returning a Boolean. Both of these can make use of the isTypeOf
function.
Code
Example 1 (filter
)
function Vector(points) {
if (points.constructor !== Array) {
throw new Error('Expected an array');
}
let badValues = ?.filter(? => !isTypeOf(?, ?));
if (0 < badValues.length) {
throw new Error('Expected all values to be numbers');
}
let pts = points.slice();
Object.defineProperty(this, 'points', {
get: () => pts;
});
}
Example 2 (reduce
)
function Vector(points) {
if (?.constructor !== Array) {
throw new Error('Expected an array');
}
let containsBadValues = ?.reduce(previous, current) =>
previous || (typeOf (current) !== 'number');
if (containsBadValues) {
throw new Error('Expected all values to be numbers');
}
let pts = points.slice();
Object.defineProperty(this, 'points', {
get: () => pts;
});
}
Now you will modify the Vector
method such that you will change the shape of it without changing the behavior.
- You will create methods that handle the type checking.
- You will then utilize these methods instead of having the error checking embedded in the
Vector
constructor.
Create a function called assertArray
which will look at the type of the parameter values
and throw an exception if values
is not an array.
Hints
You already have the code, you will just add it into a new function.
Code
Example
function assertArray(values) {
if (?.constructor !== Array) {
throw new Error('Expected an array');
}
}
Now create a function called assertArrayOfType
that compares all items in values
with the given type
and throws an exception if any of the values are not the correct type.
Hints
Again you already have this code. You just need to add it in the new function.
Code
Example 1 (filter
)
function assertArrayOfType(type, values) {
let badValues = ?.filter(? => !isTypeOf(?, ?));
if (0 < badValues.length) {
throw new Error('Expected all values to be numbers');
}
}
Example 2 (reduce
)
function assertArrayOfType(type, values) {
let containsBadValues = ?.reduce(previous, current) =>
previous || !isTypeOf(?, ?);
if (containsBadValues) {
throw new Error('Expected all values to be numbers');
}
}
Modify the assertArrayOfType
function to call the assertArray
function before it checks the values in the array.
Hints
Example 1 (filter
)
function assertArrayOfType(type, values) {
assertArray(?);
let badValues = values.filter(value => !isTypeOf(type, value));
if (0 < badValues.length) {
throw new Error('Expected all values to be numbers');
}
}
Example 2 (reduce
)
function assertArrayOfType(type, values) {
assertArray(?);
let containsBadValues = values.reduce(previous, current) => previous || !isTypeOf(type, value);
if (containsBadValues) {
throw new Error('Expected all values to be numbers');
}
}
Now modify the Vector
function to call the assertArrayOfType
instead of the logic to check the type.
Hints
You will delete all the type checking code and replace it with the call to assertArrayOfType
.
Code
Example
function Vector(points) {
assertArrayOfType(?);
let pts = points.slice();
Object.defineProperty(this, 'points', {
get: () => pts;
});
}