Prototypal inheritance with nested objects - javascript

I'm trying to get my head around prototype inheritance in Javascript. I think I got the basic concept, but when I was playing around with this I ran into the following which still has me puzzled.
There is a very similar question and answer here but it doesn't fully answer why this is happening, at least not for me.
I create a new object like this:
var User = {
username: "",
name: {
first: "",
last: ""
}
}
Next I create two "instances" of that object:
var user1 = Object.create(User);
var user2 = Object.create(User);
Now I set the name property like so:
user1.name = { first: "John", last: "Jackson"}
user2.name = { first: "James", last: "Jameson"}
Now I do
alert(user1.name.first) \\ -> John
alert(user2.name.first) \\ -> James
All as expected. So far so good.
However, if I set the name.first property like this:
user1.name.first = "John";
user2.name.first = "James";
and I get
alert(user1.name.first) \\ -> James
alert(user2.name.first) \\ -> James
Clearly now the property is being set on the prototype object User (or rather the contained name object) instead of overriding it in the current object user1. Why does that occur?
Further if I do
user1.name.middle = "Mortimer"
I can now do
alert(User.name.middle) // -> Mortimer
which is not what I would expect. Generally, whenever a property is set on a derived object, that object either already has that property as an ownProperty in which case the value is simply assigned, or the property is newly created as an ownProperty on the derived object, overriding the prototype property. Just like happens when I assign to user1.name.
So why does assigning to an object contained in the prototype object cause such (at least to me) unexpected and counter-intuitive behavior?
The way I understand it, when the assignment is made the first check is to see if user1 has an ownProperty called name, which it doesn't. If this were a read operation the prototype property would now be looked up and User checked to see if it has ownProperty name. But since this is a set operation why walk the prototype chain when usually a missing ownProperty is simply created?

But since this is a set operation why walk the prototype chain when usually a missing ownProperty is simply created?
When you say user1.name.first = "John", the user1.name part has to be resolved before the .first property can be retrieved or set. And in your example the user1.name part only exists on the prototype object, so it is that object whose .first property you are setting.
Similarly, when you say user1.name.middle = "Mortimer", again the user1.name part resolves to the nested object from the prototype, so then you create a .middle property on that object, which is why User.name.middle also returns "Mortimer".
If you said user1.name.first and user1.name could not be resolved (on the current object or in its prototype chain) then you'd have a TypeError: Cannot set property 'first' of undefined. (You can try that concept with your existing code by saying user1.address.street = "something" - you'd get the TypeError, because user1.address doesn't exist on user1 or its prototype chain.)

Since you've already read over similar questions and answers and still seem to be having trouble understanding the behavior, I'll try to make my explanation as clear as possible. Here is where I think you are going wrong (emphasis mine):
Generally, whenever a property is set on a derived object, that object either already has that property as an ownProperty in which case the value is simply assigned, or the property is newly created as an ownProperty on the derived object, overriding the prototype property. Just like happens when I assign to user1.name.
The problem here is that you assume that user.name.first counts as "a property . . . set on a derived object" (an instance of User). However, this is simply not the case. In JavaScript, inheritance of properties is only shallow (a single layer deep). user.name is just an object value shared by reference through the prototype, so modifications to it from one place are reflected everywhere.
Think of user1.name and user2.name like firstReference and secondReference in the following example snippet, and hopefully the behavior will seem a bit clearer to you.
var User = {
username: "",
name: {
first: "",
last: ""
}
}
var firstReference = User.name
var secondReference = User.name
firstReference.name.first = 'First!'
console.log(secondReference.name) //=> 'First!' (logical and expected result)

The Object.create method creates an object using the first parameter as a prototype, and the second, optional, parameter is an additional object of own properties.
I think the problem here is that name is, by definition, in the prototype, and so not an own property.
If you want separate properties, then you should use the second parameter. The prototype is where you store methods and shared properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
has more details.
The key is that you want both prototype and own properties.

Related

is Object.create() method performs shallow copy?

I am newbee to JavaScript. When I read the Object.create documentation, it is written like 'The Object.create() method creates a new object, using an existing object' (Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create). It do not mention anything about the shallow copy of the object. But when I experimented with below script, I confirmed that, create method is performing shallow copy.
var foo = {
a : 100,
details : {
version : 1.1,
name : 'Demo of object inheritance'
},
printInfo : function(){
console.log(this.details.version);
console.log(this.details.name);
console.log(this.a);
}
}
var bar = Object.create(foo);
foo.printInfo();
bar.printInfo();
console.log("\n Updating the details and property a of bar object");
bar.details.version = 2.2;
bar.details.name = "Bar object changed the name";
bar.a = 123456;
console.log("\n")
foo.printInfo();
bar.printInfo();
Is my understanding correct? Please point to me any documentation that confirms create() method performs shallow copy.
When I executed in Scratchpad, I seen below output in console.
1.1
Demo of object inheritance
100
1.1
Demo of object inheritance
100
Updating the details and property a of bar object Scratchpad/1:21:1
2.2
Bar object changed the name
100
2.2
Bar object changed the name
123456
Object.Create does not copy anything at all, it just sets
the passed object as the prototype of a new object:
const person = {name: 'Alex', age: 29}
const newPerson = Object.create(person)
console.log(newPerson)
In order to make a shallow copy, you can use Object.assign.
const newPersonObj = Object.assign({}, person)
console.log(newPersonObj)
This will create entirely new copy.
It has nothing to with shallow copy.
Instead, you have figure it out how Prototype Inheritance works in Javascript world.
To have a better understanding, let's break this down into following 2 parts:
Reading property from child object:
When you try to access a property from a child object, Javascript parser will try to search through up prototype chain until it manages to find it else return undefined if not found.
Write property in child object
It will first try to locate that the object to which the your targeted property belongs, and then directly set that property on that object.
Let's take part of your code as example:
bar.details.version = 2.2;
What Javascript parser does:
First, details will be located through prototype chain searching and it will be found on foo (ie: bar.details === foo.details)
Second, version is directly set on details object(that's why this.details.version and this.details.name are both "unexpectedly" changed as you saw in your result and as a result bar.details.version === foo.details.version === 2.2))
bar.a = 123456;
What Javascript parser does:
Before anything gets started, bar is already located and there's no need to search though prototype chain, as a result, a will be directly set on the bar
(ie. that's why only bar.a is affected with foo.a still holding the origin value:123456)
create method is performing shallow copy.
-- EDIT --
Nope, it may seem that way but the term shallow copy would be inaccurate. It would be more in line with prototypal inheritance MDN article here
Given an object oA that has a name property, function Object.create(oA) creates a new object oB. Trying to access property oB.name would look up the prototype chain until it finds one on its prototype at oA.name.
Sample code below:
/* create one object adam */
const adam = {
name: 'Adam',
}
console.log(`does adam have own 'name' property?`, adam.hasOwnProperty('name')) // true
/* create new object bob, whose prototype 'ancestor' is adam */
const bob = Object.create(adam)
console.log(`does bob have own 'name' property? (1)`, bob.hasOwnProperty('name')) // false; the 'name' prop is on adam
/* assigning 'name' on bob doesn't change adam's name, it just 'shadows' it -- accessing 'name' on bob returns the one on bob */
bob.name = 'Bob'
console.log(`does bob have own 'name' property? (2)`, bob.hasOwnProperty('name')) // now true
/* adam and bob now each have their own 'name' property */
console.log(adam.name) // Adam
console.log(bob.name) // Bob
/* show the relationship of adam and bob */
console.log(`is adam a prototype 'ancestor' of bob?`, adam.isPrototypeOf(bob)) // true, linked via Object.create()
console.log(`is bob a prototype 'ancestor' of adam?`, bob.isPrototypeOf(adam)) // false, the prototype points one way
Hope this helps. Cheers,

Javascript: Why are getters used over methods?

So I'm wondering what the difference is between methods and getters?
Reading the Mozilla documentation:
Sometimes it is desirable to allow access to a property that returns a
dynamically computed value, or you may want to reflect the status of
an internal variable without requiring the use of explicit method
calls
But don't they achieve the same thing? Why is one better than the other?
Why is saying:
var obj = {
log: ['a', 'b', 'c'],
get latest() {
if (this.log.length == 0) {
return undefined;
}
return this.log[this.log.length - 1];
}
}
console.log(obj.latest);
Sometimes preferable to just having a latest() method and calling obj.latest(). If the code is run in both cases, then what's the point? Aren't they both dynamic?
The documentation also reads:
Getters give you a way to define a property of an object, but they do
not calculate the property's value until it is accessed.
What exactly is the difference between 'accessed' and 'called'? A method isn't run until it's called, just as a property isn't accessed until its included? So where does the significance lie?
Note, the following is an opinion.
A getter is for retrieving the value of a specific property, and it allows your object to have dynamic properties based on the value of other properties but otherwise behave just as you would expect properties to behave. The getter allows you to define a method that is called when you access that property, even though you access it with normal object property accessors, myObject.dynamicProp and myObject['dynamicProp'].
Getters are provided for exactly that purpose. If that's the intent of the method you are writing, then use the getter syntax.
Methods exist to do other things that are verbal, action oriented, and not to simply return some property of the object you're writing.
You can always write a method to be a getter, but why?
So it depends on your intent. Getters specify intent, and if your intent is to provide a dynamic property that behaves just like an object property then use a getter over a method.
Hope that helps!
Updated:
While there is technical nuance to be understood here, and though I've leaned toward using the getters above, I'd say, after further reflection, that the approach you wish to take is up to you based on:
(1) Do you want to use normal accessors to access this property? Are you ok with dynamic properties that hide some logic? (They don't really 'hide' the logic per se, but they look just like normal props to the caller). Then use the get().
(2) Do you want to make it obvious and more readable that your property is dynamic? Are you doing things besides just computing a dynamic property based on the current values of existing properties of this object? Are you ok with having to call the method explicitly? Then use a method, e.g. getLatest().
According to the documentation for Object.defineProperty() on MSDN
Property descriptors present in objects come in two main flavors: data
descriptors and accessor descriptors. A data descriptor is a property
that has a value, which may or may not be writable. An accessor
descriptor is a property described by a getter-setter pair of
functions. A descriptor must be one of these two flavors; it cannot be
both.
Both data and accessor descriptors are objects. They share the
following optional keys:
configurable true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object. Defaults to false.
enumerable true if and only if this property shows up during enumeration of the properties on the corresponding object. Defaults to false.
A data descriptor also has the following optional keys:
value The value associated with the property. Can be any valid JavaScript value (number, object, function, etc). Defaults to undefined.
writable true if and only if the value associated with the property may be changed with an assignment operator. Defaults to false.
An accessor descriptor also has the following optional keys:
get A function which serves as a getter for the property, or undefined if there is no getter. When the property is accessed, this function is called without arguments and with this set to the object through which the property is accessed (this may not be the object on which the property is defined due to inheritance). The return value will be used as the value of the property. Defaults to undefined.
set A function which serves as a setter for the property, or undefined if there is no setter. When the property is assigned to, this function is called with one argument (the value being assigned to the property) and with this set to the object through which the property is assigned. Defaults to undefined.
If a descriptor has neither of value, writable, get and set
keys, it is treated as a data descriptor. If a descriptor has both
value or writable and get or set keys, an exception is thrown.
This indicates that get and set intercept access calls and assignment calls respectively.
A very good example (and description) here points out a clear example of when this is useful.
Take a case where you have the first and last name on a person, with the common need for the full name.
person.setLastName('Smith');
person.setFirstName('Jimmy');
person.getFullName(); // Jimmy Smith
Using the get and set keys, gives you the option of declaring the object like so:
var person = {
firstName: 'Jimmy',
lastName: 'Smith',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
set fullName (name) {
var words = name.toString().split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
}
and assigning it, and accessing it like so:
person.fullName = 'Jack Franklin';
console.log(person.firstName); // Jack
console.log(person.lastName) // Franklin
console.log(person.fullName) // Jack Franklin
This allows the developer to interact with fullname, without accidentally leaving firstname or lastname unassigned or improperly assigned.
Lastly,
The use strict directive will enforce read and write attempts to attributes defined through get or set accordingly. See W3Schools.
"use strict";
var obj = {get x() {return 0} };
obj.x = 3.14; // This will cause an error

Dive into getter/setters

I want to create objects which they will have an object named generic_object as their prototype object. generic_object must have a getter/setter property whose role will be to assign different functions to newly created objects. Virtually, that's the same how accessor property .onclick works. We may give different listeners to different DOM elements by just using the setter:
elem.onclick=function(){...};
I 've constructed the following:
var generic_object={
temp:null,
set onair(callback){
callback();
this.temp=callback;
},
get onair(){
return this.temp;
}
};
var obj1=Object.create(generic_object);
var obj2=Object.create(generic_object);
obj1.onair=function(){console.log(1);};
obj2.onair=function(){console.log(2);};
This does my job, however I want to eliminate .temp property because it is being created inside obj1 and obj2 as an own property (.temp inside generic_object is shadowed) and that's something I do not want.So:
Question1: Is it possible to transform the above code and get the same result without using temp?
Question2: Let's take the previous example.Since .onair property never becomes own property of obj1, obj2 (getter/setters are not shadowed), how JS engine tells between obj1.onair and obj2.onair? Are these function objects stored inside the memory spots reserved for each of the objects obj1 and obj2 even, I repeat, these are not their own properties? I am confused about that...
Thank you

Best practice for a simple object literal: using "that" or the object's name?

Let's say there is a simple object literal which name will never change:
var car = {
wheels : 4,
construct : function() {
var that = this;
setTimeout(function() {
console.log(that.wheels);
console.log(car.wheels);
}, 500);
}
};
My question is: Which way is better? Referencing by the object's name or creating a new variable (which may take some time and memory and probbaly must be done in multiple functions)?
Within the object, you should always refer to the object via this (or a copy of it, e.g. that, if required) to prevent the following breakage:
var car = ...
// do stuff
car = undefined; // or anything else, perhaps by a code hacker in the JS console
// class is now broken
You should treat the variable name that happens to have been given to your object on the outside as unknown to you, and subject to change.
Someone else might call it something else, there might be multiple names, the name might suddenly point at some other object altogether. Such variables are for the benefit of the "owners" of references to the object, and not for the object itself.

using the extend function

this is my very first post! I have a quick question in regarding inheritance in javascript.
Of course the 'extend2' method is used to inherit child objects from the parent object using a for-in loop.
var extend2 = function (child, parent) {
var c = child.prototype;
var p = parent.prototype;
for (var i in p) {
c[i] = p[i];
}
}
I'm currently reading "Object Oriented Javascript" by Stoyan Stefanov. It's an awesome book.
Can anyone give me a good detailed explanation of how the child's prototype object is not entirely overwritten or replaced but it's just augmented?
How is it that when the objects inherit, they copy (primitive data types) instead of being looked up as a reference by using the extend2 function?
This would really help thanks!
Primitive data types in javascript are passed via value, rather than reference. Thus when you copy a value it is actually copying it, not referring to it.
Traditionally this is because a primitive was literally encoded in the memory in such a way (so the primitive int 7 would be encoded in memory as 0x7. When dealing with objects, however, they are encoded as a pointer to the memory location where the actualy object is. Thus, when you copy the value it is merely a copy of the reference pointer, not the object that that pointer referrs to.
As for the fact that the child's prototype is not replaced, that is because a prototype in java is merely another object. So a prototype may look like:
{
someField: 5
}
Which would indicate that instances of the object would initialize with a field called someField whose value is 5. with the above code, each entry in the object is copied to the child prototype, but nothing is deleted. Thus if the child prototype looks like:
{
someField: 10
someOtherField: 3
}
Then performing the above extend2 command will overwrite someField, but not someOtherField, so the resulting prototype would be:
{
someField: 5
someOtherField: 3
}

Categories