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';
Related
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)
So I have an object created by the eventemitter3 node module. The event emitter has a property _events, which, when I look at it in the Chrome inspector, shows Events {}. However, when I attempt to look at its constructor (temp1.constructor), I get undefined! Under the description on MDN (here), it says "All objects will have a constructor property", yet I have a clear counterexample here in my Chrome console (I also tried this in Firefox, so it's unlikely to be a problem with V8).
I can see in the eventemitter3 source the following lines that define the Events class:
//
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null);
//
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
So I suppose it makes sense that they're trying to get rid of the constructor property but:
How does Chrome still know where the object came from enough to print its name in the console?
Is the MDN description wrong about constructors?
How can I glean the string "Events" from the object in JavaScript?
I have read about javascript prototype and read some stackoverflow question as well like how-does-javascript-prototype-work and I was finally understanding prototype properly but then as I was going through documentation on Mozilla Developer Netork (MDN) regarding prototype under this
Details_of_the_Object_Model MDN
Under that there is a note
Note: Directly assigning to FunctionName.prototype removes its original prototype's "constructor" property. As a result, (new WorkerBee).constructor yields "Employee" (instead of expected "WorkerBee"). Care must be taken to preserve the original prototype's constructor. For instance, assign the parent to FunctionName.prototype.__proto__ instead. For example, WorkerBee.prototype.__proto__ = new Employee; This way, (new WorkerBee).constructor yields expected "WorkerBee".
I can't seem to understand what this statement mean
Directly assigning to FunctionName.prototype removes its original prototype's "constructor" property.
Does it mean that Manager.prototype = new Employee will replace Manager.__proto__ = Function.prototype to Employee.prototype?
As a result, (new WorkerBee).constructor yields "Employee" (instead of expected "WorkerBee")
What does it mean that it will yield Employee? I know we should not directly use __proto__ but the above statement specify to use FunctionName.prototype.__proto__. In which case this is true?
Can someone explain with example what does it try to convey?
Prototype on functions are used as proto for objects generated by these functions.
A functions proto points to Function.prototype since a function is also an object instance by itself. Setting it's proto does not affect it's prototype but re setting it's prototype does affect the prototype. constructor
You should not set Child.prototype to an instance of Parent, use Object.create instead.
What prototype.constructor is and more is explained here: Prototypical inheritance - writing up
I want to know the difference between __proto__ and Object.create method. Take this example:
var ob1 = {a:1};
var ob2 = Object.create(ob1);
ob2.__proto__ === ob1; // TRUE
This implies Object.create method creates a new object and sets __proto__ link to the object received as parameter.
Why don't we directly use __proto__ link instead of using create method ?
__proto__ is nonstandard and won't be supported everywhere. Object.create is part of the official spec and should be supported by every environment going forward.
It also is implemented differently in different places.
From Effective Javascript:
Environments differ for example, on the treatment of objects with a
null prototype. In some environments, __proto__ is inherited from
Object.prototype, so an object with a null prototype has no special
__proto__ property
Moving forward the accepted way to create objects and implement inheritance is the Object.create function, and if you do need to access the prototype, you'll want to use Object.getPrototypeOf These functions are standardized and should work the same in all modern environments
Why don't we directly use proto link instead of using create method ?
Because __proto__ is a non-standard property and therefore not necessarily available in every browser.
However it seemed to be considered for ES.next. More info: MDN - __proto__.
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;