How to escape "this" problem in a javascript class? - javascript

get_O3(e)
{
e.preventDefault();
let station = document.getElementById(e.target.id);
let lon = station.getAttribute('lon');
let lat = station.getAttribute('lat');
let code_station = station.getAttribute('code');
this.get_previsions(lon, lat, "O3").bind(this).then((data) =>
{
console.log(data);
});
}
I have a "this" problem, when i call the function get_previsions i get the error :
Uncaught TypeError: this.get_previsions is not a function.
It might be because of the (e) parameter because when i do a console.log(this) it returns the target. I would like that this == my class.
Thanks for the help

At any given point you can check what the current this reference is pointing to by doing the 4 following rules:
New: Was the function called using new then the this points to the new instance.
Explicit Binding: Was the function called using Function#call, Function#apply or Function#bind
Implicit Binding: Was the function called by its owner? (i.e. owner.foo() or owner["bar"]())
Default Rule: If none of the other rules happen then this is set to the window object if the script is running in "use strict" mode otherwise undefined.
Event-listeners call a function using Explicit binding (callBack.call(target, ...)) so the this reference gets set to the target. To change the this reference you either need to wrap it and call it implicitly or use Function#bind.
Implicit call Example (+ closure):
var something = {
foo: function() {
var self = this;
addEventListener("click", function(e) {
self.bar(e);
});
},
bar: function() {
}
};
Explicit call Example (Function#bind):
var something = {
foo: function() {
addEventListener("click", this.bar.bind(this));
},
bar: function() {
}
};

I'm assuming you have a class defined similar to
class thing {
get_O3(e) { ... },
get_previsions() { ... }
}
There are a few options for you. First option, you can bind all functions to this in the constructor:
class thing {
constructor () {
this.get_03 = this.get03.bind(this);
this.get_previsions = this.get_previsions.bind(this);
}
get_O3(e) { ... },
get_previsions() { ... }
}
This can get awkward, especially if you have many functions. You can write a helper bindAll function, but a less awkward/verbose solution is to use a factory method instead, bypassing this altogether:
function makeThing {
const thing = {
get_O3(e) {
...
thing.get_previsions();
},
get_previsions() { ... }
};
return thing;
}
Eric Elliot on Medium has some good reading on the topic if you want to get more in depth.

Related

Javascript OOP use function inside object

I have JS object
var widget = {
check_balance: function(){...},
function_called_in_init: function(){
.....
this.check_balance();
};
};
this is code screenshot for better understanding..
and when it try to call this.check_balance(); it returns me error TypeError: this.check_balanceis not a function
the question would be - how to call function inside object which was also created inside object?
Also I can't init this function at the moment when all object is inited, becouse this is a recursion with ajax callback.
Its a little tricky to see what you are asking but the gist of it is you are looking to have the correct context. The tool for that is the whatever.bind(theContext) function. You pass in theContext to the object and that makes theContext object the context of whatever.
var parent = {
foo: function () {
var widget = {
check_balance: function(){ console.log('checking'); },
function_called_in_init: function(){
this.bar();
}.bind(this),
};
widget.function_called_in_init();
},
bar: function () {
console.log('bar');
},
};
parent.foo();
see fiddle
bind documentation
Use private function and closure
var widget = (function() {
var check_balance = function() {
//do what your check_balance has to do
}
return {
check_balance: check_balance,
function_called_in_init: function(){
.....
check_balance();
};
};
})();

javascript reference to prototype function is undefined in constructor

Given the following object:
function MyObject() {
window.myFunc1 = this.myFunc1;
window.myFunc2 = this.__proto__.myFunc2;
}
MyObject.prototype.anotherFunc = function () {
window.myFunc3 = this.myFunc3;
window.myFunc4 = this.__proto__.myFunc4;
}
MyObject.prototype.myFunc1 = function () { console.log(1); }
MyObject.prototype.myFunc2 = function () { console.log(2); }
MyObject.prototype.myFunc3 = function () { console.log(3); }
MyObject.prototype.myFunc4 = function () { console.log(4); }
var o = new MyObject();
o.anotherFunc();
window.myFunc1();
window.myFunc2();
window.myFunc3();
window.myFunc4();
In fiddle I'm getting what is expected, but when testing with an ios app under ionic framework the results (tested with safari debugger) are:
window.myFunc1 -> undefined
window.myFunc2 -> it works!
window.myFunc3 -> it works!
window.myFunc4 -> it works!
Isn't the prototype attached to the object before it is created?
EDIT
attached fiddle and working example, fine tuned the question
It may happen if you create your object in an unusual way, and this is bound to something different from what you expect. In my case I was exporting a constructor from the node module, and creating an object with new require('module')();, which had the same effect of prototype functions being undefined.
I'm getting the following results […]
I cannot reproduce that. Try this:
function MyObject() {
console.log(this.myFunc1);
console.log(Object.getPrototypeOf(this).myFunc2);
}
MyObject.prototype.anotherFunc = function () {
console.log(this.myFunc3);
console.log(Object.getPrototypeOf(this).myFunc4);
}
MyObject.prototype.myFunc1 = function () { }
MyObject.prototype.myFunc2 = function () { }
MyObject.prototype.myFunc3 = function () { }
MyObject.prototype.myFunc4 = function () { }
var o = new MyObject();
o.anotherFunc()
Isn't the prototype attached to the object before it is created?
Uh, no. The prototype cannot really be attached to a non-existing object :-) Best think of it as happening simultaneously, that an object is created with a prototype, it doesn't exist without one.
What you might have meant that the object (with its prototype) is created before the constructor is executed - yes.

Crockford's Prototypical Inheritance - Usage

I've been building a small JS framework for use at my job, and I'd like to employ Douglas Crockford's prototypical inheritance patterns. I think I get the general idea of how the prototype object works, but what isn't clear is the way in which I would use this pattern beyond the simplest example.
I'll flesh it out to the point that I understand it.
(function () {
'use strict';
var Vehicles = {};
Vehicles.Vehicle = function () {
this.go = function () {
//go forwards
};
this.stop = function () {
//stop
};
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
}());
So now my Vehicles.Airplane object can go() and stop(), but I want more. I want to add takeOff() and land() methods to this object. I could just use ugly dot notation afterwards:
Vehicles.Airplane.takeOff = function () {
//take off stuff
}
But that seems wrong, especially if I were to add many methods or properties. The question asked at here seems to be very similar to mine, but the answer doesn't quite ring true for me. The answer suggests that I should build an object literal before using Object.create, and that I should pass that object literal into the create method. In the example code given, however, it looks like their new object inherits nothing at all now.
What I'm hoping for is some syntax similar to:
Vehicles.Airplane = Object.create(Vehicles.Vehicle({
this.takeOff = function () {
//takeOff stuff
};
this.land = function () {
//land stuff
};
}));
I know this syntax will break terribly with Object.create right now, because of course I'm passing Vehicle.Vehicle a function rather than an object literal. That's beside the point. I'm wondering in what way I should build new properties into an object that inherits from another without having to list them out one at a time with dot notation after the fact.
EDIT:
Bergi, after some anguished thought on the topic, I think I really want to go with what you described as the "Classical Pattern". Here is my first stab at it (now with actual code snippets rather than mocked up hypotheticals - You even get to see my crappy method stubs):
CS.Button = function (o) {
o = o || {};
function init(self) {
self.domNode = dce('a');
self.text = o.text || '';
self.displayType = 'inline-block';
self.disabled = o.disabled || false;
self.domNode.appendChild(ctn(self.text));
if (o.handler) {
self.addListener('click', function () {
o.handler(self);
});
}
}
this.setText = function (newText) {
if (this.domNode.firstChild) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.domNode.appendChild(ctn(newText));
};
init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
constructor: {value: CS.Button, configurable: true}
});
CS.Displayable = function (o) { // o = CS Object
o = o || {};
var f = Object.create(new CS.Element(o));
function init(self) {
if (!self.domAnchor) {
self.domAnchor = self.domNode;
}
if (self.renderTo) {
self.renderTo.appendChild(self.domAnchor);
}
}
//Public Methods
this.addClass = function (newClass) {
if (typeof newClass === 'string') {
this.domNode.className += ' ' + newClass;
}
};
this.addListener = function (event, func, capture) {
if (this.domNode.addEventListener) {
this.domNode.addEventListener(event, func, capture);
} else if (this.domNode.attachEvent) {
this.domNode.attachEvent('on' + event, func);
}
};
this.blur = function () {
this.domNode.blur();
};
this.disable = function () {
this.disabled = true;
};
this.enable = function () {
this.disabled = false;
};
this.focus = function () {
this.domNode.focus();
};
this.getHeight = function () {
return this.domNode.offsetHeight;
};
this.getWidth = function () {
return this.domNode.offsetWidth;
};
this.hide = function () {
this.domNode.style.display = 'none';
};
this.isDisabled = function () {
return this.disabled;
};
this.removeClass = function (classToRemove) {
var classArray = this.domNode.className.split(' ');
classArray.splice(classArray.indexOf(classToRemove), 1);
this.domNode.className = classArray.join(' ');
};
this.removeListener = function () {
//Remove DOM element listener
};
this.show = function () {
this.domNode.style.display = this.displayType;
};
init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
constructor: {value: CS.Displayable, configurable: true}
});
I should be quite clear and say that it's not quite working yet, but mostly I'd like your opinion on whether I'm even on the right track. You mentioned "instance-specific properties and methods" in a comment in your example. Does that mean that my this.setText method and others are wrongly placed, and won't be available to descendant items on the prototype chain?
Also, when used, it seems that the order of declaration now matters (I can't access CS.Displayable.prototype, because (I think) CS.Button is listed first, and CS.Displayable is undefined at the time that I'm trying to reference it). Is that something I'll just have to man up and deal with (put things in order of ancestry in the code rather than my OCD alphabetical order) or is there something I'm overlooking there as well?
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
That line is wrong. You seem to want to use new Vehicles.Vehicle - never call a constructor without new!
Still, I'm not sure which pattern you want to use. Two are coming to my mind:
Classical Pattern
You are using constructor functions just as in standard JS. Inheritance is done by inheriting the prototype objects from each other, and applying the parent constructor on child instances. Your code should then look like this:
Vehicles.Vehicle = function () {
// instance-specific properties and methods,
// initialising
}
Vehicles.Vehicle.prototype.go = function () {
//go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
//stop
};
Vehicles.Airplane = function() {
// Vehicles.Vehicle.apply(this, arguments);
// not needed here as "Vehicle" is empty
// maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property
Vehicles.Airplane.prototype.takeOff = function () {
//take off stuff
};
// usage:
var airplane = new Vehicles.Airplace(params);
Pure Prototypical Pattern
You are using plain objects instead of constructor functions - no initialisation. To create instances, and to set up inheritance, only Object.create is used. It is like having only the prototype objects, and empty constructors. instancof does not work here. The code would look like this:
Vehicles.Vehicle = {
go: function () {
//go forwards
},
stop: function () {
//stop
}
}; // just an object literal
Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
//take off stuff
};
// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me
You got Object.create wrong. The first argument should be an object (maybe that's why people suggested you pass a literal).
In your first example, you're actually passing undefined:
Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
// return undefined
The following would work, but it's not very Crockford-ish:
Vehicles.Airplane = Object.create(new Vehicles.Vehicle());
The way I believe Crockford would do it (or, at least, wouldn't complain of):
var Vehicles = {};
Vehicles.Vehicle = {
go : function() {
// go stuff
},
stop : function() {
// go stuff
}
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
takeOff : {
value : function() {
// take-off stuff
}
},
land : {
value: function() {
// land stuff
}
}
});
Note that Vehicles.Vehicle is just a literal, which will be used as the prototype for other objects. When we call Object.create, we pass Vehicles.Vehicle as the prototype, and takeOff and land will be own properties of Vehicles.Airplane. You may then call Object.create again, passing Vehicles.Airplane as the prototype, if you want to create e.g. a Boeing.
The own properties passed as the second parameter are packed in an object that contains a representation of their property descriptors. The outer keys are the names of your properties/methods, and each one points to another object containing the actual implementation as the value. You may also include other keys like enumerable; if you don't they'll take the default values. You can read more about descriptors on the MDN page about Object.defineProperty.

Invoke javascript function from string

I have the following code in my javascript module, however this requires me to make the functions visible to the outside world.
var mymodule = function() {
var self = null,
init = function () {
self = this;
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
},
send = function () {
// some logic
},
finish = function () {
// some logic
},
delete = function () {
// some logic
};
return {
init: init,
send: send,
finish: finish,
delete: delete
};
}();
mymodule.init();
So the only thing I want to return in my module is the init function. However when I do this I cant invoke the functions, because the object (self) only contains the init function visible on the outside.
return {
init: init
};
Is there any solution to invoke my functions like this without making them visible to the outside world? Please no if else statements, because my workflow is bigger then the 3 actions in this example. I want to make my module as closed as possible because this reduces the dependencies.
Update
Here is a updated jsfiddle with one of the proposed solutions, however this is giving me another issue. http://jsfiddle.net/marcofranssen/bU2Ke/
Something like this would work:
var mymodule = function() {
var self = this;
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'), action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
}
self.send = function () {
console.log('send');
}
self.finish = function () {
console.log('finish');
}
self.delete = function (item) {
console.log('delete');
};
return {
init: init,
};
}();
mymodule.init();​
Here's the fiddle:
http://jsfiddle.net/yngvebn/SRqN3/
By setting the self-variable to this, outside the init-function, and attaching the send, finish and delete functions to self, you can use the self[action] syntax from within the init-function
Yes, there is an easy (but perhaps slightly messy) way you can do this without making the functions visible to the global object:
var privateFunctions = { deleter: deleter, send: send};
Then, instead of self[action]();, just do privateFunctions[action](); and you're good to go.
Note that I changed delete to deleter, because delete is a reserved keyword...
var mymodule = function() {
var self = {},
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
};
self.send = function () {
// some logic
};
self.finish = function () {
// some logic
};
self.delete = function () {
// some logic
};
return{
init:init
}
}();
mymodule.init();
This should Work!!
Even if you return an object just with the init property and you populate the rest dynamically such that your module uses them, you would still be making them visible to the outside at runtime. Anyone who wants to debug your module would easily get to them.
You can still create anonymous methods at runtime and they would also be visible together with their implementation.
In your code example, it is vague what "self" really is. You should keep it simple, use encapsulated functions as "private" methods and return a "public" (or "privileged" as Crockford calls it) function that have access to them.
This is the YUI way of doing singletons with private functions and variables. Example pattern:
var mymodule = (function() {
var internal = {
'send': function() {},
'finish': function() {},
'delete': function() {}
};
return {
'init': function(action) {
// access to internals, f.ex:
if ( internal.hasOwnProperty(action) ) {
internal[action].call(this); // bring the caller context
}
}
};
}());
mymodule.init('send');

JavaScript convention: How do you assign arguments to the parent scope

var problemtest = function () {
var parameters;
return function (parameters) {
parameters = parameters;
}
}
var mysolutiontest = function () {
var parameters;
return function (parametersIn) {
parameters = parametersIn;
}
}
This is more of a JavaScript convention question.
Usually I have code similar to that on top. A function take arguments and assigns it to parent scope. However, I cannot use it as in problemtest, as the parameters that are arguments hide the parameters from problemtest.
In OO Programming we can use this, but in JavaScript I cannot use this, so I usually implement a solution similar to mysolutiontest. However, I am not fully satisfied with this solution. Is there a better way of doing this?
If your functions need to share some properties, then assigning them to an object is an elegant and common pattern:
var object = {
property: ['item'],
methodOne: function() {
console.log(this.property);
},
methodTwo: function() {
console.log(this.property);
}
};
object.methodOne(); // ['item']
object.methodTwo(); // ['item']
For further information on how 'this' works within an object - http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/
I usually use _parameters as a convention. This stems from my Java past. This isn't isolated to your example or to Javascript. Any language that does not force you to qualify the variables of any enclosing scope would lead you to the same problem.
var mysolutiontest = function () {
var _parameters;
return function (parameters) {
_parameters = parameters;
}
}
I've also seen people use p_parameters to quality function argument names. This is not one of my favorites tho.
var mysolutiontest = function () {
var parameters;
return function (p_parameters) {
parameters = p_parameters;
}
}
My 2c.

Categories

Resources