I created a simple class as follows:
var Class = function() {};
Class.prototype.testObj = {a:2, b:3};
Now if I do console.log(Class.testObj) I get undefined. But if i create instance of that Class as follows:
var instance = new Class();
console.log(instance.testObj)
I get expected output.
In my understanding, all variables are treated as Objects and have prototype property. When some key is not found in the object, prototype chain is traversed to look for the key-value pair. But in case of Class, it is not traversing the prototype chain.
What am I missing? What additional does new keyword do such that property is accessible?
You must be clear that Class() is your constructor, not an instance object. so Class.testObject will return undefined because Class doesn't have that property.
You can think of a prototype as a recipe for an object. Almost every function has a prototype property that is used during the creation of new instances and that prototype is shared among all of the object instances
A constructor is simply a function that is used with new to create an object
When you do this var instance = new Class(); It means you are creating an instance object of Class, hence instance will inherit prototype properties of Class.
Test:
console.log(instance instanceof Class); // => true
console.log(instance.constructor === Class); // => true
console.log(Object.prototype.isPrototypeOf(Class)); // => true
console.log(Class.prototype.isPrototypeOf(instance)); // => true
Whenever you create an object, such as a function, it inherits from the Object constructor, so this
var Class = function() {};
is an object, and it has it's own prototype property that can be accessed with
Class.prototype
and you can create new properties on the prototype object with
Class.prototype.testObj = {a:2, b:3};
if you wanted to access that, you would actually have to do
console.log(Class.prototype.testObj)
as that's where it is, a property added to the prototype property of Class.
When you use the new keyword some magic happens, a whole new object is created (called an instance), and that object inherits from Class.prototype, from the docs
When the code new Class(...) is executed, the following things happen:
A new object is created, inheriting from Class.prototype.
The constructor function Class is called with the specified arguments
and this bound to the newly created object. new Class is equivalent to
new Class(), i.e. if no argument list is specified, Class is called
without arguments.
The object returned by the constructor function becomes the result
of the whole new expression. If the constructor function doesn't
explicitly return an object, the object created in step 1 is used
instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation
process.)
You have misunderstood the relationship between Class and Class.prototype.
Class is a constructor. Strictly speaking, JavaScript does not differentiate between constructors and general purpose functions, but that's not important right now. Class.prototype is the prototype of instances of Class. That is to say, when you instantiate the class - var x = new Class() - Class.prototype is attached to x as a delegate object.
What this means is that lookups to x will propagate up the delegate chain, and reference variables on that prototype instance.
In my understanding, all variables are treated as Objects and have prototype property.
This is incorrect. JavaScript has many primitive types, and only functions have a property named 'prototype'. Prototypical delegates are only attached to reference types (objects, arrays, and functions) when they are created using new with a constructor or the ECMAScript 5 object.create() notation.
Incidentally, Class does have a prototype delegate, as it's an instance of Function(). One instance of a delegate function method is apply().
Simply put, Class is not an instance of Class, it's an instance of Function, so why would it inherit Class.prototype? The first link in Class's prototype chain is Function.prototype.
We can see that if we pick a function from Function.prototype, such as call, it exists on Class.
typeof Class.call; //function
Object instances have their prototype chain setup with their constructor's prototype. Therefore, nnly objects that are instances of Class will have their prototype chain setup with Class.prototype.
You added the object to the prototype,
If you did console.log(Class.prototype), you would see the object.
The new keyword instantiates the object, which brings out properties attached to its prototype as properties of the instantiated object.
Because the newly instantiated object is actually inheriting the prototype of the parent object, not the parent object itself.
Related
I did a quick search but can't seem to find the answer to this question, just things referring to copying function prototypes when inheriting.
Why not add properties to the constructor functions prototype obj, instead of using the this keyword. I'm sure there is a reason not to, but I'm trying to understand the nuances of javascript better. for example in normal prototype inheritance you would "this".
function Dog(name,age,breed){
this.name=name;
this.age=age;
this.breed=breed;
}
Dog.prototype.bark=function(){console.log("bark bark bark");}
let spike=new Dog("Spike",6,"lab");
let rover=new Dog("Rover",8,"poodle");
//^with the constructor function, no instance has the bark function but
//but refers to the same function on the constructor prototype obj, so the
//same function isn't being duplicated. However new keyword changes the
//context of this to refer to the obj so these properties are duplicated
//on every instance.
//I'm curious as to the reason why new doesn't change this to refer to the
//prototype obj for example and then have the instance refers its
//constructor's prototype like with the bark function?
//for example why isn't this a common pattern and what are the reasons I
//should use it.
function Dog(name,age,breed){
Dog.prototype.name=name;
Dog.prototype.age=age;
Dog.prototype.breed=breed;
}
let spike=new Dog("Spike",6,"lab");
let rover=new Dog("rover",8,"poodle");
//I feel like the above code would be more DRY, I'm sure there is a reason
// this isn't common and I'm curious as to why
When you have properties on the prototype, you are overriding the properties with new values each time you instantiate the Class i.e in your example, from the two statements below :
let spike=new Dog("Spike",6,"lab");
let rover=new Dog("rover",8,"poodle");
Here, according to your expectation, spike.name should be Spike and rover.name should be rover, but if you execute this code and check, both of them are rover.
The properties of spike are overridden by the properties of rover when you created the new instance rover.
Each time, you create a new instance, the properties are overridden, the reason for this is
methods or properties attached to the prototype are created only once and are inherited to their sub classes every time a new instance is created.
The reason we create a Constructor function and new instances from it is because we have different properties for each instance like Spike and rover. In case of methods, methods are generic to a Constructor which can be reused for all the instances which need not be created every time a new instance is created, hence, we attach them to the prototype instead of defining it with this keyword in the Constructor.
On MDN they state the following:
Properties are variables contained in the class; every instance of the
object has those properties. Properties should be set in the prototype
property of the class (function) so that inheritance works correctly.
Looking at the sections I've set to bold I assumed this meant:
myClass.prototype.newProperty = ...
However their example shows the following:
function Person(firstName) {
this.firstName = firstName;
console.log('Person instantiated');
}
var person1 = new Person('Alice');
var person2 = new Person('Bob');
// Show the firstName properties of the objects
console.log('person1 is ' + person1.firstName); // logs "person1 is Alice"
console.log('person2 is ' + person2.firstName); // logs "person2 is Bob"
In their example they're adding the property 'firstName' directly to the class/function using 'this'.
Does this mean:
a) That the function declaration is the prototype? I.e. myClass is the prototype which also has a property prototype which by default is set to Object?
b) That using 'this.' in the function declaration does actually add the property to the myClass.prototype
Edit: Updated title
JavaScript doesn't have classes, stop thinking in classes, because that doesn't work with JavaScript (yes, even with the ES6 class syntax, it's just sugar, there aren't actual classes in JavaScript).
By adding the property to the object's prototype, you ensure that it and any other objects with the same prototype will share this property, this means that they'll all have it, but it also mean that if you change it on one, it will change with all of them. Oops.
The creation of a new object with the new keyword is fairly straightforward:
Create an empty object
Make that object prototype the same prototype as the constructor's
Call the constructor with this as the newly created object.
So adding a property to the prototype of an object will have it shared among all instances of the same constructor, while adding it to this in the constructor will have it only on this specific instance. Because it's set in the constructor, it's safe to assume that all instances will have that variable in them, although it won't be shared with all other instances.
When you call a JavaScript property, the engine will look for it in the following fasion:
Look for the property on the object itself (that's this inside of methods of that object)
If not found, look for the property on the object's prototype
If not found, go up the prototype chain and look for it there
So in your Person example, the lookup chain will look like:
this > Person.prototype > Object.prototype
The constructor's will look like this:
this > Person.prototype > Function.prototype > Object.prototype
Person is a function, so its prototype is inherited from Function.prototype, similarly any function is an Object.
So to your specific questions:
The function declaration is not the prototype. See the object creation process above.
No, this applies the property on this instance, while prototype is shared among all instances.
Whenever i redefined prototype of function and create an new object of it, its constructor starts pointing to the root Object function instead of the function itself. Let me explain it with scenario:
var Person=function(firstName,lastName)
{
this.firstName=firstName;
this.lastName=lastName;
}
Person.prototype.getFullName=function()
{
return this.firstName+" "+this.lastName;
}
var student=new Person("Ankur","Aggarwal");
student.constructor //shows Person which is correct
After that if I redefined the Person prototype and create a new object of the changed
Person.prototype={}
var manager=new Person('John','Smith');
manager.constructor // Points to Object. Why?
Also if it is pointing to the Object not Person, how come it has the access to the Person properties like firstName and lastName?
Given the constructor:
var Person=function(firstName,lastName)
{
this.firstName=firstName;
this.lastName=lastName;
}
It has a default prototype property that is an object whose constructor property references the Person constructor. It is this property that (by default) instances inherit through the [[Prototype]] chain.
When you create an instance, the firstName and lastName properties are defined on the instance, as if by:
var person = {firstName:..., lastName:...};
so access to those properties is unaffected by the constructor's prototype.
When a new Object is assigned to the constructor's prototype:
Person.prototype = {};
it inherits a constructor property from Object.prototype (which is its constructor). So accessing an instance's constructor first looks on the instance, then on its [[Prototype]] (Person.prototype), then on its [[Prototype]] (Object.prototype) and finds a reference to Object. You can fix that by doing:
Person.prototype.constructor = Person;
You can find more information on MDN: Inheritance and the prototype chain.
Constructing an object does not set its constructor property to the function that constructed it. Instead, a function's default prototype is initialized with a constructor property pointing to that function, and objects constructed by that function inherit the constructor property from the prototype.
When you replace the function's prototype, the replacement does not automatically get that function assigned to its constructor property. Instead, the new prototype inherits constructor from its prototype, which is Object.prototype. Thus, objects created by the function will now inherit a constructor of Object from their grandprototype, Object.prototype.
I am new to JavaScript and I am following Douglas Crockford's book, The Good Parts.
It says:
Every function object is also created with a prototype property. Its value is an object with a constructor property whose value is the function. This is distinct from the hidden link to Function.prototype.
I understand that function objects are linked to Function.prototype, but what does the above line mean?
Can someone simplify it for me?
Every function object is also created with a prototype property.
var fn = function() { };
fn.hasOwnProperty("prototype"); // true
That is, for every function in JavaScript, each one has a prototype property, just like any other JavaScript object.
Its value is an object with a constructor property whose value is the function.
The object that prototype points to has a constructor property which points to the original function.
fn.prototype.constructor === fn // true;
That is, you can derive the constructor function from a constructed object by looking at obj.prototype.constructor (unless it's been overwritten).
This is distinct from the hidden link to Function.prototype
The function you create's prototype object is not the same as the Function's prototype.
fn.prototype != Function.prototype // true
That is, if you augment Function.prototype, then the methods will be available on function references. If you augment your function's prototype, then those methods will be available on constructed objects using that constructor.
I have a method that will let me select the prototype object when creating a new object (copied from "Javascript: The Good Parts" book):
Object.create = function(o) {
var F = function() {};
F.prototype=o;
return new F();
}
Now say, I have an object:
var car = {
model: "Nissan"
};
And I create a new object based on this object, using the "Create" method:
var car1 = Object.create(car);
I can then add a property to car and it will dynamically get added to car1 (dynamic prototyping). So for eg:
car.year=2011; // Gets added to "car"...
alert(car1.year); // ... Is also avaialable to car1
Q1) This behavior indicates that "year" got added to car's prototype, which is why it is available to car1. Is this correct? If not, then where does "year" get added and why is it available to both "car" and "car1"?
Also, per the rule of delegation, if a method cannot be found on an object, it will search its prototype and then check all the prototypes up the chain till it gets to Object.prototype. So now, if I type something like this:
Object.prototype.originCountry = "Japan";
alert(car.originCountry); // Outputs Japan
alert(car1.originCountry); // Outputs Japan
So far so good; however, if I do:
Object.carColor= "White";
alert(car.carColor); // Error!
Q2) When I add a property to "car" (see car.year example above, it gets added to car's prototype. However, when I add a property to Object, it does not get added to Object's prototype? If it does get added to Object's prototype, then why is it not available to "car", per the rule of delegation?
Why is this happening?
When you do this:
Object.carColor = "White";
Then the property carColor does not get added to the Object's prototype. It is now a property of Object. To see what you expect, what you would do is:
Object.prototype.carColor = "White";
Then after that:
alert(({}).carColor); // Will alert "White"
So what happens here is that. any object created including {} (which is nothing but an empty object) is a new instance of Object and hence shares the properties of whatever is set in the prototype of Object.
As for how your Object.create function works. Let us look at it line-by-line:
1. var F = function() {};
You just create a new function, an essentially blank object. The reason you use a function and not something like {} is because a function can be coupled with a new call to create a new instance of that object wherein the function would act as a constructor.
2. F.prototype=o;
You set the prototype of the new blank function to the object you've created. Now, this is purely a reference. It is not a deep-copy. What I mean is that as the object o changes, so will any instances of the objects (actually they won't change, but they would 'seem to' change. More on that later).
3. return new F();
Now you just create a new instance of that function, which has a prototype as the object you passed.
When you do the following:
var car1 = Object.create(car);
You get an object car1 which has the prototype has car. So when you do this:
car.year = 2011
It isn't like car1 changes. It is more like the object that the prototype refers to changes. So when you do something like:
car1.year
A search is made (first in the prototype, then in the object) for a property called year and turns out, that the prototype has it and hence car1.year will return 2011.
So the bottom line is this:
A prototype is shared amongst instances.
Changing the properties of an Object will not manifest into any instances changing.
In your first example, you are adding to the prototype of your car1 because car === F.prototype and car1 instanceof F. So to Q1: yes.
Object is the constructor function of all objects, as F is to your car1. If you add something on Object.prototype, it will be available on all objects - that's the reason why you should not, such non-enumerable properties mess up all for-in-loops. If you set a property of the Object constructor function, nothing changes for the things that inherit from it. Don't forget: Object equals the F function, not the o parameter for prototype setting. new Object() is like Object.create(Object.prototype).
That function doesn't let you select the prototype object it creates a nmew object constructor, the object argument as prototype and then return a new object based on the constructor.
That new object will inherit methods and properties from the object arguments. This is to allow the creation of new objects that inherits from others.
The reason this works (and by the way Object is a core javascript object and should not be extended)
Object.prototype.originCountry = "Japan";
alert(car.originCountry); // Outputs Japan
alert(car1.originCountry);
and this doesn't
Object.carColor= "White";
is because the first extend the prototype object of Object, which means object build with the Object constructor will inherits those methods and properties.
When the later is what we call a static function which does not get passed to object created from the Object constructor.
I would recommend reading more about prototypal inheritance in Javascript. here is few links.
http://www.webreference.com/programming/javascript/prototypal_inheritance/index.html
http://www.htmlgoodies.com/html5/tutorials/javascript-prototypical-inheritance-explained.html#fbid=xEJ2PwtH2Oh
http://unscriptable.com/2007/04/17/inheritance-explained/