Reputation: 473
I am trying to become more familiar with Javascript OOP and so I wrote a little test script but I keep getting an exception when I test it:
Exception:
Uncaught TypeError: Cannot call method 'day' of undefined
Code:
(function () {
function Time (date) {
var self = this;
var timeInWeek = 604800000;
var timeInDay = 86400000;
var dateInMilliSeconds = date.getTime();
self.add = function (num) {
self.day = function () {
var newDate = new Date();
newDate.setTime(dateInMilliSeconds + (timeInDay * num));
return newDate;
};
};
};
var date = new Date();
var time = new Time(date).add(1).day();
console.log(time);
})();
And when I run the test script outside of the IIFE pattern I am getting the exception Time is undefined
, I am new to Javascript OOP so when I tried reading other Javascript libraries a good chunk was over my head. Any help is appreciated.
Upvotes: 1
Views: 131
Reputation: 7298
You've missed a return this;
(which must be there to allow daisy chaining)
function Time (date) {
var self = this;
var timeInWeek = 604800000;
var timeInDay = 86400000;
var dateInMilliSeconds = date.getTime();
self.add = function (num) {
self.day = function () {
var newDate = new Date();
newDate.setTime(dateInMilliSeconds + (timeInDay * num));
return newDate;
};
return this; //this
};
};
Without the return
statement, in new Time(date).add(1).day())
the day()
is being treated as a method of the value returned by the add
method of the new Time()
instance. If the add
method doesn't return an object that has a day
method, you're sure to get an error.
The return this
is necessary only for chaining.
Your code would work fine with
var time = new Time(date);
time.add(1);
time.day();
OR
var time = new Time(date).add(1);
time.day()
Upvotes: 3
Reputation: 61885
The add
method returns undefined as it has no return
statement. The problem is unrelated to the use of the IIFE.
Instead, the add
method simply added (possibly overwriting) the day
method when it was executed so the following would have "worked":
var time = new Time(date)
t.add(1); // returns undefined, but has a side-effect of adding "day" to t
t.day();
However, I suspect that the issue is two-fold:
add
should return
an object of a compatible type for Method Chaining; this can either be same object (for a mutable design) or it can be new object (for an immutable design).
day
should be added directly to each Time object; this would make it so new Time(date).day()
would work.
For instance:
function Time (date) {
var self = this;
var timeInWeek = 604800000;
var timeInDay = 86400000;
var dateInMilliSeconds = date.getTime();
self.add = function (num) {
// The idea here is to return an object of the same type for "chaining".
// Here I returned a new Time object (you'll have to work out the details),
// although for mutable objects, "return self" would be appropriate.
return new Time(dateInMilliSeconds + (timeInDay * num));
};
self.day = function () {
// actually return the "day", whatever that is.
return ...;
};
};
While this is can be a nice exercise, for production code I'd recommend moment.js unless there is as compelling reason otherwise. The source code for moment.js (which might make a good reference) is on github/moment. The "add" (with omissions and additional comments) looks like:
add : function (input, val) {
// mutate this objects data (but not methods)
addOrSubtractDurationFromMoment(this, dur, 1);
// returns the same object for chaining
return this;
},
Upvotes: 4