问题
I was reading about JavaScript classes, and came across this term "public class fields syntax". On digging a bit deeper into it I came across this Babel's documentation on class properties.
Can someone please explain - implementation-wise what are the use-cases for this new syntax? (What solutions/benefits does it offer to JavaScript, which were missing so far?)
Here's an example below (ran without errors in Google Chrome):
class Person {
firstName = "Mike";
lastName = "Patel";
// this is a public class field syntax
getName = () => {
return this.firstName + " " + this.lastName;
};
}
var p = new Person();
console.log(p.firstName); // Mike
console.log(p.lastName); // Patel
console.log(p.getName); // () => { return this.firstName + " " + this.lastName; }
console.log(typeof p.getName); // function
console.log(p.getName()); // Mike Patel
回答1:
Simply put, the reason to use this is ease of understanding the code. Without class field declarations, you'd do something like
class Person {
constructor() {
this.firstName = "Mike";
this.lastName = "Patel";
this.getName = () => {
return this.firstName + " " + this.lastName;
};
}
}
var p = new Person();
console.log(p.firstName); // Mike
console.log(p.lastName); // Patel
console.log(p.getName); // () => { return this.firstName + " " + this.lastName; }
console.log(typeof p.getName); // function
console.log(p.getName()); // Mike Patel
This works but now you have both the method declaration and the instance properties all collected in the constructor. You could have even more methods, however, which means that your class definition would look rather meaningless overall:
class MyClass() {
constructor(someArg) {
this.prop1 = 1;
this.prop2 = 2;
this.prop3 = 3;
this.prop4 = someArg;
this.method1 = () => {}
this.method2 = () => {}
this.method3 = () => {}
this.method4 = () => {}
}
}
and so on. Again, everything is in the constructor. If you have a lot of code, it becomes harder to read what is what. And if the constructor takes any arguments, then you have the extra overhead of keeping track of those. Simply put, it's hard to read, hard to maintain for no real benefit. You are stuffing everything in the same place.
With class field declarations, you separate them and you get
class MyClass() {
/* properties - don't depend on the constructor*/
prop1 = 1;
prop2 = 2;
prop3 = 3;
prop4; /* this is a property that this class will have -
I don't need to look at the constructor to know about it */
/* easy to see what the constructor does that is only about *constructing* the object */
constructor(someArg) {
this.prop4 = someArg;
}
/* methods are separated from the rest of the properties and construction logic */
method1 = () => {}
method2 = () => {}
method3 = () => {}
method4 = () => {}
}
So, all in all, it's not revolutionary but it is slightly nicer syntax that makes it easier to express what a class has.
回答2:
To quote from the class fields proposal
By declaring fields up-front, class definitions become more self-documenting; instances go through fewer state transitions, as declared fields are always present.
The introduction of class fields also allows for private class fields, which also come with a few benefits:
By defining things which are not visible outside of the class, ESnext provides stronger encapsulation, ensuring that your classes' users don't accidentally trip themselves up by depending on internals, which may change version to version.
回答3:
It originates from this proposal where an "issue" is being tackled down.
PRE-PROPOSAL
Assume that you want to have a class Foo
that holds a default attribute, then you can write inituitvely the following
class Foo {
defaultAttribute = 'default';
getDefault() { return this.defaultAttribute; }
}
Remember that the proposal here above did not got implemented atm. The defaultAttribute = '...'
is being ignored when setting up the class object (when compiling). It is even not a part of the prototypes or field members (of the function object).
That is because the defaultAttribute
is not picked up by the compiler. Therefore you cannot do foo.defaultAttribute
.
Calling getDefault()
will throw an error here because it is undefined at that moment. That function does work if you provide a value to defaultAttribute
in the constructor;
class Foo {
defaultAttribute = 'default';
constructor() {
this.defaultAttribute = 'hello';
}
getDefault() { return this.defaultAttribute; }
}
In this situation, the defaultAttribute
is set to 'Hello' but it did not override the original variable with 'default'
.
POST-PROPOSAL
With that proposal, the "ignoring" problem is tackled down that you can do that what you just described:
class Foo {
defaultAttribute = 'default';
getDefault() { return this.defaultAttribute; }
}
With this, you can skip the use of constructor()
class Foo1 {
defaultAttribute = 'default';
getDefault() { return this.defaultAttribute; }
}
class Foo2 {
defaultAttribute = 'default';
constructor() {this.defaultAttribute = 'hello';}
getDefault() { return this.defaultAttribute; }
}
const foo1 = new Foo1();
const foo2 = new Foo2();
console.log(foo1.getDefault());
console.log(foo2.getDefault());
来源:https://stackoverflow.com/questions/57608525/what-are-class-fields-in-javascript