I implemented a new feature to our CRM and everything works as it should on Safari (macOS), but it throws Uncaught TypeError: X is not a function
on every other browser we tested it on (Chrome, Firefox, Edge). This is the piece of code that is the culprit:
if (window.changeLabel === 'undefined') {
function changeLabel() {
// Do something
}
changeLabel();
} else {
changeLabel();
}
Why is it working only on Safari? Why is changeLabel
not a function even after I check for its existence? Is this not the way to check if a function exists or not?
window.changeLabel === 'undefined'
will returnfalse
regardless of browser because you are comparing a property value with the string'undefined'
and not the valueundefined
.In Safari's sloppy mode the following will "leak" the function declaration outside of the block within which is was declared.
Chrome's sloppy mode behaves differently due to a different interpretation of the spec (the Web Compatibility Annex IIRC), meaning the function does not "leak", and remains undeclared.
[More details at the end ]
And so in Safari your code accidentally works by leaking the function declaration, making it visible outside the block.
Two things to do:
typeof
for its intended purpose of checking for undeclared identifiersMore details
The reason for all this faffing is that the scope of function declarations inside blocks was never really properly specified in early versions of JavaScript (it was exclusively a function-scoped language back then), and so browser vendors did their own different things. When TC-39 finally decided to standardise this behavior in ES2015, the "Don't Break The Web" requirement meant that the sloppy mode specification for this edge case ended-up being insanely complicated and counterintuitive, leading to two different implementations (only one of which is correct).