Well, i understand my question is a little bit strange and the answer seems obvious ("it's impossible !"), but, as JS is a very open language, i go on anyway :)
Let's say, we have the following code :
function dummy() {
}
var obj = new dummy();
var result = obj.aFunction('a','b');
Obviously, the JS interpretor says :
obj.aFunction is not a function
I understand his anger :) but is there a trick to bypass the interpretor (or something like that) or to create on-the-fly the aFunction function into the obj object before the interpretor evaluates all the stuff ?
I've red all about using dynamic function names and so on (using eval() and other tricks) but that don't solve my (weird) problem ...
Thanks in advance.
EDIT : Well, folks, thanks for your answers, but it's not my problematic.
In fact, i used to code in Java with AOP and what i want is :
Create a Valve (or something like this) that catches all the exceptions
Analyse the exception
if the exception corresponds to my 'no function' error, i create from scratch the function and execute it
I garbage this exception
if it's not the good exception i let it to continue its job
Unfortunatly, You cannot do that in JS. Using an ExceptionHandler is not sufficient because its API is too poor ...
But, Thanks to all ...
There are many ways to achieve this. Here is one:
function dummy() {
}
dummy.prototype.aFunction = function (a, b) {
alert(a + ', ' + b);
};
var obj = new dummy();
var result = obj.aFunction('a','b');
Here is a working example. Warning: it will pop up an alert box.
http://jsfiddle.net/X4F67/
Here is another, if you don't know the function name at runtime:
var name = 'aFunction';
function dummy() {
}
dummy.prototype[name] = function (a, b) {
alert(a + ', ' + b);
};
var obj = new dummy();
var result = obj[name]('a','b');
Fiddle: http://jsfiddle.net/X4F67/1/
Note that both of these affect the dummy prototype itself, so all instances of dummy would contain aFunction. If you instead want to work directly on the obj, you could use something like this:
...
var obj = new dummy();
obj[name] = function () { };
You can create a function on the fly, your code just has some issues.
First of all, setting:
var result = obj.aFunction('a','b');
Is making the browser think that obj.aFunction is an existing function that you are calling, you have to set the function.
obj.aFunction = function(a,b)
{
alert(a + ' ' + b);
}.bind(obj);
...then you could state:
var result = obj.aFunction;
If you were to say:
var result = obj.aFunction('a', 'b');
...after declaring obj.aFunction, it would be called at that line and alert right away.
Instead, you can do this:
window['result']('a', 'b');
As for declaring a function on the fly, such as writing out a function as a string and using eval() to evaluate the function and append it to a variable, I'm not sure why it doesnt work. This is what I tried:
obj.aFunction = eval("function(a,b){alert(a + ' ' + b);}.bind(obj)");
And I get:
Uncaught Syntax Error: Unexpected token (
Either way, hope that helps.
Related
By Pure, I mean in the sense of the λ-calculus, i.e., a single-argument function containing nothing on its body other than single-argument functions and single argument function calls. By recovering the source code, I mean up to variable renaming. So, for example,
n2 = function(v0){return function(v1){return v0(v0(v1))}}
console.log(source(n2));
console.log(source(n2(n2)));
Should print:
function(v0){return function(v0){return v0(v0(v1))}}
function(v0){return function(v0){return v0(v0(v0(v0(v1))))}}
That is, the first line shows the original source of the function n2, and the second one shows the source of the function that is returned by the evaluation of n2(n2).
I've managed to implement it as follows:
function source(f){
var nextVarId = 0;
return (function recur(f){
if (typeof f === "function"){
if (f.isVarFunc) return f(null);
else {
var varName = "v"+(nextVarId++);
var varFunc = function rec(res){
var varFunc = function(arg){
return arg === null
? "("+res.join(")(")+")"
: rec(res.concat(recur(arg)));
};
varFunc.isVarFunc = true;
return varFunc;
};
varFunc.isVarFunc = true;
var body = f(varFunc([varName]));
body = body.isVarFunc ? body(null) : recur(body);
return "(function("+varName+"){return "+body+"})";
};
} else return f;
})(f);
};
The issue is that I'm using some rather ugly method of tagging functions by setting their names to a specific value, and that it won't work in functions that are applied more than once (such as a(b)(b)). Is there any better principled way to solve this problem?
Edit: I managed to design a version that seems to be correct in all cases, but it is still an ugly unreadable unprincipled mess.
Finally, this is a considerably cleaned up version of the mess above.
// source :: PureFunction -> String
// Evaluates a pure JavaScript function to normal form and returns the
// source code of the resulting function as a string.
function source(fn){
var nextVarId = 0;
return (function normalize(fn){
// This is responsible for collecting the argument list of a bound
// variable. For example, in `function(x){return x(a)(b)(c)}`, it
// collects `a`, `b`, `c` as the arguments of `x`. For that, it
// creates a variadic argumented function that is applied to many
// arguments, collecting them in a closure, until it is applied to
// `null`. When it is, it returns the JS source string for the
// application of the collected argument list.
function application(argList){
var app = function(arg){
return arg === null
? "("+argList.join(")(")+")"
: application(argList.concat(normalize(arg)));
};
app.isApplication = true;
return app;
};
// If we try to normalize an application, we apply
// it to `null` to stop the argument-collecting.
if (fn.isApplication)
return fn(null);
// Otherwise, it is a JavaScript function. We need to create an
// application for its variable, and call the function on it.
// We then normalize the resulting body and return the JS
// source for the function.
else {
var varName = "v"+(nextVarId++);
var body = normalize(fn(application([varName])));
return "(function("+varName+"){return "+body+"})";
};
})(fn);
};
It is still not perfect but looks much better nether less. It works as expected:
console.log(source(function(a){return function(b){return a(b)}}))
Outputs:
(function(v0){return (function(v1){return (v0)((v1))})})
I wonder how inefficient that is, though.
I don't really know how to explain this but I'll show you code and tell you what I'd like to achieve.
Let's say I make a quick object:
var test = {};
And then I set a property to it: (I insist on the syntax, it mustn't use any function as the setter)
test.hello = 'world';
Pretty simple, eh? Now I'd like to add a function to that object that would get called everytime a new property gets set. Like this:
var test = {
newPropertyHasBeenSet: function(name){
console.log(name + 'has been set.');
}
};
test.hello = 'world';
// Now newPropertyHasBeenSet gets called with 'hello' as an argument.
// hello has been set.
I don't know if it's possible, but that would be quite amazing. Anyone has an idea of how to achieve so?
EDIT: I'd like also to be able to do the same for property get (so test.hello would call get('hello') for example).
EDIT2: This is for server-side javascript using node.js.
Thanks a lot and have a nice day!
try this example in chrome (as mentioned in previous comments it uses ES6 Proxy):
var p = new Proxy(
{},
{
get: function(obj, name) {
console.log('read request to ' + name + ' property');
if (name == 'test_test') return 1234;
else return 'Meh';
},
set: function(obj, name, value) {
console.log('write request to ' + name + ' property with ' + value + ' value');
},
}
);
console.log(p.test_test);
console.log(p.test);
p.qqq = 'test';
result:
read request to test_test property
1234
read request to test property
Meh
write request to qqq property with test value
var test = {};
Object.defineProperty(test, "hello", {
get : function () {
return this._hello;
},
set : function (val) {
alert(val);
this._hello = val;
}
});
test.hello = "world";
Something like that. But it will not work on old browsers.
You can find more options here: http://robertnyman.com/javascript/javascript-getters-setters.html
If you really insist on keeping the test.hello = "world" syntax to detect changes for existing properties, then you'll have to wait a few years for Object.watch to become part of the next EcmaScript standard.
Luckily, you can do the same in EcmaScript 5 using Object.defineProperty. Eli Grey made a nice Object.watch polyfill which you can call like this:
var test = {};
test.watch("hello", function(propertyName, oldValue, newValue) {
console.log(propertyName + " has been set to " + newValue);
});
test.hello = "world"; // triggers the watch handler
You could modify his code to trigger a different handler inside the getter as well, so you can detect property accesses.
Unfortunately, browser support is limited to modern browsers including Internet Explorer 9, Firefox 4, Chrome, Opera 12 and Safari 5.
If you want to trigger a handler when a new property is set, you'll have even more trouble. The best you could do is wrapping your object inside a proxy and placing a set trap. You can then detect whether the property already existed by testing if this.getOwnPropertyDescriptor(name) returns a 'truthy' value. The Proxy API is very experimental though and only a few browsers provide a prototype implementation to play with. You'll probably have to wait quite a while to get a completed API with decent browser support.
you need a library that provides key-value observing and bindings.
ember-metal is one such library.
basically you create objects, and you can register observers on properties of those objects.
var obj = Em.Object.create({
val: null
valDidChange:function(){...}.observes('val')
});
valDidChange will fire whenever val property changes, so
obj.set('val', 'newValue');
will cause the observer to fire.
What about something like this? Here's a jsfiddle.
var objectManager = function(obj, setCallback){
this.obj = obj;
this.setCallback = setCallback;
};
objectManager.prototype.setProperty = function(prop, value){
this.obj[prop] = value;
this.setCallback(prop);
};
objectManager.prototype.getObj = function(){
return this.obj;
};
// USAGE:
var testMgr = new objectManager({}, function(prop){
console.log(name + ' has been set.');
});
testMgr.setProperty("hello", "world"); //should log "hello has been set.";
I'm attempting to create a function lookup in Javascript essentially mapping a data type to a function that does something for that data type. Right now I have something similar to:
var Namespace = Namespace || {};
Namespace.MyObj = function () {
var stringFunc = function(someData) {
//Do some string stuff with someData
};
var intFunc = function(someData) {
//Do some int stuff with someData
};
var myLookUp = {
'string': stringFunc,
'int' : intFunc
};
return {
PublicMethod: function (dataType, someData) {
myLookUp[dataType](someData);
}
};
} ();
When I invoke Namespace.MyObj.PublicMethod(dataType, someData) I get an error that myLookUp is not defined. I'm assuming I'm not going about setting up the function lookup object correctly, but not sure how to do so. Thanks for any help.
The problem might simply be incorrect case
myLookup[dataType](someData);
should be (notice the capital U)
myLookUp[dataType](someData);
Just looked at my post after I wrote it up, stupid oversight, I'm declaring the properties as strings, instead of just properties.
....
var myLookUp = {
string: stringFunc,
int: intFunc
};
....
Fixes the issue.
Some additional follow up, in my actual code dataType is the result of a jQuery select. Don't know why or if this would be browser dependant (I'm using FireFox), but using double quotes around the property definition works, single quotes does not, and no quotes works as well. :-\
String.prototype.parse = function(f) {
alert(this.replace(f, ""));
};
var a = "Hello World";
parse.apply(a, ["Hello"]);
Is the code correct?
No, that’s not correct. The function is defined as String.prototype.parse, so it is not available as parse (in fact, parse is undefined).
You could run it like the following:
String.prototype.parse.apply(a, ["Hello"]);
But actually, the reason why you add the function to the prototype of String is that you extend String objects with that function. So you actually should just run the function like this:
a.parse("Hello");
edit:
Oh, and in response to your question title “Why does this function return as undefined?”: The function doesn’t return anything, because you don’t tell the function to return anything. You could for example define it like this to return the replaced string (instead of alerting it):
String.prototype.parse = function(f) {
return this.replace(f, "");
};
And then you could alert the return value of the function:
alert(a.parse("Hello"));
There is no such variable parse defined in your code sample. If you really want to apply the function later on, you should do this:
// Capture function as a local variable first
var parse = function(f) { alert(this.replace(f, "")); };
String.prototype.parse = parse;
I am new to Javascript. I am trying to understand where "this" is bound to using different examples. I am using console.log to print some values as shown below.
function FuncObject(value) {
this.answer = value;
this.get_answer = function () {
return this.answer;
}
};
var f = new FuncObject(42);
var fanswer = f.get_answer;
console.log(fanswer())
console.log prints "function" instead of "undefined". document.writeln seems to print "undefined" which is the right one because this is bound to the window object which does not have answer. Now printing function confuses me. Now I am wondering what i should be using for logging. I am unable to find an explanation for this.
thanks mohan
Just incase you didn't notice, there's a typo in your posted code of
this.get_answer = funcition ()
With that in mind, I'm not entirely sure of your experience level so let me cover all the bases.
function FuncObject(value) {
this.answer = value;
this.get_answer = function () {
return this.answer;
}
};
var f = new FuncObject(42);
var fanswer = f.get_answer;
console.log(fanswer())
You're setting fanswer = f.get_answer where f.get_answer is a function, so as such it sets fanswer to the function equivalent of this.get_answer.
If you want the return value of f.get_answer you need to call f.get_answer(), which returns 42.
With what you put, console.log(fanswer()) does print undefined as expected.
If you simply do console.log(fanswer) it records it as function, also as expected.
I'm not sure why you would receive function as you stated in your question, because I definitely do not, jsbin.