I'm working with jQuery and creating some OOP Javascript functionality. The object and its constructor has the following structure:
var zoomin = new Object();
zoomin = function() { // This is my constructor
this.someFunction();
};
zoomin.prototype = {
someFunction: function() {
// More code here
}
};
When I create an instance of zoomin (e.g. var my_zoom = new zoomin();), the call to this.someFunction() in the constructor does not work. It seems like jQuery is taking possession of "this" and this is why it is breaking.
I would really appreciate any help on how to get this to work properly.
Thanks!
Your class definition should be like this:
function zoomin() { // This is my constructor
this.someFunction();
};
For the member method definition, it should be:
zoomin.prototype.someFunction = function() {
// More code here
};
or:
$.extend(zoomin.prototype, {
someFunction : function() {
// More code here
}
});
Hope this helps. Cheers
your code is pretty messed up. you're creating a new Object, then assigning function to the same variable. so the first statement is not necessary (and not a good practice), then assigning object (with some properties) to the prototype of the zoomin object, instead of adding a property (someFunction) to it.
try to define your constructor like this:
function zoomin(){
// this is your constructor
}
zoomin.prototype.someFunction = function(){
// this is your method.
}
var zm = new zoomin();
zm.someFunction();
See the other answers as to cleaner ways to organize this. This answer just tries to address the notion of "jQuerying breaking 'this' in constructor".
A function-object (e.g. zoomin) must be invoked with new to act as a Constructor (constructors are just functions, there is nothing special without new). That is, if zoomin is used as a callback function in jQuery then this will be a particular (but arbitrary) object which is likely not a new object instance. This could give the illusion that jQuery breaks the constructor; however, it does not, because the constructor was not invoked with new and thus acts like a "normal function".
If using new fnObject then this (inside the fnOjbect function) will always be a new "fnObject" instance. There is no way for jQuery to change this, even if it tried really, really hard.
Also, the zoomin = new Object is 100% useless because the next line just assigns a new value (the constructor function-object) to zoomin :-)
Happy coding.
Related
The other day I was fiddling with Javascript, and I noticed that I can't write a prototype to an object within a prototype function. Like this:
var obj = function() { }
obj.prototype.First = function() {
this.prototype.Second = function() {
alert("Second Prototype");
}
}
obj.First();
obj.Second();
For some reason, the second prototype won't work and the code doesn't run. Is my syntax wrong or are there some prototype limitations that I don't know about? Thanks!
Edit:
I'm not trying to add a prototype to a prototype... that wouldn't make much sense. This is what I'm trying to do: Add two separate prototypes to obj. Prototype 2 is defined when prototype 1 is called. I thought that this would contain a reference to object, so this.prototype would be the same as obj.prototype, but it doesn't work as expected.
This is an old question, but I thought I'd add my two cents.
This code is trying to add functions on 'prototype'. However, this can only be done on the class name. What you have is a variable pointing to an anonymous class. To access the variable of an anonymous variable use 'proto'. The below is the same as your example, except, using proto it is 'successful'. Although, I don't see the benefit of using prototypes like this as the prototype added methods only apply to the anonymous instance 'obj'. http://jsbin.com/zapocamipi/edit?js,console
var obj = function() { }
obj.__proto__.First = function() {
console.log("First Prototype");
this.__proto__.Second = function() {
console.log(this);
}
}
obj.First();
obj.Second();
Maybe this could help you out understanding the role of a constructor function and the prototype.
Depending on what you're trying to do (obj, First and Second doesn't really show your intention) you could do:
A Person has Eyes. This can be done through composition.
An Employer is a Person but a Person is not necessarily an Employer (can be Client or Consultant too). This could be done through inheritance.
A Cat can move. In a Class based language Cat has to implement Movable but in JavaScript you can use mix ins and leave the implementation to the default implementation that Movable provides or override it. JavaScript does not compile time check if you do implement certain things.
If you would like to change the type of the object instance after calling a certain function then it's dangerous to meddle with the prototype because that will affect all instances of that type.
Maybe you should return an instance of another type.
var Customer = function(name) {
this.name=name || 'unknown';
};
Customer.createVipCustomer = function() {
return new VipCustomer(this);
}
var VipCustomer=function(customer){
//re use Customer constructor
Customer.call(this,customer.name);
this.isVip=true;
}
//inherit the protype defined members
VipCustomer.prototype=Object.create(Customer.prototype);
VipCustomer.prototype.constructor=VipCustomer;
VipCustomer.prototype.second=function(){
console.log('this is second');
}
var aCustomer = new Customer('Ben');
//update to VipCustomer
aCustomer = Customer.createVipCustomer(aCustomer);
aCustomer.second();
this.prototype doesn't exist.
If you want to add a property to the instance, use this.
If you want to add a property to the prototype, use Constructor.prototype.
Also, obj is a function (class), not an instance,
You want to create an instance using the new keyword, and you should name the constructor function as UpperCamelCase.
I've been working a lot lately on making cleaner Javascript code, and using objects with prototypes etc. But I'm confused on some points...
function TimeCard(){
this.date = new Date();
this.pay_period_begin = null;
this.pay_period_end = null;
}
Here is my timecard object with some initial values. I have a bunch of functions that I've written and more to come that are part of that timecard and, if I understand correctly, they will be prototype functions. Here is some of what I have so far:
TimeCard.prototype = {
init : function(){
this.pay_period_begin = $("#pay_period_begin");
this.pay_period_end = $("#pay_period_end");
},
getTimeCardData : function(){
//ajax request
},
selectAll : function(){
this.getTimeCardData();
}
...
};
My problem is that when I try to call this.getTimeCardData() it says that my object has no such method. I can obviously access the other variables because they are declared in my constructor, but I don't understand how prototype scopes I guess. So far I have gotten around this by using tc.getTimeCardData() instead of this.getTimeCardData(), with tc being the instance of my object declared outside - var tc = new TimeCard();. I'm sure that that's not the correct way to go about this, but what is?
My problem is that when I try to call this.getTimeCardData() it says that my object has no such method.
It sounds like this is no longer referring to your instance. You'll have to show us the actual call for us to be sure, but in JavaScript, this is set primarily by how a function is called, not where it's defined, and so it's fairly easy for this to end up being something different.
Here's a hypothetical example:
TimeCard.prototype = {
// ...
doSomething: function() {
// here, `this` probably refers to the timecard
someArray.forEach(function() {
this.getTimeCardData(); // <=== Problem, `this` has changed
});
}
// ...
};
If I call this.doSomething(); on a TimeCard object, within the call this will refer to the timecard. But within the forEach callback, this will no longer refer to the timecard. The same sort of thign happens with all kinds of callbacks; ajax, etc.
To work around it, you can remember this to a variable:
TimeCard.prototype = {
// ...
doSomething: function() {
var thisCard = this;
someArray.forEach(function() {
thisCard.getTimeCardData(); // <=== Problem
});
}
// ...
};
There are also various other ways to work around it, depending on your specific situation. For instance, you have selectAll calling getTimeCardData. But suppose selectAll is called with the wrong this value? In your comment, you said you were doing it like this:
$('#container').on('click', '#selectAll', tc.selectAll);
That means that when selectAll is called, this will refer to the DOM element, not to your object.
You have three options in that specific situation:
Since you're using jQuery, you can use $.proxy, which accepts a function and a value to use as this, and returns a new function that, when called, will call the original with this set to the desired value:
$('#container').on('click', '#selectAll', $.proxy(tc.selectAll, tc));
Use ES5's Function#bind, which does the same thing. Note that IE8 and earlier don't have it unless you include an "ES5 shim" (hence my noting $.proxy above; you know you have that):
$('#container').on('click', '#selectAll', tc.selectAll.bind(tc));
Use a closure (don't let the name bother you, closures are not complicated):
More (on my blog):
$('#container').on('click', '#selectAll', function() {
tc.selectAll();
});
In all of the above, you'll lose the benefit of this referring to the DOM element. In that particular case, you probably don't care, but if you did, you can get it from the event object's currentTarget property. For instance, this calls tc.selectAll with this referring to tc and passing in what would have been this (the DOM element you hooked the handler on) as the first argument:
$('#container').on('click', '#selectAll', function(e) {
tc.selectAll(e.currentTarget);
});
Mythical methods
You must remember this
Another, less likely, possibility relates to how you're updating TimeCard.prototype. The way you're doing it, it's possible to create objects via new TimeCard() before your code that replaces the TimeCard.prototype object runs, which means they'll have the old prototype.
In general, I strongly recommend not replacing the object automatically created for the prototype property of the constructor function. Instead, just add to the object already there, like this:
function TimeCard(){
this.date = new Date();
this.pay_period_begin = null;
this.pay_period_end = null;
}
TimeCard.prototype.getTimeCardData = function(){
//ajax request
};
// ...
Here's why: Timing. If you replace the object on the prototype property, any objects you create via new TimeCard() before you do that replacement will have the old prototype, not the new one.
I also recommend always creating these within a scoping function so you know that the declaration and the prototype additions happen at the same time:
var TimeCard = (function() {
function TimeCard(){
this.date = new Date();
this.pay_period_begin = null;
this.pay_period_end = null;
}
TimeCard.prototype.getTimeCardData = function(){
//ajax request
};
// ...
return TimeCard;
})();
...primarily because it prevents the timing issue.
Can someone clear up what I'm doing wrong with my code? I'm a total newb, simply trying to experiment with how to create objects, prototypes, and what the this command refers to in the context of prototypes. However, none of my code is working on jFiddle. I know this code is completely nonsensical, but I'm just trying to find out what the document prints in various cases to get a more concrete grasp of how objects, prototypes, constructors, and the "this" keyword works. But NOTHING is showing up at all!
function Person(identity) {
this.identity = "fellow";
this.logYou = function() {
document.write('hi');
};
};
Person.prototype.logMe = function() {
document.write(this.identity);
};
var Dude = new Person("stud");
Person.logYou();
Person.logMe();
Dude.logMe();
function Person() {
this.identity = "fellow";
this.logYou = function() {
document.write('hi');
};
};
When you call this.identity = "fellow"; the this keyword refers the context in which the function is running.
So if you call the function in the Global scope, this refers to window object:
Person();
alert(window.identity); // fellow
And if you instanciate the function, this keyword refers to the new object:
var Dude = new Person();
alert(Dude.identity); // fellow
So the function Person has no property identity.
If you want to have some property for your function:
Person.identity = "fellow";
alert(Person.identity); // fellow
logYou and logMe are methods stored on the prototype of Person so that inheriting objects may have that function to call. You can't call logYou on Person but what you can do is call it on its child, Dude:
Dude.logYou(); // hi
Dude.logMe(); // fellow
These two lines
Person.logYou();
Person.logMe();
throw an error an couse the code to stop executing because logYou and logMe do not exist as properties of Person.
Dude.logMe() and Dude.logYou() will work, printing fellow and hi, respectively.
You cannot call
Person.logYou();
Person.logMe();
as Person has no method logYou or logMe, when you remove these two lines, your code will start working
The below doesnt work and im struggling to work out why....
function CommentHandler() {
var Text;
}
var myCommentHandler = new CommentHandler();
myCommentHandler.prototype.SayHello = function () {
document.write('Hello World');
}
I get the error : 'myCommentHandler.prototype is undefined'
I just cant see why? I have declared a variable called myCommentHandler and copied in the object CommentHandler then Im trying to access the protoype of it and assign a function but I cant...Anyone know why?
The prototype belongs to the class, not the instance:
CommentHandler.prototype.MyFunction = ...
However you can also access the prototype via the instance's constructor property:
myObj.constructor.prototype.MyFunction = ...
This will of course add that function to every instance of your CommentHandler.
If instead you only wanted to add that function to that one instance, you'd do:
myObj.MyFunction = ...
Note that in none of these variants would it be possible for MyFunction to access your private variable Text.
This is the compromise forced by the prototype inheritance model - variables and methods must be public (e.g. this.Text) if they're to be accessed from outside, and in this case functions declared in the prototype are effectively "outside".
The alternative (using closure-scoped variables and methods) seems more natural to OO programmers, but makes inheritance much harder.
I think what you actually want here is this:
CommentHandler.prototype.SayHello = function () {
document.write('Hello World');
};
You have to use
CommentHandler.prototype.SayHello = function(){....}
You can find more about the prototype object in the following references
http://www.javascriptkit.com/javatutors/proto.shtmlhttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/prototypehttp://msdn.microsoft.com/en-us/magazine/cc163419.aspx
You need to prototype the class function rather than the instance. Also if you want the Text variable to be accessible outside the constructor, in the other method functions, then you need to add the this keyword.
function CommentHandler() {
this.Text = 'Hello World';
}
CommentHandler.prototype.SayHello = function () {
document.write(this.Text);
}
var myCommentHandler = new CommentHandler();
myCommentHandler.SayHello();
As a side point you could create the SayHello function inside the constructor, but this would add increased overhead to the creation of each instance of CommentHandler. Thus the prototyping method that you are using is a better option.
myCommentHandler does not have a prototype, because it is an instance. CommentHandler does have a prototype that can be modified. I am guessing you want to edit the object after initializing, and can do so like this:
myCommentHandler.SayHello = function() {
document.write('Hello World');
}
Here is a good explanation of prototypes and object oriented programming in javascript with many examples: http://mckoss.com/jscript/object.htm
there is a way to know what class own a function?
Example:
function globalFunc(){
//alert MyObject
}
function MyObject(){
}
MyObject.prototype.test=function(){
globalFunc();
}
var o=new MyObject();
o.test(); //alert MyObject
Now im using this workaround:
function globalFunc(){
alert(globalFunc.caller.__class__);
}
function MyObject(){
}
MyObject.prototype.test=function(){
globalFunc();
}
MyObject.prototype.test.__class__=MyObject;
var o=new MyObject();
o.test(); //alert MyObject
But there is a big problem, look this:
function globalFunc(){
alert(globalFunc.caller.__class__);
}
function MyObject(){
}
MyObject.prototype.test=function(){
var temp=function(){
globalFunc();
}
temp();
/* to simulate a simple closure, this may be for example:
element.addEventListener("click",temp,false);
*/
}
MyObject.prototype.test.__class__=MyObject;
var o=new MyObject();
o.test(); //alert undefined
So, there is a clear way to obtain this?
I know where is the problem(class property is a property of only test and not temp), but i can't add class to temp too.
Thanks.
Thanks for reply, some clarification.
Im trying to do a personal framwork OO oriented with private members.
So:
globalFunc is a special function, im using it to get "private" property and i can't call it with call method or passing some arguments, the only arguments im pass is "this":
Example, $() is global
Class({
public:{
MyClass:function(){
},
setName:function(name) {
$(this).name=name; //set the private var name
},
getName:function(){
return $(this).name;
}
},
private:{
name:"UNKNOWN"
}
})
var o=new MyClass();
o.getName(); // UNKNOWN
o.setName("TEST!!!");
o.getName(); // TEST!!!
o.name; //undefined
$(o).name; //undefined
To works with inheritance, $(), i need to know what class call it and the object of the class.
All works good, but if i need to access a private members in a clousure i must add
__class__
property to clouser!! And i not want this!
Thanks again and sorry for my bad english, im not native speaker.
In javascript there are no Classes. Instead several objects can "own" the same function. For example:
function myFun(){
alert(this.name);
}
function Obj1(){
this.name = "obj1";
}
Obj1.prototype.fun = myFun;
function Obj2(){
this.name = "obj2";
}
Obj2.prototype.fun = myFun;
var obj1 = new Obj1();
var obj2 = new Obj2();
obj1.fun();
obj2.fun();
You scenario is not entirely clear but here are some options:-
function globalFunc()
{
alert(this.__class__);
//Note globalFunc now has access to much more.
}
function MyObject(){ }
MyObject.prototype.test=function(){
globalFunc.call(this);
}
MyObject.prototype.__class__=MyObject;
To add a closure for event handling
MyObject.prototype.test = function(){
var self = this;
var elem = //get some element;
//Not cross-browser but for illustration
elem.addEventListener('click', fnEvent);
function fnEvent() { globalFunc.call(self); }
elem = null
}
I don't understand well what you are trying to do, but here's an idea that could inspire you something.
The "constructor" property helped me well when I was trying to use JS as a OO language.
o.constructor will give you the myObject function reference.
But in my opinion, you should give functional programming a try instead of OO to get the most from Javascript
temp is the caller but it does not have the property __class__. You only defined that for test.
caller is non-standardised and really brittle; best avoided. Much better to pass the value explicitly:
MyObject.prototype.test= function() {
element.addEventListener('click', function() {
globalFunc(MyObject);
});
};
or:
MyObject.prototype.test= function() {
var myclass= arguments.callee.__class__;
element.addEventListener('click', function() {
globalFunc(myclass);
});
};
Functions are not "owned" in JavaScript. Functions are first class objects in JavaScript, which means they can be passed around like any other variable and assigned as properties of many objects.
this inside a function body will give you a reference to the object on which the function was called, if called as a method (e.g. myObj.myMethod()) or via the Function prototype's call() or apply() methods (e.g. myFunc.call(myObj)). Inside a function called on its own (e.g. myFunc()), this will usually be the global object (the same as window in most browsers).
Short answer, there really is no way to do what you are trying to do. Javascript just doesn't work like that.
Long answer...
Almost every programmer I've met who has come to JavaScript from a language that uses a classical object model has tried to do what you are doing: emulate the classical OOP style they are used to with Javascript.
There is no shame here, I did this, even the famous Douglas Crockford experimented with this, and then later gave it up. I did too. I still think it was a neccessary thing for me to try this stuff in order to really learn the langauage
Do not be fooled by the curly braces, and familiar looking operators. At it's core it has very little in common with Java! JavaScript is a functional langauge: functions are objects in their own right.
Bluntly: There is no way to do what you are trying to do - have objects with private properties you can access through this
Saying that is easy :) Understanding how to apply that is more difficult
There are no classes, there are only objects.
Objects only have properties.
Properties of objects may have values.
Those values may be functions.
this is set when the function gets called, it could be anything
Closure is the only way to achieve true privacy in JavaScript. Everything else either leaks into some enclosing scope, relies on obfuscated property names, or can be circumvented in some way by the caller, or leaks memory even after your objects are no longer referenced (as there are no destructor function to do your clean-up in).
What you are doing is still a good thing to try and do, by attempting it you will become a better JavaScript programmer by figuring out:
why you can't do some of it
more importantly why you probably shouldn't