I would like to do the following.I have a code like this:
var obj = {
method : function() {}
};
var func = function() {
return method(); //method is undefined here
};
func(); // What to do here?
Is it possible to call func in a way that it will see the method inside from obj as it was given for example as a parameter. I want to use obj.method inside func, without writing 'obj.' before and without modifying func itself. Is there any hack possible to achieve this?
In other words, is it possible to force obj as a closure into func?
I tried:
with(obj) {
func();
}
But it doesn't work. Anyone, any ideas? Or is it the only option to get the body of the function as string, put 'with(obj)' inside it and then create a new function out of it?
Clarification:
Because this code will be in a helper class 'eval' is OK. Which I don't want is the modification of the function through .toString(), because browsers implement it differently.
This is a solution, using eval (MDN):
var obj = {
method : function() {console.log("it workes!");}
};
var func = function() {
return method(); //method is undefined here
};
var newfunc = (function (obj, func) {
var method = obj.method;
eval("var f = " + func.toString());
return f;
}(obj, func));
newfunc(); //it workes
Basically you're just creating a new scope with a local variable called method and re-evaluating the function body in this scope. So you're basically creating a new function with the same body. I don't really like this approach and I wouldn't recommend it, but considering your constraints, it might be the only solution.
And yes, it still requires you to write obj.method, but not inside of func. So I figured, it should be ok.
EDIT
So here is a version, in which you don't have to specify the property name manually:
var newfunc = (function (__obj__, __func__) {
for (var __key__ in __obj__) {
if (__obj__.hasOwnProperty(__key__)) {
eval("var " + __key__ + " = " + __obj__[__key__]);
}
}
eval("var __f__ = " + func.toString());
return __f__;
}(obj, func));
This also done by using eval().
Note that I changed all remaining local variables to a names containing underscores, to minimize the probability of name collisions with properties inside obj.
Note also that not all valid property names are valid variable names. You could have an object like this:
var obj = {
"my func": function () {}
}
But if you would use this object you would generate a syntax error with the above method, because it would try to evaluate:
var my func = ...
As apsillers said in the comment section, it gets even worse if you don't have control over the properties of obj. In this case you shouldn't use eval at all, because you would make cross-site scripting attacks very easy (example from apsillers):
var obj = {
"a; alert('xss'); var b": function () {}
}
would evaluate to 3 different statements:
var a;
alert('xss');
var b = function () {};
This is not possible unless you define method separately:
var obj = {
method : function() {}
},
method = obj.method;
// rest of code
This is because the method reference inside func() assumes the window. namespace; thus, without modifying func() itself, it can't be done sanely.
More clarified version based on basilikum's answer, and I've found a simplification with 'with':
var obj = {
method : function() { return "it workes!"; }
};
var func = function() {
return method(); //method is undefined here
};
(function (obj, func) {
with(obj) {
eval("var __res__ = (" + func.toString() + ")()");
}
return __res__;
}(obj, func));
>> "It workes!"
Related
I need the current function name as a string to log to our log facility. But arguments.callee.name only works in loose mode. How to get the function name under "use strict"?
For logging/debugging purposes, you can create a new Error object in the logger and inspect its .stack property, e.g.
function logIt(message) {
var stack = new Error().stack,
caller = stack.split('\n')[2].trim();
console.log(caller + ":" + message);
}
function a(b) {
b()
}
a(function xyz() {
logIt('hello');
});
You can bind function as its context then you can access its name via this.nameproperty:
function x(){
console.log(this.name);
}
x.bind(x)();
After little research here is a good solution :
function getFnName(fn) {
var f = typeof fn == 'function';
var s = f && ((fn.name && ['', fn.name]) || fn.toString().match(/function ([^\(]+)/));
return (!f && 'not a function') || (s && s[1] || 'anonymous');
}
function test(){
console.log(getFnName(this));
}
test = test.bind(test);
test(); // 'test'
Source : https://gist.github.com/dfkaye/6384439
Building on #georg solution, this one returns just the function name. Note though that it may fail if called from an anonymous function
function getFncName() {
const stackLine = (new Error())!.stack!.split('\n')[2].trim()
const fncName = stackLine.match(/at Object.([^ ]+)/)?.[1]
return fncName
}
function Foo() {
console.log(getFncName()) // prints 'Foo'
}
A simple solution to dynamically retrieve function names [like magic variables] is the use of scoped variables, and the Function.name property.
{
function foo() {
alert (a.name);
}; let a = foo
}
{
function foo2() {
alert(a.name)
}; let a = foo2
};
foo();//logs foo
foo2();//logs foo2
Note: Nested functions cease to be source elements, and are hence not hoisted. Also, this technique cannot work with anonymous functions.
If (like me) you want to define this elsewhere and call it generically, you can store the code as a string somewhere global or import it, then eval() it wherever to access the current function name. (Using eval keeps the context at the point of invocation.)
There's gotta be a way to do this without using a string, but whatever.
SomeObject.whatFunc =
'const s = new Error().stack;' +
"const stackLine = new Error().stack.split('\\n')[2].trim();" +
'const fncName = stackLine.match(/(?<=at )(.*)(?= \\()/gm)[0];' +
'console.log(fncName);'
// Whereever you want the func name
function countBananas('weeee') {
eval(SomeObject.whatFunc)
// blah blah blah
}
countBananas() // logs 'countBananas'
just an update to get the full name :
function logIt(message) {
var stack = new Error().stack,
// update is on this line
caller = stack.split('\n')[2].trim().split(/\s+/)[1];
console.log(caller.trim().split(/\s+/)[1];);
}
function a(b) {
b()
}
a(function xyz() {
logIt('hello');
});
var foo = (function(){
var x = "bar";
return function(){
console.log(x);
};
})();
console.log(foo.toString()); // function() {console.log(x);}
(foo)(); // 'bar'
eval('(' + foo.toString()+')()')); // error: x is undefined
Is there a technique for resolving (modifying) a function, so references from outer scope become local references, like:
function() {console.log(x);}
becomes:
function() {console.log("bar");}
The function can now be stringified and transported across a network and executed in another runtime.
Maybe one could parse the function to an Abstract Syntax Tree and then modify it? The reference will always be out of scope (not available), right?
The objective:
I am serializing a filter function from a node runtime to a postgresql plv8 runtime. Right now the filter function has interface: dbClient.filter((row, age) => row.age > age), ageFromOuterScope).then(matches => ...)
I want interface dbClient.filter((row) => row.age > age)).then(matches => ...), where age is a reference from outer scope.
Update:
I can only imagine one solution. Analyze the function, detect references to variables outside the function, and then rewrite the original function:
function(row) {
return row.age > age
}
To:
function(row, age) {
return row.age > age
}
Detected variables should also be added to a string that represent an array, like:
var arrayString = '[age]'
And then eval the string:
var functionArgs = eval(arrayString)
And finally:
dbClient.filter(modifiedFunction, ...functionArgs).then(matches => ...)
To expose the private variable outside the scope you need another function within the scope that rewrites the method description returned by toString. Then you use that function instead of toString to retrieve the method description.
var foo = (function(){
var x = "bar";
var f = function(){
console.log(x);
};
f.decl = function() {
return f.toString().replace("(x)", "(\""+x+"\")");
}
return f;
})();
console.log(foo.decl()); // function() {console.log("bar");}
eval("("+foo.decl()+")()"); // bar
I ran your top codebox's foo through Google's Closure Compiler, and it gave me this:
var foo=function(){return function(){console.log("bar")}}();foo;
not EXACTLY what you want, but you can get what you want from there using eval() and/or toString() as you're been tinkering with already.
I don't know how robust this is, and it makes other code mangles, but for the simple kind of functions you show, it does seem to consistently inline non-repeated primitives appearing in code.
You can bind the x to the function object itself.
var foo = (function(){
var x = "bar";
return function(){
this.x = x;
console.log(this.x);
};
})();
(foo)() // 'bar'
console.log(foo.toString()); // 'function() { this.x = x; console.log(this.x) }'
eval('(' + foo.toString()+')()'); // 'bar'
I have a text string that is correctly formatted as a function and I would like to convert it to an actual function. The text string looks like:
function object_123(){
object_123_Action();
my_clicked(obj);
}
Apart from the word "function" and {}; all other text is dynamically constructed. i.e. It may never contain object_123_Action(), however, it will be something similar, basic function calls. The only issue will be the obj will need to be the object that the function is assigned to.
Basically, I need:
this.func = eval(func_txt); //to actually work.
Where func_txt is:
function object_123(){
object_123_Action();
my_clicked(this);
}
Your string contains a function declaration.
When evaluated, it creates a object_123 variable in the scope of eval (which you can then call later) and returns nothing.
If you want to assign the function to this.func then you need to convert the declaration into a function expression. You can do that by wrapping it in parens.
this.func = eval("(" + func_txt ")" );
var func_txt = "function object_123(){\
object_123_Action();\
my_clicked(obj);\
}";
this.func = eval("(" + func_txt + ")");
var obj = "...";
function object_123_Action() {
alert(1);
}
function my_clicked() {
alert(2);
}
this.func();
You can store your functions in an array on the object.
Then, loop though the functions in another function and execute them.
var myObj = { 'myfunctions': [ ] };
and to add functions:
myObj.myFunctions.push (function () { /*function code here*/ });
Or if you already have a named function:
myObj.myFunctions.push (nameOfFunction);
And to call all the functions, use this function (don't add this function to myObj)
function executeMyFunctions (myObj) {
for (var i = 0; i < myObj.myFunctions.length; i++) {
myObj.myFunctions[i]();
}
}
And if it's possible to avoid using eval, you should.
Try this:
function parseStringToFunction(func) {
func = func || '(function(){return null;})';
return (new Function('return ' + func)());
};
var stringifyFunction = '(function(a, b) { return a+b; })';
var callStringifyFunction = parseStringToFunction(stringifyFunction)(1, 2);
alert(callStringifyFunction); // results is 3
Also read this out: detail about eval() and new Function()
Try this-
$("<script type='text/javascript'></script>").html(func_txt).insertAfter($("script"));
This works:
assert( eval("(function(a, b) { return a+b; })")(1, 2) == 3 )
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#eval_as_a_string_defining_function_requires_(and)_as_prefix_and_suffix
Simply wrap the function in parens.
Basically I want to do this:
someFunction() // do something
someFunction.somePropertyFunction()
someFunction() // Now someFunction is modified; it should now exhibit a different behaviour
Is this possible?
EDIT:
I'm not looking for what #Kolink was suggesting. Basically I want to augment a function's functionality by calling one of it's property function.
Specifically, I need to: 1. have access to the original function inside my property function (which is entirely doable using this), and 2. bind a new function to the original function's name (which I'm not sure if it's possible).
Just to be clear, I don't have access to the internal definition of the function that I want to augment. I want to attach a function to Function.prototype (so that it will be available as a property of the function that I want to augment), and then I will call func.augmentThis(), and then func should be augmented. But I'm not sure how, hence the question :P
Easily. Here's an example:
var derp = 123;
someFunction = function() {alert(derp);};
someFunction.somePropertyFunction = function() {derp = 456;};
someFunction(); // alerts 123
someFunction.somePropertyFunction();
someFunction(); // alerts 456
Okay, that's an oversimplified example, but yeah, it's entirely possible.
If your question is whether a function attached as a property to another function has a way to access the function to which it is attached, the answer is no. After all, the same function could be attached to any number of functions of objects.
So one alternative is to explicitly refer to the "mother" function within the function that is attached to it and intended to change its behavior:
function f (n) { alert (n + f.offset); }
f.offset = 0;
f.change_offset = function (i) { f.offset = i; };
f (1); //1
f.change_offset (100);
f (1); //101
Here, f is hard-wired into the definition of change_offset. If this bothers you, or you want something slightly more general, write a little routine to set a function as a property on another function, while binding its this to the function being attached to:
function set_func_as_func_prop ( propname, func_to_set, func_to_set_on ) {
func_to_set_on[propname] = func_to_set.bind(func_to_set_on);
}
Now you can write the function more generally
function change_offset (i) {
this.offset = i;
}
and set it on f or any other function.
set_func_as_func_prop ("change_offset", change_offset, f);
set_func_as_func_prop ("change_offset", change_offset, g);
Sort of:
function someFunction() {
return realFunction.apply(this, arguments);
}
function someFunctionA(name) {
return 'Hello, ' + name + '!';
}
function someFunctionB(name) {
return 'Goodbye, ' + name + '...';
}
var realFunction = someFunctionA;
someFunction.somePropertyFunction = function () {
realFunction = someFunctionB;
};
Sure it's possible. It's not recommended, but it's possible. For example:
function a() {
alert("a");
}
function b() {
alert("b");
}
function c() {
return c.f.apply(this, arguments);
}
c.f = a;
c.toggle = function () {
c.f = c.f === a ? b : a;
};
Now let's test it:
c(); // alerts "a"
c.toggle();
c(); // alerts "b"
See the demo: http://jsfiddle.net/LwKM3/
I want to attach a function to Function.prototype. Then I need to bind a new function to the original function's name (which I'm not sure if it's possible).
That indeed is impossible, you don't know what refers to the function. And you cannot change the internal representation of a function, which is immutable.
The only thing you can do is to create a new function and return that, to let the caller of your method use it somehow - specifically assigning it to the original variable:
somefunction = somefunction.augmentSomehow();
Your method for that will look like this:
Function.prototype.augmentSomehow = function() {
var origFn = this;
return function() {
// in here, do something special
// which might include invoking origFn() in a different way
};
};
Not sure if this helps, but I would implement described problem in following way:
// defined by somebody else - unknown to developer
var someFunction = function() {
alert("this is initial behavior");
}
someFunction(); // returns "this is initial behavior"
// defines parent object on which someFunction() is called
var parentObject = this; // returns window object (as called direclty in the
// browser)
// if you are calling someFunction from some object (object.someFunction())
// it would be:
// var parentObject = object;
// augumentThis definition
someFunction.augumentThis = function() {
var newFunction = function() {
alert("this is changed behavior");
};
parentObject.someFunction.somePropertyFunction = function() {
parentObject.someFunction = newFunction;
parentObject.someFunction();
};
};
someFunction.augumentThis(); // change function behavior
someFunction(); // "this is initial behavior"
someFunction.somePropertyFunction(); // "this is changed behavior"
someFunction(); // "this is changed behavior"
In some Javascript code (node.js specifically), I need to call a function with an unknown set of arguments without changing the context. For example:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(this, args);
}
The problem in the above is that when I call apply, I'm change the context by passing this as the first argument. I'd like to pass args to the function being called without changing the context of the function being called. I essentially want to do this:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(<otherFn's original context>, args);
}
Edit: Adding more detail regarding my specific question. I am creating a Client class that contains a socket (socket.io) object among other info pertaining to a connection. I am exposing the socket's event listeners via the client object itself.
class Client
constructor: (socket) ->
#socket = socket
#avatar = socket.handshake.avatar
#listeners = {}
addListener: (name, handler) ->
#listeners[name] ||= {}
#listeners[name][handler.clientListenerId] = wrapper = =>
# append client object as the first argument before passing to handler
args = Array.prototype.slice.call(arguments)
args.unshift(this)
handler.apply(this, args) # <---- HANDLER'S CONTEXT IS CHANGING HERE :(
#socket.addListener(name, wrapper)
removeListener: (name, handler) ->
try
obj = #listeners[name]
#socket.removeListener(obj[handler.clientListenerId])
delete obj[handler.clientListenerId]
Note that clientListenerId is a custom unique identifier property that is essentially the same as the answer found here.
If I understand you correctly:
changes context
| n | y |
accepts array n | func() | func.call() |
of arguments y | ???????? | func.apply() |
PHP has a function for this, call_user_func_array. Unfortunately, JavaScript is lacking in this regard. It looks like you simulate this behavior using eval().
Function.prototype.invoke = function(args) {
var i, code = 'this(';
for (i=0; i<args.length; i++) {
if (i) { code += ',' }
code += 'args[' + i + ']';
}
eval(code + ');');
}
Yes, I know. Nobody likes eval(). It's slow and dangerous. However, in this situation you probably don't have to worry about cross-site scripting, at least, as all variables are contained within the function. Really, it's too bad that JavaScript doesn't have a native function for this, but I suppose that it's for situations like this that we have eval.
Proof that it works:
function showArgs() {
for (x in arguments) {console.log(arguments[x]);}
}
showArgs.invoke(['foo',/bar/g]);
showArgs.invoke([window,[1,2,3]]);
Firefox console output:
--
[12:31:05.778] "foo"
[12:31:05.778] [object RegExp]
[12:31:05.778] [object Window]
[12:31:05.778] [object Array]
Simply put, just assign the this to what you want it to be, which is otherFn:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(otherFn, args);
}
'this' is a reference to your function's context. That's really the point.
If you mean to call it in the context of a different object like this:
otherObj.otherFn(args)
then simply substitute that object in for the context:
otherObj.otherFn.apply(otherObj, args);
That should be it.
If you bind the function to an object and you use everywhere the bound function, you can call apply with null, but still get the correct context
var Person = function(name){
this.name = name;
}
Person.prototype.printName = function(){
console.log("Name: " + this.name);
}
var bob = new Person("Bob");
bob.printName.apply(null); //window.name
bob.printName.bind(bob).apply(null); //"Bob"
One way that you can work around the change of context that can happen in JavaScript when functions are called, is to use methods that are part of the object's constructor if you need them to be able to operate in a context where this is not going to mean the parent object, by effectively creating a local private variable to store the original this identifier.
I concede that - like most discussions of scope in JavaScript - this is not entirely clear, so here is an example of how I have done this:
function CounterType()
{
var counter=1;
var self=this; // 'self' will now be visible to all
var incrementCount = function()
{
// it doesn't matter that 'this' has changed because 'self' now points to CounterType()
self.counter++;
};
}
function SecondaryType()
{
var myCounter = new CounterType();
console.log("First Counter : "+myCounter.counter); // 0
myCounter.incrementCount.apply(this);
console.log("Second Counter: "+myCounter.counter); // 1
}
These days you can use rest parameters:
function fn(...args) {
otherFn(...args);
}
The only downside is, if you want to use some specific params in fn, you have to extract it from args:
function fn(...args) {
let importantParam = args[2]; //third param
// ...
otherFn(...args);
}
Here's an example to try (ES next version to keep it short):
// a one-line "sum any number of arguments" function
const sum = (...args) => args.reduce((sum, value) => sum + value);
// a "proxy" function to test:
var pass = (...args) => sum(...args);
console.log(pass(1, 2, 15));
I'm not going to accept this as an answer, as I'm still hoping for something more suitable. But here's the approach I'm using right now based upon the feedback on this question so far.
For any class that will be calling Client.prototype.addListener or Client.prototype.removeListener, I did added the following code to their constructor:
class ExampleClass
constructor: ->
# ...
for name, fn of this
this[name] = fn.bind(this) if typeof(fn) == 'function'
message: (recipient, body) ->
# ...
broadcast: (body) ->
# ...
In the above example, message and broadcast will always be bound to the new ExampleClass prototype object when it's instantiated, allowing the addListener code in my original question to work.
I'm sure some of you are wondering why I didn't just do something like the following:
example = new ExampleClass
client.addListener('message', example.bind(example))
# ...
client.removeListener('message', example.bind(example))
The problem is that every time .bind( ) is called, it's a new object. So that means that the following is true:
example.bind(example) != example.bind(example)
As such, the removeListener would never work successfully, thus my binding the method once when the object is instantiated.
Since you seem to want to be using the bind function as it is defined in Javascript 1.8.5, and be able to retrieve the original this object you pass the bind function, I recommend redefining the Function.prototype.bind function:
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
/** here's the additional code **/
fBound.getContext = function() {
return oThis;
};
/**/
return fBound;
};
Now you can retrieve the original context that you called the bind function with:
function A() {
return this.foo+' '+this.bar;
}
var HelloWorld = A.bind({
foo: 'hello',
bar: 'world',
});
HelloWorld(); // returns "hello world";
HelloWorld.getContext(); // returns {foo:"hello", bar:"world"};
I was just reminded of this question after a long time. Looking back now, I think what I was really trying to accomplish here was something similar to how the React library works with its automatic binding.
Essentially, each function is a wrapped bound function being called:
function SomeClass() {
};
SomeClass.prototype.whoami = function () {
return this;
};
SomeClass.createInstance = function () {
var obj = new SomeClass();
for (var fn in obj) {
if (typeof obj[fn] == 'function') {
var original = obj[fn];
obj[fn] = function () {
return original.apply(obj, arguments);
};
}
}
return obj;
};
var instance = SomeClass.createInstance();
instance.whoami() == instance; // true
instance.whoami.apply(null) == instance; // true
Just push properties directly to the function's object and call it with it's own "context".
function otherFn() {
console.log(this.foo+' '+this.bar); // prints: "hello world" when called from rootFn()
}
otherFn.foo = 'hello';
otherFn.bar = 'world';
function rootFn() {
// by the way, unless you are removing or adding elements to 'arguments',
// just pass the arguments object directly instead of casting it to Array
otherFn.apply(otherFn, arguments);
}