Node.js
I have a function that works with different callbacks. Now I'm wondering if there is a better solution handling different callback functions.
My situation:
function prepare(type, callback){
let obj = {}; //in real is this an external database
for(let i=1; i<= 10; i++){
obj['progress_'+i] = 0;
}
if(type === 'createNewUser'){
callback(obj); //callback for function createNewUser(obj);
}
if(type === 'addToExistingUser'){
callback(obj); //callback for function addToExistingUser(obj);
}
}
My callback functions are:
createNewUser(obj){
//create new user with obj;
}
addToExistingUser(obj){
//add obj to existing user
}
2 ways to use prepare();
prepare('createNewUser', createNewUser);
prepare('addToExistingUser', addToExistingUser);
What is the best practice for this case? I would like to write good code.
How about this?
Basically it's called bracket notation you create a map that has REFERENCE to the function, you can call that function with the desired params.
var callbacks = {
createNewUser : createNewUserCallback,
addToExistingUser: addToExistingUserCallback
}
function prepare(type){
let obj = {};
for(let i=1; i<= 10; i++){
obj['progress_'+i] = 0;
}
callbacks[type](obj)
}
function addToExistingUserCallback(obj) {
// Do stuff
}
function createNewUserCallback(obj) {
// Do stuff
}
OR
var callbacks = {
createNewUser : (obj) => { // function code goes here.} ,
addToExistingUser: (obj) => {// function code goes here.}
}
function prepare(type){
let obj = {};
for(let i=1; i<= 10; i++){
obj['progress_'+i] = 0;
}
callbacks[type](obj)
}
To be fair option 1 is more readable.
You can try below example and add multiple callbacks in the same function according to your requirement -
function prepare(type, callback, secondcallback) {
let obj = {};
for (let i = 1; i <= 10; i++) {
obj['progress_' + i] = 0;
}
if (type === 'createNewUser') {
callback(obj); //callback for function createNewUser(obj);
}
if (type === 'addToExistingUser') {
secondcallback(obj); //callback for function addToExistingUser(obj);
}
}
prepare('createNewUser', function (data) {
console.log('First Callback data:', data);
}, function (data) {
console.log('Second Callback data:' + data);
})
prepare('addToExistingUser', function (data) {
console.log('First Callback data:', data);
}, function (data) {
console.log('Second Callback data:' + data);
})
If type is a string containing the name of the function to call, you can use window[functionName]()
function foo()
{
console.log("called foo");
}
function bar()
{
console.log("called bar");
}
function baz(str)
{
window[str]();
}
baz('foo');
baz('bar');
Or you can create your own object and store the functions in it :
const FunctionObject = {}
FunctionObject.foo = function ()
{
console.log("called foo");
};
FunctionObject.bar = function ()
{
console.log("called bar");
};
function baz(str)
{
FunctionObject[str]();
}
baz('foo');
baz('bar');
replace
if(type === 'createNewUser'){
callback(obj); //callback for function createNewUser(obj);
}
if(type === 'addToExistingUser'){
callback(obj); //callback for function addToExistingUser(obj);
}
With
fnc = window[callback];
if( fnc && typeof fnc === "function" ) { //make sure it exists and it is a function
fnc(); //execute it
}
I am not sure why you need type when you know what method to call or clarify if the type is not hardcoded, you simply can use prototypes as per your defined requirements.
function Prepare(obj) {
this.obj = obj;
}
Prepare.prototype = {
createNewUser(obj = this.obj) {
console.log('existing', obj);
},
addToExistingUser(obj = this.obj) {
console.log('new', obj)
}
}
let prep = new Prepare({a:1});
prep.createNewUser();
//or
prep.createNewUser({c: 3});
prep.addToExistingUser({b:2})
If the type is not hardcoded
function Prepare(type, obj) {
this.obj = obj;
this.callback = this[type];
}
Prepare.prototype = {
createNewUser(obj = this.obj) {
console.log('existing', obj);
},
addToExistingUser(obj = this.obj) {
console.log('new', obj)
}
}
//if type is not hardcoded
let prep = new Prepare('createNewUser');
prep.callback({d: 4});
Related
I am trying to write a memoization function, but keep getting the following error.
Error - "TypeError: getNthFibonacciNo is not a function
at dabebimaya.js:28:38
at https://static.jsbin.com/js/prod/runner-4.1.4.min.js:1:13924
at https://static.jsbin.com/js/prod/runner-4.1.4.min.js:1:10866"
How can I find this error in my code? I have tried googling the error with no avail. Please point out any additional errors too if possible.
function memoize(fn) {
var cache = {};
if (cache[arguments[0]]!==undefined) {
return cache[arguments[0]];
}
else {
var value = fn.apply(this, arguments);
cache[arguments[0]] = value;
return value;
}
}
var getNthFibonacciNo = memoize(function(n){
//1,1,2,3,5,8,13,21,34
if(i<=2)
return 1;
var fib = [0,1,1];
for(var i=3;i<=n;i++) {
fib[i] = fib[i-2]+fib[i-1];
}
return fib[n];
});
console.log(getNthFibonacciNo(7));
Your memoize function isn't returning a function.
function memoize(fn) {
var cache = {};
return function() {
if (cache[arguments[0]]!==undefined) {
return cache[arguments[0]];
}
else {
var value = fn.apply(this, arguments);
cache[arguments[0]] = value;
return value;
}
}
}
now returns a function so that it can be called multiple times.
Usage
function test(a) {
console.log('calling test', a);
return a + 1;
}
const memoized = memoize(test);
memoized(1); // prints calling test and returns 2
memoized(1); // returns 2
memoized(2); // prints calling test and returns 3
I managed to fix my code after suggestions by AnilRedshift. Below is the fixed code.
function memoize(fn) {
var cache = {};
return function() {
var key = JSON.stringify(arguments);
if (cache[key]) {
console.log('cache used');
return cache[key];
}
else {
var value = fn.apply(this, arguments);
cache[key] = value;
console.log('cache not used');
return value;
}
};
}
var fibonacciMemoized = memoize(function(n) {
//1,1,2,3,5,8,13,21,34
if(i<=2)
return 1;
var fib = [0,1,1];
for(var i=3;i<=n;i++) {
fib[i] = fibonacciMemoized(i-2)+fibonacciMemoized(i-1);
}
return fib[n];
});
console.log(fibonacciMemoized(7));
console.log(fibonacciMemoized(9));
How to check if the string parameter passed in a function is too callable/function but not directly under window..
I know the open/ directly callable function can be checked using the syntax window['functionName']
But how about the member function declared inside an object to be checked?
In below example openFunction() can be called but how to call obj1.foo()?
Prefer not to use eval()
Example Code:
var obj1 = {
foo: function() {
alert("I'm a function");
}
}
function openFunction() {
alert("I know i am easily callable");
}
function callSomeone(txtcallback) {
var fn = window[txtcallback];
if (typeof fn === 'function') {
fn();
}
console.log(typeof fn);
}
callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //undefined
It returns undefined because, your code is equivalent to window["obj1.foo"] which is not correct.
The correct way to access to foo function is window["obj1"]["foo"].
So you have to "cycle" through the string obj1.foo.
Here I added a GetProp function that do that cycle and is recursive, so the level of nesting is not a problem.
var obj1 = {
foo: function() {
alert("I'm a function");
}
}
function openFunction() {
alert("I know i am easily callable");
}
function callSomeone(txtcallback) {
var fn = GetProp(window, txtcallback.split("."));
if (typeof fn === 'function') {
fn();
}
console.log(typeof fn);
}
function GetProp(obj, props) {
if(props.length == 0) {
return obj;
} else if(obj[props[0]] != undefined) {
obj = obj[props[0]];
return GetProp(obj, props.slice(1));
}
}
callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //undefined
try this
var obj1 = {
foo: function() {
alert("I'm a function");
}
}
function openFunction() {
alert("I know i am easily callable");
}
function callSomeone(txtcallback) {
str =txtcallback.split(".");
temp = window;
for(check in str){
temp = temp[str[check]];
if (typeof temp === 'function') {
temp();
break;
}else if(typeof temp === 'undefined'){
break;
}
}
console.log(typeof temp);
}
callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //function
If you ant to look for members inside nested maps you have to use a recursive approach.
function callSomeone(txtcallback) {
var keyPath = txtcallback.split(".");
var fn = keyPath.reduce(function (member, key) {
return member[key];
}, window);
if (typeof fn === 'function') {
fn();
}
console.log(typeof fn);
}
the downside in this example is that the function is executed in the global scope. If you need to keep the scope of the container object you need to also save the scope.
var obj1 = {
foo: function() {
alert("I'm a function");
return this;
}
}
function openFunction() {
alert("I know i am easily callable");
return this;
}
function callSomeone(txtcallback) {
var keyPath = txtcallback.split(".");
var scope = null;
var context = null;
var fn = keyPath.reduce(function (member, key) {
scope = member;
return member[key];
}, window);
if (typeof fn === 'function') {
context = fn.call(scope);
}
console.log(typeof fn, context);
}
callSomeone('openFunction'); //function
callSomeone('obj1.foo'); //undefined
This doesn't work.
var genericClickHandler = function () {
this.handlers = [];
if (console && console.log) {
console.log("this:", this);
console.log("event:", event);
}
};
genericClickHandler.addHandler = function (handlerSpec) {
this.handlers.push(handlerSpec);
return this;
};
genericClickHandler.executeHandler = function (handlerName) {
for (var i = 0; i < this.handlers.length; i++) {
if (handlerName === this.handlers[i][0]) {
this.handlers[i][1]();
}
}
return this;
};
It doesn't work because the addHandler can't see the this.handlers in genericClickHandler.
Anyway what I'm after is function that gets defined once, but has methods and properties. I want to be able to use the function with Google Maps like this:
heatmap.addListener("click", genericClickHandler)
circle.addListener("click", genericClickHandler)
polygons.addListener("click", genericClickHandler)
So in the first instance, it only reports the this and event object. However, I then want to write code which extends the genericClickHandler dynamically so that it can implement map-object-specific behaviour.
Here's an example of what I meant using an object rather than a function.
var genericClickHandler = {
handlers: []
};
genericClickHandler.addHandler = function (name, fn) {
this.handlers.push([name, fn]);
return this;
};
genericClickHandler.executeHandler = function (name) {
for (var i = 0, l = this.handlers.length; i < l; i++) {
if (this.handlers[i][0] === name) this.handlers[i][1]();
}
};
genericClickHandler.addHandler('click', function () {
console.log('hi');
});
genericClickHandler.addHandler('click', function () {
console.log('hallo again');
});
genericClickHandler.executeHandler('click'); // hi... hallo again
DEMO
if you want to create an object, here you can see 2 ways to do the same thing, javascript got multiple way to write the same things.
var genericClickHandler = function()
{
this.handlers = [];
this.addHandler = function (handlerSpec)
{
this.handlers.push(handlerSpec);
return this;
},
this.executeHandler = function (handlerName)
{
this.handlers[handlerName]();
return this;
}
};
//sample:
var tmp = new genericClickHandler();
console.log(tmp.handlers);
console.log(tmp.addHandler("TEST"));
Another way to write the same object, but more optimised : prototype will be stored once for each object
var genericClickHandler = function(){}
genericClickHandler.prototype =
{
handlers:[],
addHandler : function (handlerSpec)
{
this.handlers.push(handlerSpec);
return this;
},
executeHandler : function (handlerName)
{
this.handlers[handlerName]();
return this;
}
}
//sample:
var tmp = new genericClickHandler();
console.log(tmp.handlers);
console.log(tmp.addHandler("TEST"));
I have this code. It does that always when there is function, that is interested on some key in Session, it will be called whenever the key changed its value.
The problem is that to track what keys the function is interested, i need to run that function once and it can have some sideffects (if that function will manipulate DOM for instance). How can i run that function without affecting the current environment, if it is possible....?
var checkRunning = false;
var keys = [];
var checks = {};
var Session = {
get: function (key) {
if (checkRunning) {
keys.push(key);
}
},
set: function (key, value) {
if (checks[key]) {
var l = checks[key].lenght;
for (var i = 0; i < l; i++) {
checks[key][i]();
}
}
}
};
function check(f) {
checkRunning = true;
f();
checkRunning = false;
var l = keys.lenght;
for (var i = 0; i < l; i++) {
if (checks[keys[i]]) {
checks[keys[i]].push(f);
}
else {
checks[keys[i]] = [f];
}
}
keys = [];
}
//how to use
var a = "something";
check(function () {
// this function should be run always when Session key "a_dep" will change
a = Session.get("a_dep");
});
Session.set("a_dep", 10);
Session.set("a_dep", 20);
So I refactored your code to achieve what I understood you wanted.
I first redefined Session and its getter/setter. I store the values in a literal object with the key to access it :
function Session () {
this.myDictionnary = {};
this.myCallbacks = {};
}
Session.prototype.set = function (key, value) {
this.myDictionnary[key] = value;
}
Session.prototype.get = function (key) {
return this.myDictionnary[key];
}
Here we have our core code. Now you want to call a function every time you set a value to a key, so we have to
Set a function (or function set) - key couple
Call this function (or these functions) when a value is set to key
Note the myCallbacks literal object which will handle these couples. So first, create the key - function couple :
Session.prototype.callbackWhenSet = function (key, callback) {
if(!this.myCallbacks[key]) {
this.myCallbacks[key] = [];
}
this.myCallbacks[key].push(callback);
}
Then call it when set is called (here I rewrite the set function )
Session.prototype.set = function (key, value) {
this.myDictionnary[key] = value;
if(this.myCallbacks[key]) {
for(var i = 0; i < this.myCallbacks[key].length; i++) {
this.myCallbacks[key][i]();
}
}
}
Finally we can test it !
var test = new Session();
test.callbackWhenSet("a_dep", function () {
alert("a_dep is set to " + test.get("a_dep"));
});
test.set("a_dep", 10);
test.set("something", 250);
test.set("a_dep", 20);
As you can see here, we have an alert showed each time "a_dep" is set.
Or you can use ProAct.js and do stuff like this:
var obj = ProAct.prob({
a: 4
});
obj.p('a').on(function () {
console.log('obj.a has changed!');
});
I'm keeping a record of each time various functions are called. I have a function called
record_activity( function_name );
I really don't want to have to write this at the top of every function I want to track. Currently there are lots of functions in the format:
Object.Key.Func = function() { ... }
I've written this, which seems to work but I'm really not sure about it's implications:
function sub ( variable, func ) {
var temp_func = function ( args ) {
record_activity( variable );
return func.apply(this,arguments);
}
eval( variable + ' = ' + temp_func );
}
sub( 'Object.Key.Func', function (name) { alert('hi ' + name) } );
Object.Key.Func('test');
If there is a way of doing this without an eval I'd be much happier.
Thanks
I think you can create a wrapper func for each func that you want to track. Hope the following code will help you:
var counter = {};
// assume this is what record_activity has to do.
function record_activity (f, func_name, args) {
counter[func_name] = counter[func_name]===undefined ? 1 : counter[func_name]+1;
}
// assume that you want to keep track of functional call obj.foo and obj.bar
var obj = {
a: 3,
foo: function () {
console.log("foo");
},
bar: function (b) {
console.log("bar", this.a, b);
}
};
function create_func (f, func_name) {
return function () {
record_activity(f, func_name, arguments);
f.apply(obj, arguments);
};
}
for(var prop in obj) {
if (typeof obj[prop] === 'function') {
// create the wrapper func
obj[prop] = create_func(obj[prop], prop);
}
};
// test
for (var i = 0; i < 3; i++) {
obj.foo();
};
for (var i = 0; i < 10; i++) {
obj.bar(i);
};
console.log(counter);