Javascript Metaprogramming - javascript

Is there a way to specify something similar to the following in javascript?
var c = {};
c.a = function() { }
c.__call__ = function (function_name, args) {
c[function_name] = function () { }; //it doesn't have to capture c... we can also have the obj passed in
return c[function_name](args);
}
c.a(); //calls c.a() directly
c.b(); //goes into c.__call__ because c.b() doesn't exist

Mozilla implements noSuchMethod but otherwise...no.

No, not really. There are some alternatives - though not as nice or convenient as your example.
For example:
function MethodManager(object) {
var methods = {};
this.defineMethod = function (methodName, func) {
methods[methodName] = func;
};
this.call = function (methodName, args, thisp) {
var method = methods[methodName] = methods[methodName] || function () {};
return methods[methodName].apply(thisp || object, args);
};
}
var obj = new MethodManager({});
obj.defineMethod('hello', function (name) { console.log("hello " + name); });
obj.call('hello', ['world']);
// "hello world"
obj.call('dne');

Almost 6 years later and there's finally a way, using Proxy:
const c = new Proxy({}, {
get (target, key) {
if (key in target) return target[key];
return function () {
console.log(`invoked ${key}() from proxy`);
};
}
});
c.a = function () {
console.log('invoked a()');
};
c.a();
c.b();

No.

Related

Javascript function does not return the right value

So i have this code:
function Class1() {
this.i = 1;
var that=this;
function nn() {
return 21;
}
this.aa = function() {
nn();
};
this.bb = function() {
this.aa();
};
this.cc = function() {
this.bb();
};
}
var o = new Class1();
var b=o.cc();
alert(b); //undefined
But when the alert is fired, I get an undefined error and not 21, Does the private method can not use a return? Thanks!
When using the function() {} syntax to define a function, you always explicitly need to return the value, i.e. not only from nn, but from all intermediate functions as well.
function Class1() {
this.i = 1;
var that = this;
function nn() {
return 21;
}
this.aa = function() {
return nn();
}
this.bb = function() {
return this.aa();
}
this.cc = function() {
return this.bb();
}
}
var o = new Class1();
var b = o.cc();
alert(b); // "21"
Apart from the answer above, the 'this' context seems weird in your functions. Maybe you are better of with arrow functions if you dont want to bind the this context to each function. I also think that it is better to actually separate private and public functions when using a 'class' like this.
function Class1() {
var _nn = function () {
return 21;
}
var _aa = function () {
return _nn();
}
var _bb = function () {
return _aa();
}
var cc = function () {
return _bb();
};
return {
cc
};
}
var o = new Class1();
var a = o.cc();
console.log(a);
Much easier to understand that it is only cc that is a public function.
So with arrow function it would instead look like this, and you can use the Class1 this context inside of your private functions without doing
var that = this; or using bind.
function Class1() {
this.privateThing = 'private';
var _nn = () => { return this.privateThing; };
var _aa = () => { return _nn(); };
var _bb = () => { return _aa(); };
var cc = () => { return _bb(); };
return {
cc
};
}

JavaScript object inheritance issue

I'm a beginner with JavaScript Objects and Prototypes and trying to develop my first " multi-level inherited" JS Objects, an unexpected issue came up.
This is my code:
var Utils = function () {};
Utils.prototype = {
sayHelloGeneral: function(){
console.log('hello');
}
};
var FormTools = function () {
Utils.call(this);
this.fields = [];
};
FormTools.prototype = Object.create(Utils.prototype);
FormTools.prototype.constructor = FormTools;
FormTools.prototype.sayHelloForm= function (fields) {
console.log('hello form');
};
function GroupManager(value) {
FormTools.call(this);
this.val = typeof values === 'undefined' ? 1 : value;
};
GroupManager.prototype = Object.create(FormTools.prototype);
GroupManager.prototype.constructor = GroupManager;
GroupManager.prototype.helloGroupManager= function (givenValue) {
console.log('Hello group manager');
};
Why when I try to call the group manager, it prints only the sayHelloGeneral function?
var GM = new GroupManager;
GM.sayHelloGeneral(); //->ok
GM.helloGroupManager(); //--> ok
GM.sayHelloForm(); //->sayHelloForm is not a function
It seems to be working fine. See the snippet below
var Utils = function () {};
Utils.prototype = {
sayHelloGeneral: function(){
console.log('hello');
}
};
var FormTools = function () {
Utils.call(this);
this.fields = [];
};
FormTools.prototype = Object.create(Utils.prototype);
FormTools.prototype.constructor = FormTools;
FormTools.prototype.sayHelloForm= function (fields) {
console.log('hello form');
};
function GroupManager(value) {
FormTools.call(this);
this.val = typeof values === 'undefined' ? 1 : value;
};
GroupManager.prototype = Object.create(FormTools.prototype);
GroupManager.prototype.constructor = GroupManager;
GroupManager.prototype.helloGroupManager= function (givenValue) {
console.log('Hello group manager');
};
var GM = new GroupManager;
//GM.sayhello(); //->ok---> should be sayHelloGeneral()
GM.sayHelloGeneral();
GM.helloGroupManager(); //--> ok
GM.sayHelloForm(); //->Works fine too

influence Javascript function invokation

I want to invoke function X everytime any other function is invoked. I want to keep this as generic as possible.
having these two functions
function x(){ console.log("invoke BEFORE"); }
function someFunction(something){ console.log(something); }
When someFunction is invoked
someFunction("testoutput");
I want the console to output this:
>> invoke BEFORE
>> testoutput
I also want this behaviour to apply to any function of a certain object.
For example:
var myFunctions = {
first:function(){/* do something */},
second:function(){/* do something else*/}
}
myFunctions.before(function(){/do something before/});
Anyone know a solution?
EDIT:
I have come up with a solution like this:
Object.prototype.before = function(x){
for(var key in this){
if(typeof this[key] === "function")
this[key] = (function(x, f) {
var g = f;
return (function() {
x();
return g.apply(this, arguments);
});
}(x, this[key]));
}
}
var test = { func: function(){console.log("test")}};
test.before(function(){console.log("before")});
test();
results in:
>> before
>> test
YAAAYYY
how do you like this?
This is a bad idea that will make understanding and debugging your program much harder.
You can use what in Python is called "monkey-patching" to achieve this:
(function() {
{
var origSomeFunction = someFunction;
someFunction = (function() {
x();
return origSomeFunction.apply(this, arguments);
});
}();
This works because I changed the (global) name someFunction to refer to a new function that I defined. Within the closure of that function I keep a reference to the original function that you want to pass the call on to.
In my opinion, event binding is more flexible than function wrapping since you can remove x whenever you want. Here is a possible implementation:
// Observable
var Observable = {};
Observable.create = function (options) {
var observable = { events: {} };
var events = options.events || [];
var i, l = events.length;
for (i = 0; i < l; i++) {
observable.events[events[i]] = [];
}
return observable;
};
Observable.one = function (observable, event, handler) {
Observable.on(observable, event, function f () {
Observable.un(observable, event, f);
handler.apply(this, arguments);
});
};
Observable.on = function (observable, event, handler) {
observable.events[event].push(handler);
};
Observable.un = function (observable, event, handler) {
observable.events[event].splice(
observable.events[event].indexOf(handler), 1
);
};
Observable.emit = function (observable, event, params) {
var handlers = observable.events[event];
var i, l = handlers.length;
if (!params) params = {};
params.source = observable;
for (i = 0; i < l; i++) {
handlers[i].call(observable, params);
}
};
Observable.observeMethod = function (observable, name) {
var meth = observable[name];
var before = 'before' + name.toLowerCase();
var after = 'after' + name.toLowerCase();
observable.events[before] = [];
observable.events[after] = [];
observable[name] = function () {
var ret;
Observable.emit(observable, before);
ret = meth.apply(observable, arguments);
Observable.emit(observable, after, { value: ret });
return ret;
};
};
// init
var printer = init({
sayHello: function () {
this.print('Hello World.');
},
sayHi: function (e) {
this.print('Hi ' + e.pseudo + '.');
},
print: function (msg) {
print(msg);
}
});
var clock = init({
tid: null,
events: ['tick'],
stop: function () {
clearTimeout(this.tid);
},
start: function () {
var me = this;
var time = 0;
clearTimeout(this.tid);
(function tick () {
me.tid = setTimeout(tick, 1000);
me.emit('tick', { time: time++ });
})();
}
});
// demo: printer
printer.on('afterprint', printNewline);
printer.on('beforesayhello', printBullet);
printer.sayHello();
printer.sayHello();
printer.un('beforesayhello', printBullet);
printer.sayHello();
// demo: clock
clock.on('tick', function (e) {
if (e.time) printer.print('tick ' + e.time);
if (e.time === 3) this.stop();
});
clock.one('afterstop', clock.start);
clock.start();
// helpers
function init (obj) {
obj = initObservable(obj);
obj.one = function (event, handler) {
Observable.one(this, event, handler);
};
obj.on = function (event, handler) {
Observable.on(this, event, handler);
};
obj.un = function (event, handler) {
Observable.un(this, event, handler);
};
obj.emit = function (event, params) {
Observable.emit(this, event, params);
};
return obj;
}
function initObservable (obj) {
var k, observable;
observable = Observable.create({
events: obj.events
});
for (k in observable) {
obj[k] = observable[k];
}
for (k in obj) {
if (typeof obj[k] === 'function') {
Observable.observeMethod(obj, k);
}
}
return obj;
}
function printBullet () {
print('• ');
}
function printNewline () {
print('<br />');
}
function print (html) {
document.body.innerHTML += html;
}

How to "new" a returned function in Javascript

I am trying to simulate a namespace feature in Javascript.
var com = {};
com.domain = {};
com.domain.system = {};
com.domain.net = {};
com.domain.net.ip = {};
com.domain.net.ip.tcp = {};
com.domain.net.ip.udp = {};
com.domain.net.ip.ssl = {};
com.domain.util = {};
com.domain.util.timer = {};
com.domain.plugins = {};
com.domain.session = {};
com.domain.io = {};
com.domain.algorithm = {};
com.domain.debug = {};
This is the namespaces declaration. Later I will add functions to these namespaces.
This is my selector function:
For a convenient way to use namespaces, I add a function named $. This function will walk all namespaces in com. If the selected name exists, return the object.
function $ (selector) {
function digger (namespace, selector) {
for (var prop in namespace) {
if (typeof namespace[prop] == "array" || typeof namespace[prop] == "object") {
if (prop == selector) {
return namespace[prop];
}
var dig = digger(namespace[prop], selector);
if (dig != null) {
return dig;
}
} else {
if (prop == selector) {
return namespace[prop];
}
}
}
}
return digger (com, selector);
}
After that, I add a timer to namespace com.doamin.util.
com.domain.util.timer = function () {
this._handle = new InnerObj.SystemTimer(io);
return this;
};
com.domain.util.timer.prototype.expiresFromNow = function (seconds, cbHandler) {
this._handle.ExpiresFromNow (seconds, cbHandler);
};
com.domain.util.timer.prototype.wait = function (seconds, cbHandler) {
this._handle.Wait (seconds, cbHandler);
};
com.domain.util.timer.prototype.expiresAt = function (seconds, cbHandler) {
this._handle.Wait (seconds, cbHandler);
};
com.domain.util.timer.prototype.cancel = function () {
this._handle.Cancel ();
};
Usage:
1. var timer = new com.domain.util.timer (); OK
timer.expiresAt (1, {}); OK
2. var func = $("timer"); OK
var timer = new func (); OK
timer.expiresAt (1, {}); OK
But but but but but
var timer = new $("timer") (); NG
Can anyone tell me why the last new function is not working?
Try var timer = new ($("timer"))();.
Your question is not clear but I guess since $("timer") returns a function, you want a new instance of the result of $("timer") and not a new instance of $().

how to call function using name such as "function someName(){}"?

I have a name of a private function in JavaScript as a string, how do I call that function?
var test = function () {
this.callFunction = function(index) {
return this["func" + index]();
}
function func1() { }
function func2() { }
...
function funcN() { }
}
var obj = new test();
obj.callFunction(1);
func1 and friends are local variables, not members of the object. You can't call them like that (at least not in any sane way).
Define them with function expressions (instead of function declarations) and store them in an array.
var test = function () {
this.callFunction = function(index) {
return funcs[index]();
}
var funcs = [
function () {},
function () {},
function () {}
];
}
var obj = new test();
obj.callFunction(0);
As your code stands, the functions are not present as properties of the instance. What you need to do is create them as properties of the context.
var test = function () {
this.callFunction = function(index) {
return this["func" + index];
}
this.func1 = function() { }
this.func2 = function() { }
...
}
var obj = new test();
obj.callFunction(1)();
you can use eval
var test = function () {
this.callFunction = function(index) {
return eval("func" + index + '()');
}
function func1() {
return 1;
}
function func2() {
return 2;
}
function funcN() { }
};
var obj = new test();
obj.callFunction(2);
eval is evil
You can use a private array of functions:
var test = function() {
var func = [
function() { return "one" },
function() { return "two"; }
]
this.callFunction = function(index) {
return func[index]();
}
}
var obj = new test();
var ret = obj.callFunction(1);
console.log(ret);​
​
http://jsfiddle.net/V8FaJ/

Categories

Resources