When modifying constructor function definition after it was already used to create an object, do the object properties get overwritten as if the object was recreated on its own? Similar to how prototype chain keeps methods updated when prototype gets modified.
function ParentFunction(a){
this.a = a;
this.method = function(){console.log(this.name);}
}
var parent = {};
parent.__proto__ = ParentFunction.prototype;
ParentFunction.call(parent,1);
//child linking
var child = {};
child.__proto__ = parent; // child now has a and method
// changing parent constructor
parent.__proto__.constructor = function ParentFunction(a){
this.a = a;
this.method = function(){console.log("new");}
}
// this does not change child.method function. why not?
Don't use proto as it's deprecated, use prototype in conjunction with Object.create e.g:
SomeClass.prototype = Object.create(SomeOtherClass.prototype);
Don't put methods inside of your constructor. By doing so, that method will be created for each instance. Put that method on a prototype so members can share it e.g.
SomeClass.prototype.someMethod = () => {...}
When it comes to your question, it has been answered in one of the comments already.
If you want to change a method for all instances of a type then don't attach the method to the instance of the object with the constructor function, attach it to the prototype.
function Obj() {
this.x = 5;
}
Obj.prototype.method = function () {
console.log(`Original method displays the value of x: ${this.x}`);
}
let instance = new Obj();
instance.method();
Obj.prototype.method = function () {
console.log(`New method displays the value of x times 2: ${this.x * 2}`);
}
instance.method();
Related
I find this is most recommended way to do inheritance in javascript.
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
}
what if I already have methods in child's prototype, aren't they will overwrite, shouldn't we preserve them.
function extend(Child, Parent) {
var c = child.prototype;
var oldProto = new C();
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
for(var i in oldProto ){
Child.prototype[i] = oldProto[i]
}
}
I'm not sure if this is any good to you, but it's well important to remember: prototypes are not the same things as classes. What you're doing is trying to make JS behave like a traditional OO language, which is trying to teach a dolphin to dance ballet, or forcing a tiger to become vegan: Admirable, but destined to end in tears.
I can't really see why you'd want to use the extend function to do whatever it is you're trying to do. Why not simply use this:
function Parent()
{};
function Child()
{};
//augment parent proto
Parent.prototype.parentMethod1 = function()
{};
//set Child's proto to Parent
Child.prototype = new Parent();
Child.prototype.constructor = Child;
//Then augment the Child's prototype
Child.prototype.childMethod1 = function()
{};
var foo = new Child();
foo.parentMethod1();//works
foo.childMethod1();//works, too
IMO, this solves the problem entirely. Sure, it's a tad more verbose, but OOP always is.
The pattern you're trying to achieve is called multiple inheritance. And it's highly not recommended for the use because of the issue you're experiencing, called diamond problem. Just use mixin pattern instead.
The code below is the one of the best I have seen for doing inheritance in JavaScript.
Object.create(proto [, propertiesObject ]) is discussed on MDN here.
Below, Jon defines a base empty object called ExtendBase then adds a function property called extend which is not enumerable which takes as its argument a single new object.
That object should contain enumerable properties such as methods and data that will be added to the base object.
He gets all the enumerable properties from the passed object, then creates an array of the necessary descriptors to pass into Object.create using those properties' names. He then uses the parent object as the prototype and resultant descriptors as new properties to be added to the child object directly in the Object.create() call.
As you can see, you can use an object argument with properties, including methods, to extend a parent without losing that passed object's properties with the result being a child object with the parent as the prototype and the enumerable objects of the passed object added directly to the child.
However, this maintains a clean prototype chain while intending to extend parent objects using other objects which are created sanely to extend the parent into a new child in a way that makes sense:
Live sample here (Press F12 in Chrome for console output, or use FireBug in FireFox, etc.)
JavaScript:
// Original Author: FireFly - Jonas Höglund - ##javascript channel
// on irc.freenode.net - see THANKS File. Updated to private data
// members and passable initial parameters by Scott Sanbar
///////////////
// Library code
///////////////
var ExtendBase = {};
Object.defineProperty(ExtendBase, 'extend', {
enumerable:false, value:function (obj) {
'use strict';
var descs = {};
Object.getOwnPropertyNames(obj).forEach(function (key) {
descs[key] = Object.getOwnPropertyDescriptor(obj, key)
});
return Object.create(this, descs);
}
});
///////////////
// Sample Usage
///////////////
function PersonObj(nam) {
return {
name:new function () {
var name = nam;
this.set = function (value) {
name = value;
};
this.get = function () {
return name;
}
},
// A person can tell you its name.
talk:function () {
return "Hello, I'm " + this.name.get();
}
}
}
;
function WorkingPersonObj(occ) {
return {
occupation:new function () {
var occupation = occ;
this.set = function (value) {
occupation = value;
};
this.get = function () {
return occupation;
}
},
// A working person also tells you their occupation when they talk.
talk:function () {
return Person.talk.call(this) + " and I am a " + this.occupation.get();
}
}
}
;
var hush = {
hush:function () {
return "I am supposed to be quiet";
}
};
var Person = ExtendBase.extend(new PersonObj('Harry'));
var WorkingPerson = Person.extend(new WorkingPersonObj('wizard'));
var wp1 = WorkingPerson.extend(hush);
console.log(wp1.talk()); // "Hello, I'm Harry and I am a wizard"
console.log(wp1.hush()); // "I am supposed to be quiet"
wp1.name.set("Elijah");
wp1.occupation.set("prophet");
console.log(wp1.talk()); // "Hello, I'm Elijah and I am a prophet"
console.log(wp1.name.get());
console.log(wp1.occupation.get());
I am using Lightbox2
https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js
And I don't understand why all the inner members of Lightbox are prototyped (Lightbox.prototype.init) and not simply members (Lightbox.init)?
If they are specific to each instance of lightbox wouldn't it be easier to use this.init?
Confused? Don't be...
Think of it this way:
Lightbox is your class definition, but it's not yet an instance.
Whatever you put directly on the class is like a static member:
Lightbox.staticFunc = function() {
// "this" will not point to instance object
};
Whatever you put on its prototype is a shared instance member:
Lightbox.prototype.instanceFunc = function() {
// "this" will point to object instance so members can be accessed
};
When you create an instance of a class, all instance members are accessible throught this keyword, but static ones through class definition:
var someData = Lightbox.staticFunc();
var l = new Lightbox();
l.instanceFunc();
Does this clear you understanding of prototype members?
Lightbox code then
The code that you've been looking at means this:
// this is a constructor that accesses instance properties (using "this")
// ------
// since properties are accessed via "this.something" means that they are
// not shared between instances but are part of one particular instance
// ------
function Lightbox(options) {
this.options = options;
this.album = [];
this.currentImageIndex = void 0;
this.init();
}
// adding an instance method that will be accessible to lightbox object instance
// that's why it can also access instance members (using "this")
// ------
// all functions that are defined on the prototype are shared between
// all instances so they consume less resources because not every
// object instance created them separately.
// ------
Lightbox.prototype.init = function() {
this.enable();
return this.build();
};
But some parts of this code are a bit confusing i.e.
LightboxOptions = (function() {
function LightboxOptions() {
this.fileLoadingImage = 'images/loading.gif';
this.fileCloseImage = 'images/close.png';
this.resizeDuration = 700;
this.fadeDuration = 500;
this.labelImage = "Image";
this.labelOf = "of";
}
return LightboxOptions;
})();
LightboxOptions class is contained within a function closure even though it doesn't define any private data, so the outer immediately executing function could be omitted in this example while having identical results:
LightboxOptions = function() {
this.fileLoadingImage = 'images/loading.gif';
this.fileCloseImage = 'images/close.png';
this.resizeDuration = 700;
this.fadeDuration = 500;
this.labelImage = "Image";
this.labelOf = "of";
};
It would of course be possible to define those functions in a constructor using this but then they wouldn't be shared between instances hence every object instance would define the same function hence consuming more resources. So this is not the same although from the execution point it does look the same:
CustomClass = function() {
this.prop = true;
};
CustomClass.prototype.method = function() { alert("I'm shared."); };
is slightly different than:
CustomClass = function() {
this.prop = true;
this.method = function() { alert("I'm duplicated in every instance."); };
};
The later consumes more resources while function is defined for every object instance.
...and a bit more to completely clear this thing
Suppose we have this class definition:
var C = function() {
this.prop = true;
this.method = function() { console.log("Per instance method"); };
}
C.prototype.method = function() { console.log("Shared instance method"); };
What happens here if we call these lines of code
var a = new C();
var b = new C();
a.method();
b.method();
delete a.method;
a.method();
b.method();
What do you think the output would be? You should get at least a little confused what happens after delete? Which method will get deleted? Per instance? Shared? Both? Well as it should be per instance method gets deleted on object instance a, that's why afterwards it reports that the shared method has been called. But only on a. b still has its own per instance method.
So without any further ado, output looks like this:
Per instance method // a.method
Per instance method // b.method
Shared instance method // a.method
Per instance method // b.method
What about prototype properties
These are different. When you create an object instance all those properties get copied to every object and are not shared. So whatever you do on them within the realm of a particular object will not get reflected to others.
If you'd then delete such property on a particular object it would still be available with its initial value as it was when object got instantiated.
var C = new function() {};
C.prototype.prop = 1;
var a = new C();
var b = new C();
a.prop = 10; // does not change the value of "b.prop"
delete a.prop; // "a.prop" is now back to 1
If they are specific to each instance of lightbox wouldn't it be
easier to use this.init?
They shouldn't be that's why they are putting everything in prototype object. When you use prototype, all methods still become available to you only that they do not become instance members.
JavaScript works with prototype chain, when it sees a method, it searches through the prototype chain till it finds the specified method. This process goes till the final Object object if not found in the middle.
You should only create instance members (via this) that you think are reasonable or needed because it adds an overhead (computational waste) if you put unnecessary methods using this keyword eg instance members.
Here is an example:
var Box = function() {
this.id = generateUniqueId();
};
Box.prototype = {
add:function(parent) {
parent.appendChild(this.elm);
}
};
var NewBox = function() {
this.newThing = true;
};
NewBox.prototype = new Box();
NewBox.prototype.remove = function() {
this.elm.parentNode.removeChild(this.elm);
};
var a = new NewBox();
var b = new NewBox();
alert(a.id); // alerts 0
alert(b.id); // also alerts 0! :#
I would like to have a and b (basically each time I create a NewBox) have their own id. I understand that NewBox is using prototypal inheritance and is thus inheriting from a single instance of the generated id that it gets from Box, but I would like it so that each NewBox gets its own id without having to explicitly say that inside the NewBox constructor.
Maybe it's impossible or I'm doing something really wrong, so please help!
Thanks a lot!
In your example, the Box constructor gets executed only when you set the NewBox.prototype object.
You could workaround this by calling the Box constructor function inside NewBox with the Function.prototype.apply method, to set the this value and forward all the argument values, for example:
//..
var NewBox = function() {
Box.apply(this, arguments);
this.newThing = true;
};
//..
var a = new NewBox();
var b = new NewBox();
// assuming your `generateUniqueId` function
// increments a number
alert(a.id); // will alert 1
alert(b.id); // will alert 2
Now, each time the NewBox constructor is called to create a new object instance (new NewBox();), it will call the Box function to apply all its logic on it. This will help you to avoid repeating the logic of the parent constructor over and over.
The apply, is used call the "parent" constructor function setting the this value to the object instance that is being created by the NewBox constructor and we pass all arguments provided to this function.
Your example also shows a common problem, when you express inheritance relationship through NewBox.prototype = new Box(); the Box constructor gets called and it has side effects, this new object will be initialized and your generateUniqueId function will be executed for the first time, if you want to avoid that, you need to either use a temp constructor, just to make a new object that inherits from Box.prototype, or use the new ECMAScript 5 Object.create method for the same purpose, for example:
function inherit(o) {
function Tmp() {}
Tmp.prototype = o;
return new Tmp();
}
//.....
NewBox.prototype = inherit(Box.prototype);
Or:
NewBox.prototype = Object.create(Box.prototype);
In that way, you express the same inheritance hierarchy without running your Box constructor that first time, avoiding any side effect that it might cause.
At last but not least, whenever you replace a function's prototype property is always recommended to restore the constructor property of this new prototype object, otherwise it will point to the wrong function.
In your example, since you replace the NewBox.prototype with a new instance of Box, the NewBox.prototype.constructor property will point to Box, (your instances are affected, e.g. a.constructor === Box; // true) instead of to NewBox as you would expect, we need to set it back, e.g.:
NewBox.prototype = someObject; // as any of the examples above
NewBox.prototype.constructor = NewBox;
You could abstract those details into a function, as I did in the inherit function above:
function inherits(child, parent) {
var obj, Tmp = function () {};
Tmp.prototype = parent.prototype;
obj = new Tmp();
child.prototype = obj;
child.prototype.constructor = child;
}
//...
// instead of setting the `NewBox.prototype` manually
inherits(NewBox, Box); // "NewBox inherits from Box"
//...
Maybe it's impossible or I'm doing something really wrong, so please help!
You're doing something wrong.
If you want instance-specific values, initialize them in the constructor, not the prototype.
Matt Ball has the right idea. Instead try:
var Box = (function(){
var numberOfBoxes = 0;
function() {
this.id = numberOfBoxes++;
}
})();
Or in the case you want all your (different) classes to have unique ids:
var generateUniqueID = (function(){
var runningCount = 0;
return function (){
return runningCount++;
}
})();
var Box = function() {
this.id = generateUniqueId();
};
var NewBox = function() {
this.id = generateUniqueId();
this.newThing = true;
};
I AM trying to understand js prototype property: my sample code
function Container(param) {
this.member = param;
}
var newc = new Container('abc');
Container.prototype.stamp = function (string) {
return this.member + string;
}
document.write(newc.stamp('def'));
function Box() {
this.color = "red";
this.member = "why";
}
Container.prototype = new Box();
Box.prototype.test = "whatever";
var b = new Box();
document.write(newc.test);
here the last line is undefined - even though Container's prototype is a Box and Box's prototype has a property test, why is the newc which refers to test in Box doesnt work? can any one please explain how the 'Prototype' works in my above context.
Thanks...
You are setting Container prototype to Box() after the newc instance was already created.
Reorder the statements as follows:
function Container(param) {
this.member = param;
}
function Box() {
this.color = "red";
this.member = "why";
}
Container.prototype = new Box();
Box.prototype.test = "whatever";
Container.prototype.stamp = function (string) {
return this.member + string;
}
//Here the containers prototype setup is complete.
var newc = new Container('abc');
document.write(newc.stamp('def'));
document.write(newc.test);
If sounds like you want to know WHY it is behaving the way it is, and not just "fix" the code. So here's what's going on.
As you saw, if you change the prototype of "Container", you will actually change the properties for new objects AND objects already instantiated. So:
function Container(param) {
this.member = param;
}
var newc = new Container('abc');
// setting a new property of the prototype, after newc instantiated.
Container.prototype.stamp = function (string) {
return this.member + string;
}
// This already-instantiated object can access the stamp function
document.write(newc.stamp('123')); // output: abc123
So there's no problem with the above, as long as you don't call the new method before it's defined. Now the next point. Add this to the above:
// Our Box object
function Box() {
this.color = "red";
this.member = "why";
}
Container.prototype = new Box();
var newd = new Container('fgh');
document.write(newd.stamp('456')); // output: ERROR
Error! But that makes sense, right? You totally wiped out the "Container" prototype and replaced it with the one from "Box", which has no "stamp" function.
I am going to assume you want "Box" to inherit from "Container". That would be logical from the naming convention. If you want to do that, replace the previous section with this:
// Our Box object
function Box() {
this.color = "red";
this.member = "why";
}
// This inherits from Container. Note that we can
// do this before or after we declare "Box"
Box.prototype = new Container();
Box.prototype.test = "Whatever";
var b = new Box("jkl"); // note: "jkl" is ignored because "Box" sets "member" to "why"
document.write(b.test); // output: Whatever
document.write("<br>");
document.write(b.stamp("345")); // output: why345
So now we have a "Box" that can call its own methods and parameters, and also call them from its parent "Container".
So the big picture is that an object will look at its own prototype for a method or something, and if it doesn't find it there it will look in the prototype of the thing it inherited from, and so on. The other big point is that setting something in the prototype makes it immediately available in all future AND current instances of that object.
An object does not contain a reference to its constructor which it uses to get at the prototype. If it did, then the code would work as you expected.
Instead, an object contains a reference to its prototype that is set when it is created.
From the language spec section 4.2.1:
Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s “prototype” property. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain. When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name. In other words, first the object mentioned directly is examined for such a property; if that object contains the named property, that is the property to which the reference refers; if that object does not contain the named property, the prototype for that object is examined next; and so on.
I am currently switching from AS3 to JavaScript.
I still have some trouble with understanding inheritance-concepts.
What I do not understand is why the following code is not working properly:
Base = function () {
this.coolVar = "great";
}
SmallControl = function () {
// Inheritance:
this.prototype = new Base();
this.prototype.constructor = SmallControl;
this.prototype.init = function(aMap) {
console.log('init');
console.log('coolVar?: ' + this.coolVar);
}
}
var foo = new SmallControl();
//foo.init(); // --> TypeError: foo.init is not a function
foo.prototype.init(); // --> works
If I put the prototype definitions outside of the "SmallControl"-Function everything works fine... but I don't understand that.
I think you want something like this:
// Create the super class
Base = function () {
this.coolVar = "great";
};
// Create the new class
SmallControl = function () {
};
// Set the prototype of SmallControl to be an instance of Base.
// This runs the Base constructor _immediately_ which sets up the variable
SmallControl.prototype = new Base();
// Add the init method to the SmallControl class
SmallControl.prototype.init = function(aMap) {
console.log('init');
console.log('coolVar?: ' + this.coolVar);
}
// Create an instance of SmallControl
var foo = new SmallControl();
foo.init();
prototype is only a meaningful property of constructors. The object's actual prototype (which is accessible in some environments as the property __proto__, but this is not portable) is set to be the constructor's prototype attribute at the time the object is constructed. Changes to the constructor's prototype (adding properties to the prototype) will be reflected in live objects, but not if you set Constructor.prototype to be a completely different object.
In your constructor, you're setting the prototype attribute of the constructed object (this). This attribute has no special meaning on something that's not a constructor function. When you set it outside of the function, you set it on the constructor function.