How does the array property work in JS object - javascript

For the following code, why the propB of myObj is updated? And why test.childObj doesn't have the own property propB?
var myObj = { propA: '', propB: [] }
var fatherObj = {
childObj: null,
init: function() {
this.childObj = Object.create(myObj);
this.childObj.propA = 'A';
this.childObj.propB.push(2);
}
}
var test = Object.create(fatherObj);
test.init();
console.log(myObj.propB.length);
console.log(test.childObj.hasOwnProperty('propA'));
console.log(test.childObj.hasOwnProperty('propB'));

Using Object.create you do not copy an object, but you create a new object that inherits the passed one:
this.childObj { } -> myObj { propA: "", propB: [] }
Now when you get a property, it gets looked up in the inheritance chain. As it can't be found in the child object, it gets looked up in myObj. That is usually not a problem, as setting an objects property directly sets it on the object itself without using the inheritance chain, therefore this:
this.childObj.propA += "test";
looks up propA on myObj but sets propA on the childObj. With reference types however, you do not rewrite the property, therefore this:
this.childObj.propB.push(1);
looks up propB in myObj, and pushes to that, the result is:
this.childObj { propA: "test" } -> myObj { propA: "", propB: [1] }
To resolve that, the childObj has to have its own propB array:
this.childObj.propB = this.childObj.propB.slice();
That results in:
this.childObj { propA: "test", propB: [1] } -> myObj { propA: "", propB: [1] }
and now pushing to propB pushes to the array in childObj.

That is because myObj becomes a prototype of newly created test object (caused by creating it via Object.create.
Due to you just modify underlying prop propB of childObj (and not assign the new value like for propA) you only modify the prop of prototype. Please read more about inheritance in javascript.

By using Object.create(myObj);, myObj becomes the prototype of childObj. If a property is not found in an object and is found in the prototype, the one from the prototype is used. Also note that hasOwnProperty tells if the objects owns the property itself, and will return false if it exists only in the prototype and not in the object. By assigning directly a property on an object, you set it on the object, not on the prototype, but when you modify the property propB with push, the property is not found directly in childObject, so you modify the prototype.
You have to be careful with that, as all objects created this way will share the same prototype object and by modifying one, you will modify it for all instances.
You have also to be extra careful because it can be tricky to know in javascript where in the prototype chain your property come from, as myObj also have a prototype.
var myObj = { propA: '', propB: [] }
var fatherObj = {
childObj: null,
init: function() {
this.childObj = Object.create(myObj);
this.childObj.propA = 'A';
this.childObj.propB.push(2);
}
}
var test = Object.create(fatherObj);
test.init();
console.log('test: ', test);
console.log('test prototype: ', test.__proto__);
console.log('test.childObj: ', test.childObj);
console.log('test.childObj prototype: ', test.childObj.__proto__);
console.log(test.childObj.hasOwnProperty('propA'));
console.log(test.childObj.hasOwnProperty('propB'));

Javascript inheritance does not work like in most other languages. When using var x = Object.create(someObj), the new object x is actually empty (it has no properties of its own) along with a reference to its prototype: the separate object someObj.
Any property or function that you then try to access from x which does not resolve there, will be looked up in someObj - or even higher up, if someObj also has a prototype object, etc.
Things can get confusing when inherited properties are modifiable objects, as in your example. As we have seen, trying to modify the array x.propB by pushing an item to it, will actually modify the inherited array contained in someObj.
I'm strongly convinced that it is bad style to have modifiable objects (both arrays and regular objects) in prototype objects. Instead, prototype objects should contain only functions and no data, or at least no data beyond simple strings, numbers and booleans which are not modifiable.
To summarize:
Functions are useful to inherit, they are not modifiable (you can't change the function body), and can still be overridden/replaced if needed.
Data becomes shared data by all inheritors, unless/until they override it by a re-assignment, which is a burden in itself.

Related

How can I prevent javascript assignment from creating ownProperty

Here is an example:
var cat1 = Object.create({
name: "mia"
});
cat1.hasOwnProperty('name') // false
cat1.name = "haha";
cat1.hasOwnProperty('name') // true
This is rather surprising to me.
1) What is the design intention here?
2) How can I use = without create new properties?
What is the design intention here?
I didn't design JavaScript, but my guess would be to restrict the "scope" of mutations. Imagine you had two objects:
var proto = {name: "mia"};
var cat1 = Object.create(proto);
var cat2 = Object.create(proto);
If assignment to cat1.name would not create a new property, but update the prototype property instead, then cat2.name would suddenly be updated as well.
In other words, if assignment was updating prototype properties instead of the object's own properties, other objects could be affected by the change, without you even knowing it.
How can I use = without create new properties?
You cannot. You could assign to the prototype explicitly, but that requires you to know that the property is defined on the prototype:
Object.getPrototypeOf(cat1).name = 'haha';
hasOwnProperty shows properties defined on the object. At the same time:
Object.create({
name: "mia"
});
Creates new object with prototype {name: "mia"}. This means that property name will be defined for prototype not for object. Using prototypes makes some sort of optimization. Different object share the same logic from the prototype. Also the methods are not duplicated, so less memory is used.
When you defines own property by = you are showdowing the property with the same name from the prototype, so as #Felix Kling indicated you are not changing property in the prototype and other objects that use the same prototype will not be affected. You also can use Object.defineProperty(obj, prop, descriptor) instead of = in order to define own property of the object, but you can not use = on object without defining new properties you can do that on the prototype directly like this:
cat1.__proto__.name="other name" //avoid, this is not the best practive at all
By design, When you create object from a prototype, the new objects will share the properties with prototype object until you assign value explicitly to that property on that object, once its assigned since then the object will maintain its own state. below is sample code for the same.
var x = {"name":"mia"};
var o1= Object.create(x);
var o2= Object.create(x);
console.log(x.name,o1.name,o2.name); //output: mia,mia,mia
x.name="xxx";
console.log(x.name,o1.name,o2.name); //output: xxx,xxx,xxx
o1.name="yyy";
console.log(x.name,o1.name,o2.name); //output: xxx,yyy,xxx

why property in prototype of an object can not modified by object?

When I execute the following js code, I found something weird:
function Contact(name, email) {
this.name = name;
this.email = email;
}
Contact.prototype = {
a: 10,
b: 20
};
var obj = new Contact('ssj', 'ssh');
obj.a = 'ssjssh';
console.log(obj);
console.log(Contact.prototype);
//output: { name: 'ssj', email: 'ssh', a: 'ssjssh' },{ a: 10, b: 20 }
so my question is that why obj.a = 'ssjssh' only add a property in obj, instead of change the property a in Contact.prototype?
Properties in the prototype are only used as a default when reading them. This allows all the members of a class to get the same initial or default values for properties. But each object can override these default properties by having its own values. When you assign to the property, it always goes to the object's own properties, otherwise there wouldn't be any way to override the defaults.
From Eloquent Javascript by Marijn Haverbeke :
When you add a property to an object, whether it is present in the
prototype or not, the property is added to the object itself, which
will henceforth have it as its own property. If there is a property by
the same name in the proto- type, this property will no longer affect
the object. The prototype itself is not changed.
This is the way object properties are queried and modified.
Initially a is not an own property of the obj, but inherited (contrary to name and email, which are own properties). When querying it, the interpreter searches for the property in the prototype chain, and finds the a in the Contact.prototype.
When you assign a value to inherited a, JavaScript creates a new own property in the object, without affecting the prototype. Any later modifications of the own property do not affect the prototype. The inherited property will now be hidden.
If you would like to modify directly the prototype, just use Contact.prototype.a = 'new value'. But notice that this will affect all the instances of the Contact constructor, which use the inherited a property.
Use the method obj.hasOwnProperty('a') to check if the property is own or inherited.
The statement var obj = new Contact('ssj', 'ssh'); will create a new object newObj (assume jst for reference) internally and it will return newobj.
Before returning newObj, the statement
this.name = name;
this.email = email;
will set the newObj's name & email property to 'ssj' & 'ssh' as because of implicit binding 'this' is bound to newobj.
Now after returning obj will point to newObj with values as
name-'ssj' & email-'ssh'
After this you are creating a new property "a" on obj.
newObj which we created previously is pointing internally to Contact.prototype via [[prototype]] link. so newObj & Contact.prototype are both different object & you are setting property on newobj.
And by the concept of shadowing and a being not marked as read only it will not traverse up the prototype chain, instead it will set the a property directly on the immediate object.

JavaScript: assigning a value to a non enumerable property changes it to enumerable?

I think I'm misunderstanding something here. I have an object that contains a property that I wish to be non enumerable. I want to be able to assign values to it from within the object's functions itself, but when I do so, the property becomes enumerable.
Example:
function ObjectNode() {
}
Object.defineProperty(ObjectNode.prototype, "parent", {
value:null
// enumerable and configurable default to false
}
// Pass an ObjectNode instance and set this object node as its parent
ObjectNode.prototype.addChild = function(node) {
node.parent = this; // <-- node.parent is now enumerable
...more code...
};
What actually happens is that assigning node.parent a value creates a new "own" property called parent, which overrides the original.
Is there a way I can internally assign a value to a non enumerable property without doing this? Or do I have to resort to getter/setter properties that reference a variable within a closure?
EDIT:
GGG deserves credit for answering this, because it was his advice that led me to my ultimate answer.
What I wanted to do was essentially hide a simple property from the 'for(key in object)' statement. I was able to do this as so:
function ObjectNode() {
Object.defineProperty(this, "parent", {
value:null,
writable:true
});
}
One reason I balked at this to start with was the mistaken understanding that I did not want the property to be recreated with each instance. But, duh, that's what an instance property is!!!
Thanks, GGG, for helping me realign my brain!
You have created a property of the prototype property which is non-configurable, but it can still be shadowed by properties of objects created by the constructor.
function ObjectNode() {
}
Object.defineProperty(ObjectNode.prototype, "parent", {
value: null,
configurable: false
})
ObjectNode.prototype.addChild = function (node) {
node.parent = this; // <-- node.parent is now enumerable
}
var mom = new ObjectNode();
var kid = new ObjectNode();
mom.addChild(kid);
console.log(kid.parent == mom); // works, logs true
kid.parent = "foo";
console.log(kid.parent); // works, logs "foo"
ObjectNode.prototype.parent = "foo";
console.log(ObjectNode.prototype.parent); // fails, logs null
The thing to remember is that properties of an object are properties of that object only, not properties of other objects with that object in their prototype chain. Instead, try creating a non-configurable property of the new object in the constructor:
function ObjectNode() {
Object.defineProperty(this, "parent", {
value: null,
configurable: false
})
}
Of course, this will prevent your addChild function from working, since the property is no longer configurable. You could consider defining the property in that function instead, but it will still be configurable until it is set with addChild.
All in all, your best bet in general is to not worry about making things unconfigurable if it's not absolutely critical (and I'm not sure of any case where it would be). The world has gotten along pretty well without that functionality for a long time. ;)

Object.defineProperties/create really needed?

Some projects use Object.create() or Object.defineProperties() function. I wonder is is recommended? Whats the difference between
x = Object.create(null);
vs
x = {}
And
x = {}
x.__proto__.hello = function() {
console.log("hello");
}
vs
x = Object.create(null);
Object.defineProperty(x, "hello", {
value: function() {
console.log("hello");
}
});
defineProperty/create seems very verbose and long to me. When/Why do I use them? Perhaps the good might be to enforce getters/setters/overriding properties?
There is a huge difference. Have a look at the docs!
Object.create does create an Object that inherits from the first argument, null in your case. In contrast, {} - or new Object() - creates a new object that inherits from Object.prototype.
__proto__ is non-standard and should not be used. However, in your case you just do Object.prototype.hello = function() {…};. Never extend that object with enumerable properties, never ever!
Object.defineProperty does define a property on an object with a special descriptor object. The enumerable, configurable and writable attributes default to false, which means that you wont be able to delete x.hello for example, or assign any other value.
Your first snippet creates a plain object, which inherits a hello method from Object.prototype, while your second snippet creates an object inheriting from nothing and having a non-editable hello property. I don't see much relatedness.

Does a new object in Javascript have a prototype property?

This is a purely trivial question for academic value:
If I create a new object, either by doing:
var o = { x:5, y:6 };
or
var o = Object.create({ x:5, y:6 });
when I query the o.prototype property, I get undefined. I thought that any newly created object automatically inherits the Object.prototype prototype.
Furthermore, invoking toString(), (a method of Object.prototype) on this object works just fine, implying that o does inherit from Object.prototype. So why do I get undefined?
There is a difference between instances and their constructors.
When creating an object like {a: 1}, you're creating an instance of the Object constructor. Object.prototype is indeed available, and all functions inside that prototype are available:
var o = {a: 1};
o.hasOwnProperty === Object.prototype.hasOwnProperty; // true
But Object.create does something different. It creates an instance (an object), but inserts an additional prototype chain:
var o = {a: 1};
var p = Object.create(o);
The chain will be:
Object.prototype - o - p
This means that:
p.hasOwnProperty === Object.prototype.hasOwnProperty; // true
p.a === o.a; // true
To get the prototype "under" an instance, you can use Object.getPrototypeOf:
var o = {a: 1};
var p = Object.create(o);
Object.getPrototypeOf(p) === o; // true
Object.getPrototypeOf(o) === Object.prototype; // true
(Previously, you could access an instance's prototype with o.__proto__, but this has been deprecated.)
Note that you could also access the prototype as follows:
o.constructor === Object; // true
So:
o.constructor.prototype === Object.prototype // true
o.constructor.prototype === Object.getPrototypeOf(o); // true
This fails for Object.create-created objects because they do not have a constructor (or rather, their constructor is Object and not what you passed to Object.create because the constructor function is absent).
Not a direct answer, but knowledge that everybody, who deal with inheritance in Javascript, should have.
Prototype inheritance in Javascript is a tricky concept. Up until now, it has been impossible to create an empty object (by empty I mean lacking even properties form Object via prototype). So this means that creating a new object always had a link to the original Object prototype. However, according to the specification, the prototype chain of an object instance isn't visible, but some vendors have decided to implement their own proprietary object properties so that you could follow it, but it's highly recommended not to use it in production code.
The following sample code demonstrates just two ways of creating an object instance.
var someObject = {};
var otherObject = new Object();
var thirdObject = Object.create({});
Even though you don't manually add object properties to empty curly braces, you still get automatically added prototype chain. The same goes for the second example. To visualize it better, you can type those lines into Chrome console and then enter either someObject, otherObject or thirdObject to see details. Chrome shows the prototype chain by adding a proprietary property __proto__ which you can expand to see what is inherited and where it's from. If you executed something like
Object.prototype.sayHello = function() {
alert('hello');
};
you would be able to call it on all instances, by executing otherObject.sayHello().
However, using something that was implemented quite recently (therefore not supported by all browsers), you can actually create a truly empty object instance (doesn't inherit even from Object itself).
var emptyObject = Object.create(null);
When you enter it into Chrome console and then expand the emptyObject to see it's prototype chain, you can see that it doesn't exist. So even if you implemented the sayHello function to Object prototype, it would be impossible to call emptyObject.sayHello() since emptyObject does not inherit from Object prototype.
Hope it helps a bit with the general idea.
JavaScript has two types of objects: function object and non-function object. Conceptually, all objects have a prototype (NOT A PROTOTYPE PROPERTY). Internally, JavaScript names an object's prototype as [[Prototype]].
There are two approaches to get any object (including non-function object)'s [[prototype]]: the Object.getPrototypeOf() method and the __proto__ property. The __proto__ property is supported by many browsers and Node.js. It is to be standardized in ECMAScript 6.
Only a function (a callable) object has the prototype property. This prototype property is a regular property that has no direct relationship with the function's own [[prototype]]. When used as a constructor ( after the new operator), the function's prototype property will be assigned to the [[Prototype]] of a newly created object. In a non-function object, the prototype property is undefined . For example,
var objectOne = {x: 5}, objectTwo = Object.create({y: 6});
Both objectOne and objectTwo are non-function objects therefore they don't have a prototype property.

Categories

Resources