How to detect chained function calls in JavaScript? - javascript

var Obj = {
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
// code which detects if there is a chained function???
}
}
Obj.func1().func2();
Is there a possible implementation for function _hasChainedFunc()? This function should return true on the first call (because func2() is called afterwards), false on the second call.
In a more advanced version, _hasChainedFunc() may also returned the function which is actually called afterwards.

Technically you can never know in advance whether there's another call chained after the current call -- this plainly doesn't make sense because it implies you're aware of some code that's gonna be called before it's called. You can't do this without a pre-compiler, which I guess is not what you're after.
Conversely, it is possible to check whether there's been a previous call chained before the current call. This just requires you to keep some state in the object regarding the previous calls, and update it whenever you call a new function on it. If you only use one chain of calls, you can do this by making func1 and func2 change some state on the this object before returning it.
If you want to call multiple chains on the same object, you face the problem of how to detect the end of a chain. For this you will need to make each chained function return a wrapper around the original this, which would store the state about the previous calls.
If you use the wrapper approach, obj.func1().func2() calls func1 on obj, but func2 is called on a wrapper returned from func1 and this wrapper could be aware of the previous func1 call. If you later call obj.func2().func1() then func2 is now called on obj whereas func1 is called on the wrapper which is aware of the previous func2 call, etc.

(NOTE: This answer was originally posted by Scholle as part of the question. I extracted it from the question into an actual answer, as it should have been in the first place. It is not my solution, so I have marked it as Community Wiki.)
Scholle ultimately created a library that does what he wanted.
It's available on GitHub, and some documentation is here.
In short: Take an arbitrary JavaScript function and "chainify" it:
var Model = function() {};
Model.prototype.func1 = function() {
console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func1 processing...');
this.c_next();
});
};
Model.prototype.func2 = function() {
console.log('func2 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func2 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func2 processing...');
this.c_next();
});
};
Chainify and instantiate it,
and call some functions:
chainify(Model);
var Obj = new Model();
Obj.func1().func2();
Console output:
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
func2 has 0 succeeding functions
func2 processing...
Of course, this is a simple example. It just demonstrates that every
functions is now capable to access information about what happens
before and after the current function call.

No, it's not possible.
It's semantically identically to:
var tmp = Obj.func1();
tmp.func2();
When Obj.func1() is called, there's no way for it to know whether the subsequent result will be used to call func2.
The best you could achieve is for func2 to detect whether func1 was previously called, but for it to work the way you've described would require func1 to be capable of predicting the future.

What you can do is add a member property indicating if it's the first call made on the object or not:
var Obj = {
_first : true,
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
var isFirst = this._first;
this._first = false;
return isFirst;
}
}
Obj.func1().func2();
However, this means you have to reset the state of the object before each call (by setting this._first back to true). You may want to rethink how you're going about this.

here's how i would do this:
var Obj = {
first:0, //<--- will store whether it's the first call
func1 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called1");
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called2");
}
return this;
},
_hasChainedFunc : function() {
return (this.first++ > 0);
}
}
Obj.func1().func2();
and this seems to work:
called2
http://jsfiddle.net/2VThj/1/

Why would you want to do this?
That question aside, you could, rather than returning the actual object, make a clone of it, and add an attribute to tell you it is a returned version of the object. That is the only way I can think of. Sounds complex though, depending on how complex this object is.
Something like:
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return deepCloneWithFlag(this);
},
_hasChainedFunc : function() {
return this.flag;
}

Nope. this won't work. you could possibly tell that func1() had at some point been called on this object, but you cannot tell WHEN it was called, i.e. right before func2
for example this:
obj.func1();
obj.func2();
is equivalent to your example call. And there is no way func1 could know that func2 will be called in the future.
I solved a problem similar to this with chain functions (docs) This allows true function chaining with the ability to "look-ahead" to see what's coming in the chain.

What you could do is have two separate classes, one for the first element in the chain and one for the remaining elements. Then all you would have to do is change the first class to return an equivalent object from the second class instead of the current object.
var Class1 = function(state){
return {
func1 : function() {
// some code
// block should be CALLED
return Class2(state)
},
func2 : function() {
// some code
// block should be NOT called
return Class2(state)
}
};
}
var Class2 = function(state){
return {
func1 : function() {
// some code
return this;
},
func2 : function() {
// some code
return this;
}
};
}
Class1(initial_state).func1().func2();

Althought knowing that a function will be called after another function is impossible in Javascript, here is a solution to chainify an object :
(function(window, undefined)
{
var chainify = function(prop)
{
return new chainify.init(prop);
};
/**
*
* #param prop :
* Properties to apply to the object
* #returns {chainify.init}
*/
chainify.init = function(prop)
{
for ( var key in prop)
this[key] = prop[key];
};
chainify.init.prototype = {
_attributes : {},
_chain_in_progress : false,
_chain_level : 1,
_chain_function : '',
/**
* Returns the chained object
*
* #param name -
* name of the previous function
* #this chainify.init
* #returns {chainify.init}
*/
_chain : function(name)
{
var tmp = chainify(this);
tmp._chain_in_progress = true;
tmp._chain_function = name || '';
_chain_level++;
return tmp;
},
get : function(key)
{
return this._attributes[key];
},
set : function(key, value)
{
this._attributes[key] = value;
return this;
},
attr : function(prop)
{
for ( var key in prop)
this._attributes[key] = prop[key];
return this;
},
};
// Make global
window.chainify = chainify;
})(window);
var myObject = window.chainify({
// f1() function is using _chain()
f1 : function(s)
{
// Do something
this.set('s1', s);
if (this._chain_in_progress) alert('f1 after ' + this._chain_function);
// return the chain by calling this._chain()
return this._chain('f1');
},
// f2() function is using _chain()
f2 : function(s)
{
this.set('s2', s);
if (this._chain_in_progress) alert('f2 after ' + this._chain_function);
return this._chain('f1');
},
// that() function is not using _chain(), but we return this so the chaining
// is not broken
that : function(s)
{
// Do something
return this;
}
});
// Check if the f1 function is working
myObject.f1('a'); // Set s1 to "a"
alert(myObject.get('s1')); // should be "a"
// check if the f2 chaining is working
myObject.f1('b').f1('c'); // f1 after f1
alert(myObject.get('s1')); // should be "c" -> changed on last f1 function
// Check if the f2 function is working
myObject.f2('a');
alert(myObject.get('s2')); // should be "a"
// check if the f2 and f1 chaining is working
myObject.f2('b').f1('c').f1('d').f2('e'); // f1 after f2, f1 after f1 ...
alert(myObject.get('s1')); // should be "d" -> changed on last f1 function
alert(myObject.get('s2')); // should be "e" -> changed last f2 function
// check the chain with that() -
myObject.that('b').f1('a').f1('z'); // f1 chained after f1
alert(myObject.get('s1')); // should be "z" -> changed on last f1 function

Related

Anything in JavaScript that recognizes the end of a chain? [duplicate]

var Obj = {
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
// code which detects if there is a chained function???
}
}
Obj.func1().func2();
Is there a possible implementation for function _hasChainedFunc()? This function should return true on the first call (because func2() is called afterwards), false on the second call.
In a more advanced version, _hasChainedFunc() may also returned the function which is actually called afterwards.
Technically you can never know in advance whether there's another call chained after the current call -- this plainly doesn't make sense because it implies you're aware of some code that's gonna be called before it's called. You can't do this without a pre-compiler, which I guess is not what you're after.
Conversely, it is possible to check whether there's been a previous call chained before the current call. This just requires you to keep some state in the object regarding the previous calls, and update it whenever you call a new function on it. If you only use one chain of calls, you can do this by making func1 and func2 change some state on the this object before returning it.
If you want to call multiple chains on the same object, you face the problem of how to detect the end of a chain. For this you will need to make each chained function return a wrapper around the original this, which would store the state about the previous calls.
If you use the wrapper approach, obj.func1().func2() calls func1 on obj, but func2 is called on a wrapper returned from func1 and this wrapper could be aware of the previous func1 call. If you later call obj.func2().func1() then func2 is now called on obj whereas func1 is called on the wrapper which is aware of the previous func2 call, etc.
(NOTE: This answer was originally posted by Scholle as part of the question. I extracted it from the question into an actual answer, as it should have been in the first place. It is not my solution, so I have marked it as Community Wiki.)
Scholle ultimately created a library that does what he wanted.
It's available on GitHub, and some documentation is here.
In short: Take an arbitrary JavaScript function and "chainify" it:
var Model = function() {};
Model.prototype.func1 = function() {
console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func1 processing...');
this.c_next();
});
};
Model.prototype.func2 = function() {
console.log('func2 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func2 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func2 processing...');
this.c_next();
});
};
Chainify and instantiate it,
and call some functions:
chainify(Model);
var Obj = new Model();
Obj.func1().func2();
Console output:
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
func2 has 0 succeeding functions
func2 processing...
Of course, this is a simple example. It just demonstrates that every
functions is now capable to access information about what happens
before and after the current function call.
No, it's not possible.
It's semantically identically to:
var tmp = Obj.func1();
tmp.func2();
When Obj.func1() is called, there's no way for it to know whether the subsequent result will be used to call func2.
The best you could achieve is for func2 to detect whether func1 was previously called, but for it to work the way you've described would require func1 to be capable of predicting the future.
What you can do is add a member property indicating if it's the first call made on the object or not:
var Obj = {
_first : true,
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
var isFirst = this._first;
this._first = false;
return isFirst;
}
}
Obj.func1().func2();
However, this means you have to reset the state of the object before each call (by setting this._first back to true). You may want to rethink how you're going about this.
here's how i would do this:
var Obj = {
first:0, //<--- will store whether it's the first call
func1 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called1");
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called2");
}
return this;
},
_hasChainedFunc : function() {
return (this.first++ > 0);
}
}
Obj.func1().func2();
and this seems to work:
called2
http://jsfiddle.net/2VThj/1/
Why would you want to do this?
That question aside, you could, rather than returning the actual object, make a clone of it, and add an attribute to tell you it is a returned version of the object. That is the only way I can think of. Sounds complex though, depending on how complex this object is.
Something like:
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return deepCloneWithFlag(this);
},
_hasChainedFunc : function() {
return this.flag;
}
Nope. this won't work. you could possibly tell that func1() had at some point been called on this object, but you cannot tell WHEN it was called, i.e. right before func2
for example this:
obj.func1();
obj.func2();
is equivalent to your example call. And there is no way func1 could know that func2 will be called in the future.
I solved a problem similar to this with chain functions (docs) This allows true function chaining with the ability to "look-ahead" to see what's coming in the chain.
What you could do is have two separate classes, one for the first element in the chain and one for the remaining elements. Then all you would have to do is change the first class to return an equivalent object from the second class instead of the current object.
var Class1 = function(state){
return {
func1 : function() {
// some code
// block should be CALLED
return Class2(state)
},
func2 : function() {
// some code
// block should be NOT called
return Class2(state)
}
};
}
var Class2 = function(state){
return {
func1 : function() {
// some code
return this;
},
func2 : function() {
// some code
return this;
}
};
}
Class1(initial_state).func1().func2();
Althought knowing that a function will be called after another function is impossible in Javascript, here is a solution to chainify an object :
(function(window, undefined)
{
var chainify = function(prop)
{
return new chainify.init(prop);
};
/**
*
* #param prop :
* Properties to apply to the object
* #returns {chainify.init}
*/
chainify.init = function(prop)
{
for ( var key in prop)
this[key] = prop[key];
};
chainify.init.prototype = {
_attributes : {},
_chain_in_progress : false,
_chain_level : 1,
_chain_function : '',
/**
* Returns the chained object
*
* #param name -
* name of the previous function
* #this chainify.init
* #returns {chainify.init}
*/
_chain : function(name)
{
var tmp = chainify(this);
tmp._chain_in_progress = true;
tmp._chain_function = name || '';
_chain_level++;
return tmp;
},
get : function(key)
{
return this._attributes[key];
},
set : function(key, value)
{
this._attributes[key] = value;
return this;
},
attr : function(prop)
{
for ( var key in prop)
this._attributes[key] = prop[key];
return this;
},
};
// Make global
window.chainify = chainify;
})(window);
var myObject = window.chainify({
// f1() function is using _chain()
f1 : function(s)
{
// Do something
this.set('s1', s);
if (this._chain_in_progress) alert('f1 after ' + this._chain_function);
// return the chain by calling this._chain()
return this._chain('f1');
},
// f2() function is using _chain()
f2 : function(s)
{
this.set('s2', s);
if (this._chain_in_progress) alert('f2 after ' + this._chain_function);
return this._chain('f1');
},
// that() function is not using _chain(), but we return this so the chaining
// is not broken
that : function(s)
{
// Do something
return this;
}
});
// Check if the f1 function is working
myObject.f1('a'); // Set s1 to "a"
alert(myObject.get('s1')); // should be "a"
// check if the f2 chaining is working
myObject.f1('b').f1('c'); // f1 after f1
alert(myObject.get('s1')); // should be "c" -> changed on last f1 function
// Check if the f2 function is working
myObject.f2('a');
alert(myObject.get('s2')); // should be "a"
// check if the f2 and f1 chaining is working
myObject.f2('b').f1('c').f1('d').f2('e'); // f1 after f2, f1 after f1 ...
alert(myObject.get('s1')); // should be "d" -> changed on last f1 function
alert(myObject.get('s2')); // should be "e" -> changed last f2 function
// check the chain with that() -
myObject.that('b').f1('a').f1('z'); // f1 chained after f1
alert(myObject.get('s1')); // should be "z" -> changed on last f1 function

How to get caller function class name

I tried some thing following, it fullfils 80% of the need though not enough to use.
// Different file
function A(){}
A.prototype.ao = function(){
// Here I want ot know the fucntion caller class name, for that I used
arguments.callee.caller.prototype; // It retuens B.bo {} (object)
}
// Different file
function B(){}
B.prototype.bo = function(){
var a = new A();
a.ao();
}
How to retrieve the class name from caller prototype, as it is of type object.
I would be more helpful, if we able to get the caller function context.
How to retrive the class name from caller prototype
You can't*, for a couple of reasons:
Functions don't have a name property. They will in ES6, but they don't yet. (You could assign a property to B with the name, though.)
Although you can get a reference to the function that's on B.prototype.bo, there's no link from there back to B. The linkage is one-way. (Not least because the same function could be on multiple objects.)
Note that using arguments.callee is strongly discouraged, and disallowed in strict mode, and using caller is especially discouraged. There's almost always a better way to do what you're trying to do.
* On some engines, you might be able to figure it out from the call stack, e.g.:
A.prototype.ao = function(){
try {
throw new Error();
}
catch (e) {
// Examine e.stack here
}
};
You'd be relying on platform-specific naming and such.
But again, there's almost certainly a better option than trying to know who's calling you.
Re your comment:
Here my requirement is to track an API function usage, in order to achieve it I am following this way...And further I can't change the existed framework.
You probably can but don't realize it, because JavaScript is that powerful. :-)
For instance: Once whatever framework creates B is loaded on the page, here's code that will wrap every function on B.prototype with a version that tells you it's running:
function wrapFunctions(name, proto) {
Object.keys(proto).forEach(function(key) {
var original = proto[key];
if (typeof original === "function") {
proto[key] = function() {
var rv;
starting(name, key); // <=== Your function to tracking the start of a call
rv = original.apply(this, arguments);
stopping(name, key); // <=== Your function tracking the end of the call
return rv;
};
}
});
}
wrapFunctions("B", B.prototype);
That's the beginning of "instrumentation." But note that there are proper libraries out there for this that have solved the edge cases and such.
Live Example:
// The framework
function B() {}
B.prototype.f1 = function() {
snippet.log("Original functionality for f1");
};
B.prototype.f2 = function() {
snippet.log("Original functionality for f2");
};
B.prototype.f3 = function() {
snippet.log("Original functionality for f3 -- calling f2");
this.f2();
snippet.log("Original functionality for f3 -- done calling f2");
};
// Let's use f1 and f2 before we wrap them
snippet.log("Before wrapping:");
var b1 = new B();
b1.f1();
b1.f2();
b1.f3();
// Now your code runs and wraps them
wrapFunctions("B", B.prototype);
// Now let's use f1 and f2 again
snippet.log("After wrapping:");
var b2 = new B();
b2.f1();
b2.f2();
b1.f3();
// Our function to track that a call started
function starting(ctor, name) {
snippet.log(ctor + "#" + name + ": Started");
}
// Our function to track that a call stopped
function stopping(ctor, name) {
snippet.log(ctor + "#" + name + ": Stopped");
}
// Our function to wrap things
function wrapFunctions(name, proto) {
Object.keys(proto).forEach(function(key) {
var original = proto[key];
if (typeof original === "function") {
proto[key] = function() {
var rv;
starting(name, key); // <=== Your function to tracking the start of a call
rv = original.apply(this, arguments);
stopping(name, key); // <=== Your function tracking the end of the call
return rv;
};
}
});
}
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
You can include a method in the object which returns its type. Another option is to use instanceof and check for all possible types.
function A() {
this.getType = function () {
return "A";
}
}
A.prototype.ao = function() {
// Here I want ot know the fucntion caller class name, for that I used
var type = this.getType();
args.callee.caller.prototype; // It retuens B.bo {} (object)
}
function B(){
this.getType = function () {
return "B";
}
}
B.prototype.bo = function(){
var a = new A();
a.ao.apply(this);
}
Here you will also have to redefine getType in all of your inherited types and return the correct type. You also need to invoke the desired method with apply(this) to provide the context of the current caller.

Javascript classes and variable references

I'm trying to solve this puzzle minded Javascript OOP problem.
So I have the following class :
var ClassA = function() {
this.initialize();
}
ClassA.prototype = {
methods : ['alpha','beta','gama'],
initialize : function() {
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
}
}
var a = new ClassA();
When I call every method I expect to print the name of it, right? But here is what i get :
a.alpha(); // returns gama ?!?
a.beta(); // returns gama ?!?
a.gama(); // returns gama
But when my class looks like this :
var ClassB = function() {
this.initialize();
}
ClassB.prototype = {
methods : ['alpha', 'beta', 'gama'],
initialize: function() {
for ( var i in this.methods ) {
this.addMethod(this.methods[i]);
}
},
addMethod: function(method) {
this[method] = function() {
console.log(method);
}
}
}
var b = new ClassB();
b.alpha(); // returns alpha
b.beta(); // returns beta
b.gama(); // returns gama
Why is this happening ?
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
Your problem lies here. When this loop ends, i is the last element. Each function uses the same i, so they are all the last element.
When you use addMethod you are making a closure to "capture" the correct value.
EDIT: When you call addMethod you are "copying" the value, instead of using the i value, which changes with each loop iteration.
In your first version:
initialize : function() {
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
}
The methods that you create within initialize all refer to the same i variable from initialize - and after initialize runs i has the value "gama", so regardless of which of the methods you call that's the value of i that they'll log to the console. JS doesn't store the current value of i at the time the method is created.
JS creates a "closure" for each function - variables declared in your initialize function (i.e., i) continue to be in scope for the nested function(s) even after initialize has finished.
The second version calls addMethod to add each method:
addMethod: function(method) {
this[method] = function() {
console.log(method);
}
}
...and so when they run they'll refer to their own "copy" of the method parameter because then there is a separate closure for each of the methods.
Edit: See also this question: How do JavaScript closures work? (several answers there explain this more clearly than I did).
You can fix your first example by adding an anonymous closure:
initialize : function() {
for ( var i in this.methods ) {
(function (i) { // anonymous closure
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}).call(this, i); // use .call() if you need "this" inside
}
}
Now it will work the same way as your second example. "Anonymous" means that the closure is made by function which doesn't have a name and is called instantly as it is "created".
Note sideways: use .call(this, ...) to preserve this inside the called function, or you can do var that = this, use that instead of this and call the function normally:
for ( var i in this.methods ) {
var that = this;
(function (i) { // anonymous closure
that[that.methods[i]] = function() {
console.log(that.methods[i]);
}
})(i); // Called normally so use "that" instead of "this"!
}
Well, first of all stop using for (property in object) loops on Arrays. It's all fun and games until somebody prototypes to the Array object which is both a perfectly reasonable and very useful/popular thing to do. This will result in custom methods getting added to your for x in array loops.
As for the problem, it's doing exactly what you told it to do in version 1. The problem is that by the time you get around to firing it, i is the last thing i was, 'gamma'. When you pass a reference into a function as an argument, the function holds on to the value's state as it was passed.

javascript OOP syntax, and object that is both a var and a function

This is related to a question I asked previously here: How to implement chained method calls like jQuery?
I have been using the method from the check answer from some time there, and it works well. But I would like to alter the syntax for my toolkit even further.
foo(firstarg).bar(secondarg); // should function as the question above.
foo(onlyarg).bar // a function with one argument
foo.bar(onlyarg); // should also work, when a first argument is not appropriate.
foo.bar; // a function without an argument, or returns a static value.
I would like all 4 syntaxs to work off the same foo object, but lack the OOP understanding to do so. I have tried a few things, and so far I can get 1 & 2 to work, and 3 & 4 to work, but not to all work together. It also would be nice if chaining remained an option by having each function return the root object.
Edit: I clearly need to be more specific, here is what I have now:
var main = function(obj){ this.obj = obj; };
var tool = function(obj){ return new main(obj); };
main.prototype = {
alertThisPlus : function(plus){
alert(this.obj + ' ' + plus);
},
alertJustThis : function(){
return alert(this.obj);
}
};
usage
tool('hello').alertThisPlus('world'); // returns alert('hello world')
tool().alertJustThis('hello world'); // returns alert('hello world');
what I would like is to do this:
tool('hello').alertThisPlus('world'); // returns alert('hello world') no change
tool.alertJustThis('hello world'); // returns alert('hello world') does not work
Functions are just objects so you can add functions to 'tool'. You can do this manually:
tool.foobar = function() {};
Or if your classes are structured suitably you could use a mix-in approach. Something like this:
function Tool(prefix) {
this.prefix = prefix;
}
Tool.prototype.alertThisPlus = function(suffix) {
alert((typeof this.prefix != 'undefined' ? this.prefix + ' ' : '') + suffix);
};
Tool.prototype.alertJustThis = function(msg) {
alert(msg);
};
function tool(prefix) {
return new Tool(prefix);
}
// Mix-in the methods from a static instance of Tool onto the 'tool' function.
// This makes both tool.alertThisPlus() and tool.alertJustThis() available,
// both will be called in the context of 'staticTool'.
(function() {
var staticTool = new Tool();
for (var o in staticTool) {
if (typeof staticTool[o] == 'function') {
tool[o] = staticTool[o].bind(staticTool);
}
}
})();
tool('hello').alertThisPlus('world'); // returns alert('hello world')
tool().alertJustThis('hello world'); // returns alert('hello world')
tool.alertJustThis('hello world'); // returns alert('hello world')
The only way I can think of that would allow you to execute a function without parenthesis is if you are a) passing it as a callback param, or b) having it self execute.
var foo = (function() {
alert('foo called');
})();
Taking that a step further, lets say you've use some closures to return values.
var foo = (function() {
return {
bar: "arf arf said the dog"
}
})();
Now you can use:
foo.bar;

Getting a 'this' reference to a 2nd level prototype function

I'm fairly certain this isn't possible, but wanted to see if anyone had some ingenious ideas as to how to make it possible.
I want the following code to work:
var x = new foo();
x.a.getThis() === x; // true
In other words, I want x.a.getThis to have a reference to this being x in this case. Make sense?
In order to get this to work one level deep is simple:
function foo(){}
foo.prototype.getThis = function(){ return this; }
var x = new foo();
x.getThis() === x; // true
One thing, I want this to work as a prototype, no "cheating" by manually binding to this:
function foo(){
this.a = {
getThis : (function(){ return this; }).bind(this)
};
}
Although the above is a perfect functional example of what I'm trying to achieve, I just don't want all the extra functions for each instance :)
FYI, the actual use case here is that I'm creating classes to represent Cassandra objects in node and I want to be able to reference a super-column --> column-family --> column via foo.a.b and keep a reference to foo in the deep function.
You can't do this without a forced bind of some kind. You say you don't want to "cheat" but this breaks the standard rules about what this is, so you have to cheat. But JS lets you cheat, so it's all good.
BTW, for what it's worth coffee script makes this so trivial.
foo = ->
#a = getThis: => this
The fat arrow => preserves the context of this for from the scope it was called in. This allows you to easily forward the context to another level.
That code gets compiled to this JS:
var foo;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
foo = function() {
return this.a = {
getThis: __bind(function() {
return this;
}, this)
};
};
Which basically just does what you say you do not want to do.
Or if the value doesn't have to this specifically, you can set the "owner" in the child object.
var A = function(owner) {
this.owner = owner;
};
A.prototype.getThis = function() {
return this.owner;
};
var Foo = function() {
this.a = new A(this);
};
var foo = new Foo();
if (foo.a.getThis() === foo) {
alert('Happy dance');
} else {
window.location = 'https://commons.lbl.gov/download/attachments/73468687/sadpanda.png';
}
http://jsfiddle.net/4GQPa/
And the coffee script version of that because I am a passionate and unreasonable zealot for it:
class A
constructor: (#owner) ->
getThis: -> #owner
class Foo
constructor: -> #a = new A(this)
foo = new Foo()
if foo.a.getThis() is foo
alert 'Happy Dance'
else
window.location = 'https://commons.lbl.gov/download/attachments/73468687/sadpanda.png'
Impossible to do reliably without binding the value at the start since the value of a function's this is set by the call. You can't know beforehand how it will be called, or which functions need a special or restricted call to "preserve" the this -> this relationship.
The function or caller's this may be any object, there may not be a this -> this at all. Consider:
var x = {
a : {
b: function() {return this;}
}
}
When you call x.a.b(), then b's this is a. But if you do:
var c = x.a.b;
c(); // *this* is the global object
or
x.a.b.call(someOtherObject);
What is the value of this -> this in these cases?
Answering my own question because someone else may find it useful. Not sure if I'll end up going with this or Squeegy's solution. The functions are only ever defined once and then the containing object is cloned and has parent = this injected into it:
function foo(){
var self = this, nest = this.__nestedObjects__ || [];
nest.forEach(function(prop){
self[prop] = extend({ parent : self }, self[prop]);
});
}
// bound like this so that they're immutable
Object.defineProperties(foo.prototype, {
bar : {
enumerable : true,
value : {
foobar : function(){
return this.parent;
},
foo : function(){},
bar : function(){}
}
},
__nestedObjects__ : { value : ['bar'] }
});
var fooInst = new foo();
console.log(fooInst.bar.foobar() == fooInst);
or based on Squeegy's solution:
function foo(){
for(var cls in this.__inherit__){
if(!this.__inherit__.hasOwnProperty(cls)){ continue; }
this[cls] = new (this.__inherit__[cls])(this);
}
}
var clsA;
// bound like this so that they're immutable
Object.defineProperties(foo.prototype, {
__inherit__ : { value : {
bar : clsA = function(parent){
Object.defineProperty(this, '__parent__', { value : parent });
}
}
}
});
clsA.prototype = {
foobar : function(){
return this.__parent__;
}
};
var fooInst = new foo();
console.log(fooInst.bar.foobar() == fooInst);

Categories