Right way to use Prototypal Inheritance in JavaScript? - javascript

Using Crockford's method ( http://javascript.crockford.com/prototypal.html ) doesn't work when we have reference values in properties (an array in the supertype) as this is common among all the objects.
So, what's really the recommended way to do object inheritance in Javascript that doesn't have problem and that visually encapsulates better all the object's methods and properties?
Example:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
var p1 = {name: "nick", friends : ["a1", "a2"]};
var p2 = Object.create(p1);
p2.name ="john";
p2.friends.push("andre");
alert(p1.friends);
alert(p2.friends);
alert(p1.name);
alert(p2.name);
The friends array returns the same values for both p1 and p2, while I expect that it returns different values.

Well, the members higher in the prototype chain are "shared", that's the goal of prototypal inheritance...
However, you can assign an own property to the newly created object (just like you do with the name property, for example:
var foo = {foo: "xxx", array : ["a1", "a2"]};
var bar = Object.create(foo);
bar.foo = "yyy";
bar.array = ["b1", "b2"];
bar.array.push("bar");
alert(foo.array); // ["a1", "a2"]
alert(bar.array); // ["b1, "b2", "bar"]
alert(foo.foo); // "xxx"
alert(bar.foo); // "yyy"
The standard Object.create method, available on the latest browser versions, provides a way to create own properties, using a second argument, for example you could:
// ...
var bar = Object.create(foo, {
foo: { value: 'yyy'},
array: {value: ["b1", "b2"] }
});
// ...

Can you provide an example of what doesn't work? Prototypal inheritance is really meant for object literals. If you are trying to modify something created as an array, the only way I know of using inheritance is modifying Array.prototype.

Related

Index a JS object like a Metatable in Lua

Is there a way to index an object in JavaScript like a Metatable in Lua?
Like for example:
var obj = {a:"a",b:"b",properties:{c:"c",d:"d"}}
metatable(obj,obj.properties) // Make it so that if you try to index something that's not inside the object it will go to the parameter one
console.log(obj.a) // "a"
console.log(obj.c) // "c"
To LMD:
How do I do it for multiple objects? Like for example:
var objs = [
obj1 = {name:"Button";class:"button";properties:{text:"Press this"}]
]
for (i in objs){
metatable(objs[i],objs[i].properties)
}
console.log(objs.obj1.text) // "Press this"
Yes: JavaScript has prototypes. These aren't exactly the same as Lua but can be used for simple metatable indexing purposes. One way to achieve your example would be as follows:
const properties = {c: "c", d: "d"} // prototype
const obj = Object.create(properties) // create object with prototype
obj.a = "a"; obj.b = "b";
console.log(obj.a) // "a"
console.log(obj.c) // "c"
Or if you already have the objects given, as in your second example, you may want to use Object.setPrototypeOf(object, prototype), which is comparable to setmetatable(object, {__index = prototype}) in Lua:
const objs = [{name:"Button", class:"button", properties: {text:"Press this"}}]
for (const obj of objs) Object.setPrototypeOf(obj, obj.properties)
console.log(objs[0].text) // "Press this"
that is, the metatable function you've been searching for literally is Object.setPrototypeOf!

Object.create copying reference of array and object properties [duplicate]

This question already has answers here:
Crockford's Prototypal inheritance - Issues with nested objects
(3 answers)
Closed 7 years ago.
I am trying to understand how Object.create copies arrays and objects properties when initiating a new object. It seems to be different then copying a string or number. For example if we have a basic Object with a number and array property. jsfiddle example
var obj = {
num: 0, arr: []
};
We then initiate 3 new Objects from this base.
var set1 = Object.create(obj);
set1.num = 10;
set1.arr.push(1);
var set2 = Object.create(obj);
var set3 = Object.create(obj, {arr: []});
I was expecting set2.num and set2.arr property to be it's initial state. I found this to be true for the number, but not the array. Of course one work around is to pass {arr: []} when initiating the Object or creating a initiation function that resets the arr property.
// false
console.log(set1.num === set2.num);
// true - why is this true???
console.log(set1.arr === set2.arr);
// false
console.log(set1.arr === set3.arr);
Is this the normal behavior? Is Object.create keeping a reference to all of the Object's array and object properties? It would be very nice to not have to create new arrays and objects when initiating a new Object.
It would be very nice to not have to create new arrays and objects when initiating a new Object
Write a function in your favourite style
Returning a literal
function makeMyObject() {
return {num: 0, arr: []};
}
// usage
var obj = MyObject();
Returning an Object.created object, and assigning to it,
function makeMyObject() {
var o = Object.create(null); // or some prototype instead of `null`
return Object.assign(o, {num: 0, arr: []});
}
// usage
var obj = MyObject();
Using new
function MyObject() {
this.num = 0;
this.arr = [];
}
// usage
var obj = new MyObject();
Cloning is a bit more complicated, a basic example might be
function shallowClone(o) {
var e;
if (typeof o !== 'object')
return o;
e = Object.create(Object.getPrototypeOf(o));
// copy enumerable references
Object.assign(e, o);
// or to keep non-enumerable properties
// Object.defineProperties(b, Object.getOwnPropertyNames(o).map(Object.getOwnPropertyDescriptor.bind(Object, o)));
return e;
}
Deep cloning requires looping over properties (e.g. for..in for enumerable only) and type checking instead of simply copying everything over. You usually end up needing to recurse on properties which are Objects themselves.
For known types, you can teach it to use the correct constructor too, e.g.
if (Array.isArray(o)) {
e = [];
o.forEach((v, i) => e[i] = recurse(v));
}
Where recurse would be the name of the clone function

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

Safely inheriting prototypes in JavaScript [duplicate]

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.

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