How to access methods of parent class inside subclass in javascript - javascript

I have a 'model' class/prototype defined as below, it has subclass named 'given' which tries to access method 'getNodes()' of model class.
But it gives exception for 'this.getNodes' saying undefined.
var model = {
constructor: function(/*string*/ mode, /*string*/ name, /*object*/ properties) {
this._mode = mode;
this.beginX = 100;
this.beginY = 100;
this.nodeWidth = 200;
this.nodeHeight = 200;
this.x = this.beginX;
this.y = this.beginY;
this.lastNodeVisible = null;
this.ID = 1;
this.taskName = name;
this.properties = properties;
this.checkedNodes = new Array();
// this.model = #call_build_method;
/*
add subclasses with model accessors
*/
this.given = {
getNodes: this.getNodes,
setNodeName: this.setNodeName
};
},
getNodes: function() {
// Summary: returns an array containing the nodes in the given model
return #someobject;
},
}

I assume that you want to call a method in the parent class with the correct scope.
Here are two ways to do this, one using dojo hitch, and one without:
require([
"dojo/_base/lang"
],function(lang){
model = function(){
var obj = {
data: "ok",
getData4: function(){
return this.data;
}
};
obj.sub = {
getData5: lang.hitch(obj, obj.getData4),
getData6: function(){return obj.getData4.apply(obj,arguments);}
};
return obj;
};
m = new model();
console.log("call getData4: ", m.getData4()); // returns "ok"
console.log("call getData5: ", m.sub.getData5()); // returns "ok"
console.log("call getData6: ", m.sub.getData6()); // returns "ok"
});

You need to store this in variable in outter scope:
this.model = <SOMETHING>;
var self = this;
this.given = {
getNodes: function(){self.getNodes(self.model);}
// inside a function this is this.given
};

Related

JavaScript Objects not instantiating

I have a class dynObj, but it appears that seperate instances of it adopt the values of the most recently defined instance.
draw() {
tmov1 = new dynObj(args...); //Displays as a white ball on webpage, as intended
tmov2 = new dynObj(different args...); //Seemingly overwrites the properties of tmov1
objects.push(tmov1, tmov2)
for (var i in objects) {
objects[i].dostuff() //Normally causes the object to display as intended,
}; //but will only ever display one
};
The class dynObj is as follows:
class baseObj {
constructor(position, dimentions, properties) {
this.pos = createVector(position.x,position.y) || null;
this.shape = properties.shape
if (this.shape == "ellipse") {
this.dim = dimentions || {diam:0}
} else if (this.shape == "quadrilateral") {
this.dim = dimentions || { x: 0, y: 0 };
}
};
};
class dynObj extends baseObj {
constructor(position, dimentions, suvat, properties) {
super(position, dimentions, properties);
self = this
self.type = 'dynamic'
self.properties = properties
//more definitions with self.x = someval
};
getDistance(a,b){
if (a == undefined || b == undefined) return false;
var dist = p5.Vector.sub(b,a)
//console.log(dist)
return dist
};
tick(ticksize) {
self.assignLastTick(function(lasttick){
self.lasttick = lasttick
self.time = self.time + ticksize
self.updateSuvat(ticksize)
})
};
//assorted methods...
}
Why do the instances affect eachother?
(Can supply a link to this in action if more context is needed)
The problem is that you're creating a global variable self, and using that instead of this. All the instances are accessing the same global variable, which contains the value of this from the last object that was created.
In the callback function in tick(), you need a way to reference the original object, so you need to bind a local variable self there, rather than using a global variable. See How to access the correct `this` inside a callback?
class dynObj extends baseObj {
constructor(position, dimentions, suvat, properties) {
super(position, dimentions, properties);
this.type = 'dynamic'
this.properties = properties
//more definitions with this.x = someval
};
getDistance(a,b){
if (a == undefined || b == undefined) return false;
var dist = p5.Vector.sub(b,a)
//console.log(dist)
return dist
};
tick(ticksize) {
let self = this;
this.assignLastTick(function(lasttick){
self.lasttick = lasttick
self.time = self.time + ticksize
self.updateSuvat(ticksize)
})
};
//assorted methods...
}

Creating object by using string as a name

My function gets model name as string, I need to create new instance of object based on its name.
ex.:
modelName = 'MockA';
model = new modelName();
this is ofcourse not working. in php i would use
model = new $$modelName
thanks in advance
If MockA is in global scope you can use:
var model = new window[modelName]();
if not then you should reconsider the way you store your models, eg. with an object of models:
var my_models = {
MockA: function() {},
MockB: function() {}
}
and to access
var MockA = my_models.MockA;
// or
var model_name = 'MockA';
var MockA = my_models[model_name];
You can use an object factory or bracket notation.
Sample of code:
// First example: Use a Factory
var MockA = function() {
this.sayHello = function() {
console.log('Hi from MockA ');
};
},
MockB = function() {
this.sayHello = function() {
console.log('Hi from MockB ');
}
},
factory = function(type) {
var obj;
switch (type) {
case 'MockA':
obj = new MockA();
break;
case 'MockB':
obj = new MockB();
break;
}
return obj;
}
var objA = factory('MockA');
objA.sayHello();
var objB = factory('MockB');
objB.sayHello();
// Second example: Using bracket notation
var models = {
BaseMockA: {
sayHello: function() {
console.log('Hi from BaseMockA ');
}
},
BaseMockB: {
sayHello: function() {
console.log('Hi from BaseMockB ');
}
}
};
var baseObjA = Object.create(models['BaseMockA']);
baseObjA.sayHello();
var baseObjB = Object.create(models['BaseMockB']);
baseObjB.sayHello();

Javascript, get "this" from prototype function own field [duplicate]

This question already has answers here:
prototype: deep scope of "this" to access instance's scope
(5 answers)
Closed 6 years ago.
Suppose I have following code:
var Model = function() {};
Model.prototype.a = function() {//do smth
Model.prototype.a.on = function() {//do smth
var m = new Model();
m.a();
m.a.on();
Now I need reference to specific object m from m.a() and m.a.on() calls.
When calling m.a(), i have this referring to m.
Is it possible to get reference to m from m.a.on() call somehow?
It's a very bad idea to do so, as it leads to very strange behaviour in some cases, but it's possible:
var Model = function(x) { this.x = x };
Object.defineProperty(Model.prototype, 'a', (function() {
var lastSelf;
function get() { return lastSelf.x }
get.on = function () { return lastSelf.x * 2 };
return { get() { lastSelf=this; return get } };
})());
var m = new Model(17);
console.log(m.a(), m.a.on());
Why? I see your answer below, trying to realize what are bad cases.
You can't pass a through the variable.
You must grant access to on immediately after getting property a of the same object:
var Model = function(x) { this.x = x };
Object.defineProperty(Model.prototype, 'a', (function() {
var lastSelf;
function get() { return lastSelf.x }
get.on = function () { return lastSelf.x * 2 };
return { get() { lastSelf=this; return get } };
})());
var m1 = new Model(1), m2 = new Model(3);
console.log(m1.a(), m2.a(), m1.a.on(), m2.a.on()); // 1 3 2 6 - ok
var a1 = m1.a, a2 = m2.a;
console.log(m1.a(), m2.a(), a1.on(), a2.on()); // 1 3 6 6 - ooops!
console.log(m1.a(), m2.a(), m1.a(), a1.on(), a2.on()); // 1 3 1 2 2 - ooops!
And the other solution, but with using __proto__. According to ES6 this solution is valid for browser enviroments and for server enviroments __proto__ have to be replaced by Object.setPrototypeOf. Be sure to check browser support and other warnings.
This solution adds 1 function and 1 object per each instance.
function Model(x) {
this.x = x;
this.a = function () { return Model.prototype.a.call(this, arguments) };
this.a.__proto__ = Object.create(Model.prototype.a);
this.a.this = this;
}
Model.prototype.a = function () { return this.x };
Model.prototype.a.on = function () { return this.this.x * 2 };
var m1 = new Model(1), m2 = new Model(3);
console.log([m1.a(), m2.a(), m1.a.on(), m2.a.on()] == "1,3,2,6");
var a1 = m1.a, a2 = m2.a;
console.log([m1.a(), m2.a(), a1.on(), a2.on()] == "1,3,2,6");
console.log([m1.a(), m2.a(), m1.a(), a1.on(), a2.on()] == "1,3,1,2,6");
You can rebind the 'grandchild' methods manually in the constructor:
bindAll = function(self, obj) {
Object.keys(obj).forEach(function(k) {
if(typeof obj[k] === 'function')
obj[k] = obj[k].bind(self);
});
}
var Model = function() {
bindAll(this, this.a);
this.x = 123;
};
Model.prototype.a = function() {}
Model.prototype.a.on = function() {
console.log(this.x);
}
var m = new Model();
m.a();
m.a.on();
A more memory-savvy way is to use an explicit pointer to the root class and consistently use this.root instead of just this in methods:
var Model = function(x) {
this.x = x;
this.model = this;
this.a = Object.create(this.a);
this.a.model = this;
};
Model.prototype.a = function() {
console.log(this.model.x);
}
Model.prototype.a.on = function() {
console.log(this.model.x);
};
var m1 = new Model(11), m2 = new Model(22);
m1.a.on();
m2.a.on();
m1.a.on();
You can't access directly the parent object from a.on. You have to define some property (e.g. parent) linked to main object before calling a.on:
var Model = function() {};
Model.prototype.a = function() {//do smth
console.log(this.i);
}
Model.prototype.a.on = function() {//do smth
console.log(this.parent.i);
}
var m = new Model();
m.i = 11;
m.a();
m.a.parent = m
m.a.on();

Javascript Object-Oriented-Programming

I found a Module pattern in JS:
<script>
var MODULENAME = (function(my, $) {
my.publicVar = "5";
my.publicFn = function() {};
return my;
}(MODULENAME || {}, jQuery));
</script>
However I cannot perform instantiation. Does the module pattern allow for that?
Instantiantion means basically that you'll run a function using new.
So maybe you're looking for this?
var Some = function (param) {
var somePrivateVar = 'private';
this.somePublicVar = 'public';
this.method = function () {
return param;
};
};
var some = new Some('abla');
console.log(some.method());
// some.somePrivateVar === undefined
// some.somePublicVar === 'public'
In your case MODULENAME is an object (object, not a function) with publicVar and publicFn. It's not meant to be instantiated the same way you wouldn't call new jQuery().
Your module object can contain anything. Perhaps you're looking for including a constructor in it:
var MODULENAME = (function(my, $) {
var privateVar = 10;
my.SomeConstructor = function() {
this.publicVar = 5;
}
my.SomeConstructor.prototype.someMethod = function() {};
my.SomeConstructor.prototype.getPrivate = function() { return 10; };
return my;
}(MODULENAME || {}, jQuery));
var instance = new MODULENAME.SomeConstructor();
instance.publicVar; // 5
instance.privateVar; // undefined
instance.getPrivate(); // 10
You can do this also with prototype Inheritance :
var MyClass = function(name)
{
//sharing name within the whole class
this.name = name;
}
MyClass.prototype.getName = function(){
return this.name;//now name is visible to getName method too
}
MyClass.StaticMethod = function()
{
console.log("Im Static");
// and since is not in prototype chain, this.name is not visible
}
var myclass = new MyClass("Carlos");
console.log(myclass.getName())//print "Carlos"
MyClass.StaticMethod()// print "Im Static"
myclass.StaticMethod() // error
Se all this article

Super in Backbone

When I override the clone() method of a Backbone.Model, is there a way to call this overriden method from my implantation? Something like this:
var MyModel = Backbone.Model.extend({
clone: function(){
super.clone();//calling the original clone method
}
})
You'll want to use:
Backbone.Model.prototype.clone.call(this);
This will call the original clone() method from Backbone.Model with the context of this(The current model).
From Backbone docs:
Brief aside on super: JavaScript does not provide a simple way to call
super — the function of the same name defined higher on the prototype
chain. If you override a core function like set, or save, and you want
to invoke the parent object's implementation, you'll have to
explicitly call it.
var Note = Backbone.Model.extend({
set: function(attributes, options) {
Backbone.Model.prototype.set.apply(this, arguments);
...
}
});
You can also use the __super__ property which is a reference to the parent class prototype:
var MyModel = Backbone.Model.extend({
clone: function(){
MyModel.__super__.clone.call(this);
}
});
Josh Nielsen found an elegant solution for this, which hides a lot of the ugliness.
Just add this snippet to your app to extend Backbone's model:
Backbone.Model.prototype._super = function(funcName){
return this.constructor.prototype[funcName].apply(this, _.rest(arguments));
}
Then use it like this:
Model = Backbone.model.extend({
set: function(arg){
// your code here
// call the super class function
this._super('set', arg);
}
});
Working from the answers given by geek_dave and charlysisto, I wrote this to add this._super(funcName, ...) support in classes that have multiple levels of inheritance. It's worked well in my code.
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === arguments.callee.caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
return scan.constructor.__super__[funcName].apply(this, _.rest(arguments));
};
A year later I've fixed some bugs and made things faster. Below is the code that I use now.
var superCache = {};
// Hack "super" functionality into backbone.
Backbone.View.prototype._superFn = Backbone.Model.prototype._superFn = function(funcName, _caller) {
var caller = _caller == null ? arguments.callee.caller : _caller;
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
var className = scan.constructor.className;
if (className != null) {
var result = superCache[className + ":" + funcName];
if (result != null) {
for (var i = 0; i < result.length; i++) {
if (result[i].caller === caller) {
return result[i].fn;
}
}
}
}
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
var result = scan.constructor.__super__[funcName];
if (className != null) {
var entry = superCache[className + ":" + funcName];
if (entry == null) {
entry = [];
superCache[className + ":" + funcName] = entry;
}
entry.push({
caller: caller,
fn: result
});
}
return result;
};
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
var args = new Array(arguments.length - 1);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i + 1];
}
return this._superFn(funcName, arguments.callee.caller).apply(this, args);
};
Then given this code:
var A = Backbone.Model.extend({
// className: "A",
go1: function() { console.log("A1"); },
go2: function() { console.log("A2"); },
});
var B = A.extend({
// className: "B",
go2: function() { this._super("go2"); console.log("B2"); },
});
var C = B.extend({
// className: "C",
go1: function() { this._super("go1"); console.log("C1"); },
go2: function() { this._super("go2"); console.log("C2"); }
});
var c = new C();
c.go1();
c.go2();
The output in the console is this:
A1
C1
A2
B2
C2
What's interesting is that class C's call to this._super("go1") scans the class hierarchy until it gets a hit in class A. Other solutions do not do this.
P.S. Uncomment the className entries of the class definitions to enable caching of the _super lookup. (The assumption is that these class names will be unique in the application.)
If you want just to call this._super(); without passing the function name as an argument
Backbone.Controller.prototype._super = function(){
var fn = Backbone.Controller.prototype._super.caller, funcName;
$.each(this, function (propName, prop) {
if (prop == fn) {
funcName = propName;
}
});
return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
}
Better use this plugin:
https://github.com/lukasolson/Backbone-Super
I believe you can cache the original method (although not tested):
var MyModel = Backbone.Model.extend({
origclone: Backbone.Model.clone,
clone: function(){
origclone();//calling the original clone method
}
});
backbone._super.js, from my gists: https://gist.github.com/sarink/a3cf3f08c17691395edf
// Forked/modified from: https://gist.github.com/maxbrunsfeld/1542120
// This method gives you an easier way of calling super when you're using Backbone in plain javascript.
// It lets you avoid writing the constructor's name multiple times.
// You still have to specify the name of the method.
//
// So, instead of having to write:
//
// var Animal = Backbone.Model.extend({
// word: "",
// say: function() {
// return "I say " + this.word;
// }
// });
// var Cow = Animal.extend({
// word: "moo",
// say: function() {
// return Animal.prototype.say.apply(this, arguments) + "!!!"
// }
// });
//
//
// You get to write:
//
// var Animal = Backbone.Model.extend({
// word: "",
// say: function() {
// return "I say " + this.word;
// }
// });
// var Cow = Animal.extend({
// word: "moo",
// say: function() {
// return this._super("say", arguments) + "!!!"
// }
// });
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define(["underscore", "backbone"], function(_, Backbone) {
return factory(_, Backbone);
});
}
else if (typeof exports !== "undefined") {
var _ = require("underscore");
var Backbone = require("backbone");
module.exports = factory(_, Backbone);
}
else {
factory(root._, root.Backbone);
}
}(this, function(_, Backbone) {
"use strict";
// Finds the next object up the prototype chain that has a different implementation of the method.
var findSuper = function(methodName, childObject) {
var object = childObject;
while (object[methodName] === childObject[methodName]) {
object = object.constructor.__super__;
}
return object;
};
var _super = function(methodName) {
// Keep track of how far up the prototype chain we have traversed, in order to handle nested calls to `_super`.
this.__superCallObjects__ || (this.__superCallObjects__ = {});
var currentObject = this.__superCallObjects__[methodName] || this;
var parentObject = findSuper(methodName, currentObject);
this.__superCallObjects__[methodName] = parentObject;
// If `methodName` is a function, call it with `this` as the context and `args` as the arguments, if it's an object, simply return it.
var args = _.tail(arguments);
var result = (_.isFunction(parentObject[methodName])) ? parentObject[methodName].apply(this, args) : parentObject[methodName];
delete this.__superCallObjects__[methodName];
return result;
};
// Mix in to Backbone classes
_.each(["Model", "Collection", "View", "Router"], function(klass) {
Backbone[klass].prototype._super = _super;
});
return Backbone;
}));
In the case that you don't know what the parent class is exactly (multiple inheritance or you want a helper function) then you can use the following:
var ChildModel = ParentModel.extend({
initialize: function() {
this.__proto__.constructor.__super__.initialize.apply(this, arguments);
// Do child model initialization.
}
});
With helper function:
function parent(instance) {
return instance.__proto__.constructor.__super__;
};
var ChildModel = ParentModel.extend({
initialize: function() {
parent(this).initialize.apply(this, arguments);
// Do child model initialization.
}
});
Pass the parent class as an option during instantiation:
BaseModel = Backbone.Model.extend({
initialize: function(attributes, options) {
var self = this;
this.myModel = new MyModel({parent: self});
}
});
Then in your MyModel you can call parent methods like this
this.options.parent.method();
Keep in mind this creates a retain cycle on the two objects. So to let the garbage collector do it's job you would need to manually destroy the retain on one of the objects when finished with it. If you're application is pretty large. I would encourage you to look more into hierarchal setups so events can travel up to the correct object.
2 functions below, one requires you pass in the function name, the other can "discover" which function we want the super version of
Discover.Model = Backbone.Model.extend({
_super:function(func) {
var proto = this.constructor.__super__;
if (_.isUndefined(proto[func])) {
throw "Invalid super method: " + func + " does not exist in prototype chain.";
}
return proto[func].apply(this, _.rest(arguments));
},
_superElegant:function() {
t = arguments;
var proto = this.constructor.__super__;
var name;
for (name in this) {
if (this[name] === arguments.callee.caller) {
console.log("FOUND IT " + name);
break;
} else {
console.log("NOT IT " + name);
}
}
if (_.isUndefined(proto[name])) {
throw "Super method for: " + name + " does not exist.";
} else {
console.log("Super method for: " + name + " does exist!");
}
return proto[name].apply(this, arguments);
},
});
Here is how I would do this:
ParentClassName.prototype.MethodToInvokeName.apply(this);
so for your example this is:
Model.prototype.clone.apply(this)

Categories