Function Arguments
Another example of a TDZ violation can be seen with ES6 default parameter values (see the ES6 & Beyond title of this series):
var b = 3;
function foo( a = 42, b = a + b + 5 ) {
// ..
}
The b
reference in the assignment would happen in the TDZ for the parameter b
(not pull in the outer b
reference), so it will throw an error. However, the a
in the assignment is fine since by that time it’s past the TDZ for parameter a
.
When using ES6’s default parameter values, the default value is applied to the parameter if you either omit an argument, or you pass an undefined
value in its place:
function foo( a = 42, b = a + 1 ) {
console.log( a, b );
}
foo(); // 42 43
foo( undefined ); // 42 43
foo( 5 ); // 5 6
foo( void 0, 7 ); // 42 7
foo( null ); // null 1
Note: null
is coerced to a 0
value in the a + 1
expression. See Chapter 4 for more info.
From the ES6 default parameter values perspective, there’s no difference between omitting an argument and passing an undefined
value. However, there is a way to detect the difference in some cases:
function foo( a = 42, b = a + 1 ) {
console.log(
arguments.length, a, b,
arguments[0], arguments[1]
);
}
foo(); // 0 42 43 undefined undefined
foo( 10 ); // 1 10 11 10 undefined
foo( 10, undefined ); // 2 10 11 10 undefined
foo( 10, null ); // 2 10 null 10 null
Even though the default parameter values are applied to the a
and b
parameters, if no arguments were passed in those slots, the arguments
array will not have entries.
Conversely, if you pass an undefined
argument explicitly, an entry will exist in the arguments
array for that argument, but it will be undefined
and not (necessarily) the same as the default value that was applied to the named parameter for that same slot.
While ES6 default parameter values can create divergence between the arguments
array slot and the corresponding named parameter variable, this same disjointedness can also occur in tricky ways in ES5:
function foo(a) {
a = 42;
console.log( arguments[0] );
}
foo( 2 ); // 42 (linked)
foo(); // undefined (not linked)
If you pass an argument, the arguments
slot and the named parameter are linked to always have the same value. If you omit the argument, no such linkage occurs.
But in strict
mode, the linkage doesn’t exist regardless:
function foo(a) {
"use strict";
a = 42;
console.log( arguments[0] );
}
foo( 2 ); // 2 (not linked)
foo(); // undefined (not linked)
It’s almost certainly a bad idea to ever rely on any such linkage, and in fact the linkage itself is a leaky abstraction that’s exposing an underlying implementation detail of the engine, rather than a properly designed feature.
Use of the arguments
array has been deprecated (especially in favor of ES6 ...
rest parameters — see the ES6 & Beyond title of this series), but that doesn’t mean that it’s all bad.
Prior to ES6, arguments
is the only way to get an array of all passed arguments to pass along to other functions, which turns out to be quite useful. You can also mix named parameters with the arguments
array and be safe, as long as you follow one simple rule: never refer to a named parameter and its corresponding arguments
slot at the same time. If you avoid that bad practice, you’ll never expose the leaky linkage behavior.
function foo(a) {
console.log( a + arguments[1] ); // safe!
}
foo( 10, 32 ); // 42