This code is not working as expected. I am trying to use the Google Geolocation API to figure out my current location. However, when I try to log the result for the google.maps.LatLng object, I got (0,0) as the latitude and longitude coordinates.
$(document).ready(function(){
var func = {
lat:0,
long:0,
success:function(pos) {
var crd = pos.coords;
this.lat = crd.latitude;
this.long = crd.longitude;
},
error:function(err) {
console.log('ERROR(' + err.code + '): ' + err.message);
},
init:function(){
navigator.geolocation.getCurrentPosition(this.success, this.error);
}
};
func.init();
$('button').on('click',function(){
var loc = new google.maps.LatLng(func.lat, func.long);
alert(loc);
});
});
However, the code underneath works. All I did was changing "this" keyword to the object's name. It shouldn't make a difference.
$(document).ready(function(){
var func = {
lat:0,
long:0,
success:function(pos) {
var crd = pos.coords;
func.lat = crd.latitude;
func.long = crd.longitude;
},
error:function(err) {
console.log('ERROR(' + err.code + '): ' + err.message);
},
init:function(){
navigator.geolocation.getCurrentPosition(func.success, func.error);
}
};
func.init();
$('button').on('click',function(){
var loc = new google.maps.LatLng(func.lat, func.long);
alert(loc);
});
});
I am not sure why the code snippet on the top produces incorrect output? I am not too familiar with Objected Oriented JavaScript. I would appreciate it if anyone could help me understand what is going on.
In your first example, when you call:
getCurrentPosition(this.success, this.error);
You are merely passing the success and error functions into getCurrentPosition. Even though you reference them here via this, that is not carried through to the point where the functions are actually called. It only passes the function references themselves, not the this value that you were using here.
Another way to understand the problem: the value of this inside a function is determined at the time the function is called. When you write foo.bar() you are calling the bar() function with foo as the this value inside the function. But when you write foo.bar without the (), you are only getting a reference to bar itself. foo is out of the picture after that. If you pass foo.bar into another function which expects a callback, when it finally calls bar() there is no longer any association with foo.
That's why your second example works. It does not depend on this but uses func which is valid throughout the outer function.
Related
I would like to know the best way to detect when a method or function is directly called through the console. As far as I currently understand, it's not possible to directly detect it on identical function calls, but using the .call() and .apply() methods of a function I can pass additional data through the this object.
Given the following code structure:
(function(){
var Player = {money: 0};
window.giveMoney = function(amount){
if (this.legit !== true)
throw new Error("Don't try to cheat!");
Player.money += amount;
}
})();
I could call the function using
window.giveMoney.call({legit: true}, 300);
in my actual code to tell a direct call from the console and my own code apart, but this is obviously not fool-proof, since the same code can also be executed from the console to achieve the desired effect.
I would want a way to be able to call the function from both places and then tell the locations of the call apart. If there's no way to do that, what's the best way to try and prevent the execution anyway? Is it best to just not expose any methods at all, and keep everything inside a single closed-off anonymous function?
To prevent global access make sure your code is in a closure. If you want to expose an API you can do so using the module pattern.
Closure
(function() {
var Game = {};
Game.giveMoney = function(money) {
console.log('Gave money (' + money + ')');
};
})();
Wrap all your private code in an IIFE (Immediately Invoked Function Expression) which will lock it up into a closure.
Module
Then expose only custom functions back out of the closure so you can use them on the console (with supervision of course).
window.Game = (function() {
var player = {
money: 500;
};
player.giveMoney = function(money) {
console.log('Gave money (' + money + ')');
player.money += money;
};
player.takeMoney = function(money) {
console.log('Took money (' + money + ')');
player.money -= money;
};
return {
giveMoney: function(money) {
console.error('Don\'t Cheat! A fine was charged.');
player.takeMoney(Math.floor(player.money / 0.05));
}
};
})();
window.Game.giveMoney(200);
You can spool all function calls through a central access point with a boolean variable, that can serve as a indicator whether the call is from a console or not....
var maths = {
add: function(p1,p2)
{
console.log(p1,p2);
}
}
var invoker = {
invoke: function(fu,isconsole)
{
if(isconsole)
{
console.log("Called from console");
}
//invokes the function with all parameters intact
fu;
}
}
//Call without console
invoker.invoke(maths.add(2,3));
//Call with console
invoker.invoke(maths.add(2,3),true);
Hope it helps!!!
You can use the monitor() command in the console to monitor when a function is called. https://developer.chrome.com/devtools/docs/commandline-api#monitorfunction
Just run monitor(functionName); and whenever the function is called it will output a message in the console.
Eric Miraglia of Yahoo/Google presents a very clean looking way to implement information hiding in JavaScript:
http://www.yuiblog.com/blog/2007/06/12/module-pattern/
Please note some experiments here:
http://jsfiddle.net/TvsW6/5/
My question is, why can I access the seemingly "public" variable "this.setting2" (and of course not _setting1) YET I cannot access the function "this.logSetting_priv" although it is in the same scope as this.setting2 (isn't it!?!?!?) Does any one know why?
Also, with the use of the return object for the public methods, I can't seem to add a function as I might normally with "LogSystem.prototype.publicFunc1." Why is that?
Mystery of the ages . . .
Pls checkout my JSFiddle but the JS is also below:
function LogSystem() {
//default
var _divId = "log";
var _setting1 = "default stuff";
this.setting2 = "default stuff as well";; //This is accessible!
function _printLog(msg) {
msg = msg || "";
$("#" + _divId).append(msg + "<br/>");
};
//this is **not** accessible - bc of return object below?
this.logSetting_priv = function () {
_printLog("PRIV: Setting1 is: " + _setting1);
_printLog("PRIV: Setting2 is: " + this.setting2);
};
/*
* Key Distinguishing feature of this pattern
*/
return {
printLog: function (msg) {
console.log("PRINTING:" + msg);
_printLog(msg);
},
logSetting_pub: function () {
this.printLog("PUB: Setting1 is: " + _setting1);
this.printLog("PUB: Setting2 is: " + this.setting2);
},
publicFunc2: function () {
_setting1 = "Fixed Deal returnFunction";
this.setting2 = "floating hamster";
}
};
};
//THIS DOESNT WORK!! . . . . bc of the return object??
LogSystem.prototype.publicFunc1 = function () {
_setting1 = "Fixed Deal";
this.setting2 = "floating midget";
};
/*******************************/
/*********Testing Code**********/
/*******************************/
$(document).ready(function () {
var logInst = new LogSystem();
//TESTING METHODS!
try {
logInst.publicFunc1(); //THIS DOESNT WORK!!
} catch (e) {
logInst.printLog("The call to the prototype function does not work - WHY?");
logInst.publicFunc2();
}
try {
logInst.logSetting_pub();
logInst.logSetting_priv();
} catch (e) {
logInst.printLog("ERR!!: " + e.message);
}
//TESTING MEMBERS!
logInst.printLog("We know this does not work? " + logInst._setting1); //undef
logInst.printLog("Why Does THIS WORK? " + logInst.setting2); //def
});
Thank you!
EDIT: Holy crap - and when I manipulate the prototype of the INSTANCE variable, i seem to break the whole object that was returned: http://jsfiddle.net/TvsW6/7/
If any one understands JS at this level, PLEASE explain that! :)
Thank you all so much. Obviously any one in this conversation is at a level way beyond "I do some jQuery" :)
Using private instance variables prevents you from using prototype (functions that need to access them need to be in the constructor body where the privates are declared with var) at the end of this answer is link to a pattern that implements protected. It may take some time to understand how prototpe works and would advice trying to understand the basic workings first before trying to mix it with closures to simulate private/public modifier.
Pointy answered you question correctly that when invoking a function with new but then returning an object would not return the Object referred to as this in the function:
function Test(){
this.name="some test";
return {name:"something else"};
}
console.log((new Test()).name);//name:something else
Not returning an object or returning a primitive (string, boolean, number) would cause the this object to be returned:
function Test(){
this.name="some test";
return "hello";
}
console.log((new Test()).name);//name:some test
Your constructor is returning a different object than the one build implicitly with new. Thus, inside the constructor this refers to a different object than the one you actually end up with outside, and that object doesn't have a property called "logSetting_priv".
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...
Need this for the youtube api // the onStateChange callback functions!
I want to programmatically create functions which will listen to the "onStateChange" event emitted by several youtube player. Adding the listener works already:
function onYouTubePlayerReady(playerId) {
var ytpStateManager = playerId +"_StateManager";
document.getElementById(playerId).addEventListener("onStateChange", ytpStateManager );
...
The function I need to create based on the playerId variable ("ytp_1", "ytp_2", ...) is
function ytpStateManager(newState) {
ytpStateHelper(playerId , newState);
}
So the result for the playerId "ytp_1" would look like this:
function ytp_1_StateManager(newState) {
ytpStateHelper("ytp_1", newState);
}
Works also but right now I need to add them manually for each player, which is not what I need. I want to create them automatically when a new player sends a readyState event.
My problem is that it seems like these functions need to be a global functions to work properly. I tried several options for days now. My problem is that I do not know how (if there is a way) to define a global function, incl. the function name, programmatically, based on another variable.
Its a bummer that the ytp does not emit an event which includes the state AND the player/target. Would make things much easier. All this is basically the workaround as I need all to do stuff on all stateChanges.
If there is a better/simpler way, PLEASE let me know :) Otherwise a solution for this question is highly welcome.
Maybe there is a way to rerout the event, to make it more "accessible"?
I read in the spec that .addEventListener also takes a object, so I tried to bind the event to a dedicated object. But again, it did not get triggered. Feels like I tested everything ...
UPDATE
I am now switching to the iframe player (from swfobject) because that one provides an event which includes playerId and state :D Yeahhh!! After spending week with the wrong ytplayer this feels like a great advancement. Also seems like yt wants us to use the iframe player which can dynamically use html5 when supported.
You create a function that returns a function:
function createStateManager(playerId) {
return function (newState) {
ytpStateHelper(playerId , newState);
}
}
Then you call your function factory when setting up the event listener:
var player = document.getElementById(playerId);
player.addEventListener("onStateChange", createStateManager(playerId));
DEBUGGING
I'm not sure why that's not working, but here is a debugging suggestion. I suspect you may not be getting the playerId on your onYouTubePlayerReady handler.
function onYouTubePlayerReady(playerId) {
console.log('Player ready. The player id is: ' + playerId);
var ytpStateManager = playerId +"_StateManager";
var player = document.getElementById(playerId);
player.addEventListener("onStateChange", createStateManager(playerId));
}
function createStateManager(playerId) {
return function (newState) {
console.log('State changed for player ' + playerId + '. New state is ' + newState);
ytpStateHelper(playerId , newState);
}
}
Could you try that, and post what you get from both console.log calls?
1)You can create Function object new Function([params], "BODY")
So you can combine body of your function as string variable and put into as BODY
Example:
var twoNumAverage = new Function("x", "y", "return (x + y)/2")
console.log(twoNumAverage(3,7))
2)And new can create dynamically name and BODY
Example
var globalObject ={};
var nameFn ='MyNewFunction';
var createFn = function(object,functionName, Body){
object[functionName]= new Function(Body);
}
createFn(globalObject,nameFn,"return (arguments[0] + arguments[1])/2");
You can call your new function:
globalObject[nameFn](10,20);
Result: 15
Please note that in body your function you can get params via collection arguments
window["foo"+"bar"] = function(){ console.log("foobar is called"); }
Here's a way to create a named proxy function that executes another function with the context you supply.
function createNamedProxy(name, fn, context) {
var template = [
'(function #name() {',
' #name.fn.apply(#name.context || window, arguments);',
'})'
].join('').replace(/#name/g, name),
result = eval(template);
result.fn = fn;
result.context = context;
return result;
}
// Example Usage
var anonymous = function() { alert( document === this ); },
named = createNamedProxy('Named', anonymous, document);
// Will alert 'true'
named();
The solution above creates a function that can create and return a named function that executed whatever you'd like. If you don't supply context, it will assume the window object just like a normal anonymous function would. To create the solution you wanted you would do:
var varName = 'ytp_1';
window[varName + '_StateManager'] =
createNamedProxy(varName + '_StateManager', function(newState) {
ytpStateHelper(varName, newState);
});
Where varName could be any programmatic prefix you'd like. When invoking ytp_1_StateManager() you would pass in your newState value and the code would call ytpStateHelper with your variable name and the newState.
Hope this helps.
I wanted to play with geolocation API on my Android. I know that there is a "navigator" object that is defined and that should be used to aquire user position. So, I created this sample code:
function GeolocationTester()
{
// here I want to store all acquired locations
this.locations = new Array();
alert("this.locations defined: " + this.locations);
this.onSuccess = function(position)
{
alert("Entered onSuccess");
alert("this.locations defined: " + this.locations);
}
this.onError = function(error)
{
alert("error acquiring location");
}
navigator.geolocation.watchPosition(this.onSuccess, this.onError, { enableHighAccuracy: true });
}
And it doesn't work for me. Each time watchPosition call onSuccess the this.locations field isn't defined (and it is defined just after new Array). I known that I'm doing somethind wrong, but as it is one of my JavaScript attempts, not sure what. So, anybody could find a problem here?
The problem is with the scoping of this. When the onSuccess or onError is called, this isn't bound to the object containing the locations array. You need to create an explicit variable outside of the functions to which the array should be assigned and then use this variable in the callbacks, like this:
var allLocations = this.locations = [a, b, c];
this.onSuccess = function(position) {
alert("allLocations: " + allLocations);
alert("this.locations: " + this.locations);
}
Its cause you using this. This will change cause its depends on the context your function is calling on. Just use the scope of the function to declare location:
function GeolocationTester()
{
// here I want to store all acquired locations
var locations = [];
alert("locations defined: " + locations);
function onSuccess(position) {
alert("Entered onSuccess");
alert("locations defined: " + locations);
}
function onError(error){
alert("error acquiring location");
}
navigator.geolocation.watchPosition(onSuccess, onError, { enableHighAccuracy: true });
}
To really understand what this read this blog post http://dmitrysoshnikov.com/ecmascript/chapter-3-this/
Try to define onSuccess like this:
this.onSuccess = (function(locations) {
return function(position)
{
alert("Entered onSuccess");
alert("this.locations defined: " + locations);
}
})(this.locations);