try {
const val = 'correct value';
(() => {
((arg = val) => {
const val = 'ignored value';
alert(arg);
})();
})();
} catch (err) {
alert(err.message || 'Unknown error');
}
On OS X Chrome, OS X Safari, Android Chrome, Windows Chrome, Windows Firefox, and even Windows Edge, it alerts "correct value". On iOS Safari and iOS Chrome, it alerts "Can't find variable: val".
The following snippets all work on iOS:
Not using default argument (snippet 2):
try {
const val = 'correct value';
(() => {
alert(val);
(() => {
const val = 'wrong value';
})();
})();
} catch (err) {
alert(err.message || 'Unknown error');
}
No nested functions (snippet 3):
try {
const val = 'correct value';
((arg = val) => {
const val = 'ignored value';
alert(val || 'wrong value');
})();
} catch (err) {
alert(err.message || 'Unknown error');
}
Not overriding variable (snippet 4):
try {
const val = 'correct value';
(() => {
((arg = val) => {
alert(arg);
})();
})();
} catch (err) {
alert(err.message || 'Unknown error');
}
Block scope instead of function (snippet 5):
try {
const val = 'correct value';
{
((arg = val) => {
const val = 'ignored value';
alert(arg);
})();
}
} catch (err) {
alert(err.message || 'Unknown error');
}
Based on the snippet 3, it's clear that the val
in arg = val
should come from the parent scope, not the scope of the inner function.
In the first snippet, the browser can't find val
in the current scope, but instead of checking the ancestor scopes, it uses the child scope, which causes the temporal dead zone.
Is this an iOS bug or am I misunderstanding the proper JS behavior?
This bug is occurring in our Webpack+Babel+Terser output, so we can't just rewrite the code to avoid this bug.
I think this is an unwanted consequence of a buggy implementation of the Param default values and their TDZs. I suspect that iOS Safari thinks you're trying to assign to something you have't initialized yet.
For reference -- the error location:
Workaround 1 Don't initialize an inner scope const w/ the same name as the default param & the outer scope's one
try {
const val = 'correct value';
(() => {
((arg = val) => {
const val_ = 'ignored value'; // <----
alert(arg);
})();
})();
} catch (err) {
console.error(err);
console.error('msg', err.message || 'Unknown error');
}
Workaround 2
Force const
to let
:
try {
let val = 'correct value'; // <----
(() => {
((arg = val) => {
const val = 'ignored value';
alert(arg);
})();
})();
} catch (err) {
console.error(err);
console.error('msg', err.message || 'Unknown error');
}
Workaround 3
Don't reinitialize const val
in the innermost closure at all:
try {
const val = 'correct value';
(() => {
((arg = val) => {
// const val = 'ignored value'; // <--
alert(arg);
})();
})();
} catch (err) {
console.error(err);
console.error('msg', err.message || 'Unknown error');
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With