Javascript - pass THIS in function doesnt work - javascript

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

Related

Can't access "this" from only one method

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 :)

"Uncaught TypeError: undefined is not a function" Error message in Javascript when calling a method in an object

I have a problem when calling a function from a button in HTML that gives me the: "Uncaught TypeError: undefined is not a function" error. I don't think there's anything wrong here.. Or there is something that I haven't taken into account. Thanks in advance for answering!
I have a lot of JS files, this is because this is a school assignment and we're now learning the Model, View, Controller (MVC) method.
I have this button:
<button onClick="ControllerBKE.reageerOpKlik()">Ok!</button>
I then have this Javascript code that creates an object of ^ "ControllerBKE":
"use strict"
window.onload = reageerOpStart();
function reageerOpStart()
{
var controllerBKE = new ControllerBKE();
}
Here is the line of code that is in the "ControllerBKE" that should, but is not reacting to the button:
function ControllerBKE(){
this.reageerOpKlik = reageerOpKlik;
function reageerOpKlik(){
alert('hoi');
}
}
This is just a small portion of a big code. But I get the error message when I click on the button instead of getting an alert with 'hoi'.
reageerOpKlik is an instance method. You have to use it from an instance. The simplest solution (not the best) is to create a global controller instance. There are many ways you could get rid of that global variable, but it's beyond the scope of the question.
function reageerOpStart()
{
window.controllerBKE = new ControllerBKE();
}
<button onClick="window.controllerBKE.reageerOpKlik()">Ok!</button>
The problem is that your code
<button onClick="ControllerBKE.reageerOpKlik()">Ok!</button>
is trying to call reageerOpKlik on your prototype object ControllerBKE.
What you probably mean is
<button onClick="controllerBKE.reageerOpKlik()">Ok!</button>
where controllerBKE is an instance of your prototype.
However, you have another problem. The function:
function reageerOpStart()
{
var controllerBKE = new ControllerBKE();
}
Creates controllerBKE in the scope of the reageerOpStart function, meaning that it's not avaiable in the global scope, which is where your button click handler would expect it.
You might want to consider:
<button onClick="APP.controllerBKE.reageerOpKlik()">Ok!</button>
APP = {}
function reageerOpStart()
{
APP.controllerBKE = new ControllerBKE();
}
Or, better still:
<button id="myButton">Ok!</button>
function reageerOpStart()
{
var controllerBKE = new ControllerBKE();
document.getElementById("myButton").addEventListener("click", function() {
controllerBKE.reageerOpKlik();
});
}
What you have is referred to as a closure. Your function has a limited scope. That is, it can only be called inside of ControllerBKE() where it is defined, not from outside the function.
What you have effectively done though is expose that closure via a property on your instance of ControllerBKE. While this works, it would fit more with the prototypal structure of JavaScript to add it to ControllerBKE.prototype.
It's important to remember that JavaScript is Prototypal not Object Oriented. While this may act similar to object oriented encapsulation, the two have different concepts and uses.
Look at the following example:
HTML:
<button onclick="controllerBKE.reageerOpKlik()">Ok!</button>
JavaScript:
"use strict";
window.controllerBKE = new ControllerBKE();
function ControllerBKE () { }
ControllerBKE.prototype.reageerOpKlik = function () {
alert('hoi');
}
I've simplified some of your code and refactored it to support the prototype object that JavaScript provides us with.
The first line is adding the controllerBKE variable to the window object. This gives it a global scope across the page, allowing your onclick function to have access to it.
The next line is a simple function wrapper. This will create an instance of ControllerBKE of type object.
The function you're trying to call is now attached to the prototype of ControllerBKE. This means that any instances of ControllerBKE created with the new keyword will have access to this function.
Check out the full functionality in the fiddle below:
FIDDLE
References:
Object.prototype
Object Oriented JavaScript

Why can this javascript function not access a variable in the same class?

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.

rails 3 javascript fine coffeescript referenceerror (class) is not defined

Someone on the RubyRogues podcast once said "Learn CoffeeScript because CoffeeScript writes better javascript than you do." Sorry, can't remember who said it...
So, I took a very simple WORKING javascript function:
togglingii.js
function pdtogglejs(id) { $('div .partybackground').removeClass("hidden"); }
Which is being called by this line:
Read More...
Then I converted it into this coffeescript:
toggling.js.coffee
pdtogglecs(id) ->
jQuery('div .partybackground').removeClass("hidden")
and changed the html to reference the pdtoggle*c*s instead of pdtoggle*j*s.
I can see BOTH of them just fine in my application.js file:
(function() {
pdtogglecs(id)(function() {
return jQuery('div .partybackground').removeClass("hidden");
});
}).call(this);
function pdtogglejs(id) { $('div .partybackground').removeClass("hidden"); }
;
(function() {
}).call(this);
However, only the pure javascript works. The coffeescript always returns Uncaught ReferenceError: pdtogglecs is not defined.
Based on other stackoverflow questions it must be some sort of namespace error. Probably because of the way pdtogglecs is, itself, inside of a function?? However, I have tried defining the coffeescript function with: window.pdtogglecs, this.pdtogglecs, root.pdtogglecs and the coffescript one always fails with that error.
What am I missing??
Thanks!!
You have two problems, one is a bit of CoffeeScript syntax confusion and the other is the namespace problem that you know about.
We'll start by sorting out your syntax confusion. This:
f(x) -> ...
is interpreted like this:
f(x)(-> ...)
So when given this:
pdtogglecs(id) ->
jQuery('div .partybackground').removeClass("hidden")
CoffeeScript thinks you're trying to call pdtogglecs as a function with id as an argument. Then it thinks that pdtogglecs(id) returns a function and you want to call that function with your -> jQuery(...) function as an argument. So it ends up sort of like this:
callback = -> jQuery(...)
returned_function = pdtogglecs(id)
returned_function(callback)
And that's nothing like your original JavaScript. You want to create a function named pdtogglecs which takes id as an argument and then runs your jQuery stuff:
pdtogglecs = (id) ->
# -----^ this is sort of important
jQuery('div .partybackground').removeClass("hidden")
You can see what's going on by looking at the generated JavaScript.
The namespace problem is easy and you can probably figure that out based on the other question you found. However, I'll take care of it right here for completeness.
CoffeeScript wraps each .coffee file in a self-executing function to avoid polluting the global namespace:
(function() {
// JavaScript version of your CoffeeScript goes here...
})();
That wrapper makes everything scoped to the .coffee file. If you want to pollute the global namespace then you have to say so:
window.pdtogglecs = (id) -> ...
You can also say:
#pdtogglecs = (id) -> ...
but I prefer the explicitness of directly referencing window, that also saves you from worrying about what # (AKA this) is when you're code is parsed.

How do I pass the name of a function as a parameter then reference that function later?

I want to pass the name of a function "testMath" as a string into a wrapper function called "runTest" as a parameter. Then inside 'runTest' I would call the function that was passed. The reason I'm doing this is because we have a set of generic data that will populate into variables regardless of the test, then a specific test can be called, based on whatever the user wants to test. I am trying to do this using javascript/jquery. In reality the function is much more complex including some ajax calls, but this scenario highlights the basic challenge.
//This is the wrapper that will trigger all the tests to be ran
function performMytests(){
runTest("testMath"); //This is the area that I'm not sure is possible
runTest("someOtherTestFunction");
runTest("someOtherTestFunctionA");
runTest("someOtherTestFunctionB");
}
//This is the reusable function that will load generic data and call the function
function runTest(myFunction){
var testQuery = "ABC";
var testResult = "EFG";
myFunction(testQuery, testResult); //This is the area that I'm not sure is possible
}
//each project will have unique tests that they can configure using the standardized data
function testMath(strTestA, strTestB){
//perform some test
}
Do you need the function names as string? If not, you can just pass the function like this:
runTheTest(yourFunction);
function runTheTest(f)
{
f();
}
Otherwise, you can call
window[f]();
This works, because everything in the 'global' scope is actually part of the window object.
Inside runTests, use something like this:
window[functionName]();
Make sure testMath in the global scope, though.
I preffer to use apply/call approach when passing params:
...
myFunction.call(this, testQuery, testResult);
...
More info here.

Categories