javascriptfunctionvarletiife

IIFE strange behavior linked to variables declared with let or var


I try to understand why the variable 'Permissions' will be typeof 'function' when Permissions declared with var but typeof 'object' when declared with let (which is the excepted behavior)

With 'var' - Permissions is typeof 'function'

var Permissions;

(function (Permissions) {
    Permissions[Permissions["ADMIN"] = 0] = "ADMIN";
    Permissions[Permissions["READ_ONLY"] = 1] = "READ_ONLY";
})(Permissions || (Permissions = {}));

console.log(typeof Permissions);

With 'let' - Permissions is typeof 'object'

let Permissions;

(function (Permissions) {
    Permissions[Permissions["ADMIN"] = 0] = "ADMIN";
    Permissions[Permissions["READ_ONLY"] = 1] = "READ_ONLY";
})(Permissions || (Permissions = {}));

console.log(typeof Permissions);

I excepted both scenario to be 'object'. why using 'var' Permissions is typeof 'function'?


Solution

  • Declaring a variable with var in a global scope makes it a property of window. Since you haven't initialized it, it's actually pointing to the existing window.Permissions property, which is a constructor of navigator.permissions.

    let works differently and declares a separate variable in the current scope:

    console.log(Permissions);
    console.log(navigator.permissions.__proto__ === Permissions.prototype);
    
    var test = 'test';
    console.log(window.test);

    If you put your var into a function scope, it would work like let:

    (function(){
    
      var Permissions;
    
      (function (Permissions) {
          Permissions[Permissions["ADMIN"] = 0] = "ADMIN";
          Permissions[Permissions["READ_ONLY"] = 1] = "READ_ONLY";
      })(Permissions || (Permissions = {}));
    
      console.log(typeof Permissions);
    
    })();

    Also works as let in a module scope:

    <script type="module">
    
    var Permissions;
    
    (function (Permissions) {
        Permissions[Permissions["ADMIN"] = 0] = "ADMIN";
        Permissions[Permissions["READ_ONLY"] = 1] = "READ_ONLY";
    })(Permissions || (Permissions = {}));
    
    console.log(typeof Permissions);
    
    </script>

    With this and other many problems inherited to var I treat var as legacy and dangerous and prohibit it with ESLint.