window.changeLabel === 'undefined'
will return false
regardless of browser because you are comparing a property value with the string 'undefined'
and not the value undefined
.
In Safari's sloppy mode the following will "leak" the function declaration outside of the block within which is was declared.
if(false) { function foo() {} }console.log(foo) // function foo() {} in Safari, undefined in Chrome-based browsers
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:
- Use strict mode in all production-facing code
- Use
typeof
for its intended purpose of checking for undeclared identifiers
if (typeof changeLabel !== 'function') { window.changeLabel = function() { // Do something. }}// Now you can always use changeLabel...
More 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).