Object.defineProperties/create really needed? - javascript

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.

Related

Correct way to pass prototype to an object

My understanding of prototypes is as follows:
let Animal = function() {
this.bark = "woof";
}
Animal.prototype.barkLoud = function() {
return this.bark.toUpperCase();
}
let x = new Animal();
x.barkLoud() = "WOOF";
Everything above makes sense to me but then I saw a tutorial what seemingly appears to be 2 different ways to pass prototypes to an object. Are these the same thing? If so, which approach is better:
let obj = {
age: 45;
__proto__: Animal
}
vs
let obj = {
age: 45;
}
obj.prototype = Object.create(Animal.protoype);
As a rule of thumb, the more underscore (_) characters you see around a property name in JS, the more of implementation details it is - and the more discouraged you are from even querying it, let alone attempting to modify.
While __proto__ is indeed supported by all the existing browsers, its usage to set up a prototype is not recommended. Use Object.create() instead.
BTW, two ways you've showed are not even equivalent. See, __proto__ refers to the prototype object, but Animal is not the one - it's a function. Animal.prototype is an object that will be used as a prototype (__proto__ value) for all the objects created by this function with new` operator.
So the first object won't be able to resolve barkLoud name from the prototype chain:
> obj.barkLoud // undefined
__proto__ way is deprecated by the way and I've never seen it implemented anywhere.
Go with Object.create as the official and recommended way to create/assign prototype of existing object to the newly created object.

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

Extend already defined class in JavaScript

The traditional way of extending classes in JS is something like this:
// define constructor function
function Fuu(){}
// extend prototype and override prototype's constructor
Fuu.prototype = Object.create(OtherClass.prototype, {
constructor: {
value: Fuu,
enumerable: false,
writable: true,
configurable: true
}
});
Then you add the methods you want to the prototype
Fuu.prototype.method = function() {}
And just like that you have a function extending another. A nice example of inheritance in JS!
My question is how to extend when the sub class already has a prototype with methods and properties. I could try to copy the the methods of the old prototype to the new one using a for in loop but the methods are non enumerable(class is created with a transpiler) and doing something with getOwnPropertyNames doesn't seem right. Any suggestion? can I do something like keeping the prototype and adding a prototype to the prototype?
Edit: example
class Fuu {
someMethod(){} // non enumerable method in Fuu's prototype
}
// My first option: (extending this way `someMethod` is lost)
Fuu.protoype = Object.create(HTMLElement.prototype, {//...same as before})
// Option 2: copy methods from old to new prototype
// Option 3: prototype of prototype?
// Fuu.prototype.prototype = Object.create(HTMLElement.prototype, {...})
You want something like
┌──> Fuu.prototype
instances ──┤
└──> OtherClass.prototype
But that's not possible, because objects only have one [[Prototype]].
Therefore, you must achieve one of these:
instances ───> Fuu.prototype ───> OtherClass.prototype
instances ───> OtherClass.prototype ───> Fuu.prototype
So you must set the [[Prototype]] of one of those to be the other one. I will assume the first possibility.
There are two main ways to set the [[Prototype]]:
Object.create, when creating the object
The problem is that both Fuu.prototype and OtherClass.prototype have been created already.
However, you can create a new object with the right [[Prototype]] and assign the properties of the old one.
Since there may be non-enumerable properties, you must use getOwnPropertyNames. Using defineProperty and getOwnPropertyDescriptor may also be a good idea, in case there are getters or setters.
var old = Fuu.prototype,
props = Object.getOwnPropertyNames(old);
Fuu.prototype = Object.create(OtherClass.prototype);
for(var i=0; i<props.length; ++i)
Object.defineProperty(
Fuu.prototype,
props[i],
Object.getOwnPropertyDescriptor(old, props[i])
);
setPrototypeOf or __proto__ (ES6), once the object has been created:
Object.setPrototypeOf(Fuu.prototype, OtherClass.prototype);
Fuu.prototype.__proto__ = OtherClass.prototype;
However, be aware that
Mutating the [[Prototype]] of an object is, by the nature of how
modern JavaScript engines optimize property accesses, a very slow
operation, in every browser and JavaScript engine. The effects on
performance of mutating prototypes [...] may extend to any code that
has access to any object whose [[Prototype]] has been mutated. If you
care about performance you should avoid mutating the [[Prototype]] of
an object.
I think the method you have suggested is probably the bet way to go. Is there a reason why you think it is wrong?
var old = Fuu.prototype;
Fuu.prototype = Object.create(OtherClass.prototype, {
constructor: {
value: Fuu,
enumerable: false,
writable: true,
configurable: true
}
});
var names = Object.getOwnPropertyNames(old);
for (var i = 0; i < names.length; i++) {
var name = names[i];
Fuu.prototype[name] = old[name];
}
The only thing I'd be concerned about is your constructor method being overridden by the old version, and for your old prototype's prototype chain being lost; however you can do things to fix this.

I don't understand writable and configurable property attributes of Objects

I don't understand the Writable and Configurable attributes of Objects. For example, in the MDN for Object.prototype, there is a table where I can clearly see that Configurable, Writable and Enumerable Property Attributes of Object.prototype are locked.
However, I can write and extend Object.prototype, for example with the following code:
// Example 1
Object.prototype.testing=999;
console.log(Object.testing); // 999
// Example 2
var o = {};
console.log(o.testing); // 999
What the MDN is referring to is the property prototype of Object itself. You cannot overwrite Object.prototype itself. If you try and make Object.prototype undefined, that will fail:
Object.prototype = 1;
console.log(Object.prototype); // [object Object]
If you try this in strict mode, you will get a TypeError upon attempting to assign to a non-writable property:
'use strict';
Object.prototype = 1; // TypeError: Cannot assign to read only property 'prototype' of function Object() { [native code] }
You can write to an object's own properties without changing what the object's reference is, and those have separate attributes. For example, see this:
var descriptor = Object.getOwnPropertyDescriptor(Object.prototype, 'toString');
console.log(descriptor.writable); // true
console.log(descriptor.enumerable); // false
console.log(descriptor.configurable); // true
There is a separate [[Extensible]] internal property that prevents the creation of new properties on an object -- this is set to false if you call Object.preventExtensions, Object.seal or Object.freeze.
Note that it's not a good idea to call Object.freeze on something like Object.prototype, as really weird things can happen:
Object.freeze(Object.prototype);
var building = {};
building.name = 'Alcove Apartments';
building.constructor = 'Meriton Apartments Pty Ltd';
console.log(building.constructor); // function Object() { [native code] }
Just like the previous example, it will also throw a TypeError in strict mode.
Basically, even though it would be a property on the object itself, it uses the attributes from the prototype chain to check whether or not it can assign the property. This has been considered as a mistake in the language by some people, however others consider this behaviour to be by design.
From: http://ejohn.org/blog/ecmascript-5-objects-and-properties/
Writable: If false, the value of the property can not be changed.
Configurable: If false, any attempts to delete the property or change its attributes (Writable, Configurable, or Enumerable) will fail.
Enumerable: If true, the property will be iterated over when a user does for (var prop in obj){} (or similar).
The Writable, Enumerable and Configurable attributes in MDN appear to be about the Object.prototype object itself, not its properties.
So, what that means is that you can't replace Object.prototype with a different object, but you are allowed to add properties to it.
So, what that means is if you do this:
Object.prototype = {foo: "whatever"}; // doesn't work - is just ignored
var j = {};
console.log(j.foo); // undefined
Then, the first line of code won't do anything.
I can clearly see that Configurable, Writable and Enumerable Property Attributes of Object.prototype are locked.
However, I can write Object.prototype.
No. The writability only concerns the prototype property of the Object object:
Object.prototype = {}; // Error: Invalid assignment (in strict mode)
// simply fails in lax mode
And I can extend Object.prototype
Yes. You can extend the Object.prototype object (regardless how you refer to it); that's a different attribute (of the object, not of a single property):
var proto = Object.getPrototypeOf({});
proto.testing1 = 9999; // works
Object.preventExtensions(proto);
proto.testing2 = 9999; // Error: Invalid assignment (in strict mode)

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