Javascript object options: function or null - javascript

This question has probably been asked before, but I don't really know the proper keywords to find a solution on Google, so all my researches returned 0 :) Or better, all my researches ended up with "optional parameters" but for functions.
Let's assume I have this class:
var Class = function (params) {
// I get the data somehow
params.name(data);
}
The problem is that not every time I instantiate new Class() I need also to set name() like:
new Class({
name: function (data) {
// Do something with the data
}
... other params ...
})
But, with the above syntax, if I don't declare name parameter as a function, an error params.name is not a function is thrown:
// Error
new Class({
... other params ...
})
My question is: is there a way to set params.name() as an optional parameter (in the object) that can be a function or completely not exist (e.g. null)? Something like saying: "when you instantiate the class:
if you declare name: function (data) {} => then: params.name(data) is a function
if you skip name => it's anyway ok (no error is thrown)"

In ES6, You can use default function parameter option, like
var Class = function(params = {
name: (x) => x
}) {
// I get the data somehow
data='value';
params.name(data);
}
Class();
Class({name: x => console.log(x)});
Hope this will help!
EDIT
Yes #brigo, you are right, sorry I overlooked that point. We are actually looking for nested default rather. A possible solution could be to destructure the object inside the function like
var Class = function(params = {}) {
// I get the data somehow
data = 'value';
withDefaultParams = {
name: (x) => x,
...params
}
withDefaultParams.name(data);
}
Class();
Class({
name: x => console.log('non-default')
});
Class({
others: x => console.log('non-default')
});

yes, you could do something like
if (typeof param.name === 'function') {
param.name(data);
} else {
// dont do anything or do something different
}

If using jQuery is an option, you could use its extend method (API docs) like in the following example:
var Class = function(params) {
var _defaults = {
name: function(){},
some: 'other stuff'
};
params = jQuery.extend(_defaults,params);
// I get the data somehow
data='value';
params.name(data);
}
Class();
Class({foo:'bar'});
Class({name: console.log });

Related

Add method dynamically/Selective override prototype method in Flow

I have a constructor like this:
function IDBCrud(table: string): void {
...
}
IDBCrud.prototype.get = function(...) { ... }
IDBCrud.prototype.post = function(...) { ... }
And it using like this:
const accounts = new IDBCrud('Accounts');
accounts.get( ... );
accounts.create( ... );
But sometimes, I want to define method to object directly with same name as in property, so that invokes instead of prototype's method.
// Override get method for some reason
accounts.get = function( ... ) {
// Do some stuffs...
...
// Now call prototype get
return this.__proto__.get.apply(this, arguments);
}
But when I ran flow, it fails with this:
16: accounts.get = function(match, options) {
^^^ property `get`. Property not found in
16: accounts.get = function(match, options) {
^^^^^^^^^^^^ new object
Because IDBCrud doesn't have "get" property(or method). But if I just write them with empty value like this:
function IDBCrud(...): ... {
this.get = function() {};
this.create = function() {};
...
}
If should be work in that case, but if do that, I have to redefine every "get" method to invoke prototype's get method.
const accounts = new IDBCrud('accounts');
accounts.get = function() { ... }; // Override
accounts.get(); // works
const users = new IDBCrud('users');
users.get(); // Invokes users.get and it's empty function, instead of prototype.get
I don't wanna do that everytime I made IDBCrud instance, I just want to override it only it needed.
Without flow, it's not a problem, but with it, it fails.
So how do I achieve this with flow? Any advice will very appreciate it.
Override it only over the object instances where you want to achieve a different behavior:
function IDBCrud(table){
}
IDBCrud.prototype.get = function() { console.log('get1'); }
var a = new IDBCrud();
a.get(); // get1
a.get = function() { console.log('get2'); }
a.get(); // get2
var b = new IDBCrud();
b.get(); // get1
Flow was intentionally built for supporting es6 class, and it blocks me to add method in runtime for safety reasons.
Solution was simple, convert constructor to class and make new class that extends IDBCrud and override method and it's working now.

"this" is a global object after using call()

I have stored the names of the methods in a list.
var list = ['fn1', 'fn2', 'fn3', 'fn4'];
I select the method using some criteria dynamically. The methods are part of a larger class that are attached using 'prototype
MyObj.prototype.selectfn = function(criteria) {
var fn = list[sel];
this[fn].call(this, arg1);
}
MyObj.prototype.fn1 = function(args) { // do something }
MyObj.prototype.fn2 = function(args) { // do something}
And so on. The problem is inside the selected "fn" function, the this variable appears as a global object even though I used call() I read the mozilla docs, but I'm not able to understand why this is so, can someone help out please?
It it helps, my environment is node.js 0.10.12.
Edit : It's a little hard to provide the correct sample code because my code involves callbacks in a lot of places, but I'll try to elucidate.
Assume two files User.js and Helper.js.
User.js
var m, h;
var Helper = require('./Helper');
function init() {
// to simplify, assume the `this` here refers to `User`
h = new Helper(this);
}
function doSomething() {
// pass some criteria string
h.selectfn(criteria);
}
Helper.js
var Helper = module.exports = function(user) {
this.user = user;
}
Helper.prototype.selectfn = function(criteria) {
// based on some criteria string, choose the function name from "list" array
// here, the string "sel" holds the selected function name
var fn = list[sel];
this[fn].call(this.user, arg1);
// if I print to console, `this.user` is correct over here, but inside the function it shows as undefined
}
Helper.prototype.fn1 = function(args) {
// Here, I talk to databases, so I have callbacks. Say, on a callback, the user property is to be updated. This is why I want to use `call()` so that the `this` refers to `User` and can be updated.
// For example, if we want to update the "last-seen" date.
this.lastseen = new Date();
}
Hope the little example made it clearer.
the first parameter of call() is your function context "this"
as example:
var someObject = {
withAFunction: function(text) { alert('hello ' + text); }
};
var testFunction = function(text) {
this.withAFunction(text);
};
testFunction.call(someObject, 'world');
I tried to review your code:
var list = ['fn1', 'fn2', 'fn3', 'fn4'];
//MyObj is copy of Object?
var MyObj = Object;
//or a preudoclass:
//var MyObj = function(){};
//inside this function you use sel and arg1 that seems undefined and use like arguments criteria that seems useless
MyObj.prototype.selectfn = function(sel,arg1) {
var fn = list[sel];
this[fn].call(this, arg1); // this is MyObj istance.
MyObj.prototype.fn1 = function(args) { console.log("fn1", args);/* do something */ }
MyObj.prototype.fn2 = function(args) { console.log("fn2",args); /* do something */ }
xxx = new MyObj();
xxx.selectfn(1,"ciao");
//call xxx.fn1("ciao");
see the console for response.

How to Properly Access Class Properties in 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');

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.

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