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.
Related
In this related question, the answer for setting a constructor is like so:
function OldClass() {
alert("old class with incomplete constructor")
}
function ChangedClass(){
alert('new class with same prototype and more complete constructor');
}
ChangedClass.prototype = OldClass.prototype;
var theClassIWant = new ChangedClass();
This is not really setting the constructor, it's making a new object and inheriting the prototype. How can I set the constructor of an Object without changing to a new Object? Is it possible?
P.S. I want something like this:
//non working code
var Foo = function() {}
Foo.prototype.constructor = function(bar_val) {
this.bar = bar_val;
}
var fez = new Foo("cat")
console.log(fez.bar); // outputs: cat;
constructor is a property of the constructor function's prototype, which points to the function itself by default.
When you modify Foo.prototype.constructor, this property points to another function. However, the constructor function Foo remains. So when you create an object, you are still instantiate the constructor function Foo, not the one Foo.prototype.constructor points to.
This is why your code doesn't work. Then let's talk why we need to change the constructor and how to.
In ES5 and before, we take advantage of prototype to simulate a class inheritance mechanism. Which looks like this:
// parent class
function Person(name) {
this.name = name
}
// define parent method
Person.prototype.say = function() {
console.log(this.name)
}
// child class
function Coder(name, major) {
Person.call(this, name) // inherits parent properties
this.job = 'Coding'
this.major = major
}
// inherits properties and methods from parent prototype
Coder.prototype = new Person()
// define child method
Coder.prototype.work = function() {
console.log('I write ' + this.major)
}
// instantiate a child object
var me = new Coder('Leo', 'JavaScript')
console.log(me) // {name: "Leo", job: "Coding", major: "JavaScript"}
Everything looks perfect, but there a problem:
console.log(me.constructor) // Person
What? Isn't me constructed with Coder? Why?
Go back to the first line of this answer, read it again: constructor is a property of the constructor function's prototype.
Originally Coder.prototype.constructor is Coder itself. But with this line: Coder.prototype = new Person(), it got changed. Coder.prototype.constructor now equals Person.prototype.constructor, which is Person.
Well, some would say, try instanceof. Yes, that works:
me instanceof Coder // true
But it's also true for Person, because Coder is a sub class of it:
me instanceof Person // true
This doesn't make sense so we need to fix the constructor problem. That's why we use Coder.prototype.constructor = Coder after Coder.prototype = new Person(), to get back the original constructor.
Full code looks like this:
// parent class
function Person(name) {
this.name = name
}
Person.prototype.say = function() {
console.log(this.name)
}
// child class
function Coder(name, major) {
Person.call(this, name) // inherits parent class
this.job = 'Coding'
this.major = major
}
Coder.prototype = new Person() // inherits parent's prototype
Coder.prototype.constructor = Coder // get back constructor
Coder.prototype.work = function() {
console.log('I write ' + this.major)
}
myClass = {
constructor: function(text){
console.log(text);
}
}
let ob = Object.create(myClass);
ob.constructor("old constructor "); //Output: old constructor
myClass.constructor = function(){
console.log("new constructor ")
}
ob.constructor(); //Output: new constructor
You can do this with every property or function of a class.
Watch this Video here. It explains it perfectly.
I've recently seen JavaScript-code in which getter- and setter-methods where defined on the prototype-object on a constructor method.
For example:
Person.prototype.getFirstName = function() {
return this.firstName;
}
Is this correct?
I mean:
'this' points to the describer of the the object upon which the method was called.
What happens when I call ...
console.log(Person.getFirstName());
... without making an object prior?
Moreover:
Is there a general rule when to attach properties to the constructor-method and when to attach to the prototype-object?
From my view is attaching to the prototype correct if you don't necessarily have to create an object. Because the method doesn't use values of an object which have to set object-individually.
What happens when I call ...
console.log(Person.getFirstName());
... without making an object prior?
You get an error, Person has no getFirstName property.
Is there a general rule when to attach properties to the constructor-method and when to attach to the prototype-object?
Yes:
If you want the method to be available on objects created via the constructor function (e.g., on obj in var obj = new Person()), put the method on the constructor function's prototype object (shared) or assign the method to the object within the constructor by assigning to a property on this (not shared).
If you want the method to be available on the constructor function itself, the way some of the built-in JavaScript methods are (like Date.parse or Object.create), assign it to a property on the constructor:
Person.doSomething = function() {
// ...
};
Those methods are not specific to objects created by the constructor (this within them is a reference to the constructor function itself, unless you do something to make it different).
If you're coming from a class-based language, a very loose analogy is that properties (including methods) you assign to the prototype object on the constructor or to this within the constructor are instance properties, and ones you assign to the constructor itself are static properties. It's a very loose analogy, but sometimes helpful.
Here's a full example in the old ES5 and earlier syntax:
function Person(name) {
this.getName = function() {
return name;
};
}
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.getName());
};
Person.copy = function(other) {
return new Person(other.getName());
};
// Usage example
var joe = new Person("Joe");
joe.sayHello();
console.log("joe's name is " + joe.getName());
var joeagain = Person.copy(joe);
joeagain.sayHello();
In the above:
getName is available on instances created by new Person. It's created separately for each object and is not shared with other instances. It lets us get the person's name, which in this example is read-only.
sayHello is available on instances created by new Person because they inherit it from their prototype, which is assigned to them by new Person from the Person.prototype property. It's shared between instances.
copy is not available on instances (joe.copy() would be an error), it's a property on Person itself. In this case we use it to copy instances, but it could be for anything.
Prototypes get inherited by instantiating it with new keyword
So the code
var Person = function(){}
Person.prototype.getFirstName = function() {
return this.firstName;
}
Person.prototype.firstName = "test";
console.log(Person.getFirstName()); // Throw Uncaught TypeError
Correct method is:
var Person2 = new Person()
Person2.getFirstName() // "test"
To test this Object:
so Prototype of Person is set to this Object of Person2 (Inheritance)
This is similar to Class as in Java or C# language.
Personally I'm not fond of adding a getter to the prototype if all you want is to access the instance values. You can already access those values via the instance:
function Person(name) {
this.firstname = name;
}
Person.prototype.getFirstname = function() {
return this.firstname;
}
var p = new Person('o-o');
console.log(p.firstname); //o-o
console.log(p.getFirstname()); //o-o
You could add a getter to the prototype to pass a different context:
var firstname = Person.prototype.getFirstname.call({firstname: 'other'});
console.log(firstname); // other
If so it'd make sense to add a getter to the prototype to add up some computation:
Person.prototype.getFirstname = function() {
return new Date().toISOString + ': ' + this.firstname;
};
If that strategy suits your needs, you could orchestrate your code differently:
var common = {
getValue = function(propName) {
return new Date().toISOString() + ': ' + this[propName];
}
};
function Person(name) {
this.firstname = name;
}
$.extend(Person.prototype, common); //jQuery extend
var p = new Person('o-o');
console.log(p.firstname); //o-o
console.log(p.getValue('firstname')); //today: o-o
Extend your prototype with a versatile common: add logging information during development and drop those superfluous logging information in production.
I want to add a static function to a class in EcmaScript 5 JavaScript. My class definition looks as follows:
var Account = {};
Object.defineProperty(Account, 'id', {
value : null
});
And I would create a new instance like this:
var theAccount = Object.create(Account);
theAccount.id = 123456;
Now I want to add a static function to the Account class. If I had created the Account class using a constructor function and the prototype property like this:
var Account = function () {
this.id = null;
};
...I could just do:
Account.instances = {};
Account.createInstance = function () {
var account = new Account();
account.id = uuid.v4();
Account.instances[account.id] = account;
return account;
};
But since I am using Object.defineProperty and not the prototype property to add members, Account.instances and Account.createInstance would also be instantiated when calling Object.create and therefore be properties of the instance.
How do i add a static member to a class when using EcmaScript 5 style object creation?
For ES 5 if you want static methods:
// A static method; this method only
// exists on the class and doesn't exist
// on child objects
Person.sayName = function() {
alert("I am a Person object ;)");
};
// An instance method;
// All Person objects will have this method
Person.prototype.setName = function(nameIn) {
this.name = nameIn;
}
see #https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/
You can't.
My class definition looks as follows var Account = {};
That's not a class (if we would call the classical model like that), but just a prototoype object. And as you only have that, you will need to use other variables for static members like an instances-cache or a create function:
var Account = {...};
var instances = [];
function createAccount(){...}
You could namespace them, of course:
var Account = {
proto: {...},
instances: [],
instantiate: function create(){...}
};
...but that looks very close to the classical pattern, doesn't it? The only difference would be that you had a create function on a namespace object instead of a constructor function as the namespace object.
You might also be interested in the question Object.create Prototype Chains, where I have discussed a full inheritance model working like that, with create and inherit methods that all "class objects" inherit from a base.
Further answer on your question in the comments:
Doesn't Object.create make the new operator obsolete in EcmaScript 5?
No. The new keyword does two things: Set up the prototype chain of the new instance, and apply the the constructor function. Object.create does only the first thing, so you can use it when you dont need a function (or dont want it to execute).
In your case, you have such a function, so the classical model wouldn't be wrong here as well. See also Using "Object.create" instead of "new".
You seem to have some different things mixed up. The prototype is going to be shared fallback properties. If you want to define a static (I assume by what you're doing you mean non-writable property?) you can use defineProperty in the constructor.
function Account(){
Object.defineProperty(this, 'id', {
value: uuid.v4()
});
Account.instances[this.id] = this;
}
Account.instances = {};
Account.prototype.id = null;
var account = new Account;
But since I am using Object.defineProperty and not the prototype
property to add members, Account.instances and Account.createInstance
would also be instantiated when calling Object.create and therefore be
properties of the instance.
Any static properties or methods declared on the source object will not be deemed properties of the instance - they will be read from the prototype.
var obj = {};
obj.static = function() { alert('hello'); }
var instance = Object.create(obj);
instance.ownProperty = 'hello';
alert(!!instance.static); //true - it has .static
alert(instance.hasOwnProperty('static')); //false - but it's not its own
alert(instance.hasOwnProperty('ownProperty')); //true
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.
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.