We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hello !
How do you make a DOM element callback to a function if both the DOM element and the function are inside the same object. I have made this little example:
this is my object :
function Snape()
{
this.myInput = createInput("");
this.myInput.changed(this.erase);
this.erase = function()
{
}
}
But I get the error 11913: Uncaught TypeError: Cannot read property 'bind' of undefined
Is that possible ?
Answers
@ statement
this.myInput.changed(this.erase);
, much probably property erase doesn't exist yet.You may try out to move up
this.erase = function() {}
beforethis.myInput.changed(this.erase);
.Or place erase() outside your class Snape.
Keyword
this
inside the callback function refers to the p5.Element which called it back. :PLook up changed() web reference too: :D
http://p5js.org/reference/#/p5.Element/changed
I didn’t think it would but moving up the function before the callback actually fixed it. That’s a bit messy though, being force to put a function in the middle of the rest. is there another way to let it know that this function exists before, and explicit the function later ?
Another way, in which callback erase() is a Snape's method: :bz
However, if you really need to access
this
inside the callback as an instance of class Snape instead ofthis
representing the p5.Element which invoked the callback, you're better off passing it as an arrow lambda expression for change(): :(|)Actually, I needed to access both the instance of my class and the p5.Element which invoked the callback :) so I just added
var self = this;
at the beginning of the constructor functionI can’t use the
=>
function because I need to callthis.erase
a lot of times in different instance of the object, and from multiple DOM elements.I don’t seem to be able to declare a class in p5.js editor :
Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
I was suggested to do something like this :
this.myInput.changed(function(){ myself.erase(); });
but this is not working either.I get the same error
Uncaught TypeError: undefined is not a function
I tried all the different possibilities :neither of those are working.
Such error message should only show up on JS engines older than 1 year. [-(
It means your p5.js IDE editor is internally relying on some deprecated Electron browser.
Rather than using such old technology, why not online editors, such as these: :bz
Regardless, just type in
"use strict";
as the 1st statement for your ".js" file and the error should go away. ;)https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
If you really need for callback erase() to be flexible enough to be re-used by other p5.Element createInput(), you're gonna need to apply some advanced stuff. :ar!
For regular
function
, itsthis
refers to the object which invoked it.As argument for method change(), that object would be a p5.Element.
The
var self = this;
idea would create a closure for callback erase().Thus we got self for the instance object and
this
for p5.Element inside erase(). *-:)In my latest example below, I've added method addNewInput() which push() a new createInput() for array myInputs[]. And passes erase() as callback for changed().
Callback erase() knows about both self and
this
inside its body.And besides logging those, it also changes Snape's radius property.
I see that
this.erase
is still defined as a function inside the class andaddNewInput()
is consider as a method of the class. Could you elaborate a bit on the difference between the two ? when is it better to use the one or the other ? Could I defineerase
as a method and then use a callback from a DOM object calling this method ?Given they can be called as
snape.erase();
and assnape.addNewInput();
, they're both methods of class Snape. ~:>Sure! B-) They mainly differ on actual location, when created and how many times created:
1) Where does each 1 dwell?
Let's start w/ erase(). Given it's created as
this.erase = function () {}
, it is surely a property of the object created bynew Snape;
. And it is thus annexed to it.So erase() is an instance property of class Snape too, just like myInputs[] & radius. :>
But what about method addNewInput()? It isn't located inside instances of class Snape, but rather in its prototype object.
Here's its actual location: Snape.prototype.addNewInput. @-)
https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype
Now you'd ask: How come
snape.addNewInput();
finds out where Snape.prototype.addNewInput is, given it's not actually inside instance object snape? 8-}Explanation: Inside all JS objects, there's a hidden property called
__proto__
.And every time a property isn't found inside an object at 1st, JS turns its search to
__proto__
.https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
When objects are created via
new
,__proto__
is automatically initialized to point to its class constructor() function's prototype object. For class Snape, that's Snape.prototype object.So for
snape.addNewInput();
, given addNewInput isn't an own property of instance object snape, it ends up finding addNewInput @ Snape.prototype.addNewInput via snape's__proto__
. :-B2) When is each 1 created?
Method addNewInput() is created at the same time its class is created.
But erase() is created when its class' constructor() function is called by
new
, like all the other instance properties. :-\"3) How many times is each 1 created?
Method addNewInput() is created once only, just like its class.
But erase() is created every time a
new
is issued and its constructor() is then called. 8-|Same for the rest of the instance properties: myInputs[] & radius. :P
Given prototype located methods are created once only, if you value RAM, you should always favor such over own instance methods. :-bd
Although own instance methods got slightly faster access b/c they're found right up and skip looking up its prototype via
__proto__
. \m/However, I find such CPU gains are insignificant compared to RAM savings for most cases. :-@
Another advantage of own instance methods is they can use the revealing pattern in order to emulate
private
properties in JS, if you value such feature as well. :(|)Oops! Almost forgot that 1... #-o
As I had explained already, the
this
inside a function points to the object which had called that function.If "as a method" you mean the case where the method resides in its constructor's prototype object, you won't be able to use the
const self = this;
closure trick on it. :-SSThat is, you won't be able to access the instance reference provided by the self variable closure. :(
For example, the prototype erase() method can't access, modify or invoke properties of its own class, b/c its
this
is the callback caller. That is, The p5.Element created by createInput(). :-BIn that exceptional case where a method is used as a callback, it needs to be defined inside its constructor in order to inherit a closure storing its instance object
this
.Of course, for 95%+ cases, stick w/ prototype methods in order to conserve RAM. :-bd