JS fn optional-parameter(s) given ‘undefined’ (to get default) fails for built-ins at least ‘new Date(yr,moi,d,hr,min,s,ms)’ +Array.prototype.splice

Viewed 250

Official JS docs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters says ‘In JavaScript, function parameters default to [pseudo-value ]undefined [when the the parameters are omitted].’ (and indeed I've always found this to be the case), plus it seems to suggest (so as I routinely do in my for in calling my own JS functions) that caller can specify that {the default value be used for an optional parameter} by simply there ensuring for that optional parameter than ‘undefined is passed’ explicitly. However I'm 1st-finding doing the latter causes errors else other odd behavior for the optional parameters of JS built-in methods -at least ‘new Date’ and ‘Array.prototype.splice’.

For instance, JavaScript simple method call of form ‘new Date(yr,moi,d,hr,min,s,ms)’ where one of these parameters which can be optional so is then given ‘undefined’ (so to there requests the default value be used) then gives (seemingly wrong) ‘Invalid date’ {in at least in top web browsers Chrome & Firefox} even tho the official docs seem to suggest the result should be the valid date which is returned when the ‘undefined’ parameter(s) have have simply been omitted, as in case (1 of each of the 5 optional parameters) of:

  1. ‘new Date(2020,6,undefined)’ gives Invalid when seems should give ‘new Date(2020,6)’
  2. ‘new Date(2020,6,15,undefined)’ gives Invalid when seems should give ‘new Date(2020,6,15)’
  3. ‘new Date(2020,6,15,12,undefined)’ gives Invalid when seems should give ‘new Date(2020,6,12)’
  4. ‘new Date(2020,6,15,12,30,undefined)’ gives Invalid when seems should give ‘new Date(2020,6,12,30)’
  5. ‘new Date(2020,6,15,12,30,30,undefined)’ gives Invalid when seems should give ‘new Date(2020,6,15,12,30,30)’

But ‘new Date’ official docs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#Individual_date_and_time_component_values say for these parameters from ‘day’-down are optional per its 1st ‘[, day [, hours [, minutes [, seconds [, milliseconds]]]]]’, so seems ‘undefined’ would work here.

So, as a hack, would it work by instead calling with value ‘null?’ not ‘undefined’ (even tho that would seem wrong). For at least ‘new Date’ in Chrome, I tried the hack of using ‘null’ instead of ‘undefined’ for optional parameter(s), finding oddly that works but just sometimes & bizarrely: for instance ‘new Date(2020,6,15,null)’ is same as ‘new Date(2020,6,15)’, BUT ‘new Date(2020,6,null)’ instead of giving ‘new Date(2020,6)’ it bizarrely gives that minus 1 day. and ‘new Date(2020,null)’ should not be allowed if ‘null’ is reduced to 0 as it often is but instead gives valid ‘new Date(2020,1)’ --bazar!, but certainly not predictable enough to be a fix ‘undefined’ failing.

More important, to test {this: problems when {passing undefined for omitted}} on other built-ins, searching for other JS built-in methods with optional parameters via https://developer.mozilla.org/en-US/search?q=optional, I find https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice I see ‘[0,1,2].splice(1)’ expectedly returns ‘[1, 2]’ BUT ‘[0,1,2].splice(1,undefined)’ returns mysterious ‘[]’.

So what's wrong and how to best fix?

It seems hard to believe that this late in JS development that these core could have such simple bugs for their optional optional, so I gather I must somehow be doing something wrong. But then if so, how does one explicitly request a built-in function be set its optional parameter to use its default-value w/o the {ugliness: needless and possibly problematic redundancy} of {having to explicitly {call the function with, so know & compute} every defaulted parameter's default value, nor explicitly code every possible call of the function its every every possible parameter omission}?

2 Answers

The reason Date gives different results when you explicit pass undefined and omit the parameter entirely is that, internally, it checks how many arguments you've passed, and carries out logic accordingly. For example:

‘new Date(2020,6,undefined)’ gives Invalid when seems should give ‘new Date(2020,6)’

You can replicate this with:

const something = (...args) => {
  const arg0 = args[0] * 10000;
  const arg1 = args[1] * 100;
  const arg2 = args.length === 2 ? 0 : parseInt(args[2]);
  return arg0 + arg1 + arg2;
};
console.log(something(1, 2, 3)); // normal, works
console.log(something(1, 2)); // omit third parameter
console.log(something(1, 2, undefined)); // pass undefined

See the specification:

d. If numberOfArgs > 2, let dt be ? ToNumber(values[2]); else let dt be 1.

e. If numberOfArgs > 3, let h be ? ToNumber(values[3]); else let h be +0.

Etc - it checks the number of arguments, and tries to parse each argument that's passed even if the argument is undefined.

Passing null results in a different behavior because ToNumber converts null to 0, but undefined to NaN.

More important, to test {this: problems when {passing undefined for omitted}} on other built-ins, searching for other JS built-in methods with optional parameters via https://developer.mozilla.org/en-US/search?q=optional, I find https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice I see ‘[0,1,2].splice(1)’ expectedly returns ‘[1, 2]’ BUT ‘[0,1,2].splice(1,undefined)’ returns mysterious ‘[]’.

splice does the same thing, though with slightly different wording.

Array.prototype.splice ( start, deleteCount, ...items )

  1. Else if deleteCount is not present, then

a. Let insertCount be 0.

b. Let actualDeleteCount be len - actualStart.

The "not present" detects whether an argument was actually passed - not whether the argument is undefined or not.

I think this is due to confusion over ECMAScript's undefined value and the concept of undefined being "not defined".

Because undefined is an actual value, it can't be used as a placeholder for an optional value, i.e. it doesn't represent, or elide, an omitted value because it represents the undefined value (which is the only value of the undefined Type).

As a result, you can't elide optional parameters. If a function takes, say, 3 optional parameters, you can't just supply the first and third and elide the second parameter with undefined. As a consequence, if you pass undefined to any parameter when calling Date as a constructor, you'll get an invalid date (because ECMA-262 says that's what must happen if any parameter has the value undefined, or generally, if ToNumber(value) returns NaN).

The algorithms for optional parameters are typically like "If is present, …" (e.g. the Date constructor) so if you pass undefined then the parameter is present and has the value undefined.

Related