Dynamic Parameters for metaprogramming in JavaScript? - javascript

I want to delegate several methods from one JavaScript-Object to another. So i thought about using metaprogramming to not have several methods be defined just as delegates. So far i ended up with this method:
function delegate_to(_method, _obj) {
return function(_args) { // One parameter, what's about multiple parameters?
return _obj[_method](_args)
}
}
So as an example-code how it should work:
var that = {}
var delegate = {}
that.foo = function(_message) { console.log("foo: " + _message) }
that.bar = function(_message) { console.log("bar: " + _message) }
that.baz = function(_message) { console.log("baz: " + _message) }
function delegate_to(_method, _obj) {
return function(_args) { // One parameter, what's about multiple parameters?
return _obj[_method](_args)
}
}
['foo', 'bar', 'baz'].forEach(function(method) {
delegate[method] = delegate_to(method, that)
})
delegate.foo('Hello JS') // foo: Hello JS
delegate.bar('Hello JS') // bar: Hello JS
delegate.baz('Hello JS') // baz: Hello JS
The code does work, but what's if i want to delegate a method that does have more than one parameter? How about n parameters? Is it possible to change the code to have any number of parameters? Is this running in any browser?
Regards, Rainer

Try this:
function delegate_to(_method, _obj) {
return function() {
return _obj[_method].apply(_obj, [].slice.call(arguments))
}
}

Function has methods called 'apply' to pass variable number of parameters as an array. Refer MDC:Function.apply
You can convert all the parameters passed to a function into an array by
Array.prototype.slice.call(arguments, 0)
Using these two principals, I have modified you code to taken multiple number of parameters. See the JSBin http://jsbin.com/iwiwix/3/watch
Relevant code extract:
delegate.foo('Hello JS', "from foo"); // foo: Hello JS
function delegate_to(_method, _obj) {
return function() {
var argArray = Array.prototype.slice.call(arguments, 0);
return _obj[_method].apply(_obj, argArray);
};
}
that.foo = function() { console.log("foo: " + arguments[0] + ' ' + arguments[1]); };

Related

How to make a property/method invokable or not?

I want to achieve this functionality:
I have an object var obj = {};
I have three properties on that obj, obj.zero & obj.one& obj.binaryString
obj.zero & obj.one are methods while obj.binaryString is a string
When I chain the properties, I want them to add their respective digit to the binaryString. So for example:
obj.one.zero.zero.one => makes obj.binaryString = 1001
obj.one.zero.one.one.zero => makes obj.binaryString = 10110
I have achieved the above functionality with this:
function Binary () {
var obj = { binaryString: '' };
Object.defineProperty(obj, 'zero', {
get: function() {
obj.binaryString += '0';
return obj;
}
});
Object.defineProperty(obj, 'one', {
get: function() {
obj.binaryString += '1';
return obj;
}
});
return obj;
}
var binary = new Binary();
binary.one.zero.zero.one // => obj.binaryString becomes '1001'
Now I want to log out the completed binaryString, plus and additionalString which I have accomplished with the code below:
// placed inside Binary constructor function
Object.defineProperty(obj, 'log', {
get: function() {
return function(additionalString) {
console.log(obj.binaryString + additionalString);
};
}
});
So with this current code I can do this:
binary.one.zero.one.zero.one.log(' is the answer');
// logs out `10101 is the answer`
What I want to do is get rid of the log and make the one and zero methods invokable or not so I can achieve this functionality:
binary.one.one.zero.one(' is the result')
// => logs out `1101 is the result`
How can I do this?
I believe it would be similar functionality to how Chalk works:
chalk.blue.bold('Hello world!');
// `blue` is not invoked here but it adds the color blue to the style
chalk.blue('Hello world!');
// `blue` IS invoked here. It adds blue to the style and returns the stylized string
Just Make the obj as function , and print what ever you want .
function Binary () {
var obj = function(msg){ console.log(msg+this.binaryString ) };
obj.binaryString = ''
Object.defineProperty(obj, 'zero', {
get: function() {
obj.binaryString += '0';
return obj;
}
});
Object.defineProperty(obj, 'one', {
get: function() {
obj.binaryString += '1';
return obj;
}
});
return obj;
}
var binary = new Binary();
binary.one.zero.zero.one.zero(" is the result ")
I would like to point out that what you're doing is a very bad/dangerous idea: abusing read properties to mutate the object itself is asking for trouble. You may not see it now, but it's going to lead to pain and heartache down the line in the form of difficult-to-find bugs and convoluted patterns.
What you can do is, which is not so dangerous, is instead of mutating the object itself, return a new instance of Binary with every call to #one or #zero. For example:
function Binary(s) {
this.binaryString = s || ''
}
Object.defineProperty(Binary.prototype, 'zero', {
get: function() {
return new Binary(this.binaryString + '0')
}})
Object.defineProperty(Binary.prototype, 'one', {
get: function() {
return new Binary(this.binaryString + '1')
}})
This is the approach taken by Chalk, and will be much safer and less error-prone.
UPDATE:
After thinking about your problem, and seeing your question, I think the best approach at all is not to use classes at all. You can solve this problem with a pure function-based approach. It's immutable, it's safe, and I believe it's less confusing. Here it is in ES5:
function bin(str) {
if(!str) str = ''
function f(msg) { return str + ' ' + msg }
return Object.defineProperties(f, {
zero: {
get: function() {
return bin(str + '0')
},
},
one: {
get: function() {
return bin(str + '1')
},
},
})
}
And if you can use ES6 (aka ES2015), you can make it much more compact:
function bin(str = '') {
return Object.defineProperties(msg => `${str} ${msg}`, {
zero: { get() { return bin(str + '0') } },
one: { get() { return bin(str + '1') } },
})
}
You would use it like this:
bin().one.zero.one.zero('is the answer') // '1010 is the answer'

Javascript: always execute function in execution context

I wrote this fast-templating function:
var templatize = function(string) {
return function (string) {
return string.replace(/{{(.*?)}}/g, function(pattern, match) {
value = this[match];
if (value) {
return value;
} else {
return pattern;
}
});
}.call(this, string);
}
Which does this:
var foo = "bar", bar = "foo";
templatize("We are {{foo}} and {{bar}}, but not {{crazy}}"); // "We are bar and foo but not {{crazy}}"
I'm quite happy with this except that I have scoping problem. For sure, the templatize method will be accessible through namedscope, but then, the current context of execution of templatize is not accessible in my function automatically.
Something like calling $.proxy(templatize, this)("We are {{foo}} and {{bar}}, but not {{crazy}}") should work, right?
But I'd like to achieve this without needing to call $.proxy() (and without any jQuery preferably) so that context is automatically transfered to the execution one.
I'm struggling with .call(), .apply(), and other closures, but I think I read somewhere over the internet that it was possible. Thanks
You can avoid using jQuery doing this :
var templatize = function(string) {
var me = this; // the data source
return string.replace(/{{(.*?)}}/g, function (full, key) {
// "this" refers to the string itself
return me[key] || full;
});
}
In case you want to use jQuery.proxy(), wrap the replacement function :
var templatize = function(string) {
return string.replace(/{{(.*?)}}/g, jQuery.proxy(function (full, key) {
// "this" now refers permanently to the data source
return this[key] || full;
}, this));
}
In both cases you can bind the data source to this using call :
templatize.call({ hello: 'Hi!' }, '{{hello}}');
Going further
You could optimize by compiling the template for reuse :
function compile(tpl) {
var i = -1, tmp = [];
tpl = tpl.split(/{{([^{}]+)}}/);
while (++i < tpl.length) {
if (i % 2) tmp.push('this["' + tpl[i] + '"]');
else if (tpl[i]) tmp.push('"' + tpl[i].replace(/"/g, '\\"') + '"');
}
return new Function(
'return [' + tmp.join() + '].join("");'
);
}
Usage example :
var tpl = compile('{{hello}} {{hello}}');
tpl.call({ hello: 'Hi!' }); // "Hi! Hi!"
tpl.call({ hello: 'Yo!' }); // "Yo! Yo!"
Regarding the example above, here is the function returned by compile :
function () {
return [this["hello"]," ",this["hello"]].join("");
}
Note that you can use an array as well :
var tpl = compile('{{1}} {{0}}');
tpl.call(['a', 'b']); // "b a"
Performance test : http://jsperf.com/template-compiling.
why don't you pass an object containing the view variables? would be cleaner then potentially displaying any existing variable in your view.
var templatize = function(string, variables) {
return function (string) {
return string.replace(/{{(.*?)}}/g, function(pattern, match) {
value = variables[match];
if (value) {
return value;
} else {
return pattern;
}
});
}.call(this, string);
}

How to call a function on string jQuery

I was reading through fluent api I got a doubt.
I want to take in a string upon which a jQuery function or example is called upon
Function
function compareThis(newString) {
function compare(newString) {
if (this == newString) {
alert("same string");
} else {
alert("differnt string");
}
}
}
Where it is called as
("alerting").compareThis("alerted").compare(); //alert 'different string'
I want to pass the data/string not as parameter but as called upon.
JSFiddle
Note: I would like to call the function in similar cases like finding date interval etc
You can use prototype to add function to String class:
String.prototype.compare = function(newString){
if (this == newString) {
alert("same string");
} else {
alert("differnt string");
}
};
I think you should adapt the code for your function, but it's the idea.
Maybe I missed interpreted however, it looks as it you required a form of method chaining to compare string. To do this you can create a variable and create functions inside it.
var compare = (function(){
var thisString;
var stringToCompare;
var create = function(sVal) {
thisString = sVal;
return this;
};
// Public
var compareThis = function(sVal) {
stringToCompare = sVal;
return this;
};
var compare = function(anotherString) {
return thisString == stringToCompare;
};
return {
create: create,
compareThis: compareThis,
compare: compare
};
}());
var b = compare.create('test').compareThis('test').compare();
alert(b);
Example fiddle

Deep nesting functions in JavaScript

I cannot find an proper example for the love of my life on how to do this or even if this is possible. Based on my pieced together understanding from fragments of exmaples, I have come up with the following structure
var t = function()
{
this.nestedOne = function()
{
this.nest = function()
{
alert("here");
}
}
}
t.nestedOne.nest();
However this is not working (obviously). I would greatly appreciate if someone could point me in the right direction!
That is simply done with:
var t = {
nestedOne: {
nest: function() {
alert('here');
}
}
};
Your code otherwise doesn't make sense. this inside function doesn't refer to the function itself, it refers to the object context that the function is invoked in. And you are not even invoking the functions in your code.
If I say obj.func() then this inside func will be obj for that call. So assigning this.asd = true will assign true to that object's "asd" property.
If you wanted to do a nested class, it looks very different:
ClassA = (function() {
function ClassA() {
}
ClassA.prototype.method1 = function() {
};
function ClassB() {
}
ClassB.prototype.method1 = function() {
};
return ClassA;
}())
only ClassA can now make instances of ClassB. This should achieve same goals as nested classes in java.
See http://jsfiddle.net/CstUH/
function t(){
function f(){
this.nest = function()
{
alert("here");
}
}
this.nestedOne = new f();
}
var myt=new t();
myt.nestedOne.nest()
Edit 1:
You can also use
new t().nestedOne.nest()
instead of
var myt=new t();
myt.nestedOne.nest()
(http://jsfiddle.net/CstUH/1/)
Edit 2:
Or even more condensed:
function t(){
this.nestedOne = new function(){
this.nest = function(){
alert("here");
}
}
}
new t().nestedOne.nest()
http://jsfiddle.net/CstUH/2/
In JS functions are prime class objects, and you can access them directly in the code [i.e. without using reflection or so].
The code you put inside t body would be performed when actually executing t:
t();
You wrote t.nestedOne,nest(), but t has no nestedOne property - you should do like this:
var t = {
nestedOne : {
nest : function()
{
alert("here");
}
}
};
t.nestedOne.nest(); ​
I advice you to have a trip on John Resig's Learning Advanced JavaScript tutorial, it was very enlightening for me.
A simple callback handler I wrote today as an example of how I do deep nesting. I apologize if it's not the bees knees when it comes to code style, it made the concept a little clearer for me.
function test () {
this.that = this;
this.root = this;
this.jCallback = new Array(new Array()); // 2d
this.jCallbackCount = -1;
this.str = "hello";
// Callback handler...
this.command = {
that : this, // let's keep a reference to who's above us on the food chain
root : this.root, // takes us back to the main object
// add : function() { var that = this; console.log(that.that.str); },
add : function(targetFnc, newFunc) {
var that = this;
var home = that.that; // pretty much root but left in as an example of chain traversal.
var root = this.root; // useful for climbing back up the function chain
// console.log(that.that.str);
home.jCallbackCount++;
// target, addon, active
home.jCallback[home.jCallback.length] = { 'targetFunc' : targetFnc, 'newFunc' : newFunc, 'active' : true, 'id': home.jCallbackCount};
console.log('cbacklength: ' + home.jCallback.length);
console.log('added callback targetFunction:[' + targetFnc + ']');
return home.jCallbackCount; // if we want to delete this later...
},
run : function(targetFnc) {
var that = this;
var home = that.that;
console.log('running callback check for: ' + targetFnc + ' There is : ' + (home.jCallbackCount + 1) + 'in queue.');
console.log('length of callbacks is ' + home.jCallback.length);
for(i=0;i < home.jCallback.length - 1;i++)
{
console.log('checking array for a matching callback [' + targetFnc + ']...');
console.log('current item: ' + home.jCallback[i]['targetFunc'] );
if( home.jCallback[i]['targetFunc'] == targetFnc )
{
// matched!
home.jCallback[i]['newFunc']();
}
// console.log(that.that.jCallback[i].targetFunction);
}
}
};
}
test.prototype = {
say : function () {
var that = this;
console.log('inside');
// that.command('doSay');
that.command.run('doSay');
console.log(that.str);
}
} // end proto
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
var testing = new test();
testing.command.add('doSay', function () { console.log('213123123'); } );
testing.command.add('doSay', function () { console.log('12sad31'); } );
testing.command.add('doSay', function () { console.log('asdascccc'); } );
testing.say();
live:
http://jsfiddle.net/Ps5Uf/
note: to view console output, just open inspector in chrome and click on the "console" tab.

Invoke public function from internal/private function?

Just wondering if I'm missing something or not but I attempted to do the following:
(function() {
var thing = function() {
var doIt = function() {
console.log("just do it");
this.updateValue(5);
};
return {
updateValue: function(val) {
console.log('updating value: ' + val);
},
go: function() {
doIt();
}
}
};
var t = thing();
t.go();
}())
This results in "just do it" showing up in the console followed by an error b/c it says "updateValue" is not a function.
I was wondering, can an internal/private function (e.g. "doIt") invoke a public function (e.g. "updateValue")? Perhaps this is just bad design and you should never really want to do this and I've actually refactored my code to avoid/not do this but I was curious if it was possible.
Thanks in advance.
Either use call/apply to explicitly specify the context for this (like #SLaks and #Alnitak) mentioned or else define the function at the beginning and then add it as a property to the returned object:
var thing = function() {
var updateValue = function () { /* */ },
doIt = function() {
console.log("just do it");
updateValue(5);
};
return {
updateValue: updateValue, // minor duplication here
go: function() {
doIt();
}
};
};
If the minor duplication annoys you, you can also do this:
var thing = function() {
var exposed = {
updateValue: function(val) {
console.log('updating value: ' + val);
},
go: function() {
doIt();
}
}, doIt = function() {
console.log("just do it");
exposed.updateValue(5);
};
return exposed;
};
Writing doIt(), calls the function in the global context, so this is the window object.
You need to write doIt.call(this) to pass your this as the context for doIt.
Per #SLaks answer, this is incorrect when invoked by doIt().
Instead, try:
doIt.call(this);

Categories