Paul Redmond Paul Redmond - 3 months ago 15
Javascript Question

TypeScript - Namespace/Scope variable updates

I'm getting some settings from a local JSON file. The

console.log
inside the function correctly logs the object, but the second after the function returns
undefined
, so the variable isn't being updated?

Also, within
testNameSpace
,
this
returns
window
, why?

namespace testNameSpace {

let settings: any;

function dtJSONLoad() {
let xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', './js/file.json', true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4) {
let response = xobj.responseText;
settings = JSON.parse(response);
console.log(settings);
}
};
xobj.send(null);
}

dtJSONLoad();
console.log(settings);

}


console logs 'undefined'

console logs returned object'

Answer

You are facing two of the most common issues that are being asked about in SO (at least the ones that are tagged with TypeScript).

The first is that you are making an async operation and so when you are doing this:

dtJSONLoad();
console.log(settings);

The console.log part is being executed before the dtJSONLoad has finished, and so the settings variable is undefined.
Your 2nd console.log happens when the async operation completes which is why you see the value.

The 2nd issue is the scope of this:
You are assigning a function to the xobj.onreadystatechange property, this function isn't bound to the current this so when it is executed the this is referencing the Window object.
You have two options here:

(1) Use an arrow function which saves the current scope of this:

xobj.onreadystatechange =  () => {
    // ...
};

(2) Use the Function.prototype.bind function:

xobj.onreadystatechange =  function () {
    // ...
}.bind(this);

Edit

A namespace doesn't have this, and that's because of how it's being compiled into javascript.
For example, this:

namespace mynamespace {
    console.log(this); // Error: 'this' cannot be referenced in a module or namespace body
}

Compiles into:

var mynamespace;
(function (mynamespace) {
    console.log(this);
})(mynamespace || (mynamespace = {}));

Which is equivalent of doing:

function fn() {
    console.log(this);
}

In both cases this is referencing the Window object.

If you do this however:

namespace mynamespace {
    export function fn() {
        console.log(this);
    }
}

mynamespace.fn();

It will print: Object {} which is right, and that's because the fn function resides inside mynamespace.
And the js result looks like this:

var mynamespace;
(function (mynamespace) {
    function fn() {
        console.log(this);
    }
    mynamespace.fn = fn;
})(mynamespace || (mynamespace = {}));
Comments