I'm just missing something (which is terrible considering the kind of thing I'm missing) about "this". I have a class for log formatting. In only one method, which is intended only for debugging/testing, "this" turns out to be undefined. I removed almost everything for demonstration purposes only. The code goes as following:
class LogFormatter {
constructor(source) {
// nothing relevant.
}
formatted() {
return 'returns a formatted string whatever';
}
clog(message, sub) {
console.log(this.formatted());
}
// made only for testing
showThis() {
console.log(this);
}
}
And it is used like this:
const
LogFormatter = require('log-formatter'),
lgf = new LogFormatter('asd');
lgf.clog('whatever');
If I access this from formatted method, or even by calling showThis, it works just fine. So it does not seems to be something related to console.log.
When I call clog method, the next error is thrown:
console.log(this.formatted());
^
TypeError: Cannot read property 'formatted' of undefined
So, only in clog method, "this" is undefined. So what's the elephant in front of my that I'm not seeing? :)
EDIT: Note that if I add a console.log(this) inside the formatted method, works fine too.
Also, I've noticed that if I -try to- create a new object from LogFormatter this way:
const
LogFormatter = new require('log-formatter')('whatever')
Throws an exception, saying that I cant execute LogFormatter without new keyword, which AFAIK, should work, given the fact that the class is the only thing being exported via module.exports = LogFormatter. Also, I did not have this problem using good old constructor functions syntax.
Thanks in advance :)
Related
I have problem with (most probably) the context of this:
Im new in JS and think more like c++ guy.
Please, see the code:
controller.js :
function CController(){ ...
this.myCanvas = new CCanvas(this);
}
CController.prototype.resize() {...}
canvas.js :
function CCanvas(_mainController){
var controller = _mainController;
}
CCanvas.prototype.myEvent(){
this.controller.resize(); // <--- here!
}
I get the error at mentioned line that controller is undefined.
how can it be corrected?
Most likely not a scoping issue. Assuming your _mainController is actually a controller (which I'll add in the version I'm about to show you), your problem is that in the constructor of CCanvas, you're assigning var controller, not this.controller. This in turn causes controller to be dropped (as var is the keyword for a local variable, after all.
function CCanvas(_mainController){
if (!(_mainController instanceof CController)) throw "Not a controller";
this.controller = _mainController;
}
This should work. And it prevents you from supplying a non-controller.
If you really want to stick to your guns as in the comments and having the variable not on the class but still in lexical scope, do this:
var CCanvas = function(_mainController) {
var outputCCanvas = function(){
};
outputCCanvas.prototype.myEvent = function(){
console.log("Event");
}
return outputCCanvas;
};
The level of indirection on this one is crazy, and you lose a ton of good stuff doing it:
It'll be impossible to do instanceof checks on CCanvas, as each and every instance is generated dynamically every time you call the method
Oh, yeah, your instantiation changes. Now, you're doing new (CCanvas(_mainController))(), since CCanvas is now a method returning a class
I am very new to Javascript and I just stuck with something that works in python.
The problem is that I have class where I initiate some empty lists as this.data_y_json and etc. If I make normal function inside class like normal_function(){this.data_y_json = 5} it works and the variable is changed.
However, i work with d3, and there is some trick which I cant get through:
// inside class
// in constructor all this.xxx defined
// after object initiation I call set_data()
set_data(){
d3.json("link2.json",function(data) {
for (var i=0;i<data.d.results.length;i++){
this.data_y_json.push(parseFloat(data.d.results[i].PE))
...
//end of function
// end of class
After calling function set_data() an error is raised: SCRIPT5007: Unable to get property 'data_y_json' of undefined or null reference
I am rewriting my visualization into OOP, before this, I had it solved with global variables and it worked fined. In python I would just passed 'self' as an argument to function, but here in javascript, passing THIS doesnt work and raises another error.
Simply said, I know the problem -> this.data_y_json is not recognized propably because of function(data) doesnt pass self of the object, but I dont know how to do it.
Thank in advance for advice
Are you in an ES2015 environment? Changing your callback to be an arrow function should scope this to be what you want
d3.json("link2.json", (data) => {
for (var i=0;i<data.d.results.length;i++){
this.data_y_json.push(parseFloat(data.d.results[i].PE))
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
For reading up on the arrow function and the scoping of this
As of Firefox 36, Function.__exposedProps__ was made unavailable. Instead if one wanted to expose a chrome JS object to be used in content scripts, they have to use Components.utils.cloneInto with the target scope as browser.contentWindow.wrappedJSObject.
If one does not turn on the cloneFunctions flag, only those attributes are cloned that are not functions. Turning the flag does clone functions too, but not those functions that are defined via the Function.prototype path. For those functions one has to export them via Components.utils.exportTo with the target scope as your exposed object.
Coming to the issue I'm facing. (As I am unable to put it in words, I am adding a MWE).
Chrome end JS:
function Foo(){
this._nFunc = "something";
this._func = function(){/*do something*/};
}
Foo.prototype.Bar = function(){
this._func();
}
Foo.prototype.FooBar = function(){
this._nFunc = "somthing else";
}
var myFoo = new Foo();
var targetScope = browser.contentWindow.wrappedJSObject;
targetScope.myExposedObject = Components.utils.cloneInto(myFoo, targetScope, {cloneFunctions:true});
Components.utils.exportFunction(myFoo.Bar, targetScope.myExposedObject , {defineAs:"Bar"});
Components.utils.exportFunction(myFoo.FooBar, targetScope.myExposedObject , {defineAs:"FooBar"});
Content end JS:
window.myExposedObject.FooBar(); // works
window.myExposedObject._func(); // works
window.myExposedObject.Bar() // error this._func is undefined
Upon logging the this scope received by the function Bar(), we get _func:(void 0), while _nFunc is logged correctly.
Questions:
Is there something I'm missing, or is this a limitation in Firefox? If it is a limitation, please suggest possible ways to workaround the limitation.
Initially I thought that Bar() was somehow unable to access the scope of the calling object, and I tried to supply it the scope as parameters, i.e., Foo.prototype.Bar = function(scope){ scope._func();} and window.myExposedObject.Bar(window.myExposedObject);. Interestingly upon logging, the scope object also turned out to be (void 0). Why is that? I am sure that I am missing something here. What I expected was that the exposed object would map to the original object and upon sending the exposed object as parameters the chrome end JS would be able to get the original object.
While what you're trying to do might be possible with the right combination of cloneInto/exportFunction and waiving of xrays i would suggest you simply load the unprivileged part of your class hierarchy directly into the target context with the subscript loader and only hook the minimal amount of privileged functions into the prototype once it has been created.
This should reduce the attack surface and also avoid headaches with inheritance.
Additionally, these may prove useful:
https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
https://developer.mozilla.org/en-US/docs/Components.utils.makeObjectPropsNormal
For some reason, the player_controller instance variable created on the asterisked line cannot be called in the update function below. When I run this code, the second asterisked line (the console.log) will recognize and print the specified variable from player_controller to the console. However, when I try to use player_controller in the update function below (the double asterisked line), I get the error: "TypeError: this.player_controller is undefined."
Also note that if the bold line is commented out, the other lines (which have similarly created variables) run without errors.
Can anyone tell me what's going on here?
function Engine(){
var GRAVITY = 0.3;
this.map_loader = new MapLoader();
*this.player_controller = new PlayerController();*
this.map = this.map_loader.load_next_map();
*console.log(this.player_controller.keys_down.A);*
this.update = function(){
**this.player_controller.handle_input(this.map.player);**
gravity(this.map.player);
this.map.player.x += this.map.player.dx;
this.map.player.y += this.map.player.dy;
...
...
edit: Didn't realize I couldn't do formatting in code blocks.
Here is a link to the app, if you view the web console while on this page you'll see the error I'm talking about piling up. http://lukescode.net/senior_project_game/main.html
Possibly you create engine as
var engine = Engine();
instead of
var engine = new Engine();
The problem ended up being that I was making the call to Engine.update with setInterval(engine.update, 1000/60) which, as bfavaretto mentioned in a comment, changes 'this' to be something other than the engine in the update function. The solution was to call the function like this:
setInterval(function(){return engine.update();}, 1000/60)
which causes 'this' to properly refer to the object the update function belongs to instead of the window. Thanks to bfavaretto for pointing me in the right direction.
I am currently in process of porting one of my java applet games to javascript+html5. I have never done object oriented javascript before and this prototype based OO stuff is confusing me a lot.
I tried to do a straightforward port from java but am having trouble doing two things:
1) How do I run a function inside a constructor?
2) How do I add a method that has a parameter?
Heres some example code:
function User()
{
setupStats();// I wanted to put some of the variable initializations into
// a separate function for code modularity reasons.
this.name='bob';
//However that doesn't seem to work
alert(this.gold); // gets Undefined
alert(this.name); // gets bob. Phew at least this works
//I also want to add a method with a parameter in it:
this.draw=function(ctx){drawUser(ctx);};
}
function setupStats()
{
this.gold=2;
this.exp=3;
this.blah='blah';
this.that='something else';
this.superultraomg='insert some computation';
}
function drawUser(ctx)
{
ctx.drawImage(blah,blah,blah);
alert(ctx); // Also gets undefined. Uh oh...
alert(this.name); //Undefined? WHAT IS THIS I DONT EVEN...
}
Please help guys!
Example
We are using prototype, to share the defaults in setupStats with all Users. We are using call to pass a context, being the User object, and a parameter;
function User()
{
setupStats();// I wanted to put some of the variable initializations into
// a separate function for code modularity reasons.
this.name='bob';
//However that doesn't seem to work
alert(this.gold); // gets Undefined
alert(this.name); // gets bob. Phew at least this works
//I also want to add a method with a parameter in it:
this.draw= function(ctx){ drawUser.call(this, ctx); };
}
function setupStats()
{
this.gold=2;
this.exp=3;
this.blah='blah';
this.that='something else';
this.superultraomg='insert some computation';
}
User.prototype = new setupStats();
new User().draw('pinky');
function drawUser(ctx)
{
//ctx.drawImage(blah,blah,blah);
alert(ctx); // Also gets undefined. Uh oh...
alert(this.name); //Undefined? WHAT IS THIS I DONT EVEN...
}
You aren't too far off. The trouble is mostly your use of the 'this' keyword.
You want something more like:
var user = {};
var user.setupStats = function ()
{
this.gold=2;
this.exp=3;
this.blah='blah';
this.that='something else';
this.superultraomg='insert some computation';
};
var user.init = function ()
{
this.name='bob';
//Setup the stats
this.setupStats();
//However that doesn't seem to work
alert(this.gold); // gets Undefined
alert(this.name); // gets bob. Phew at least this works
//I also want to add a method with a parameter in it:
this.draw=function(ctx){drawUser(ctx);};
};
You would continue this approach and execute calls against it by doing things like
user.init();
which would automatically chain your function references together.
I recommend reading JavaScript: The World's Most Misunderstood Programming Language by Douglas Crockford. He explains clearly how classes, private members, public members, inheritance, etc. are done in JavaScript.
You may want to consider encasing these methods in class scope, if there is still method ambiguity you can use dot notation to resolve the namespace ambiguity. this.name works because it is defined in the same function, however other functions do not know that they are intended to exist in the same scope, thus they return undefined.
ctx is not defined in drawUser() because the parameters are declared incorrectly. Javascrpit params should be delared as (NB they do not take the var keyword):
function methodName( aParam : aParamType, bParam : bParamType) {}
classes are declared using the class keyword [optional, omit square brackets]
[private public static] class ClassName [extends ParentClass] { /*methods here*/ }
hope this helps.