How to change the constructor of an object? - javascript

I am diving deeper into Javascript, and learning how constructor methods work.
In the code below, I would expect that I would be able to overwrite the constructor of an object, so that newly created instances would use the new constructor. However, I can't seem to make new instances use a new constructor.
Any insight as to what is going on would be greatly appreciated!
function constructorQuestion() {
alert("this is the original constructor");
};
c = new constructorQuestion();
constructorQuestion.constructor = function() { alert("new constructor");}
howComeConstructorHasNotChanged = new constructorQuestion();
Here's the fiddle: http://jsfiddle.net/hammerbrostime/6nxSW/1/

The constructor is a property of the prototype of the function, not the function itself. Do:
constructorQuestion.prototype.constructor = function() {
alert("new constructor");
}
For more information see: https://stackoverflow.com/a/8096017/783743
BTW, if you expect the code howComeConstructorHasNotChanged = new constructorQuestion(); to alert "new constructor" that won't happen. This is because you're not calling the new constructor, you're calling the old one. What you want is:
howComeConstructorHasNotChanged = new constructorQuestion.prototype.constructor;
Changing the constructor property doesn't magically change the constructor.
What you really want is:
function constructorQuestion() {
alert("this is the original constructor");
};
c = new constructorQuestion();
function newConstructor() {
alert("new constructor");
}
newConstructor.prototype = constructorQuestion.prototype;
howComeConstructorHasNotChanged = new newConstructor();
This will work. See: http://jsfiddle.net/GMFLv/1/

I think it will be same to create new object with same prototype like this:
function newClass(){
alert('new class with same prototype');
}
newClass.prototype = constructorQuestion.prototype;

Related

Inheritence in Javascript

I get a strange bug when I implement inheritence in Javascript using prototypes. I am wondering if someone can explain this. In the following code,
I am trying to derive a child class from a parent class:
parent_class=function(byref)
{
if( !parent_class.prototype._vtbl )
{
parent_class.prototype.parent_func= function(o) { return alert("parent_func"); }
parent_class.prototype._vtbl = true;
}
}
child=function(byref)
{
parent_class.call(this,byref);
if( !child.prototype._vtbl )
{
child.prototype = new parent_class;
child.prototype.child_func = parent_class.prototype.parent_func;
child.prototype._vtbl = true;
}
}
function dotest()
{
var pub = new child;
alert( pub.child_func );
var pub2 = new child;
alert( pub2.child_func );
}
dotest();
When you run the test in a browser (Firefox or IE), you get two alerts. The first one says that pub.child_func is undefined, the second one says that the pub.child_func is a valid function and is parent_class.parent_func. Why do you see this behavior. Is this a bug?
Order of execution in javascript of such construct:
function SomeClass () { body(); }
var x = new SomeClass();
is this:
new object which inherits from SomeClass.prototype is created (the prototype for the object is chosen here, before code of the constructor is executed)
body(); gets executed
created object is assigned to x
What you can do in your example is use .__proto__, although you really really should not:
child = function (byref) {
parent_class.call(this, byref);
if (!child.prototype._vtbl) {
child.prototype = new parent_class;
child.prototype.child_func = parent_class.prototype.parent_func;
child.prototype._vtbl = true;
}
this.__proto__ = child.prototype;
}
What you really should do is this:
child = function (byref) {
parent_class.call(this, byref);
}
child.prototype = Object.create(parent_class.prototype);
child.prototype.child_func = parent_class.prototype.parent_func;
child.prototype._vtbl = true;
An easier way to do JavaScript inheritance might be the factory pattern:
function Animal(name) {
return {
run: function() {
alert(name + " is running!")
}
}
}
var animal = Animal("fox");
animal.run();
function Rabbit(name) {
var rabbit = Animal(name);
rabbit.bounce = function() {
this.run();
console.log(name + " bounces");
}
return rabbit;
}
var rabbit = Rabbit("rabbit");
rabbit.bounce();
Source: http://javascript.info/tutorial/factory-constructor-pattern
Short answer: No, it's not browser mistake, it's expected behavior.
Detailed answer:
When a constructor function is called with new, reference to it's prototype is copied into objects's __proto__. Later on this property is used for prototypal lookups for this object.
Your code is really weird from point of view of javascript developer, when you modify prototype of constructor during constructor call execution. However, it works. Because, after var parent = new parent_class(); the following is true parent.__proto__ === parent_class.prototype. It is the SAME reference. Thus adding properties to parent_class.prototype is automatically relfected in parent object via prototypal lookup.
Unfortunately I can't post comments yet, so I have to reference from my answer, #RyszardFiński it is not a correct statement that prototype is defined before contructor called and can't be changed afterwards. It is the same object and unless you change the reference changes will be reflected immediately for all instantiated objects
However in child code in OP ruins references when child.prototype is assigned to a new object.
child.prototype = new parent_class;
child.prototype start pointing a to new instance of parent_class (#1). Instance references look like below
pub.__proto__ === child.prototype
pub2.__proto__ === parentInstance1
child.prototype === parentInstance2
If you remove the line of code where child.prototype is assigned everything will start working as you expect it
pub.__proto__ === child.prototype
pub2.__proto__ === child.prototype
child.prototype === child.prototype
child.prototype has properties _vtbl and child_func

Value of constructor and prototype gets changed after over writing the prototype object. Why?

I have the Director() function. I have created 2 instances AlfredH and JohnD out of Director() constructor. I did not write the prototype object.
function Director(){
this.genre = "Thriller";
}
var AlfredH = new Director();
var JohnD = new Director();
If I check the values of JohnD.constructor; and JohnD.constructor.prototype; I get Director() and Object() respectively.
But, if I add properties to prototype object of Director() like the below:
function Director(){
this.genre = "Thriller";
}
Director.prototype = {
noir: true
};
var AlfredH = new Director();
var JohnD = new Director();
and if I check the values of JohnD.constructor; and JohnD.constructor.prototype; I get Object() and Object() respectively. Can anyone explain this behavior? and the same can be extended to the value of JohnD.constructor.prototype.constructor;
var a = {
value:22;
}
then
var a = {
somethingelse:0
}
Can you guess what a.value is?
You are overwriting the prototype with another object.
Then add to that that
console.log({}.constructor)===Object;//=true
Maybe try adding it like this:
Director.prototype.noir = true;
Note that anything on the prototype is shared among instances, this is a good thing because it saves memory and instantiate the object quicker with less cpu.
When assigning a new value the value is assigned to the instance but when manipulating the value through functions it affects all instances
Director.prototype.someArray=[];
var d1=new Director();
var d2=new Director();
d1.someArray.push(22);
console.log(d2.someArray);//=[22]
More info on prototype here: https://stackoverflow.com/a/16063711/1641941

Collection/Instance objects style vie javascript without proto

I'm trying to make classical Collection/Instance model via javascript. So Collection object has some method for working with full collection and ((new Collection()) instanceof Instance) has methods to work with the instance. My code is rather simple.
var Collection = function Collection() {
this.message = "collection";
var I = Instance.bind(null, this);
I.__proto__ = this;
return I;
};
Collection.prototype = {
collectionMethod: function () {
console.log(this.message);
}
};
var Instance = function Instance(collection) {
this.collection = collection;
this.message = "instance";
};
Instance.prototype = {
instanceMethod: function () {
console.log(this.message);
}
};
// Test exec (values are like expected);
var C = new Collection();
var i = new C();
C.collectionMethod(); // collection
i.instanceMethod(); // instance
i.collection.collectionMethod(); // collection
C.newMethod(); // TypeError
i.newMethod(); // TypeError
Collection.prototype.newMethod = Instance.prototype.newMethod = function () {
console.log("newMethod: " + this.message);
}
C.newMethod(); // newMethod: collection
i.newMethod(); // newMethod: instance
But i don't want to use proto because it's not a part of standart and doesn't work in IE at all. Is there any way around in this case?
Some explanations about what's all about. For example you have a collection of users. And you want to be able find the user and create new one.
So you create new collection like
var User = new Collection();
Then you create new instance like.
var me = new User({name: "alex"});
And now you find this instance like
User.find_by_name("alex"); // === me
Also (in fact this is the main reason i'm doing this way instead of just creating something like User.new function to use it like var me = User.new({name: "alex"});) you can know who I am doing something like (if you for example have also var Dog = new Collection())
me instanceof Dog // false
me instanceof User // true
This code:
var I = Instance.bind(null, this);
I.__proto__ = this;
return I;
really doesn't make much sense. Function.bind creates a new function, so anyone calling your Collection function, in any way, will get back a function, not an object whose prototype is set to the function's prototype.
In general, if you want to create an object whose prototype is set to a specific object, you don't set __proto__ since that's not standard, as you stated. The best way is to just use Object.create (which is shimable if you want to support IE8).
var I = Object.create(this);
Also, the reason you're getting errors on newMethod is because you're trying to call them before you add them to the prototype:
Collection.prototype.newMethod = Instance.prototype.newMethod = function () {
console.log("newMethod: " + this.message);
}
C.newMethod(); // should work now
i.newMethod(); // should work now
So seems like it's impossible for now. More information can be found here.
How do I inherit javascript functions ?

Object Oriented JavaScript programming

I have been trying to learn OOP with JavaScript before I start attempting to learn backbone.js.
I want to be able to data bind but I can't seem to get it to work.
I've just made a simple protoype of a budget website that you can put in a budget and input how much you've spent, and it will show if you've gone over.
function BudgetItem(spent, budget){
this.setSpent = function(spent){
this.spent = spent;
}
this.setBudget = function(budget){
this.budget = budget;
}
this.getSpent = function(){
return this.spent;
}
this.getBudget = function(){
return this.budget;
}
}
function BudgetType(type){
this.getType = function(){
return type;
}
}
BudgetType.prototype = new BudgetItem();
$(document).ready(function(){
var food = new BudgetType('food');
$('.budget').html(food.getBudget());
$('.editbudget').change(function(){
food.setBudget($('.editbudget').data())
});
})
That's my code thus far. I'm not sure if I'm doing it right. Am I supposed to extend things? Also, can someone explain how to dynamically data bind without a library?
First I'll give you some theory. A Javascript function is a dynamic object, just like Object is, and a new instance can be created using the new keyword much like you are doing in your listener. When this happens, the function itself will run as a constructor while the this keyword will be bound to the newly created object. What you're doing above then is in fact adding new properties on the fly as you're passing in their values for the first time... which is fine, but not very clear to another reader.
Now for the tricky part. Every function has a link to a "hidden" Prototype object. This is an anonymous (not accessible by name) object created by the JavaScript runtime and passed as a reference to the user object through the prototype property. This Prototype object also has a reference to the function through its constructor property. To test what I'm saying for yourself, try the following:
BudgetItem.prototype.constructor === BudgetItem // true
Putting it all together, you can now think of functions as constructors to (hidden) classes that are created for you behind the scenes, accessible through the function's prototype property. So, you could add the fields to the Prototype object directly as so:
function BudgetItem(spent) {
this.spent = spent
}
BudgetItem.prototype.setSpent = function(spent) { this.spent = spent };
BudgetItem.prototype.getSpent = function() { return this.spent };
Another problem is inheritance and passing parameters to the constructor. Again, your version is valid but you lose the ability to pass the spent and budget values when initializing a BudgetType. What I would do is forget prototypes and go:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.type = type;
return instance;
}
This is close to what Scott Sauyet suggested above but more powerful. Now you can pass both parameters (and more) and have a more complicated inheritance tree.
Finally, what you can do is create private (or pseudo-private, more accurately) properties by providing a getter to an otherwise automatic variable (one passed as an argument or initialised inside the function). This is a special feature of the language and it works like so:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.getType = function() {
return type;
}
return instance;
}
Now you can access the 'type' passed in the constructor by obj.getType() but cannot override the initial value. Even if you define obj.type = 'New Value' the getType() will return the initial parameter passed because it has a reference to another context which was created when the object was initialised and never got released due to the closure.
Hope that helps...
if you want all instances of objects to reference the same members/values you can use a closure:
// create a constrctor for you object wrapped in a closure
myCon = (function() {
// define shared members up here
var mySharedObj = new function () {
this.member = "a";
}();
// return the actual constructor
return function () {
this.mySharedObj = mySharedObj;
}
}());
// create two instances of the object
var a = new myCon();
var b = new myCon();
// Altering the shared object from one
a.mySharedObj.member = "b";
// Alters it for all
console.log(b.mySharedObj.member);
If you want to build objects from other objects(sort of like other languages' class whatever extends baseClass), but do not want them to share values via reference(instead a clone of values), you can use something like the following:
Object.prototype.extendsUpon = (function (_prop, _args) {
return function (base) {
for (var key in base) {
if (_prop.call(base, key)) {
this[key] = base[key];
}
}
function con(child){
this.constructor = child;
}
con.prototype = base.prototype;
this.prototype = new con(this);
this.__base__ = base.prototype;
var args = _args.call(arguments);
args.shift();
base.constructor.apply(this, args);
}
}(Object.prototype.hasOwnProperty, Array.prototype.slice));
Then to build objects ontop of objects:
// Base Object Constructor
function Fruit(name) {
this.fruitname = name;
}
Fruit.prototype.yum = function() {
return "I had an " + this.fruitname;
}
// Object constructor that derives from the Base Object
function Favorite() {
// Derive this object from a specified base object:
// #arg0 -> Object Constructor to use as base
// #arg1+ -> arguments passed to the BaseObject's constructor
this.extendsUpon(Fruit, "apple");
// From here proceed as usual
// To access members from the base object that have been over-written,
// use "this.__base__.MEMBER.apply(this, arguments)"
}
Favorite.prototype.yum = function() {
return this.__base__.yum.apply(this) + " and it was my favorite";
}
var mmm = new Favorite();
// Outputs: "I had an apple and it was my favorite"
mmm.yum();

Javascript object that inherits QWidget

I am doing something in Javascript and Qt, and for my purpose, I need a javascript object that inherits QWidget. So far, I have tried the following:
function Test()
{
QWidget.call(this);
this.button = new QPushButton("test");
this.layout().insertWidget(1,this.button);
this.button.clicked.connect(this.slot);
}
Test.prototype.slot = function()
{
print("Test button clicked");
}
Test.prototype = new QWidget();
I instantiate object from the class "Test" and with the call of the show() method, I get the widget:
var testVariable = new Test();
testVariable.show();
However, I get the following interpreter error:
Error: In run/evaluate: TypeError:
Function.prototype.connect: target is
not a function
If I change the line this.button.clicked.connect(this.slot); to call a static method defined like this:
this.button.clicked.connect(Test.slot);
...
...
Test.slot = function () { /* code */ }
the program runs fine, but static method is a no-no. I don't want anyone to call slot() except the instantiated object.
What is wrong with this picture? Has anyone had experience with Javascript objects inheriting Qt objects?
Thanks in advance
Ok I think I might figured this out. So the magic here is:
Test.prototype = new QWidget();
needs to be before the constructor, also the constructor has to take "parent" argument too. And last but not least, in connect() there are two arguments: first is which class contains the slot (in my case it is this) and the name of the slot with this pointer.
So, having this in mind, the above code will look like this:
Test.prototype = new QWidget();
function Test(parent)
{
QWidget.call(this, parent);
this.button = new QPushButton("test");
this.layout().insertWidget(1, this.button);
this.button.clicked.connect(this, this.slot);
}
Test.prototype.slot = function()
{
print("Test button clicked");
}
This is to whom it may concern.

Categories

Resources