How to Properly Access Class Properties in Javascript - javascript

I have this class definition:
$.note = function() {}
$.note.prototype = {
init: function(note) {
this.note = note;
this.ctrl = document.getElementById(note);
},
// I have these getter functions because I was getting errors using
// myObject.note or myObject.ctrl
getNote: function() {
return this.note;
},
getCtrl: function() {
return this.ctrl;
}
}
I created a new object with this class like this:
var note = new $.note('C');
Which I can access in my console like this:
But when I try and access note.getNote(), I get undefined as the response:
Am I going about accessing these properties incorrectly? I've tried using just note.note or note.ctrl, and I get the same thing...

Nothing's going to call that "init" function if you don't.
$.note = function(note) { this.init(note); }
Some frameworks provide an object system that uses constructor helper functions like that, but plain JavaScript doesn't.

Try this:
$.note = function(note) { this.note = note;}
or you should call init function:
var note = new $.note();
note.init('C');

Related

Javascript prototype Methods inside object

I'm trying to create an Object containing other Objects and functions, in a prototype, the relevant part is the UI prototype;
var fChat = function() {
this.debug = true;
};
fChat.prototype = {
constructor: fChat,
Log: function(str){
if(this.debug){
console.log(str);
}
},
UI: {
Login: {
Show: function(){
this.Log("UI.Login.Show()");
}
}
}
};
var fChatInstance = new fChat();
fChatInstance.UI.Login.Show();
When i call fChatInstance.UI.Login.Show() It give me an error:
Uncaught TypeError: this.Log is not a function
Is that because by using this is on another scope?
Usually i do var self = this;at the start of a prototype, but i don't know how I can do that by using an Object prototype.
Yes. The problem is the javascript dynamic binding of this, to fix it you can set "this" to the object by using bind function. Change the fchat function refactor it like this:
var fChat = function() {
this.debug = true;
this.UI.Login.Show = this.UI.Login.Show.bind(this);
this.Log = this.Log.bind(this);
};

Improving the implementation of a plugin architecture

I have an extend method included in my library, making it possible for methods to be added to the core library:
library.prototype.extend = function(name,plugin) {
library.prototype[name] = plugin.init;
for (var method in plugin) {
if(method !== 'init') {
library.prototype[name].prototype[method] = plugin[method];
}
}
};
In use it looks like so:
library.prototype.extend('aReallyInterestingPlugin', {
//the init method gets added to the base libraries prototype
init: function() {
this.shouldBePrivate;
},
//another other methods are created as a prototype of the newly added Plugin
anotherMethod : function() {
//this should have access to the shouldBePrivate var
}
});
Users are then able to call the plugin like so:
var test = new library();
test.aReallyInterestingPlugin();
This works but I'm not exactly happy with the approach and have been trying to find an alternative pattern to make this work.
The problem with it, is that the init and the anotherMethod are added directly to the libraries prototype chain so their scope is also the global libraries scope which is messy because if any instance variables are declared (like shouldBePrivate above) they are also added to the libraries prototype chain.
How can I enable the plugin to be added and have it's own private scope? One way I've thought of is that a plugin could always be called as a constructor (and will therefore have it's own scope and this context) but then I'm not sure how clean that is...for instance for that to work the user would have to do something like this when calling the plugin:
var test = new library();
test.aPlugin = new aReallyInterestingPlugin();
What you could do is to have the plugin method bound to a new object, so that they don't 'pollute' the Library.
But instead of having the plugin as a method of a Library instance, have it rather as a lazy getter, so the syntax is more fluid, and you can build a new instance only if required.
The plugin syntax could then be simplified : just use a javascript class => a function that has methods defined on its prototype.
I think also that it makes sense to have 'extend' as a property of Library, not a method set on its prototype, since no instance should make use of it.
With this you can add a plugin with
library.extend('aReallyInterestingPlugin',AReallyInterestingPluginClass);
to use you can write
var myLibrary = new Library();
myLibrary.somePlugin.someMethod(arg1, arg2, ...);
The code would look like :
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
var pluginGetter = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
A plugin is just standard javascript class:
function MyPlugin() {
this.pluginProperty1 = 'some value';
}
MyPlugin.prototype = {
method1 : function() { /* do things */} ,
method2 : function() { /* do other things */ }
};
Notice that with this scheme the plugin is a singleton, i.e. every instances of Library will return the same object when asked for the same plugin.
If you prefer once plugin instance per Library, just have the Library constructor hold the plugins instances. (maybe in a hidden property).
function Library() {
// the code allready here...
var pluginInstances = {};
Object.defineProperty(this, 'pluginInstances',
{ get : function() { return pluginInstances }, enumerable : false });
}
library.extend = function(name,plugin) {
var pluginGetter = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
syntax for the plugin and for the use remains the same.
Edit : for older Browser support, you can still use a function instead of a getter :
function Library() {
// the code allready here...
this.pluginInstances= {} ;
}
library.extend = function(name,plugin) {
Library.prototype[name] = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
} ;
to use it you would do :
var myLibrary = new Library();
myLibrary.somePlugin().someMethod(arg1, arg2, ...);
Edit 2 : version with singleton plugins and with no getters is :
function Library() { /* same code */ }
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
Library.prototype[name] = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
}
That's an interesting question. There was a blog post about prototype-liked development and the fact that a lot of people are avoiding it. I'll go with something like this:
var Library = function() {
var api;
var private = "some value";
var privateMethod = function() {
console.log(private);
}
var registerPlugin = function(name, plugin) {
api[name] = plugin.call(api);
}
var publicMethod = function() {
privateMethod();
}
return api = {
show: publicMethod,
plugin: registerPlugin
}
}
// usage of the library
var library = new Library();
library.show();
// registering a plugin
library.plugin("awesome", function() {
var api, library = this;
var pluginVar = "That's a plugin";
var pluginMethod = function() {
console.log(pluginVar);
library.show();
}
return api = {
gogo: pluginMethod
}
});
// calling a method of the plugin
library.awesome.gogo();
The library is just a function which has its own scope, its own private and public methods and exports an API. The plugin is actually another function with the same capabilities, but it is invoked with the library's API as a scope. So, all the public methods of the library are available and you are able to use them. And of course you keep the privacy of the plugin. I'll suggest to read about revealing module pattern. I personally use it alot. It really saves me a lot of problems.
P.S.
Here is a jsfiddle using the code above http://jsfiddle.net/XyTJF/

Javascript inheritance on variable within a function (OpenERP)

Basically I'm trying to override a function by extending it. I have the following base (simplified) code:
openerp.point_of_sale = function(db) {
var Order = Backbone.Model.extend({
exportAsJSON: function() {
return {'bigobject'}
}
})
}
Then, I'm writing my own .js where I want to inherit and override exportAsJSON function and I'm not sure how to .extend it. Here is my erroneous approach:
openerp.my_module = function(db) {
db.point_of_sale.Order = db.point_of_sale.Order.extend({
exportAsJSON: function() {
var order_data = this._super();
//... add more stuff on object
return order_data;
}
})
}
What would be the correct way of doing it?
I hope I'm providing enough information for an answer (I'm working on OpenERP by the way). Any help will be appreciated.
EDIT:
More specifically, the error seems to be in the extension itself:
db.point_of_sale.Order = db.point_of_sale.Order.extend({
...even if I put a simple return 0; within my exportAsJSON function, the page doesn't load and I get the following error in my browser console:
"Cannot call method 'extend' of undefined"
I think you want something like SuperClass.prototype.method.call(this):
openerp.my_module = function(db) {
db.point_of_sale.Order = db.point_of_sale.Order.extend({
exportAsJSON: function() {
var order_data = db.point_of_sale.Order.prototype.exportAsJSON.call(this);
//... add more stuff on object
return order_data;
}
})
}
This is how you would normally do that in JavaScript:
var eaj = db.point_of_sale.Order.prototype.exportAsJSON;
db.point_of_sale.Order = db.point_of_sale.Order.extend({
exportAsJSON: function() {
var order_data = eaj.apply( this, arguments );
//... add more stuff on object
return order_data;
}
})
This is basically where you problem lies:
openerp.point_of_sale = function(db) {
var Order = Backbone.Model.extend({
^
|
this is a private variable
not a property!
Therefore you cannot access it at all. If it was defined like this:
openerp.point_of_sale = function(db) {
openerp.point_of_sale.Order = Backbone.Model.extend({
^
|
this is now a property of point_of_sale
(basically public variable)
then you can access it the way you're trying to:
db.point_of_sale.Order = db.point_of_sale.Order.extend({
So, the answer is you cannot do that. You need to extend or modify db.point_of_sale instead of Order.

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

Javascript function (type) to store & use data

I really never used a javascript function type or class before, I understand Java and Python, but not javascript. So, I build a class like this:
function FormStore (type) {
this.setup = () =>{
this.store = {};
this.ERR_LINE_PREFIX = '#err_';
this.NO_DISPLAY_CLASS = 'no-display';
this.settings = {
'myID':{'hide':false},
}
}
this.checkVal= () => {
var geoArr = ['id_xx','myID', (...)];
var id;
$.each( geoArr, function(val) {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
};
var FS = new FormStore();
FS.setup();
The store is filled by components on document.ready. There is a function that looks up if the aligned components (glyph, label, input) have some classes or values and for the specific component fills a dict: {label:false,glyph:false, input:false}. However, for some reason it doesn't matter. Even if I enter some values in to the store right away (in setup) or create them on the fly, in checkVal the store doesn't exist, it's undefined.
Please, anybody, what am I not understanding about javascript type and classes here? I am googling this a lot and trying to find good resources but, "javascipt variable class" (or type) just yields a lot of DOM manipulation.
edit
There is a context problem in checkVal, you are using a non-arrow (and not explicitly bound) callback function and trying to access this inside of it. Change that to an arrow function as well, and the parent context (this) will be preserved:
$.each( geoArr, (val) => {
id = geoArr[val];
console.log(this.store)
if (!(this.store[id])) {
return false;
}
});
And while you are at changing that section, it's not going to work. You will not get access to $.each's return value. You should rely on native array APIs for this task and use Array.every to determine if all geoArr items are in the store (assuming that's your goal):
// returns false if not all geoArr items are in the store
geoArr.every(id => this.store[id])
original
I don't see you calling checkVal() anywhere, but based on the error you are getting it is called prior to setup() (since setup initializes the store). You could solve that problem straight away by moving this.store = {} out of setup (right at the top), e.g.:
function FormStore(type) {
this.store = {};
...
Having said that, I would suggest either defining your methods on the prototype, or utilizing ES6 classes. Here is a simplified version of both:
ES5 class
function FormStore(type) {
// make sure user didn't forget new keyword
if (this === window) {
throw new Error('FormStore must be called with "new" keyword')
}
// initialize state, this is the constructor
this.type = type;
this.store = {};
// any other state the class manages
}
FormStore.prototype = {
setup: function() {
// do setup stuff
// "this" points to instance
console.log('setup', this.type)
},
checkVal: function() {
}
}
var formStore = new FormStore('foo')
console.log(formStore.store) // <-- not undefined
formStore.setup()
ES6 Class
class FormStore {
constructor(type) {
this.type = type;
this.store = {};
}
setup() {
console.log('setup', this.type)
}
checkVal() {
}
}
const formStore = new FormStore('bar')
console.log(formStore.store) // <-- not undefined
formStore.setup()
It has to do with scoping. Your $.each in checkVal has a normal function. Inside the function the scope if this is different. If you want to keep the original scope you could use a fat arrow function like you do when defining the methods.
this.checkVal= () => {
var geoArr = ['id_xx','myID', (...)];
var id;
$.each( geoArr, val => {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
}
When you run your original code and place a breakpoint on the line with console.log you can see in the inspector that this is set to the Window object and no longer points to your FormStore.
function FormStore () {
this.setup = function(){
this.store = {};
this.ERR_LINE_PREFIX = '#err_';
this.NO_DISPLAY_CLASS = 'no-display';
this.settings = {
'myID':{'hide':false},
}
}
this.checkVal= function(){
var geoArr = ['id_xx','myID'];
var id;
$.each( geoArr, function(val) {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
}
};
var FS = new FormStore();
FS.setup();
Works absolutely fine, the code you provided had a missing bracket and you were using some broken es6 syntax

Categories

Resources