In mdn's doc's for the prototype chain, it states
All objects inherit the Object.prototype.__proto__ setter, which can be used to set the [[Prototype]] of an existing object (if the __proto__ key is not overridden on the object).
It then goes on to say that
Object.prototype.__proto__ accessors are non-standard and deprecated. You should almost always use Object.setPrototypeOf instead.
Along with this example:
const obj = {};
// DON'T USE THIS: for example only.
obj.__proto__ = { barProp: 'bar val' };
obj.__proto__.__proto__ = { fooProp: 'foo val' };
console.log(obj.fooProp);
console.log(obj.barProp);
The part that is confusing is they start the docs out with this example:
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal.
__proto__: {
b: 3,
c: 4,
},
};
Stating that,
{ __proto__: ... } syntax is different from the obj.__proto__ accessor: the former is standard and not deprecated.
How is { __proto__: ...} different from obj.__proto__? Both are properties of an object, and I'm not quite clear on what the difference is here.
It's just the way the syntax was designed. (See here and here.) Assigning to the __proto__ of an existing object is deprecated, but specifying a __proto__ at the point when the object is created is not.
One reason for why an object literal can have it but doing so with an already existing object is not recommended is because, as MDN says on the subject of changing an object's prototype:
Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine. In addition, the effects of altering inheritance are subtle and far-flung, and are not limited to the time spent in the Object.setPrototypeOf(...) statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. You can read more in JavaScript engine fundamentals: optimizing prototypes.
Because this feature is a part of the language, it is still the burden on engine developers to implement that feature performantly (ideally). Until engine developers address this issue, if you are concerned about performance, you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().
In well-designed code, there should not be a need to dynamically change the internal prototype of an already existing object. In contrast, it's completely normal to want to specify an internal prototype when creating an object initially.
(Setting a new internal prototype of an object can be done with setPrototypeOf, which is not recommended, and by assigning to the object's __proto__, which is not only not recommended, but deprecated as well)
Related
I've read and tried understanding other answers similar to this question as well (like this one), But still the concept of prototypal inheritance is not very clear to me. and right now the thing that's confusing me the most is, that what is the actual difference between __proto__ and [[ Prototype ]]? As far as I've been able to understand is that [[ Prototype ]] is an "internal linkage that ties one object to another". But it gets ambiguous when I see a tutorial on youtube, because whenever they create an object and if they try to log it using console.log in their browser's console then it actually has the __proto__ property in it but when I try to do the same it outputs [[ Prototype ]] instead. So I'd like to know why is it so? and what is an "internal link"? Thanks in advance! :)
Below's the code that outputs "[[ Prototype ]]" in chrome and "<prototype>" in firefox.
function User(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const user = new User("Someone", "Something");
console.log(user);
Each object's prototype is saved in the internal slot named as [[prototype]] and __proto__is just a getter/setter, defined in the Object.prototype object, to get the value of the [[prototype]] internal slot of any object.
Example:
const arr = [];
Each instance of an array gets Array.prototype as its prototype. So, in the above declaration of arr, [[prototype]] internal slot contains a reference to Array.prototype and in the following expression:
arr.__proto__ === Array.prototype // true
arr.__proto__ gets the Array.prototype object from the internal [[prototype]] slot.
As mentioned above, __proto__ is just a getter/setter that gets the value of [[prototype]] internal slot and is only there for compatibility reasons. It shouldn't be used in modern javascript code; following two methods should be used to set/get the prototype of any object:
Object.setPrototypeOf()
Object.getPrototypeOf()
There are other internal slots, apart from [[prototype]], mentioned in the Ecmascript specification and these internal slots are not accessible by the javascript code we write.
If you need to know more about internal slots, read:
What is an "internal slot" of an object in JavaScript?
The double square bracket notation comes from the ECMAScript specification where it always refers to an internal entity (field, attribute, slot,...) but is not a notation you can use in JavaScript code. It is information for the implementer of the language and helps to precisely define the behaviour of the language.
In a console you may see information represented with these double brackets, as is the case in the Chrome console. Firefox uses a different notation: <prototype>.
Now to the core of your question. The link between an object and its prototype object is not an own JavaScript property of that object. It is an internal slot:
Internal slots correspond to internal state that is associated with objects and used by various ECMAScript specification algorithms. Internal slots are not object properties and they are not inherited.
You can get the prototype object via .__proto__, but that __proto__ property is a getter on the Object.prototype object. So that is like running in circles: when you write obj.__proto__, the engine needs to know what the prototype chain is before it can find that __proto__ value for you, since it needs to get it via inheritance -- it is not a property of obj itself. And to find the inheritance link the engine will use the internal "slot" [[Prototype]].
There are a lot of mentions on differentes readings that arrays are a special class of object in Javascript. For example here:
https://www.codingame.com/playgrounds/6181/javascript-arrays---tips-tricks-and-examples
So, and since an object is a collection of properties (or keys) and values, I was thinking if there is a way to start with an object and ends with an array (in the sense that the method Array.isArray() returns true for that object emulating an array). I have started looking at the arrays properties:
let arr = [0, 1, 2, 3, 4, 5];
console.log(Object.getOwnPropertyNames(arr));
console.log(Array.isArray(arr));
So I tried to emulate the same using an object:
let arrEmulation = {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, "length":6};
console.log(Object.getOwnPropertyNames(arrEmulation));
console.log(Array.isArray(arrEmulation));
But Array.isArray(arrEmulation) still returns false. First, I want to apologize if this is an stupid question, but is there any way I can manually convert an object to array adding special properties (or keys) to it?
Please, note I don't want to know how to convert object to array, I just want to understand which are those special things that make possible to interpret an object like an array.
I don't think it's possible, in the strictest sense, given the standard specification. Looking up Array.isArray:
If the value of the [[Class]] internal property of arg is "Array", then return true.
So, for Array.isArray(arrEmulation) to return true, you must somehow modify the [[Class]] of the object to be Array, rather than Object. But, looking at ES5's 8.6.2 Object Internal Properties and Methods regarding [[Class]]:
Note: This specification defines no ECMAScript language operators or built-in functions that permit a program to modify an object’s [[Class]] or [[Prototype]] internal properties or to change the value of [[Extensible]] from false to true. Implementation specific extensions that modify [[Class]], [[Prototype]] or [[Extensible]] must not violate the invariants defined in the preceding paragraph.
Also:
Note that this specification does not provide any means for a program to access that value except through Object.prototype.toString
So, the official specification doesn't provide a way to do it in ES5 - if there was a way to do it, it would be non-standard and implementation dependent.
That said, unless you absolutely need to use Array.isArray or have Object.prototype.toString.call(arrEmulation) to return [object Array], you can still use Object.setPrototypeOf to set the prototype of arrEmulation to Array.prototype, allowing you to use array methods on the object and have instanceof Array return true:
const arrEmulation = {0:0, 1:1, 2:2, "length":6};
Object.setPrototypeOf(arrEmulation, Array.prototype);
console.log(arrEmulation instanceof Array);
arrEmulation.forEach((value) => {
console.log(value);
});
// Internal [[Class]] property is still `Object`, though:
console.log(Object.prototype.toString.call(arrEmulation));
// Unlike a true array:
console.log(Object.prototype.toString.call([]));
console.log('-----');
// although you can set the `toStringTag` to the string 'Array' in ES6+,
// it is cosmetic only and does not pass an `Array.isArray` test:
arrEmulation[Symbol.toStringTag] = 'Array';
console.log(Object.prototype.toString.call(arrEmulation));
console.log(Array.isArray(arrEmulation));
But note that you should avoid using Object.setPrototypeOf in real code:
Warning: Changing 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 altering inheritance are subtle and far-flung, and are not limited to simply the time spent in the Object.setPrototypeOf(...) statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. If you care about performance you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().
(of course, Object.create involves creating a new object, which is different from what you want to do, which is to change the existing arrEmulation object)
There doesn't look to be a way to do it in ES6+ either - its text is somewhat similar, but not identical. Specifically, for Array.isArray to return true, the object in question needs to be an "Array exotic object" (or a Proxy that points to one) - but setPrototypeOf only sets the prototype, neither it nor any other method can make the object actually become an Array exotic object (which looks like it has to be natively constructed by the interpreter, and is not emulatable enough).
Javascript is all about prototypal inheritance:
Prototype Inheritance
All JavaScript objects inherit properties and methods from a
prototype:
Date objects inherit from Date.prototype Array objects inherit from
Array.prototype Person objects inherit from Person.prototype The
Object.prototype is on the top of the prototype inheritance chain:
Date objects, Array objects, and Person objects inherit from
Object.prototype.
As seen here isArray is an function in prototype chain of the Array object.
An polyfill as suggested in MDN Array.isArray() alternate if isArray is not present is:
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
So the type is determined by the prototype chain instead of what value it returns.
Similarly, as per Tio Zed's answer
const newArray = Array.from(arrEmulation) // [0, 1, 2, 3, 4, 5]
Array.isArray(newArray)
What it really does is just change to prototype from that of object to that of an Array.
A deeper go through of isArray thanks #Kaiido for making me dig deeper.
The Array is array checks these there points
If Type(arg) is not Object, return false.
If the value of the [[Class]] internal property of arg is "Array",
then return true. Return false.
And
Array instances inherit properties from the Array prototype object and
their [[Class]] internal property value is "Array". Array instances
also have the following properties.
You can convert anything that is close enough to an array by using Array.from().
In your example, we could just call:
const arrEmulation = {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, length: 6};
const newArray = Array.from(arrEmulation) // [0, 1, 2, 3, 4, 5]
Array.isArray(newArray) // true
If you create a regular javascript object using say var obj = {}; it will have the object prototype. Same goes for objects created using var obj = new MyClass(); Before Object.create was introduced there was no way around this. However nowadays it's possible to create an object with no prototype (respectively null as its prototype) using var obj = Object.create(null);.
Why is this important? What advantages does it bring? Are there any real world use cases?
It's a completely empty object (nothing inherited from any .prototype including Object.prototype), so you can be guaranteed that any property lookup will only succeed if the property was explicitly added to the object.
For example, if you want to store some data where you won't know the keys in advance, there's a possibility that a provided key will have the same name as a member of Object.prototype, and cause a bug.
In those cases, you need to do an explicit .hasOwnProperty() check to rule out unexpected inherited values. But if there's nothing inherited, your testing can be simplified to a if (key in my_object) { test, or perhaps a simple truthy test if (my_object[key]) { if appropriate.
Also, with no prototype chain, I would imagine that lookups of properties that turn out to not exist would be faster, since only the immediate object would need to be checked. Whether or not this pans out in reality (due to optimizations) would be determined with performance testing.
The only difference here between creating an object with {} and Object.create(null) is that the Object prototype will not be inherited from.
This means that any of the methods we typically assume we can call on any object will not exist, such as toString and valueOf. A list of these can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
From a performance perspective, creating an object simply by {} is actually much faster, so unless you specifically cannot have the functions under the Object prototype you should not create objects in that manner.
http://jsperf.com/null-vs-empty-object-performance
Is there any possibility to change the __proto__ property of an object in IE9 or IE10?
Or is MS still not planning to include it in their JS engine?
I need it in a very special situation where I need to change __proto__ after the object is created.
__proto__ is going to be standardized in ES6. It is currently in Appendix B of the ES6 draft which in practice means that if it is implemented it needs to have the following semantics.
__proto__ is both available as an accessor on Object.prototype which means that all objects can read and write it by default. However, it can be removed from Object.prototype (using delete). Once deleted __proto__ will work as a normal data property with no side effects on setting.
__proto__ is also a special syntactic form in object literals. It will work to set the [[Prototype]] even if Object.prototype.__proto__ was deleted.
var p = {a: 1};
var o = {
__proto__: p,
b: 2
}
ES6 also introduces Object.setPrototypeOf (not in the appendix). This is preferred over setting __proto__.
__proto__ is available in all modern browsers, including Internet Explorer 11.
__proto__ is included in IE11 found in the leaked build of Windows Blue: http://fremycompany.com/BG/2013/Internet-Explorer-11-rsquo-s-leaked-build-395/
A nonanswer as a last case resort:
Change your code so that all the properties that would originally be accessed via the changed prototype are now accessed via explicit delegation over a normal property:
{
a: 17,
__proto__: { ... }
}
to
{
a: 17,
proto: {...}
}
I'm not sure what exactly it is you're after since your question didn't specify, but for most uses of proto, you should be able to use prototype.
var foo = new Bar();
//this also affects foo
Bar.prototype.baz = something;
I was browsing MDC about new functions added to Object. One of them, Object.preventExtensions, is said to prevent mutations to the object's prototype, which can be obtained by using Object.getPrototypeOf or __proto__.
On Chrome, however, it seems to simply allow mutations to the object's prototype. This can be confirmed by just executing the code on the relevant page:
// EXTENSION (only works in engines supporting __proto__
// (which is deprecated. Use Object.getPrototypeOf instead)):
// A non-extensible object's prototype is immutable.
var fixed = Object.preventExtensions({});
fixed.__proto__ = { oh: "hai" }; // throws a TypeError
I don't get this TypeError, and fixed.__proto__.oh === 'hai', so it has been set even though it should have been disallowed. I can also add it when coding like Object.getPrototypeOf(fixed).oh = 'hai'.
Does this mean Chrome has a different interpretation of this function? How can one prevent extensions to an object's prototype (in Chrome)?
Nope, Chrome and Mozilla both implement the standards part of the spec the same. Read carefully:
Object.preventExtensions only prevents
addition of own properties. Properties
can still be added to the object
prototype.
Everything to do with .__proto__ is non-standard, and Chrome can implement that differently. You showed only that Chrome implements details with .__proto__ differently, and in my opinion, more intuitively: The spec says that the prototype is still extensible, so it makes sense that you should still be able to mutate it. The question then becomes why did Mozilla implement it that way?
For example, the following code works the same on both Chrome and FF:
var fixed = Object.preventExtensions({});
Object.getPrototypeOf(fixed).p = 99;
fixed.p; // 99
Clearly the prototype is still mutable. That makes sense with Chrome's implementation of .__proto__.
So to prevent extensions of a prototype, do so separately:
var fixed = Object.preventExtensions({});
Object.preventExtensions(Object.getPrototypeOf(fixed));
Object.getPrototypeOf(fixed).p = 99; // TypeError: Can't add property p, object is not extensible
In ECMAScript 5, objects have a boolean internal property named [[Extensible]], this property is set to false when you call the Object.preventExtensions method, and after that, no new own properties can be added to the object.
On Chrome 14.0.786.0, the assignment to __proto__ throws a TypeError as you expect.
But remember that the __proto__ extension is non-standard, so it's behavior may vary, of course syntactically it is a "property assignment", but internally it doesn't "add an own property", it mutates the object's prototype, thing that is not possible to do by any standard method.
About the example of the Object.getPrototypeOf method you show us, it's simply retrieving the object's prototype, in the case of your fixed object, it's the Object.prototype object:
Object.getPrototypeOf(fixed) === Object.prototype; // true
So your example:
Object.getPrototypeOf(fixed).oh = 'hai'
Is equivalent to:
Object.prototype.oh === 'hai';