I started recently to learn patterns in JavaScript and trying to understand how it is working by simulating "classes". So most probably this error is coming because of a bad understanding of how it is executed.
I have the following general class:
var Collection = (function(){
function Collection(){
this.collection = {};
}
var p = Collection.prototype;
p.callback = function(data){collection = data;}
return Collection;
})();
API is a singleton class. Get_Data method will execute some Ajax call but so far it is just an object. The singleton class is as follow:
var WebService = (function(){
var instance;
var init = function(){
return {
get_data: function(callback){
// Dump data without accessing the Server
callback({'id':1234, 'data':"hello world"})
}
}
}
return {
get_instance: function(){
if(!instance)
instance = init();
return instance;
}
}
})();
At some point I create a Collection class and get some data from some source (it will be from a web service but so far I'm just using an object).
var collection_objects = new Collection();
var api = WebService.get_instance();
api.get_data(collection_objects.callback);
I'm using a callback method (name callback for simplicity) in the collection data that should update the model data. My issue is that in the callback I'm not accessing to the collection property. I'm actually creating a new object called collection. At some point, I think that I should use this, but since I will use an Ajax call, I will have to save this in another variable commonly known as self. That's the theory I have read so far, but where do I have to use the self? Is that the approach?
At some point, I think that I should use "this", but since I will use
an Ajax call, I will have to save "this" in another vble commonly
known as "self"
That's a trick to overcome this problem in javascript. In my opinion, that's not so good as the function is tightly coupled to a variable outside the current context.
A better solution is to use .bind to bind the context to be your collection_objects. Like this:
api.get_data(collection_objects.callback.bind(collection_objects));
And use this in your callback, in this case, this is the collection_objects instead of the global window object
p.callback = function(data){ this.collection = data;}
In general, a method should not be concerned about how it's called. It's very unintuitive and bad if this inside Collection's prototype method does not refer to a Collection instance. Therefore, we should call the function in the context of a Collection instance. To avoid forgetting to use .bind, we could try another solution with call or apply:
get_data: function(context,callback){
// Dump data without accessing the Server
callback.call(context,{'id':1234, 'data':"hello world"});
}
And use it like this:
api.get_data(collection_objects,collection_objects.callback);
Internally, .bind uses something like call or apply to achieve the goal.
Function.prototype.bind = function(){
var fn=this,args=Array.prototype.slice.call(arguments),object=args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
This code is extracted from the book: Secret of the javascript ninja. This code could be used as a polyfill in case the browser does not support .bind method natively.
Related
I am writing a simple library that will read values from an object given a string property.
Is it possible to read the property but have a function execute without actually invoking the function?
something like:
var obj = {
fn : (function malicious(){ deleteLotsOfFiles();
})()
}
if I do
var foo = obj.fn;
is there a way just by reading the property to execute a (malicious) function?
The malicious function would have already executed anyway before you even referenced it. Once the function is parsed by the engine, it is executed straight away (self-invoking).
var obj = {
get fn() { deleteLotsOfFiles(); }
};
// later
var o = obj; // deleteLotsOfFiles has not been executed
console.log(o.fn); // you just deleted lots of files
An alternative
var o = Object.defineProperty(o, 'baz', {
get: function(){
console.log("Delete Everything!");
}
});
Then access o.baz and they are deleted
More Information on getters from MDN
Sometimes it is desirable to allow access to a property that returns a
dynamically computed value, or you may want to reflect the status of
an internal variable without requiring the use of explicit method
calls.
Seems pretty much like what you want to do.
I'm new to JS and especially to prototypes.
I have this class and I cannot figure out how to access the properties.
var Lobby = function (preloader, serverConn) {
// Hold a reference to EventBus
this.serverConn = serverConn;
this.preloader = preloader;
this.scheduleItemService = new ScheduledItemService(this.preloader);
this.stage = new createjs.Stage("lobbyCanvas");
};
Lobby.prototype.start = function(me, signedRequest) {
sendMessage(data, function() {
// inside this scope this.stage is undefined!
renderLobbyImages(this.stage, this.scheduleItemService);
});
};
function renderLobbyImages(stage, scheduleItemService) {
stage.update();
};
Calling code:
var lobby = new Lobby(preloader, serverConn);
lobby.start(me, status.authResponse.signedRequest);
What am I doing wrong accessing 'renderLobbyImages' ??
Thank you :-)
In javascript, this is not resolved based on where it is declared/used. It is resolved when it gets called. (see: How does the "this" keyword in Javascript act within an object literal?).
Therefore, in the code above, since this is called in the callback to sendMessage(), and since sendMessage is asynchronous (meaning the callback will be called long after the call to start() have returned), this is therefore referring to the global object (which is window in web browsers, something unnamed in node.js).
So effectively, your code is doing this (no pun intended):
sendMessage(data, function() {
renderLobbyImages(stage, scheduleItemService);
});
Since there are no global variables called stage or scheduleItemService both are effectively undefined!
Fortunately, there is a workaround for this. You can capture the correct object in a closure:
var foo = this;
sendMessage(data, function() {
renderLobbyImages(foo.stage, foo.scheduleItemService);
});
Alternatively, you can pass the correct object (this) into an IIFE:
(function(x){
sendMessage(data, function() {
renderLobbyImages(x.stage, x.scheduleItemService);
});
})(this); // <-------- this is how we pass this
or:
sendMessage(data, (function(a){
return function(){
renderLobbyImages(a.stage, a.scheduleItemService);
}
})(this));
Or in this case, since stage and scheduleItemService are not functions, you can even pass them directly:
sendMessage(data, (function(a,b){
return function(){
renderLobbyImages(a,b);
}
})(this.stage, this.scheduleItemService));
There are lots of solutions to this problem. Just use the one you're most comfortable with.
Two problems.
this is missing in your constructor function on scheduleItemService.
Some functions you call to assign values seem to be not returning anything.
new createjs.Stage("lobbyCanvas");
new ScheduledItemService
Your calling method is alright.
this always refers to the calling object. When you say...
varlobby = new Lobby();
lobby.start();
... your calling object is lobby which has all the fields the start() function needs. But there initialization seems to be not working properly.
Please read this MDN starter guide.
Also we are having a some discussion about classical and prototype based OOP in this question. Please see the answer of Paul S for more about the tutorial I mentioned. Please see my answer if you need to see the tutorial in classical OOP light.
I have a namespace object on which I have defined some functions. One of the functions is used to create a websocket session to a remote server.
ns.Session = function(url, config, callback){
var ws = new WebSocket(url);
ws.onmessage = function (e){
if(login(e.data)){
// This is the point at which I need to pass back the session object to callback
callback(session):
}
}
....
}
In Javascript, as far as I know if someone invokes this function using ns.Session(....) then the this object will be ns. So, how do I get the instance of the "session" to send to the callback.
arguments.callee is deprecated as far as I know.
The whole reason I am doing it this way is that the session is not considered "usable" till the server confirms the login, so I don't want to prematurely return the function object before it is actually connected and logged in. Hence the use of a callback. If there is a better way to achieve this, I am open to that too.
Session has a bunch of other inner functions like addHandler, sendData etc which I have not shown here for sake of brevity.
You can use pointer to function like this:
ns.Session = function session(...) {
// 'session' here points to your function, so you do
callback(session); // like you wrote
callback(ns.Session); // same effect if you don't change ns and ns.Session pointers
}
Also, I don't see why you use the word "instance" in this case, because functions have only one instance. If you call it with the 'new' keyword, function creates new object from the function, and now there you can use "instance" word.
I'm using the FB.Event.subscribe() observer model to find out when a user logs in. This method takes two arguments, a string containing the thing to watch, and callback function.
I'm following several events that handle the event the same way, so I've set up the callback function as a pre defined method and passed this to FB.Event.subscribe() like this:
Controller.prototype.go = function() {
FB.Event.subscribe('auth.login', this.fbHandleStatusChange);
FB.Event.subscribe('auth.logout', this.fbHandleStatusChange);
}
Controller.prototype.fbHandleStatusChange = function(response) {
// Doesn't work
this.otherFunction();
}
Controller.prototype.otherFunction = function() {
alert('hello');
}
Unfortunately this means that I loose access to 'this' within the scope of fbHandleStatusChange, obviously I don't want to start coding references to concrete versions of Controller!
I'm guessing I'm passing the function incorrectly?
Thanks.
In JavaScript, this is defined entirely by how a function is called, not where it's defined. This is different than some other languages. (JavaScript doesn't have methods, it just has functions and some syntactic sugar that makes them look like methods sometimes.) So although you're passing in your function correctly, Facebook doesn't know about your object instance and can't set this correctly when calling your function.
Check the FB.Event.subscribe docs to see if it offers a way to say what "context" to use to call the event handler function. It may offer a way to do that. (This will usually be a context or thisArg parameter.)
If not, you can readily solve the problem with a closure:
Controller.prototype.go = function() {
var self = this;
FB.Event.subscribe('auth.login', handleChange);
FB.Event.subscribe('auth.logout', handleChange);
function handleChange() {
return self.fbHandleStatusChange();
}
}
That grabs a copy of this into a variable called self, which is used by the handleChange function (which is a closure over the scope containing the self variable) to call your function with the correct context. More about closures here: Closures are not complicated More about this here: You must remember this
Alternately, though, are you really going to have multiple instances of Controller? People coming to JavaScript from class-based languages tend to use constructor functions (a rough "class" analogue) unnecessarily. They're the right choice if you need to have more than one instance of an object, but if you're only ever going to have a single Controller object on the page, then using a constructor function and fiddling about with this is overkill.
If you don't need multiple, independent Controller instances, then:
var controllerObject = (function() {
var inst = {};
inst.go = go; // Make `go` a publicly-accessible function of the object
function go() {
FB.Event.subscribe('auth.login', fbHandleStatusChange);
FB.Event.subscribe('auth.logout', fbHandleStatusChange);
}
// This is private to us, so we don't expose it as a property on the object
function fbHandleStatusChange(response) {
// Doesn't work
otherFunction();
}
// This is also private to us
function otherFunction() {
alert('hello');
}
return inst;
})();
That creates a private scope via the outer anonymous function, and within that scope creates an instance (inst) which we then return and refer to as controllerObject. controllerObject in the above only has one property, the function go. All of our other functions are truly private. (I've also taken the liberty of ensuring that the functions have names, because that helps your tools help you.)
Note that we don't actually refer to inst anywhere in our function calls, because they're all local to the closure scope. We can even have private data, by having other vars within the outer closure.
Problem & Reason
One of my team mate ended up in messy situtaion implementing function hooking in javascript. this is the actual code
function ActualMethod(){
this.doSomething = function() {
this.testMethod();
};
this.testMethod = function(){
alert("testMethod");
};
}
function ClosureTest(){
var objActual= new ActualMethod();
var closeHandler = objActual.doSomething;
closeHandler();
closeHandler.apply(objActual,arguments); //the fix i have added
this.ActualTest = function() {
alert("ActualTest");
};
}
In the above code, var closeHandler is created in the context of ClosureTest(), but it holds the handler of the ActualMethod.doSomething. Whenever calling the closeHandler() ended up in "object doesnt support this method" error.
This is because doSomething() function calls another method inside called this.testMethod();. Here this refers to the context of the caller not callee.so i assume the closeHandler is bound to the environment(ClosureTest) actually created.Even though it holds the handler to the another context, it just exposes the properties of its own context.
Solution
To avoid this i suggest to use apply to specify the conext in which it needs to execute.
closeHandler.apply(objActual,arguments);
Questions
is it perfect scenario for closures..??
What are the intersting places you have encountered closures in javascript..?
UPDATE
Yes its simple i can call the method directly. but the problem is, in a particular scenario I need to intercept the call to actuall method and run some code before that, finally execute the actual method..
say for an example, am using 3rd party aspx grid library, and all the mouseclick events are trapped by their controls. In particular group by mouse click i need to intercept the call to their ilbrary method and hook my mthod to execute instead and redirect the call to actual library method
hope this helps
Update: Because you probably left out some details in your code, it is difficult to adapt it into something workable without missing the point of your actual code. I do think I understand your underlying problem as you describe it. I hope the following helps.
Suppose the following simple example:
// Constructor function.
function Example() {
// Method:
this.method = function() {
alert("original method");
}
}
// You would use it like this:
var obj = new Example();
obj.method(); // Calls original method.
To intercept such a method call, you can do this:
function wrap(obj) {
var originalMethod = obj.method;
obj.method = function() {
alert("intercepted call");
originalMethod.apply(this, arguments);
}
return obj;
}
var obj = wrap(new Example());
obj.method(); // Calls wrapped method.
Unfortunately, because method() is defined in the constructor function, not on a prototype, you need to have an object instance to wrap the object.
Answer to original question: The doSomething() function is used as a method on objects created with ActualMethod(). You should use it as a method, not detach it and use it as a function in a different context. Why don't you just call the method directly?
function ClosureTest(){
var objActual = new ActualMethod();
// Call method directly, avoid messy apply() calls.
objActual.doSomething();
this.ActualTest = function() {
alert("ActualTest");
};
}
If you assign a method (a function on some object) to a local variable in Javascript and call it, the context will be different (the value of this changes). If you don't want it to happen, don't do it.
When I want to hook a function, I use the following Function method which is also a fine piece of Closure demonstration:
Function.prototype.wrap = function (wrapper) {
var __method = this;
return function() {
var __obj = this;
var args = [ __method.bind(__obj) ];
for(var i=0; i<arguments.length; i++) args.push(arguments[i]);
return wrapper.apply(__obj, args);
}
};
Then do something like:
ActualMethod = ActualMethod.wrap(function (proceed, option) {
// ... handle option
proceed(); // calls the wrapped function
});
proceed is bound to its initial object, so you can safely call it.