This is a general programming question, not specific to this code example.
How do I detect whether a particular method was called or not from within a function?
Imagine you want to be able to tell whether a particular method was called so that you can change what you return based on that.
var turtle = function() {
var vars = {
name: "Shelly",
age: 103
};
this.actions = function() {
var methods = {
crawl: function() {
//etc...
},
mutate: function() {
//etc...
},
eat: function() {
//etc...
}
};
if (methodWasCalled) { // <- The part I don't know how to do
return methods;
} else {
return this;
}
};
if (methodWasCalled) { // <- The part I don't know how to do
return vars;
} else {
return this;
}
};
Based on the above example, someone could use: turtle() and get access to the 'vars' object, or they could use: turtle().mutate() and the turtle object would execute the block inside that function.
To get the caller access the arguments object.
alert("caller is " + arguments.callee.caller.toString());
Callee refers to the function being executed, in your case mutate. It has a property that references the caller.
Best thing for you to do is break out a debugging kit (I personally prefer Chrome/IE) and explore the object to see the full details of info you want to see.
Related
I have this javascript object:
return {
AccDocs: {
query: function() {
...
},
deleteAndQuery: function() {
...
AccDocs.query(); //Error: AccDocs is not defined
}
}
}
But, it returns an error that says AccDocs is not defined.
How can I achieve something like this?
Variables and properties on objects are different things. You cannot access the property of an object without specifying which object you mean.
You can probably access it using the this keyword:
this.query();
Keeping in mind that the value of this will vary depending on how the function is called (when a.b.c.d.AccDocs.deleteAndQuery() is called, this inside deleteAndQuery will be AccDocs as it is the first object to the left of the last ., but if you were to first copy query to another variable and then call query(), pass it to setTimeout, or if you were to use call or apply then the value of this would change).
For more robustness (but less flexibility, since being able to change the context can be useful) you can store your object in a variable which you can access by name.
var AccDocs = {
query: function() {
...
},
deleteAndQuery: function() {
...
AccDocs.query();
}
};
return { AccDocs: AccDocs };
By using the this keyword:
return {
AccDocs: {
query: function() {
...
},
deleteAndQuery: function() {
...
this.query(); //Here
}
}
}
I am trying to fix a function I have here to be able to use it without using a that = this (or self = this as some like to use). It is a scoping issue but I am not sure how to get around it, and I would like to get in the habit of not using a that = this . So the functions are all in a return (angular factory) and I am having trouble referencing another function . Let me show you what I mean :
return {
loadStates: function() {
var that = this;
//chgeck if is loaded module, then change and fire callback
var currentModules = moduleHolder.getModules();
if (currentModules[name]) {
//works here
this.prepState();
} else {
//module cannot be found check for 5 seconds
$log.warn("Requesting " + name + "...");
var timeToCheck = true;
setTimeout(function() {
timeToCheck = false;
}, 5000);
var check = {
init: function() {
check.checkAgain();
},
checkAgain: function() {
if (timeToCheck) {
if (currentModules[name]) {
//but not here
that.prepState();
} else {
//still doesn't exists
setTimeout(check.checkAgain, 200);
}
} else {
//doesn't exist after 5 seconds
$log.error("Requested module (" + name + ") could not be found at this time.");
}
}
};
check.init();
}
},
prepState: function() {
}
}
So in the top if it finds the currentModule[name] I can use a this.prepState() and it works fine. However inside the timing functions I cannot use the this anything because it is inside a different scope so I have temporarily gotten around this by setting a that = this up top, however I would like see if I could not use this method. How does one get around this without using the that= this? Thanks!
It is a scoping issue...
No, it isn't. this and scope have essentially nothing to do with each other. (For now; ES6's arrow functions will change that.) It's an issue of how the functions are called.
If you pass a function reference to something that will call it later, unless the thing you're passing it to has a way you can use to tell it what to use for this when calling it, your function will get called with this not referring to what you want it to refer to.
You can get a new function reference that will call your original function with the correct this by using Function#bind:
var usesCorrectThis = originalFunction.bind(valueForThis);
So for example, suppose I have:
var check = {
name: "Fred",
sayHello: function() {
console.log("Hi, I'm " + this.name);
}
};
If I do:
check.sayHello();
All is good: Calling the function as part of an expression retrieving it from a property tells the JavaScript engine to use the object as this during the call.
However, if I do:
setTimeout(check.sayHello, 0);
...that doesn't work right, because when setTimeout calls the function, it doesn't use the right value for this.
So I can use Function#bind to address that:
setTimeout(check.sayHello.bind(check), 0);
More (on my blog):
Mythical methods
You must remember this
there are different ways you can do that.
One way is to use bind function.you can use
var checkInitBindFn = check.init.bind(this);
checkInitBindFn();
Secondly you can use call and apply also.
check.init.call(this);
check.init.apply(this);
Like this you can use this instead of that.
Check the complete api doc online...
It's not a scoping issue. If you want to avoid self = this you can always reference functions by objects. Makes cleaner code and since factories in angular are singletons you're not wasting memory.
angular.module('myApp').factory('myFactory', function ($timeout) {
var myFactory = {
loadState: function () {
$timeout(function () {
myFactory.check();
}, 500);
},
check: function () {
},
};
return myFactory;
});
Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...
So, I've started playing with using an object to help organize my functions. So instead of having to make super long function names, I can just have sub functions in an object with the same prefix.
In my example, I'm using 'get' as a prefix, so I could call subfunctions by doing get.function(); and get.otherfunction();. However, I want to also be able to set a "default" function for the get object, so I can just call get(); by itself and it runs a function (but I don't want that function to run if I'm calling one of the subfunctions).
Here is the code I have thus far:
var get = {
default: function() {
alert('default function');
},
secondary: function() {
alert('secondary function');
}
}
You want to make an ordinary function, then add other functions as properties:
var get = function() { ... };
get.secondary = function() { ... };
If you want to, you could also write
get.default = get;
Or
get.default = function() { return get(); };
var BigObject = (function() {
function deepCalculate(a, b, c) {
return a + b + c;
}
function calculate(x) {
deepCalculate(x, x, x);
}
return {
calculate: calculate,
api: {
deepCalculate: deepCalculate
}
}
})();
This is basic self executing function with private function I keep in api.
The problem I have is that now I can't overwrite deepCalculate from the outside of the function.
How is that a problem? I use Jasmine and want to test if function was called. For example:
spyOn(BigObject, 'calculate').andCallThrough();
expect(BigObject.api.deepCalculate).toHaveBeenCalled();
fails. However as I debug, I am sure that Jasmine binds BigObject.api.deepCalculate as a spy, however from the inside calculate still calls original deepCalculate function and not the spy.
I would like to know how can I overwrite the function and not just a reference for it.
The simple answer would be:
(function ()
{
var overWriteMe = function(foo)
{
return foo++;
},
overWrite = function(newFunc)
{
for (var p io returnVal)
{
if (returnVal[p] === overWriteMe)
{//update references
returnVal[p] = newFunc;
break;
}
}
overWriteMe = newFunc;//overwrite closure reference
},
returnVal = {
overWrite: overWrite,
myFunc: overWriteMe
};
}());
Though I must say that, I'd seriously think about alternative ways to acchieve whatever it is you're trying to do. A closure, IMO, should be treated as a whole. Replacing parts of it willy-nilly will soon prove to be a nightmare: you don't know what the closure function will be at any given point in time, where it was changed, what the previous state was, and why it was changed.
A temporary sollution might just be this:
var foo = (function()
{
var calc = function(x, callback)
{
callback = callback || defaultCall;
return callback.apply(this, [x]);
},
defaultCall(a)
{
return a*a+1;
},
return {calc: calc};
}());
foo(2);//returns 5
foo(2,function(x){ return --x;});//returns 1
foo(2);//returns 5 again
IMO, this is a lot safer, as it allows you to choose a different "internal" function to be used once, without changing the core behaviour of the code.