jay jay - 2 months ago 7
Javascript Question

Why is this function behaving weirdly

I have the following JavaScript as below

function getExpiryDate(contract) {
var expiryDay;
var contractType;
var today = new moment();
var today1 = new moment();

var x = myFunction(4, 3);
var abc =3;
var xyz= 4;
var c = myFunction(abc, xyz);

console.log("abc is: "+abc);
console.log("xyz is: "+xyz);
console.log(today1);

expiryDay = getlastDayofMonth('Thursday',today1);

console.log(today1); /* Why has the value of today changed? */
}

function getlastDayofMonth(dayName,date1) {

var endDate = date1.endOf('month');
var lastDayOfMonth = endDate.format('dddd');
var weekDayToFind = moment().day(dayName).weekday(); //change to searched day name

var searchDate = endDate; //now or change to any date

while (searchDate.weekday() !== weekDayToFind)
{
searchDate.subtract(1, 'days');
}

return searchDate;
}

function myFunction(a, b) {
return a * b; // Function returns the product of a and b
}


When I execute I get the following output.

expirydate.js:11 abc is: 3
expirydate.js:12 xyz is: 4
expirydate.js:13 Moment { _d: Wed Aug 31 2016 10:21:04 GMT+0530 }
expirydate.js:15 Moment { _d: Thu Aug 25 2016 23:59:59 GMT+0530 }


I am totally confused on why the value of today1 changes when it is used in the function.

Answer

This is happening because Moment is written in a mutable style of code. It's fairly surprising behavior. After reading through your code a few times I couldn't find any obvious problem until I looked at the Moment documentation.

The .endOf() method mutates the date, aka changes the date of the moment object you call it on:

Mutates the original moment by setting it to the end of a unit of time.

So when you call that method here:

var endDate = date1.endOf('month');

It's mutating date1, meaning it modifies date1 in place and changes its time. In fact, it looks like almost all of moment's methods mutate the moment object. There's a discussion on Github about why the API is designed poorly.

In terms of solving your specific problem, it's a personal style preference. I think it's undesirable to force the user to clone an object before passing it to a function. So I would clone the moment passed in inside the function:

function getlastDayofMonth(dayName,date1) {

    var endDate = date1.clone().endOf('month');