Safely inheriting prototypes in JavaScript [duplicate] - javascript

This question already has answers here:
Benefits of using `Object.create` for inheritance
(4 answers)
Closed 8 years ago.
Let's say I'm shooting for some basic inheritance in my application, I could achieve this by setting the prototype of my child to the parent.
// Parent "class"
var Car = function(year) {
this.year = year;
};
Car.prototype.drive = function() {
console.log('Vrooom');
};
// Child "class"
var Toyota = function() {
Car.apply(this, arguments);
};
Toyota.prototype = Car.prototype;
var myCar = new Car(2010), myToyota = new Toyota(2010);
myCar.drive(); // Vrooom
myToyota.drive(); // Vrooom
Seems to work, but is obviously bad because if I set a new method on my Toyota.prototype, it will be set on Car's prototype.
Toyota.prototype.stop = function() {
console.log('stooop');
};
myCar.stop(); // stooop <--- bad
myToyota.stop(); // stooop <--- good
To solve this, instead of Toyota.prototype = Car.prototype; I can add an intermediary:
var ctor = function() { };
ctor.prototype = Car.prototype;
Toyota.prototype = new ctor();
Toyota.prototype.stop = function() {
console.log('stooop');
};
myCar.stop(); // throws undefined error <--- good
myToyota.stop(); // stooop <--- good
But I don't understand why this works. ctor creates a new instance with its prototype set to Car's prototype, and then Toyota sets its prototype to that new instance.
But why create an empty object with a prototype that gets referenced by Toyota's prototype?
Why doesn't setting a new method on Toyota set it on Car like it does in the first example?
What if I want several layers of inheritance, it seems like I need to glue them together with a new ctor every time?

But why create an empty object with a prototype that gets referenced
by Toyota's prototype?
Because if you do this: Toyota.prototype = new Car();
Now Toyota's prototype is an instance of Car, which means that in addition to having Car's prototype in the chain, it also has any properties set in the Car constructor itself. Basically you wouldn't want Toyota.prototype to have a property called year like so: Toyota.prototype.year. Because of this it's a lot better to have an empty constructor like so:
var ctor = function() { };
ctor.prototype = Car.prototype;
Toyota.prototype = new ctor();
Now Toyota.prototype has the new ctor() instance as it's prototype, which in turn has Car.prototype in its own chain. This means that instances of Toyota now can execute methods that exist in Car.prototype.
Why doesn't setting a new method on Toyota set it on Car like it does
in the first example?
With this: Toyota.prototype = Car.prototype; you're setting Toyota' prototype to be the same exact object as what Car.prototype contains. Since it is the same object, changing it in one place also changes it everywhere else. This means that objects are passed be reference and not by value, which is another way of saying that when you assign an object to 3 different variables, it is the same object regardless of which variable you use. Strings for example are passed by value. Here is an example with strings:
var str1 = 'alpha';
var str2 = str1;
var str3 = str1;
str2 = 'beta';
// Changing str2 doesn't change the rest.
console.log(str1); //=> "alpha"
console.log(str3); //=> "alpha"
console.log(str2); //=> "beta"
Now compare to objects and their properties;
var obj1 = {name: 'joe doe'};
var obj2 = obj1;
var obj3 = obj1;
console.log(obj1.name); //=> "joe doe"
console.log(obj2.name); //=> "joe doe"
console.log(obj3.name); //=> "joe doe"
obj2.name = 'JOHN SMITH';
console.log(obj1.name); //=> "JOHN SMITH"
console.log(obj2.name); //=> "JOHN SMITH"
console.log(obj3.name); //=> "JOHN SMITH"
What if I want several layers of inheritance...
Here is another way of creating layers of inheritance:
var Car = function(year) {
this.year = year;
};
Car.prototype.drive = function() {
console.log('Vrooom');
};
var Toyota = function() {
Car.apply(this, arguments);
};
// `Object.create` does basically the same thing as what you were doing with `ctor()`
// Check out the documentation for `Object.create` as it takes a 2nd argument too.
Toyota.prototype = Object.create(Car.prototype);
// Create instances
var
myCar = new Car(2010),
myToyota = new Toyota(2010);
// Add method to Toyota's prototype
Toyota.prototype.stop = function() {
console.log('stooop');
};
Let's try it out now:
myToyota.stop(); //=> 'stooop'
myCar.stop(); //=> 'TypeError: undefined is not a function'
myCar.drive(); //=> Vrooom
myToyota.drive(); //=> Vrooom

Your problem is the following line:
Toyota.prototype = Car.prototype;
and then later modifying this object:
Toyota.prototype.stop = function() {
console.log('stooop');
};
because in the first line, you set Toyota.prototype to the exact same object as Car.prototype. This is not a copy, it is a reference to the same object! So as soon as you modify stop on Toyota.prototype, you actually modify both Toyota.prototype and Car.prototype, because it is one and the same.
What you'd really want to do is replacing the first line with:
Toyota.prototype = Object.create(Car);
so that you now have the prototype of Toyota point to the Car function, as it should, instead to Car's own prototype. Congratulations, you've used the prototype chain!
(Note: Using Object.create to do class inheritance is inherently more stable and reliable, because it does not run any code that you might have contained in the Car function, but only sets up the prototype link.)
For further discussion on what is happening here exactly, and why you might be better off not using "class inheritance" in JS at all, you might want to read Chapters 4-6 of You Don't Know JS (objects & prototypes).
On your last question: "What if I want several layers of inheritance, it seems like I need to glue them together with a new ctor every time?" – yes, you'd need to do this, and this is the standard pattern for (fake) classes and inheritance in JavaScript.

Related

difference between class.prototype.property and object.property

what is the significance of using prototype ? If i make a class as -
var myClass = function(){ this.p1 = 'p1';}
now if i want new property to be added as p2
i do it prototype property for the class and do it on object as well, like
using direct object ->
var obj = new myClass();
obj.p2 = 'p2';
using prototype ->
myClass.prototype.p2 = 'p2';
how these two are different? or are both lines different ?
Prototype helps you, to have kind of inheritance (prototypal inheritance).
you can add properties to your objects manually, or borrow the property from its prototype. Let's take a look to some examples:
var obj = new myClass();
obj.p2 = 'p - child';
console.log(obj.p2); // p - child
var obj2 = Object.assign(obj.__proto__); // will borrow the value from the obj prototype
console.log(obj.p2); // p - child
Now take a look to what happen with myClass prototype:
var obj3 = Object.assign(myClass.prototype); //will borrow the value from the myClass prototype
console.log(obj3.p2); // p - parent
And here an example with not existing properties:
var obj4 = new myClass();
var obj5 = Object.assign(obj4.__proto__);
obj4.p3 = 'P3 - value';
console.log(obj4.p3); // P3 - value
console.log(obj5.p3); // undefined
Note: __proto__ is for objects {}, prototype is for functions.
Hope this helps to clarify a little bit.

Store an object with an array inside?

Is there a way to store an object with an array[id] = "certain value", so that every single user has a list of favorite things ?
What I'm trying to achieve is having a data object with undefined values at first, and filling them in, so for example Jack and Sam could have an assigned favorite movie saved.
I tried something like this with no success:
Data.js:
module.exports = function() {
return {
favMovie: ""
}
};
App.js:
var person [] = data();
//1st person
person[811767132257839].favMovie = "Matrix";
//2nd person
person[107230716367889].favMovie = "Kill Bill";
//3rd person
person[973676332752239].favMovie = "Avatar";
...
console.log( "Favorite movie: " + person[id].favMovie );
It doesn't sound like you want any arrays at all, just objects.
// Create an object that we'll keep person objects in
var personData = {};
// Add a person object to it for person ID #123
personData[123] = {};
// Set person #123's favorite movie:
personData[123].favMovie = "Avatar";
// Add a different person; this time, we'll add the movie at
// the same time
personData[234] = {
favMovie: "Matrix"
};
When using objects as maps like that, sometimes people create the objects using Object.create(null) to avoid them having any inherited properties (like toString and valueOf and constructor):
person[123] = Object.create(null);
person[123].favMovie = "Avatar";
In ES2015 (aka "ES6"), you might want to use a Map rather than an object for the map of people:
var personData = new Map();
...and then use set and get for the individual person objects.
If the individual person objects get complicated, you might use a constructor function and associated prototype for them, using either ES5 syntax:
function Person() {
// ...
}
Person.prototype.doSomething = function() {
// ...
};
...or ES2015 syntax:
class Person {
constructor() {
// ...
}
doSomething() {
// ...
}
}
Then creating them:
personData[123] = new Person();
// or if using Map
personData.set(123, new Person());
Side note: Even when we write them as numbers, the keys (property names) in objects are always strings (unless you use ES2015 Symbols, which you probably wouldn't here). In contrast, keys in an ES2015 Map can be anything. Key equality in Map instances is determined using the special "same value zero" algorithm (which is basically === except that NaN is equal to itself [whereas it isn't in ===]).

Objects types in Javascript

This is a fairly general question coming from a newbie in the learning phase and is on something I need clarifying.
I'm currently learning about objects and at this point I'm learning about inheritance. At this point in the lesson I've learned a few different ways to create objects but objects using the this keyword seems to have the most function:
function Person(){
this.eyes = 2;
this.ears = 2;
this.arms = 2;
this.hands = 2;
this.feet = 2;
this.legs = 2;
this.species = "Homo sapien";
}
I understand what I might use this for but then there's an object created this way:
var person = new Object();
person.eyes = 2;
person.ears = 2;
person.arms = 2;
person.hands = 2;
person.feet = 2;
person.legs = 2;
person.species = "Homo sapien";
Because I seem to be able to do things with the former that I can't do with the latter, I'm wondering if there's any reason why I wouldn't just use the former method all the time. Can anyone help me out with this? I don't need a long detailed answer (although one would be appreciated). It's just a question I want to get out of my head so that I don't dwell on it.
Preface: If you're not sure what I mean by the word "prototype" below, skip down to below the divider for an explanation, then come back up here to the top of the answer. :-)
Assuming in your first example that you then call Person via new:
var person = new Person();
...then the only difference between that person and the one you'd get using your second example is related to inheritance: The one created via new Person gets assigned the object on Person.prototype as its underlying prototype.
I'm wondering if there's any reason why I wouldn't just use the former method all the time
If you don't need to use the prototype, then using a constructor function could be unnecessarily complicated. Note that your second form could more concisely be written:
var person = {
eyes: 2,
ears: 2,
arms: 2,
hands: 2,
feet: 2,
legs: 2,
species: "Homo sapien"
};
That's called an object initializer: It creates a new object with the properties you see listed. There's never any need to use x = new Object(); if you want a new, blank object, just use x = {};.
When an object is a one-off, creating it directly is often the simplest way to create it.
The key advantage constructor functions have is that they're factories for objects that are fundamentally similar: Have the same set of initial properties, have the same underlying prototype, etc. And the function can accept arguments and use them to outfit the object it's creating in an appropriate way, perhaps do some validation on the construction arguments, etc. That is: They centralize initialization logic.
Constructor functions are not the only way to have function factories. You can also do:
function buildPerson() {
return {
eyes: 2,
ears: 2,
arms: 2,
hands: 2,
feet: 2,
legs: 2,
species: "Homo sapien"
};
}
var person = buildPerson();
And if you want that person to have a prototype (ES5 browsers and higher):
var personPrototype = {
// ...properties for the person prototype...
};
function buildPerson() {
var obj = Object.create(personPrototype);
obj.eyes = 2;
obj.ears = 2;
obj.arms = 2;
obj.hands = 2;
obj.feet = 2;
obj.legs = 2;
obj.species = "Homo sapien";
return obj;
}
var person = buildPerson();
(There's another, more verbose way to define those properties as well.)
JavaScript is amazingly flexible. :-)
"Prototype"
JavaScript uses prototypical inheritance, which is a fancy way of saying that a object A can be "backed" by object B so that if you ask A for a property it doesn't have, the JavaScript engine will look to see if that property exists on B. A quick practical example:
var proto = {
name: "proto's name"
};
var obj = Object.create(proto); // Creates an object backed by the given prototype
Don't worry about Object.create, for now all you need to know is that it creates a new object and assigns its underlying prototype based on the object you pass into it. So obj is backed by proto.
obj doesn't have a name property, but if we do:
console.log(obj.name);
...we see "proto's name". That's because when the JavaScript engine tried to get name's value from obj, it found that obj didn't have a name property, so it looked to obj's prototype, proto. Having found it there, it used the value from proto.
That only happens when getting the value (except in some advanced cases we can ignore for now). When setting a property's value, it gets set on the object you set it on. So:
var proto = {
name: "proto's name"
};
var obj = Object.create(proto); // `obj` is backed by `proto`
console.log(obj.name); // "proto's name"
obj.name = "obj's name";
console.log(obj.name); // "obj's name"
The purpose of prototypes is reuse, and so it's no surprise that an object can be the prototype of several other objects:
var proto = {
name: "proto's name"
};
var a = Object.create(proto); // `a` is backed by `proto`
var b = Object.create(proto); // `b` is also backed by `proto`
console.log(a.name); // "proto's name"
console.log(b.name); // "proto's name"
a.name = "a's name";
console.log(a.name); // "a's name"
console.log(b.name); // "proto's name"
Prototype objects are normal objects; we can change them:
var proto = {
name: "proto's name"
};
var obj = Object.create(proto);
console.log(obj.name); // "proto's name"
proto.name = "updated";
console.log(obj.name); // "updated"
Since obj doesn't have its own name property, each time we access it, the JavaScript engine goes and looks at its prototype.
The new operator automatically assign a prototype to the objects it creates: It uses the object that the function's prototype property has on it. So:
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log("My name is " + this.name);
};
var p = new Person("Fred"); // Creates an object backed by Person.prototype,
// then calls Person with this referring to the
// object
p.sayName(); // "My name is Fred";
Finally: Since prototype objects are normal objects, they also can have prototypes:
var rootProto = {
name: "root proto's name"
};
var middleProto = Object.create(rootProto);
middleProto.middleProp = "middle property";
var obj = Object.create(middleProto);
console.log(obj.name); // "root proto's name"
console.log(obj.middleProp); // "middle property"
For name, the JavaScript engine looks at obj, doesn't see a name property, and so looks at middleProto. It doesn't see a name property there, either, so it looks at rootProto. It finds it there, so it uses it.
Point of confusion: A lot of people are confused by the fact that the property on constructor functions is called prototype and think that somehow it's the prototype of the function. It isn't. It's just a normal property on function objects (functions are objects and can have properties). The only way it's special is that new uses it when you call the function via new. Non-function objects don't have a prototype property, they're prototype isn't a normal property, it's an internal thing. You can get an object's prototype by passing it into Object.getPrototypeOf:
var proto = {/*...*/};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true

Accessing objects and prototype in Javascript

I'm not quite getting object creation and adding/extending methods in JavaScript. I know everything is an object and the use of functions as constructors, but I don't quite get prototype and creating/calling new methods.
var c1 = {
name: "Neil",
};
var c2 = function() {
this.name = "Neil";
};
With both the above I can add new stuff:
c1.town = "a town";
c2.town = "a town";
c1.setTown = function(newTown) { this.town = newTown;};
c2.setTown = function(newTown) { this.town = newTown;};
Where I fall over is with the function approach I can do this:
c2.prototype.setTown2 = function(newTown) { this.town = newTown;};
c3 = new c2();
c3.setTown2("new3");
1: What is the difference between setTown and setTown2 and why for c3 can I call c3.town="x" and c3.setTown2("x") but not call c3.setTown("x"); ?
2: I don't seem to have access to 'prototype' for the c1 (the literal constructor approach), why is this?
thanks.
This is because JavaScript literal constructors like c1 create an actual Object, while functions with this like c2 create constructors that can be used to create objects like you did with c3. Here's an analysis of your code through JavaScript comments:
//This is a literal object with property name equal to "Neil":
var c1 = {
name: "Neil",
};
//We do not need to access the prototype here because c1 is an object, not a constructor:
console.log(c1.name);
//This is a constructor that creates an object with property name of "Neil".
var c2 = function() {
this.name = "Neil";
};
//This creates a property town of "a town" on c1. This works because c1 is a regular object.
c1.town = "a town";
//This creates a property town of "a town" on c2. However, if you create an object with c2, the object will not inherit town because it is not on the prototype.
//This does not throw an error because even though c2 is a function, you can still set the property town on it because this property is what's called a _static property_. Static properties are properties that are set without the prototype like the property below:
c2.town = "a town";
//With c1, we do not need to access prototype because it's a regular object and with c2, we do not need to access prototype because it's a static property.
console.log(c1.town, c2.town);
//This works the same as above, except now, we're using functions instead of regular properties.
c1.setTown = function(newTown) { this.town = newTown;};
c2.setTown = function(newTown) { this.town = newTown;};
//This sets the town property of c1 like with a regular object:
c1.setTown("HI!");
//This sets the static property town of c2 because "this" in the static method c2.setTown is c2, so "this.town" is "c2.town", or the static property town which we set above:
c2.setTown("HI!");
//Both of the following outputs "HI!" because of the .setTown() calls above.
console.log(c1.town, c2.town);
//This method is set on the prototype of c2, meaning that objects made with c2 will inherit this method.
c2.prototype.setTown2 = function(newTown) { this.town = newTown;};
//This is an object made with the constructor c2. It inherits name because of the actual constructor function and it inherits setTown2 from the prototype. However, it does NOT inherit c2.town and c2.setTown because those are static properties not on the prototype.
c3 = new c2();
//This sets the town property of c3 because c3 inherited the setTown2 method from c2.prototype, so that method now sets the properties of c3.
c3.setTown2("new3");
//c3.name outputs "Neil" because of the c2 constructor function and c3.town outputs "new3" because of the above c3.setTown2() call:
console.log(c3.name, c3.town);
Are you familiar with inheritance? A common concept in programming, this is the way to simulate inheritance in javascript.
Basically, when you add new methods or variables to the prototype, you modify the main structure of the object... So when you decide to create a new object from the modify 'class' (there are no classes in JS I know), the only method it remains are the ones inside the prototype.
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
1: What is the difference between setTown and setTown2
They're both function, however you create a new function every time you want to add that as method, where with prototype you automatically inherit it:
var c3 = new c2();
var c4 = new c2();
c3.setTown = function(newTown) { this.town = newTown;};
c4.setTown = function(newTown) { this.town = newTown;};
console.log(c3.setTown === c4.setTown) // false
console.log(c3.setTown2 === c4.setTown2) // true
Also,setTown is a property of the objects c3 and c4, where setTown2 is not. It's inherited. It means:
console.log(c3.hasOwnProperty("setTown")); // true
console.log(c3.hasOwnProperty("setTown2")); // false
And therefore Object.keys won't return setTown2; but if you execute a for…in loop you will get both own properties and inherited properties.
and why for c3 can I call c3.town="x"
Because you're simply adding a property. Unless the object is frozen, or sealed you can add any properties you want, any time. You could also add c3.foo = "bar", even if it's not in the prototype or added in the constructor.
and c3.setTown2("x") but not call c3.setTown("x");
Because it's an inherited method, so the object can obtain it traversing the prototype's chain; but you never add setTown to c3 instance, or to any object in its prototype chain.
2: I don't seem to have access to 'prototype' for the c1 (the literal constructor approach), why is this?
Because prototype is a property only for functions, that you can use as constructors. c1 is already an object, so doesn't need to have another object as "prototype". You can use directly c1 as object's prototype to create other object based on it:
var cc1 = Object.create(c1);
That will inherit all the methods and property c1 has, so:
console.log(cc1.name) // "Neil"
console.log(cc1.hasOwnProperty("name")) // false
As we see in the first point.
Hope it helps.

Understanding prototypes used in the object constructor function

I understand prototypes can be used to extend properties to their children. However I don't understand why you would want to have a "Object.prototype.property" in the main object constructor class itself?
In the Person constructor why Person.prototype.name = "Nicholas"; VS this.name = "Nicholas";
function Person(){
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg" – from instance
alert(person2.name); //"Nicholas" – from prototype
Setting a prototype property in the constructor is almost certainly an error. The prototype is the shared state of all child objects. The only reason you might want to update the prototype in the constructor is if you are storing shared data. For example, you might increment a counter, or add to a sum. But you would almost never expect for a simple string property like "name" to be set on the prototype within the constructor.
Person.prototype.name = 'Nicholas' and this.name = 'Nicholas' are NOT the same.
Let's see for ourselves:
var PersonA = function () {
this.name = 'Nicholas';
this.species = 'Homo Sapiens'
return this; // optional
};
var PersonB = function () {
return this;
};
PersonB.prototype.name = 'Nicholas';
PersonB.prototype.species = 'Homo Sapiens'
var personA = new PersonA();
console.log(Object.keys(personA)); // logs ["name", "species"]
var personB = new PersonB();
console.log(Object.keys(personB)); // logs []
console.log(personA.species === personB.species); // logs true
Clearly, personA has a 2 keys, "name" and "species"; while personB no keys!.
However, the prototype of personB 2 keys, "name" and "species"
Now, if you create a million objects of PersonA, each such object would have a
"name": "Nicholas" and "species": "Homo Sapiens" key-value pairs.
On the other hand, creating a million objects of PersonB would be much more memory-efficient as each such object would have no key-value pairs at all! At the same time, both "name" and "species" will be available through prototype (prototypal inheritance).
A closer look at refinements:
Lets examine the refinement on an object obj, say obj.foo:
If "foo" is a property of obj, the corresponding value is returned.
Otherwise, the prototype of obj is looked at.
If that prototype has a property "foo", the corresponding value is returned.
Otherwise that prototype's prototype is looked at.
The process continues till we hit Object.prototype.
So in terms of memory, PersonB is superior to PersonA. This is the beauty of prototypal inheritance.
Hope this helps.

Categories

Resources