Assigning a variable name from within a string with a function - javascript

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);

Related

How to handle different optional callbacks in the same function?

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});

How to call method if I have method name as string

My requirement is on phase change I have to construct method names and call the methods and subsequent methods also,here I am able to construct method name but it is as String and not able to call the method. I have followed some of the suggestion given but I couldn't achieve. Please help.
var previousPhase = $("#currentPhase").val();
var projectPhaseArray = ["requirement", "design", "construction", "testing", "release"];
var i = 0;
$("#currentPhase").change(function() {
alert(previousPhase);
i=projectPhaseArray.indexOf(previousPhase);
for (i; i < projectPhaseArray.length; i++) {
alert(projectPhaseArray[i]);
var phaseTimeLineToCall =
projectPhaseArray[i].concat("PhasePhaseTimeLines");
executeFunctionByName(phaseTimeLineToCall,window);
}
});
function executeFunctionByName(functionName, context /*, args */) {
return context[functionName].apply(context);
}
function requirementPhaseTimeLines(){
alert("In RequirementPhaseTimelines");
}
function designPhaseTimeLines(){
alert("In DesignPhaseTimelines");
}
Thanks.
Strings don't mutate, so you need to save the value back
projectPhaseArray[i] = projectPhaseArray[i].concat("PhasePhaseTimeLines");
You could use a javascript object where you store the function name as key and the function reference as value
var lookup = {
requirementPhaseTimeLines: requirementPhaseTimeLines,
designPhaseTimeLines: designPhaseTimeLines
}
Without Arguments
We have to modify executeFunctionByName slightly
function executeFunctionByName(functionName, lookup) {
return lookup[functionName]()
}
Working Example
function requirementPhaseTimeLines() {
return "In RequirementPhaseTimelines"
}
function designPhaseTimeLines() {
return "In DesignPhaseTimelines"
}
var lookup = {
requirementPhaseTimeLines: requirementPhaseTimeLines,
designPhaseTimeLines: designPhaseTimeLines
}
function executeFunctionByName(functionName, lookup) {
return lookup[functionName]()
}
console.log(
executeFunctionByName("requirementPhaseTimeLines", functions)
)
console.log(
executeFunctionByName("designPhaseTimeLines", functions)
)
With Arguments
If we want to pass in arguments we have to curry the functions we want to let execute.
function greet(word) {
return function(name) {
return word + ', ' + name + '.'
}
}
Second, we have to create a function where we can iterate trough the arguments and set each arguement value to the function we want to execute:
function setArguments(functionRef, args) {
return args.length === 1
? functionRef
: setArguments(functionRef(args[0]), args.slice(1))
}
Working Example
function greet(word) {
return function(name) {
return word + ', ' + name + '.'
}
}
var lookup = {
greet: greet
}
function getFunction(lookup, name) {
return lookup[name] || new Function()
}
function setArguments(functionRef, args) {
return args.length === 1
? functionRef
: setArguments(functionRef(args[0]), args.slice(1))
}
function executeFunctionByName(functionName, lookup, args) {
var functionRef = getFunction(lookup, functionName)
var functionRefWithArgs = setArguments(functionRef, args)
return functionRefWithArgs(args[args.length - 1])
}
console.log(
executeFunctionByName('greet', lookup, ['Hi', 'Jon'])
)

Can I eval expression in object context?

Good day. I need to eval expression in some object context, but the only solution I found is to create stubs for every object function:
var c = {
a : function () {
return 'a';
},
b : function () {
return 'b';
}
};
function evalInObj(c, js) {
function a() {
return c.a();
}
function b() {
return c.b();
}
return eval(js);
};
console.log(evalInObj(c, 'a() + b()'));
Show me the right way, please. Can I do it with prototype?
var C = function(id) {
this.id = id;
}
C.prototype.a = function () {
return 'a' + this.id;
}
C.prototype.b = function () {
return 'b' + this.id;
}
function evalCtx(js) {
console.log(this); // C {id: 1}
return eval(js);
}
var c1 = new C(1);
evalCtx.call(c1, 'a() + b()'); // error: a is not defined
For late-comers who are still looking for the solution of the issue (or the similar). Instead of using eval(), we can create anonymous function dynamically and evaluate the expression in the object context:
function evaluate(expression, context = {}) {
try {
// console.debug("[DEBUG] Dynamic anonymous function to be defined:\n%s", `function(${[...Object.keys(context)].join()}) {\n'use strict'; return (${expression})\n}`)
const fun = Function(...Object.keys(context), `'use strict'; return (${expression})`)
// console.debug("[DEBUG] Dynamically defined anonymous function:\n%o", fun)
const result = fun(...Object.values(context))
// console.debug("[DEBUG] Evaluation result: %o", result)
return result
} catch(error) {
if(error.message === `Unexpected token ')'`) throw SyntaxError('Unexpected token, likely at the end of expression.')
else throw error
}
}
To assert:
console.assert(evaluate('a===1 && b===2', {a: 1, b: 2}) === true)
console.assert(evaluate('a===1 && b===3', {a: 1, b: 2}) === false)
console.assert(evaluate('f()', {a: 1, f: ()=>11}) === 11)
(() =>
{
// 'use strict';
function run(expression, context = {})
{
return function ()
{
return eval(expression);
}.call(context);
}
let context = {a:{b:'Bb'}};
console.log(run('this', context)); // {a:{b:'Bb'}}
console.log(run('this.a', context)); // {b:'Bb'}
console.log(run('this.a.b', context)); // 'Bb'
console.log(run('a.b', context)); // ReferenceError: a is not defined
})();
The most notable advantage of this technique is that it work without the with keyword,
Thus even in strict mode
+function()
{
// jsut pollyfills for backward browsers...
Object.prototype.keys || (Object.defineProperty(Object.prototype, 'keys', {value: function ()
{
var result = []; for (var key in this) result.push(key); return result;
}}));
Object.prototype.entries || (Object.defineProperty(Object.prototype, 'entries', {value: function ()
{
var result = []; for (var key in this) result.push([key, this[key]]); return result;
}}));
// here the magic...
function run(expression, context)
{
var variables = {};
(context instanceof Object) && context.entries().forEach(function(entry)
{
entry[0].match(/^[a-z_$][a-z0-9_$]*$/) && (variables[entry[0]] = entry[1]);
});
return (new Function('return function(' + variables.keys().join(', ') + ') { return ' + expression + '; }'))()// first get the synthetic function
.apply(context, variables.entries().map(function(entry) { return entry[1]; }));
}
var output = run("a + '#' + b", {a: 'Aa', b: 'Bb', 0: 'Zero'});
console.log(output); // Aa#Bb
}();
function runScript(ctx, js){ with(ctx){ return eval(js); }}
closed. thanks all

namespace aliasing in Javascript

Can someone advise me on below Javascript -
1) How can I use the "Base" variable?
2) In callShowMsg function, a local variable "ns" is used to alias the namespace.
Is it possible to use a global variable to alias the namespace? It will avoid the need to declare local variable in each function.
Thanks in advance.
My code is,
var Base = namespace("MyCo.MyApp.Myprogram");
MyCo.MyApp.Myprogram =
{
showMsg: function (pMsg)
{
alert(pMsg);
},
callShowMsg: function (pMsg)
{
var ns = MyCo.MyApp.Myprogram;
ns.showMsg('Hello');
}
}
something like this: (YUI with some fallback for custom namespace). Though I believe you do not have to "namespace" or reference the obj. Just refer to it as "this".
So, if you are within the obj, you can call the methods like so: this.showMsg('somevalue')
function createNamespace() {
var uniqueNS = "MyCo";
var a = arguments, o, i = 0, j, d, arg,
ns = this,
PERIOD = ".";
// force namespace to MyCo
ns.uniqueNS = ns.uniqueNS || {};
ns = ns.uniqueNS;
for (; i < a.length; i++) {
o = ns; //Reset base object per argument or it will get reused from the last
arg = a[i];
if (arg.indexOf(PERIOD) > -1) { //Skip this if no "." is present
d = arg.split(PERIOD);
for (j = (d[0] == uniqueNS) ? 1 : 0; j < d.length; j++) {
o[d[j]] = o[d[j]] || {};
o = o[d[j]];
}
} else {
o[arg] = o[arg] || {};
o = o[arg]; //Reset base object to the new object so it's returned
}
}
return o;
}
var Base = createNamespace("MyCo.MyApp.Myprogram");
Base =
{
showMsg: function (pMsg)
{
alert(pMsg);
},
callShowMsg: function (pMsg)
{
this.showMsg(pMsg);
}
}
Base.showMsg('ok');
I dont think there is some function like namespace like you wrote above,
You can do something like that:
var MYAPPLICATION = {
calculateVat: function (base) {
return base * 1.21;
},
product: function (price) {
this.price = price;
this.getPrice = function(){
return this.price;
};
},
doCalculations: function () {
var p = new MYAPPLICATION.product(100);
alert(this.calculateVat(p.getPrice()));
}
}
Or If you want to use nested namespaces you can try this:
var MYAPPLICATION = {
MODEL: {
product: function (price) {
this.price = price;
this.getPrice = function(){
return this.price;
};
}
},
LOGIC: {
calculateVat: function (base) {
return base * 1.21;
},
doCalculations: function () {
var p = new MYAPPLICATION.MODEL.product(100);
alert(this.calculateVat(p.getPrice()));
}
}
}
How can I use the "Base" variable?
That is going to depend on what value got returned by the namespace function. This is not a standard JS function and its probably specific to the libraries you are using so I cant answer.
Is it possible to use a global variable to alias the namespace?
Of course.
var ns = {
callShowMsg: function (pMsg)
{
ns.showMsg('Hello');
}
}
MyCo.MyApp.Myprogram = ns;
You can also make ns into a local function instead of a global by putting it inside an initialization functions intead of putting it on the script toplevel. The most common way of doing that is using an immediately invoked anonymous function:
(function(){
var ns = {
callShowMsg: function (pMsg)
{
ns.showMsg('Hello');
}
}
MyCo.MyApp.Myprogram = ns;
}());

How to make event listener to change a variable inside javascript code?

I set up some event listeners in a traditional way like this:
<script>
window.onload = setup;
function(setup) {
var myButton = document.getElementById("myButton");
myButton.onclick = someFunction;
}
What I want someFunction to do is to change a certain variable inside my code (when the button is pushed of course). Let's say for example, that I have a function something() in my code containing variable x and the event listener is active.
function something() {
var x;
for (;;) {
if (x === value set by someFunction) {
break;
}
}
}
How can I do that?
When an event is fired you can do stuff so:
CODE CODE CODE
....
var x = INITIAL_VALUE;
function someFunc(){
x = what ever you wish;
}
The simple way is to make x a global variable.
If you don't want to pollute the global namespace, you use an immediately executing function:
(function() {
var x;
function someFunc() {
// May assign x
}
function setup() {
var myButton = document.getElementById("myButton");
myButton.onclick = someFunction;
}
window.onload = setup;
function something() {
// Use x
}
})();
This function creates a local namespace for the x variable.
If I understand your question, the simplest way would be to define a global variable var value, such that it would be accessible by both someFunction() and by something().
Another approach, without any listener, could be:
function something() {
var x;
for (;;) {
if (x === someFunction()) {
break;
}
}
}
function someFunction() {
...
return value;
}
//
// try a 'notifier' decorator
//
function notifier( handler_fn, callback_fn ) {
// #helpers
function slc( args, i1, i2 ) {
return Array.prototype.slice.call( args, i1, i2 );
}
function prepend( targetArr, arr ) {
Array.prototype.unshift.apply( targetArr, arr );
return targetArr;
}
function thepush( targetArr, arr ) {
Array.prototype.push.apply( targetArr, arr );
return targetArr;
}
return ( function( cb, args1 ) {
var origfn = this;
return function() {
var
args = prepend( slc( arguments ), args1 ),
out = origfn.apply( this, args );
cb.apply( this, thepush( args, [out] ) );
return out;
};
} ).call( handler_fn, callback_fn, slc( arguments, 2 ) );
}
var
handlerfn = function ( e ) {
var
T = e.type.toUpperCase();
( this instanceof Node ) && ( this.innerHTML = T );
return T;
},
F = notifier(
handlerfn,
function ( f1_in, f1_out ) {
var
v1 = f1_in;
v1.target.innerHTML += ', coords:[' + v1.clientX + ', ' + v1.clientY +']';
console.log( f1_in, ', ', f1_out );
}
);
document.getElementById('btn_01').onclick = F;
//
//

Categories