The MDN page about set seems to state that an
[ECMAScript 2015] setter must not appear in an object literal ... with a
data entry for the same property.
However when using the super keyword this no longer seems to apply.
class Foo {
constructor(bar){
this.bar = bar
}
set bar(newBar){
if (!newBar.match(/\w+/))
throw Error("Invalid bar value")
// I can use super despite not being a derived class.
return super.bar = newBar
}
}
const baz = new Foo("Baz")
baz.bar = "new value" // No recursion
This seems like a useful feature as the property doesn't have to be "hidden" by prefixing it with an underscore. Plus I don't have to mess with the property enumerability to avoid the "hidden" version from showing in a loop or serialization.
But the set syntax is a bit of a black-box and I can't tell what it's actually doing.
Am I breaking something here or is it okay to use?
Also what is super referencing here?
This seems like a useful feature as the property doesn't have to be "hidden" by prefixing it with an underscore or something. Plus I don't have to mess with the property enumerability to avoid the "hidden" version from showing in a loop or serialization.
No, it's not useful. It's a hack at best, and doesn't do what you expect.
There is nothing hidden here at all. You are creating a new property with the name bar on the instance itself, shadowing any getters/setters you had defined on the prototype. The second assignment does not get your setter caller. Also the instance property is a normal enumerable property, so it will show up in for in loops and serialisation.
Also what is "super" referencing here?
The super keyword refers to the prototype of the object that the method (or setter) is defined on, i.e. Object.getPrototypeOf(Foo.prototype). This is the Object.prototype in your case, since your class Foo doesn't extend anything.
The .foo access will be looked up on that prototype, and would normally find a method that you inherited from your parent class or something. When using that, the property reference super.foo will however make the receiver of the operation (i.e. what would the this keyword in a method invocation) be the current this instance, not the prototype.
In your case, it's not a method call but an assignment. This could run a setter inherited from the parent class, but in your case there is no Object.prototype.foo property so it will fall back to standard assignment on the target - and that target is the baz instance itself, where a new own property will be created.
So no, it is not okay to use.
Related
Looking at this simple code :
class Animal {
someField = 42;
animalFunc() {
console.log('animal')
}
}
class Lion extends Animal {
lionFunc() {
console.loge('lion')
}
}
let lion = new Lion();
console.log(lion)
Result in Chrome is :
As we can see, the instance methods are on the prototype (for each constructor function)
Question:
Why are fields, as opposed to methods, not on the prototype?
I mean , someField is in Animal not in Lion.
This is the intended behavior as seen in ECMAScript - 15.7.14 Runtime Semantics: ClassDefinitionEvaluation 25.f:
f. Else if element is a ClassFieldDefinition Record, then
i. If IsStatic of e is false, append element to instanceFields.
ii. Else, append element to staticElements.
and in 29.:
Set F.[[Fields]] to instanceFields.
So after we see that this is not a bug, let's see why:
In simple words:
As we know if we have a function on the prototype and we changed that function value, it would be changed to everyone.
If we put the property on the prototype and some instance change the property value it would change the property value in other instances as well and naturally this behavior should not happen so we put the properties on the instance itself.
For static properties this is the intended behavior as we only have one instance of them, this is why they are on the prototype
Extra:
this comment by Ranando D Washington-King from class fields proposal
The quote that I mention is about the problem of add an x data property to the prototype [...]:
Every instance has a copy of the object. The expectation may or may not be exactly that. Consider in Java, the ability to define nested classes. In that case, it is definitely the desire that every instance be able to see the exact same class definition. On the other hand, consider assigning an empty array. The likely intention is for each instance to have its own copy of that array for storing instance-specific values.
I have been trying to learn about prototypes since I needed quite a bit of clarity on them. I have been using this MDN article and its related articles for reference.
After some reading, I got a bit of clarity on prototypes and tried putting them in my own words and would like to know if it is right and would love to know if and where I am wrong
START OF EXPLANATION
Every object is created using a constructor function of some sort. Say, we create an object as follows.
let obj = new Object();
Here, Object is the constructor function. The thing about functions is that, all of them (including non-constructor functions) have a property called prototype on them. This prototype property defines what will be the prototype of any object that is created using the new keyword and said constructor function. You can check the prototype property as follows:
console.log(Object.prototype);
The above piece of code will return an object with a bunch of methods that any object created using new Object() can use.
In the above example, if the wording is too confusing, you can replace all occurrences of Object with any other constructor function such as Array, Date or even custom constructor functions such as Person or something else that you may have defined.
END OF EXPLANATION
Is my understanding right? If not, can you point me where I went wrong?
Is my understanding right? If not, can you point me where I went wrong?
In big picture terms, yes, your understanding is mostly correct, but the explanation is incomplete, and there are some specifics that are incorrect.
Every object is created using a constructor function of some sort.
That's not quite correct, JavaScript also has literal forms ({} [object], [] [array], and // [regular expression]) that create objects without using a constructor function. Those forms assign Object.prototype, Array.prototype, and RegExp.prototype (respectively) to the objects they create, even though the constructor itself isn't invoked.
There are also other ways of creating objects that don't go through constructor functions at all. For instance, there's Object.create, which creates an object and assigns the prototype you supply to it:
const p = {};
const obj = Object.create(p);
console.log(Object.getPrototypeOf(obj) === p); // true
(There are also more obscure ways of creating objects through implicit conversion.) You can also change the prototype of an existing object by using Object.setPrototypeOf.
The thing about functions is that, all of them (including non-constructor functions) have a property called prototype on them.
Not quite, arrow functions and class methods do not have a prototype property and cannot be used as constructors:
const arrow = () => {};
class X {
method() {
}
static staticMethod() {
}
}
console.log("prototype" in arrow); // false
console.log("prototype" in X.prototype.method); // false
console.log("prototype" in X.staticMethod); // false
This prototype property defines what will be the prototype of any object that is created using the new keyword and said constructor function.
Correct. (Constructor functions can mess with what they return, but that's the usual, standard behavior.)
At this point in an explanation I'd probably point out the distinction between the prototype property on functions and the prototype of an object. Beginners sometimes think setting a prototype property on an object will change its prototype; it doesn't, that name is only significant on functions, and it's not the function's prototype, it's just a property that (as you said) will be used to assign the prototype of an object created using new with that function. The prototype of an object is held in an internal field of the object called [[Prototype]]. That field isn't directly accessible, but you can access it via Object.getPrototypeOf and change it via Object.setPrototypeOf (you can also use the deprecated __proto__ accessor property, which is just a wrapper around those functions — but don't use __proto__, use the functions directly).
But aside from all that, there's a big unanswered question in your explanation: What are prototypes for? What do they do? Why have them?
The answer is that they provide JavaScript's inheritance mechanism. When you get the value of a property on an object and the object doesn't have a property of its own with the given key, the JavaScript engine looks at the object's prototype to see if it has the property (and the prototype of the prototype, and so on through the chain):
const parent = {
a: "a property on base",
};
const child = Object.create(parent);
child.b = "a property on child";
const grandChild = Object.create(child);
grandChild.c = "a property on grandChild";
console.log(grandChild.a); // "a property on base"
console.log(grandChild.b); // "a property on child"
console.log(grandChild.c); // "a property on grandChild"
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`hasOwn(grandChild, "a")? ${hasOwn(grandChild, "a")}`); // false
console.log(`hasOwn(grandChild, "b")? ${hasOwn(grandChild, "b")}`); // false
console.log(`hasOwn(grandChild, "c")? ${hasOwn(grandChild, "c")}`); // true
Those example property values are strings, but this is widely used where the property values are functions, providing a means of inheriting methods from parent objects.
The property access process is asymmetrical, though; it only works as described above for getting a property's value. If you set a property's value on an object, it always sets it on the object itself, not on its prototype:
const parent = {
prop: "parent",
};
const child = Object.create(parent);
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`[Before] child.prop: ${child.prop}`);
// => "[Before] child.prop: parent"
console.log(`[Before] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[Before] hasOwn(child, "prop")? false"
child.prop = "child";
console.log(`[After] child.prop: ${child.prop}`);
// => "child.prop: child"
console.log(`[After] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[After] hasOwn(child, "prop")? true"
(This difference between getting and setting the property value applies to data properties [the kind we mostly create]; accessor properties work differently because getting and setting the property result in function calls, and the accessor's setter function can do whatever the author wants it to do.)
I was trying to explain the rare use cases for prototypal inheritance to a coworker when I got stumped by the fact that this doesn't work like I expect.
const baseobj = { baseproperty: 'original'};
const x = Object.create(baseobj);
//should add properties to x, not the base
x.newproperty = 'bar';
console.assert(x.newproperty != baseobj.newproperty);
//so far so good
//should modify the base, not x
x.baseproperty = 'foo';
console.assert(x.baseproperty == baseobj.baseproperty);
//this assert failed, property is on x!?
I'm confused. So far as I understand assignment is a two-step process, one to look up the property and another to assign to it. So why on earth is lookup of baseproperty not returning (and therefore modifying) baseobj.baseproperty?
I know something like this is key to how angularjs scopes work, but what specifically is going on here eludes me.
Assigning to a property on an object instance always creates an "own" property on the target object (if one doesn't exist of course). Prototype properties are never affected by such assignments, even if a prototype chain property matching the name does exist.
After the assignment, of course, the "own" property on the object hides the prototype chain property.
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
Is it possible to add a Method to all Javascript Prototypes (Object,array,function,string,number,boolean)?
Just for Example.
If i would like to add a new Prototype getter, which returns the Type of the Variable, the create Timestamp or something else, how could I do that?
For Information: It's not about what this function does!
Edit:
I would like to access the Function like every other Prototypen Function:
myVariable.myMethod(myParam)
Every JavaScript variable is either an object, or it auto-boxes to an object (like string, boolean and number), or it is null or undefined explicitly.
So, it seems like if you want to add a method to all those, you can add a function to Object.prototype which they all extend like Bar suggested.
Object.prototype.myMagic = function(){
console.log("Hello");
};
"thisString".myMagic();
15.13.myMagic();
([]).myMagic();
Note that you are in fact not adding a function to the prototype of a string since a string is a primitive value and doesn't have a prototype, rather, you're adding a method to the prototype of Object - and strings "box" to String instances (which are objects) that extend Object and will have this method.
Also note that on ES5 systems, it's possible to create objects that do not extend Object.prototype via Object.create(null), in ES6 it's also possible to modify the prototype of objects via setPrototypeOf or __proto__. However, outside those edge cases it should work.
You can add it to Object.prototype. All other prototype instances extend Object.prototype, and will inherit it from there.
Object.prototype.myMethod=function() {
// ... whatever
};
Please note that extending built-in prototypes is not a recommended practice for "real-world" code.