Prototype "this" not working (JS) - javascript

Trying to do an .onload method with an Image() object and apparently it isn't inheriting the "this" in its function. Any help?
function UI() {
this.canvas_obj = document.getElementById('game');
this.canvas = this.canvas_obj.getContext('2d');
this.imgcache = {};
this.imglist = [
'rooms/main-square.png'
];
for (var i = 0; i < this.imglist.length ; i++) {
var img = new Image();
img.src = this.imglist[i];
this.imgcache[this.imglist[i]] = img;
}
}
// snip //
/*
* drawImg
* Draws an image on the canvas at the specified x, y (if the image isn't in the pre-cache, it creates it as well)
* #param str image path
* #param array x,y
* #param array width, height
*/
UI.prototype.drawImg = function(path, coords, size) {
var found = false;
if (size == undefined) {
var w = 0;
var h = 0;
} else {
var w = size[0];
var h = size[1];
}
for (var i = 0; i < this.imgcache.length ; i++) {
if (path == this.imgcache[i].src) {
found = true;
}
}
if (!found) {
var img = new Image();
img.src = path;
this.imgcache[path] = img;
}
if (w == 0 && h == 0) {
this.imgcache[path].onload = function() {
this.canvas.drawImage(this.imgcache[path], coords[0], coords[1], this.imgcache[path].width, this.imgcache[path].height);
};
} else {
this.imgcache[path].onload = function() {
this.canvas.drawImage(this.imgcache[path], coords[0], coords[1], w, h);
};
}
}

As for every variable, in JavaScript, the scope of this is relative to function you are in. So, when in
this.imgcache[path].onload = function() {
// ...
};
this will be bound to the object imgcache[path]. A common approach is to keep the value of this in another variable (by convention, it's often that) in order to access it inside nested functions: it's called a closure.
var that = this;
this.imgcache[path].onload = function() {
// that will reference the "outer this"
};
Now, this is due to how JavaScript binds the this value on function invocation. It is possible to bind this to another value by using call or apply.

The value of a function's this keyword is set by how the function is called. When the drawImg method is called on an instance of UI, e.g.:
var fooUI = new UI(...);
fooUI.drawImg(...);
then in the method fooUI.drawImg, the this keyword will reference fooUI.
Within the method there is an assignment to an onload property:
this.imgcache[path].onload = function() {
this.canvas.drawImage(this.imgcache[path], coords[0], coords[1],
this.imgcache[path].width, this.imgcache[path].height);
};
The first this will reference fooUI. However, the anonymous function assigned to the onload property is called as a method of the object referenced by this.imagecache[path], so that is the object referenced by the function's this when called.
You can change that by using a closure to a local variable with a sensible name (that is not a good choice in my opinion) like:
var uiObj = this;
this.imgcache[path].onload = function() {
uiObj.canvas.drawImage(uiObj.imgcache[path], coords[0], coords[1],
uiObj.imgcache[path].width, uiObj.imgcache[path].height);
};

Related

Simplify the code by using cycle function

I have multiply functions which are using the same cycle code and i'm wondering is it possible to simplify the code by having one cycle function so i could execute the code just by calling wanted function names.
Now:
for(var i=0;i<all;i++){ someFunction(i) }
Need:
cycle(someFunction);
function cycle(name){
for(var i=0;i<all;i++){
name(i);
}
}
I tried to do this by using "window" and i get no error but the function is not executed.
var MyLines = new lineGroup();
MyLines.createLines(); // works
MyLines.addSpeed(); // doesn't work
var lineGroup = function(){
this.lAmount = 5,
this.lines = [],
this.createLines = function (){
for(var i=0,all=this.lAmount;i<all;i++){
this.lines[i] = new line();
}
},
this.addSpeed = function (){
// no error, but it's not executing addSpeed function
// if i write here a normal cycle like in createLines function
// it's working ok
this.linesCycle("addSpeed");
},
this.linesCycle = function(callFunction){
for(var i=0,all=this.lAmount;i<all;i++){
window['lineGroup.lines['+i+'].'+callFunction+'()'];
}
}
}
var line = function (){
this.addSpeed = function (){
console.log("works");
}
}
window['lineGroup.lines['+i+'].'+callFunction+'()'];
literally tries to access a property that starts with lineGroups.lines[0]. Such a property would only exist if you explicitly did window['lineGroups.lines[0]'] = ... which I'm sure you didn't.
There is no need to involve window at all. Just access the object's line property:
this.lines[i][callFunction]();
i get no error but the function is not executed.
Accessing a non-existing property doesn't generate errors. Example:
window[';dghfodstf0ap9sdufgpas9df']
This tries to access the property ;dghfodstf0ap9sdufgpas9df, but since it doesn't exist, this will result in undefined. Since nothing is done with the return value, no change can be observed.
Without a name space use:
window["functionName"](arguments);
SO wrap it up and use it thus:
cycle(someFunction);
function cycle(name){
for(var i=0;i<all;i++){
window[name](i);;
}
}
With a namespace, include that:
window["Namespace"]["myfunction"](i);
Note that this is likely a bit of overkill but using a function to make a class object (you can google the makeClass and why it is/could be useful) you can create instances of the object.
// makeClass - By Hubert Kauker (MIT Licensed)
// original by John Resig (MIT Licensed).
function makeClass() {
var isInternal;
return function (args) {
if (this instanceof arguments.callee) {
if (typeof this.init == "function") {
this.init.apply(this, isInternal ? args : arguments);
}
} else {
isInternal = true;
var instance = new arguments.callee(arguments);
isInternal = false;
return instance;
}
};
}
var line = function () {
this.addSpeed = function () {
console.log("works");
};
};
var LineGroup = makeClass();
LineGroup.prototype.init = function (lineNumber) {
this.lAmount = lineNumber?lineNumber:5,
this.lines = [],
this.createLines = function (mything) {
console.log(mything);
var i = 0;
for (; i < this.lAmount; i++) {
this.lines[i] = new line();
}
},
this.addSpeed = function () {
console.log("here");
this.linesCycle("addSpeed");
},
this.linesCycle = function (callFunction) {
console.log("called:" + callFunction);
var i = 0;
for (; i < this.lAmount; i++) {
this.lines[i][callFunction]();
}
};
};
var myLines = LineGroup();
myLines.createLines("createlines");
myLines.addSpeed();
//now add a new instance with 3 "lines"
var newLines = LineGroup(3);
newLines.createLines("createlines2")
console.log("addspeed is a:" + typeof newLines.addSpeed);
console.log("line count"+newLines.lAmount );
newLines.addSpeed();

How to have function call its own function on its creation

I have a function PublicGame which I'd like to be using similar to a class. When I create PublicGame I give it a bunch of methods by setting this.methodName = function. The only thing is that I want to call some of these methods when the PublicGame is created. Right now for instance I do this.judge = this.setJudge(), but I know this wont work where I have it because, setJudge isnt defined yet. Should I put this at the bottom of PublicGame? Is my design totally off?
Code:
'use strict';
// var GameSockets = require(‘GameSockets’);
var Games = {};
var id_counter = 0;
var minPlayers = 3;
var maxPlayers = 6;
function PublicGame (players) {
this._id = id_counter++;
this.players = players;
this.gameSocket = new GameSockets.registerPlayers(this.players, this._id, this.playerDisconnects);
this.judge = this.setJudge();
this.killGame = function() {
delete Games[this._id];
};
// When a player presses leave game
this.playerExits = function(playerToRemove) {
// Delete player from players array
this.players.splice(this.players.indexOf(playerToRemove),1);
// If less than min players
if (this.players.length < minPlayers) this.killGame();
// If less than max players
if (this.players.length < maxPlayers) {
this.needsPlayers = true;
}
gameSockets.kickPlayer(playerToRemove);
};
// When a player disconnects without warning, e.g. closes window
this.playerDisconnects = function(playerToRemove) {
// Delete player from players array
this.players.splice(this.players.indexOf(playerToRemove),1);
// If less than min players
if (this.players.length < minPlayers) this.killGame();
// If less than max players
if (this.players.length < maxPlayers) {
this.needsPlayers = true;
}
};
this.selectJudges = function() {
this.judge = this.players.pop();
this.players = this.players.unshift(this.judge);
};
this.setWinner = function(winner) {
this.winner = winner;
};
Games[this._id] = this;
}
If you define your functions on the prototype than you do not need to "wait" for the functions to be defined because the instance will already have them when the constructor's code is called
function PublicGame (players) {
//...
this.judge = this.setJudge();
}
PublicGame.prototype.killGame = function(){
//...
};
PublicGame.prototype.playerExits = function(playerToRemove){
//...
};
PublicGame.prototype.setJudge = function(){
//do whatever
return whatever;
};
So unless your functions need to access some "private" variable (ie defined within the constructor, not a global variable), or other reason requiring it, define it on the prototype instead of defining it in the constructor and it will be ready to use.
You have to use javascript prototype !
Read the comments in the code sample.
/*
* utils functions
*
* dont take care about that
**/
var el = document.getElementById('dbg');
var jj = function(val,sep){return JSON.stringify(val , null , sep || '')}
var log = function(val){el.innerHTML+='<div><pre>'+val+'</pre></div>'};
var counterId = 0;
/************************************************************************/
// You have to use prototype
// here an example of what you can achieve
// we create a Player 'class'
var Player = function( name ){
this.id = counterId ++; //<-- an attribute
this.name = name; //<-- an attribute
this.setLevel(5);//<-- a method called at 'instanciation'
return this;
};
// a method available at instanciation time
Player.prototype.setLevel = function(level){
this.level = level;
return this;
};
// we create a new Player named Toto
var Toto = new Player('Toto');
log('Toto = ' + jj(Toto));//<-- utility function just to log
// we create a new Player named Jane
var Jane = new Player('Jane');
log('Jane = ' + jj(Jane)); //<-- utility function just to log
// we change the Level of Jane
Jane.setLevel(12);
log('Jane.setLevel(12)');//<-- utility function just to log
log('Jane = ' + jj(Jane));//<-- utility function just to log
<div id='dbg'></div>

Javascript undefined error: `this` is null

Somehow when executing this code, I get the alert from line 29 .mouseOnSeat.
But I don't know why this.seats is null, while in the draw function it is not.
I call the init function from html5.
//init called by html5
function init() {
var cinema = new Cinema(8, 10);
cinema.draw("simpleCanvas");
var canvas = document.getElementById("simpleCanvas");
//add event listener and call mouseOnSeat
canvas.addEventListener('mousedown', cinema.mouseOnSeat, false);
}
var Cinema = (function () {
function Cinema(rows, seatsPerRow) {
this.seats = [];
this.rows = rows;
this.seatsPerRow = seatsPerRow;
var seatSize = 20;
var seatSpacing = 3;
var rowSpacing = 5;
var i;
var j;
for (i = 0; i < rows; i++) {
for (j = 0; j < seatsPerRow; j++) {
this.seats[(i * seatsPerRow) + j] = new Seat(i, j, new Rect(j * (seatSize + seatSpacing), i * (seatSize + rowSpacing), seatSize, seatSize));
}
}
}
Cinema.prototype.mouseOnSeat = function (event) {
//somehow this is null
if (this.seats == null) {
alert("seats was null");
return;
}
for (var i = 0; i < this.seats.length; i++) {
var s = this.seats[i];
if (s.mouseOnSeat(event)) {
alert("Mouse on a seat");
}
}
alert("Mouse not on any seat");
};
Cinema.prototype.draw = function (canvasId) {
var canvas = document.getElementById(canvasId);
var context = canvas.getContext('2d');
var i;
//somehow this isn't
for (i = 0; i < this.seats.length; i++) {
var s = this.seats[i];
context.beginPath();
var rect = context.rect(s.rect.x, s.rect.y, s.rect.width, s.rect.height);
context.fillStyle = 'green';
context.fill();
}
};
return Cinema;
})();
I tried a lot, like creating a self variable (var self = this ) and then calling from self.mouseOnSeat, it was suggested on another post, but I didn't figure it out.
The problem is that when you call addEventListener, the variable this does not carry along to the function call. This means that this is not your object.
You workaround is sound, you can use it. Or alteratively change your addEventListener call to:
canvas.addEventListener('mousedown', cinema.mouseOnSeat.bind(this), false);
Do note that you might need to use a polyfill to get Function.prototype.bind for older browsers, although it is very well supported currently. See caniuse.
I found a workaround :
canvas.addEventListener('mousedown', function (event) {
cinema.mouseOnSeat(event);
}, false);
But I have no clue why

passing array to Constructor function and keep it public

here is my code :
var BoxUtility = function() {
var boxList = Array.prototype.pop.apply(arguments);
};
Object.defineProperties(BoxUtility, {
totalArea: {
value: function(){
var x = 0;
for(var i = 0, len = boxList.length; i <= len - 1; i++){
x = x + boxList[i].area;
};
return x;
}
}
});
I'm trying to achieve this syntax for my Code :
var boxArray = [box01, box02, box03];
box are objects, box01.area => boxes have area property
var newElement = new BoxUtility(boxArray);
alert(newElement.totalArea);
I WANT TO SEE THE RESULT AS I EXPECT but I think boxList is in another scope
How can I reach it in defineProperties
You have to assign the value to a property of this in your constructor.
var BoxUtility = function() {
// this.boxList
this.boxList = Array.prototype.pop.apply(arguments);
};
// instance methods go on the prototype of the constructor
Object.defineProperties(BoxUtility.prototype, {
totalArea: {
// use get, instead of value, to execute this function when
// we access the property.
get: function(){
var x = 0;
// this.boxList
for(var i = 0, len = this.boxList.length; i <= len - 1; i++){
x = x + this.boxList[i].area;
};
return x;
}
}
});
var boxUtil = new BoxUtility([{area:123}, {area:456}]);
console.log(boxUtil.totalArea); // 579
Variable scope is always at the function level. So you declared a local variable that is only usable inside your constructor function. But every time you call the constructor function you get a new object (this). You add properties to this in order to have those properties accessible in your instance methods on the prototype.
this works
var BoxUtility = function() {
this.boxList = Array.prototype.pop.apply(arguments);
Object.defineProperties(this, {
totalArea: {
get: function(){
var x = 0;
for(var i = 0, len = this.boxList.length; i <= len - 1; i++){
x = x + this.boxList[i].area;
};
return x;
}
}
});};
var y = new BoxUtility(boxArray);
alert(y.totalArea)
This is simple way to pass array as argument in constructer and declare function prototype for public access.
function BoxUtility(boxArray) {
this.boxArray = boxArray;
this.len = boxArray.length;
}
Color.prototype.getAverage = function () {
var sum = 0;
for(let i = 0;i<this.len;i++){
sum+=this.boxArray[i];
}
return parseInt(sum);
};
var red = new BoxUtility(boxArray);
alert(red.getAverage());

Javascript function doesn't receive an argument correctly [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JavaScript closures and variable scope
Assign click handlers in for loop
I have this script:
var MyClass = {
MyArray: new Array(0, 1, 2, 3, 4),
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
var cen = document.getElementById("cen_" + i); // It is an img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc1(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
}
},
MyFunc2: function(id) {
alert(id);
}
}
My problem is that, at this line :cen.onclick = function() { MyClass.MyFunc1(i); }; the argument sent to MyFunc2 is always -1. The MyFunc1 function should create four images, each one with an onclick event. When you click on each image, the MyFunc2 function should show the corresponding i value. It looks like the i value is not "saved" for each event and image element created, but only its "pointer".
Thanks!
You should be familiar with the concept of JavaScript closures to understand why this happens. If you are, then you should remember that every instance of the
function() { MyClass.MyFunc1(i); };
function closure contains i's value of -1 (since it is the final value of this variable after the entire loop finishes executing.) To avoid this, you might either use bind:
cen.onclick = (function(i) { MyClass.MyFunc1(i); }).bind(null, i);
or use an explicitly created closure with the proper i value.
It's a normal case and misunderstand of closures, see this thread and you may get some clue, the simply way to fix this problem is to wrap your for loop body with an Immediate Invoked Function Expression
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
(function(i) {
var cen = document.getElementById("cen_" + i); // An img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc2(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
}(i));
}
}
You are capturing a variable that changes inside the loop, so you always get the last value of i.
You can easily fix that by creating a closure:
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
(function(i) {
var cen = document.getElementById("cen_" + i); // An img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc2(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
})(i);
}
},

Categories

Resources