how to pass this keyword to javascript class functions - javascript

I'm running into the undefinded error when trying to access the this.numberObject from another function init.
I'm pretty sure it's because my this keyword is referencing the window object.
My problem is that I can't figure out an elegant way to structure my code.
Current Code:
class IncrementNumbers {
constructor() {
this.numberObject = document.querySelector(".incrementNum");
if (this.numberObject != null) {
console.log(this.numberObject.innerHTML);
this.num = this.numberObject.innerHTML;
this.events();
}
}
events() {
console.log("events function ran");
this.init();
}
init() {
let target = 345;
let current = 0;
function addOneToElement() {
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current; <---- ERROR HERE
}
let addOne = setInterval(addOneToElement, 10);
}
}
export default IncrementNumbers;
Error:
IncrementNumbers.js:44 Uncaught TypeError: Cannot set property 'innerHTML' of undefined
I can fix the error by adding in
this.numberObject = document.querySelector(".incrementNum");
to the following function
init() {
let target = 345;
let current = 0;
function addOneToElement() {
this.numberObject = document.querySelector(".incrementNum"); <--- ADDED HERE
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current;
}
let addOne = setInterval(addOneToElement, 10);
}
However this feels a bit redundant because I'm already referencing the element Object in my constructor. I'd like the function, init to run once the page has loaded.
Is there a better way?
Thank you

Your problem seems to come from a misspropagation of the this object.
In your function init you set a new function named addOneToElement that is called inside of a setInterval, this setInterval instance does not have access to the this element of your class.
To fix the problem you could try to do something like
class IncrementNumbers {
constructor() {
this.numberObject = document.querySelector(".incrementNum");
if (this.numberObject != null) {
console.log(this.numberObject.innerHTML);
this.num = this.numberObject.innerHTML;
this.events();
}
}
events() {
console.log("events function ran");
this.init();
}
init() {
let target = 345;
let current = 0;
function addOneToElement() {
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current; // <---- ERROR HERE
}
let addOne = setInterval(addOneToElement.bind(this), 10);
}
}
export default IncrementNumbers;
let addOne = setInterval(addOneToElement.bind(this), 10);
Where you bind the this instance of your class to the function addOneToElement.

The problem is the inner function addOneToElement that creates its own this context within init.
A simple fix would be to use arrow functions which don't have a this context:
class IncrementNumbers {
init() {
let target = 345;
let current = 0;
// Use an arrow function here.
const addOneToElement = () => {
if (current === target) {
clearInterval(addOne);
}
current++;
this.numberObject.innerHTML = current; <---- ERROR HERE
}
let addOne = setInterval(addOneToElement, 10);
}
}
Another option would be to bind the this context to addOneToElement:
let addOne = setInterval(addOneToElement.bind(this), 10);

Related

Unable to minify javascript class

I have created a JavaScript class. I'm getting an error when I try to minify the code using javascript-minifier. Can you help me to fix this issue?
My code:
class Test {
onCompleted = () => {};
onDismissed = () => {};
onError = () => {};
isProgress = false;
popup;
payment;
startPayment(payment) {
this.payment = payment;
this.isProgress = true;
this.popup = window.open('---');
var timer = setInterval(function () {
if (this.Test.popup.closed) {
clearInterval(timer);
if (this.Test.isProgress) {
this.Test.isProgress = false;
this.Test.onDismissed();
}
}
}, 500);
}
}
const Test = new Test();
window.addEventListener('beforeunload', function () {
if (Test.popup != null && !Test.popup.closed) {
Test.popup.close();
}
});
window.Test = Test;
Error message:
// Error : Unexpected token: operator (=)
// Line : 2
// Col : 18
The way you are creating the class seems to be wrong. In classes you can use functions like this: onCompleted() {}; and you can create variables in constructor. I also fixed an issue where you have Test defined twice, one as the class and one as variable. I renamed variable to TestInstance
Here would be a fixed example:
class Test {
constructor() {
this.isProgress = false;
this.popup;
this.payment;
}
onCompleted () {};
onDismissed () {};
onError () {};
startPayment(payment) {
this.payment = payment;
this.isProgress = true;
this.popup = window.open("---");
var timer = setInterval(function () {
if (this.Test.popup.closed) {
clearInterval(timer);
if (this.Test.isProgress) {
this.Test.isProgress = false;
this.Test.onDismissed();
}
}
}, 500);
}
}
const TestInstance = new Test();
window.addEventListener("beforeunload", function () {
if (TestInstance.popup != null && !TestInstance.popup.closed) {
TestInstance.popup.close();
}
});
window.Test = TestInstance;
A minified version:
class Test{constructor(){this.isProgress=!1,this.popup,this.payment}onCompleted(){}onDismissed(){}onError(){}startPayment(s){this.payment=s,this.isProgress=!0,this.popup=window.open("---");var t=setInterval(function(){this.Test.popup.closed&&(clearInterval(t),this.Test.isProgress&&(this.Test.isProgress=!1,this.Test.onDismissed()))},500)}}const TestInstance=new Test;window.addEventListener("beforeunload",function(){null==TestInstance.popup||TestInstance.popup.closed||TestInstance.popup.close()}),window.Test=TestInstance;

Passing references of a class to another class and using its methods

Let's say you're making a game. You want to try and not pollute the global scope and possibly limit the user's ability to easily alter the game (doubtful with client-side). You feel like modules might be unnecessary for your purposes. Is it bad practice to pass references to a class to another class during instantiation to access its methods?
Contrived example:
//game.js
var Game = (function () {
function Game() {
this.currentLevel = null;
this.score = 0;
}
Game.prototype.addScore = function (num) {
this.score += num;
};
Game.prototype.goToLevel = function (diff) {
this.currentLevel = new Level(this, diff);
};
Game.prototype.returnHome = function (level) {
this.currentLevel = null;
};
return Game;
})();
//level.js
var Level = (function () {
function Level(game, difficulty) {
this.game = game; //reference to game
this.difficulty = difficulty;
this.entities = [];
this.load();
}
Level.prototype.load = function () {
this.addEntity({name: 'tim', power: 23, difficulty: this.difficulty});
};
Level.prototype.leave = function () {
this.game.returnHome();
};
Level.prototype.addEntity = function (options) {
this.entities.push(new Entity(this, options));
};
Level.prototype.removeEntity = function (entity) {
for(var x = 0; x < this.entities.length; x++) {
if(this.entities[x] === entity) this.entities.splice(x, 1);
}
};
return Level;
})();
//level.js
var Entity = (function () {
function Entity(level, options) {
this.level = level; //reference to level
this.options = options;
}
Entity.prototype.kill = function () {
this.level.removeEntity(this); // anti-pattern?
this.level.game.addScore(34.53); // too closely coupled?
};
return Entity;
})();
//main.js
var Main;
(function (Main) {
var game = null;
function documentIsReady() {
start(); // Start the game
}
function start() {
game = new Game();
game.goToLevel('hard');
}
return {
documentIsReady: documentIsReady
}
})(Main || (Main = {}));
$(document).ready(function () {
Main.documentIsReady();
});
Forgive the half-baked example. If you end up with many instances of the 'Entity' class, do all the references to 'Level', though the same instance, start taking more memory? Are there other pitfalls? Another method would be to implement some kind of interface that you can access that allow classes to talk to each other.

Javascript - Looping through classes and adding functions

I'm currently trying to create an HTML5 Canvas game and I want to be able to attach functions to buttons that activate when clicked. I can do this for unique functions but I'm struggling to find a way to do it when looping through many buttons with a predefined function.
I've created an example to show what I've tried so far:
jsFiddle: http://jsfiddle.net/ra1rb74w/1/
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction) {
this.aFunction = newFunction;
},
// Use the current function
useFunction: function () {
if (this.aFunction != null) {
this.aFunction;
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) { console.log(x); }
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction(x));
}
// Use the function in the first class
myClasses[0].useFunction();
You can see that all the functions get triggered which I don't want, and the useFunction() function doesn't work. Is there a way to do this?
So you are triggering baseFunction by calling baseFunction(x). You need to either get baseFunction to return a function which can be executed:
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction) {
this.aFunction = newFunction;
},
// Use the current function
useFunction: function () {
if (typeof this.aFunction === "function") {
this.aFunction.call(this);
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) {
return function() {
console.log(x);
};
}
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction);
}
// Use the function in the first class
myClasses[3].useFunction();
JsFiddle
Or add another parameter to addFunction which can be called like addFunction(baseFunction, x):
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction, value) {
this.aFunction = newFunction;
this.x = value;
},
// Use the current function
useFunction: function () {
if (typeof this.aFunction === "function") {
this.aFunction.call(this, this.x);
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) { console.log(x); }
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction, x);
}
// Use the function in the first class
myClasses[3].useFunction();
JsFiddle
Note I also changed your check for aFunction == null as the function passed in may be null, or a string, or anything else. You want to check if it is executable.
Change to
...
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction, x) {
this.aFunction = newFunction;
this.aFunctionX = x;
},
useFunction: function () {
if (this.aFunction != null) {
this.aFunction(this.aFunctionX);
}
}
};
...
...
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction, x);
}
...
Here is a fiddle http://jsfiddle.net/ra1rb74w/6/

Can not call method of undefined

I'm trying to develop a javascript object that creates a menu in html.
The function receives an object as an argument. Among the object elements is a function that should be executed in an event handler called from a method of my object.
Here is my code :
Menu = function(config) {
var j = 0;
this.config = config;
this.make = function() {
for (i = 0; i < this.config.items.length; i++) {
var vid = document.createElement("div");
vid.className = this.config.cls;
vid.id += i;
document.body.appendChild(vid);
var txt = document.createTextNode(this.config.items[i]);
var pp = document.createElement("p");
pp.appendChild(txt);
vid.appendChild(pp);
}
document.addEventListener("keydown", this.scrolldown, false);
document.onkeydown = function(e) {
var keyCode = e.keyCode;
alert("functional");
if (keyCode == 40) {
alert("You hit key down");
var et = document.getElementById(j);
this.config.trait1(et);
j = j + 1;
} else {
alert("no");
}
}
};
return this;
};
when I call the function make after instantiating the object I have my elements created but my event isn't handled because of :
Uncaught TypeError: Cannot call method 'trait1' of undefined .
Can anyone help me? I saw many answers of the same question but none of the suggested solutions worked.
this inside the Menu function is not the same as this inside the onkeydown function.
Store the value of this in another variable and use that.
Menu = function () {
var myMenu = this; // I'm assuming that you will be calling `new Menu()`
document.onkeydown = function () {
myMenu.config.etc.etc.etc
}
}

'this' is unequal to Bar in prototype

In the following code snippet, 'this.x()' can only be called in case 2 (see main()).
Also Bar unequals this in case 1, but is equal for case 2.
function Class_Bar() {
this.panel = null;
this.init = function () {
// do some stuff
this.panel = 20;
}
this.apply = function () {
alert(Bar == this);
Bar.x();
this.x();
}
this.x = function() {
alert("Some friendly message");
alert(Bar.panel);
}
}
var Bar = new Class_Bar();
function Class_Factory() {
this.factories = new Array();
this.add = function (init, apply) {
this.factories.push({"init":init, "apply":apply});
}
this.init = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["init"]();
}
}
this.apply = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["apply"]();
}
}
}
var Factory = new Class_Factory();
function main() {
// Case 1
Factory.add(Bar.init, Bar.apply);
Factory.init();
Factory.apply();
// Case 2
Bar.init();
Bar.apply();
}
main();
http://pastebin.com/fpjPNphx
Any ideas how to "fix" / workaround this behaviour?
I found a possible solution, but it seems to be a "bad" hack.: Javascript: How to access object member from event callback function
By passing Bar.init, you're really only passing the function but not the information that it belongs to Bar (i.e. what the this value should be). What you can do is binding that information:
Factory.add(Bar.init.bind(Bar), Bar.apply.bind(Bar));

Categories