admin管理员组文章数量:1313807
I've noticed there are two different ways to effectively write a class (or class-like thing) in JavaScript. Using a simple counter class as an example:
A real class (used as new Counter1()
)
class Counter1 {
constructor(count = 0) {
this.count = count;
}
inc() {
this.count++;
}
get() {
return this.count;
}
}
A function returning an object (used as Counter2()
)
function Counter2(count = 0) {
return {
inc() {
count++;
},
get() {
return count;
},
};
}
I have a lot of code that could be written in either one of these styles. Which should I use?
This is my understanding:
Counter1
can be used with instanceof
and inheritance, but is more verbose and doesn't have real private properties (eg count
property is exposed).
Counter2
is more concise and has real private methods/state (just local variables in the function), but can't be used with instanceof
and inheritance. It also creates new copies of the inc
and get
functions for each instance, which may impact performance? I don't know.
I've noticed there are two different ways to effectively write a class (or class-like thing) in JavaScript. Using a simple counter class as an example:
A real class (used as new Counter1()
)
class Counter1 {
constructor(count = 0) {
this.count = count;
}
inc() {
this.count++;
}
get() {
return this.count;
}
}
A function returning an object (used as Counter2()
)
function Counter2(count = 0) {
return {
inc() {
count++;
},
get() {
return count;
},
};
}
I have a lot of code that could be written in either one of these styles. Which should I use?
This is my understanding:
Counter1
can be used with instanceof
and inheritance, but is more verbose and doesn't have real private properties (eg count
property is exposed).
Counter2
is more concise and has real private methods/state (just local variables in the function), but can't be used with instanceof
and inheritance. It also creates new copies of the inc
and get
functions for each instance, which may impact performance? I don't know.
- The new key word with a function constructor may be best though properties of the prototype still cannot access private scoped variables. – user7951676 Commented Oct 15, 2017 at 2:43
5 Answers
Reset to default 4I put it in a jsperf page here to test the speed of new instance creation in either style: https://jsperf./class-vs-object-2
Here are the results on my puter (new instances created per second):
Test Chrome Firefox Edge
new Counter1(); 217,901,688 2,086,800,399 31,106,382
Counter2(); 30,495,508 36,113,817 9,232,171
I don't know why the Firefox number with class
is so high (hopefully it didn't notice the code had no effect and elide the whole thing) or why the Edge numbers are so low.
So at least with regards to performance, a real class is much faster, probably because it avoids the overhead of creating a new copy of the methods for each instance.
While the verbosity and lack of private state with classes is annoying, I think the performance benefit is more important, but it depends on the situation.
Edit
I've made the test call inc()
and get()
on the resulting object as well and the numbers are basically the same:
Test Chrome Firefox Edge
new Counter1(); 215,352,342 2,072,278,834 23,570,036
Counter2(); 14,309,836 35,564,201 9,801,748
I remend to use classes, because of that,
Class methods are non-enumerable.
A class definition sets of enumerable flag to false for all class member methods in the "prototype". That's good, because if we
for..in
over an object, we usually don't want it's class methods.Classes have a default constructor() {}.
If there is no
constructor
in theclass
constructor, then an empty function is generated, same as if we had written constructor() {}.Classes always use strict.
All code inside the class construct is automatically in strict mode.
- Classes may also include getters/setters.
Static Methods
We can also assign methods to the class function, not to its "prototype".
You can go further here.
Neither one should have a very big impact on performance. While classes (Counter1
in your example) technically have slightly better performance, this is only noticeable if you are creating several thousand instances of the class per second (which is probably a bad design choice anyway).
As for other advantages, there are advantages and disadvantages to both. FunFunFunction is a great YouTube channel for learning about JavaScript, and it's where I first learned about factory functions (Counter
). The channel owner strongly favors these over using class
es.
One of the reasons for this is that you can't get consistent behavior out of the this
keyword with the way classes are implemented in JS, as it may refer to something like an HTML element from which a function was called rather than the object that you're expecting it to.
For more details, see this video.
Of course, this is only one side of the argument, and I'm sure there's arguments for using class
as well. I prefer factory functions, but in the end you really just need to pick one and roll with it.
Which should I use?
This question is basically a matter of personal preference. True believers in OOP will of course adopt the class
approach. The one clear difference is that that would allow you to subclass Counter1
, but it's not clear why you would ever want to do that, and even OOP aficionados warn against building overly plex class hierarchies.
Yes, the second alternative does place the methods in question on each and every instance, instead of the prototype. Yes, that does impose some theoretical performance penalty. But this is not something you ever need to worry about, unless you are writing a game with one counter for each of a million objects.
Presumably you are using some kind of transpiler or running in a native ES6 environment. If for some reason you are not, then of course your second alternative has the advantage that it will run in any browser from the last ten years (if you rewrite the methods as inc: function() { count++; }
).
The biggest difference between the two is that calling new
on a class
creates a this
object, while the modular function may or may not create an instance-like state object.
I am not going to post the advantages of using a class
because there are hundreds of them in Stack Overflow. Instead, here is why you might not want to use a class
.
By using the modular function instead of a class, you:
- May get a speed bump. My tests show that not using a class gains 10% speed improvement for basic cases
- Avoid having to call
.this
on instance-like properties - Avoid having to call
Class.[name]
on static-like properties - Avoid needing to declare a separate constructor
- Use the closure instead of needing to copy constructor-like parameters
- Do not need to allocate space for a
this
object - You can easily pass the function around; clients are not required not know if they should call
new
to invoke it - What you return from the class can be a singleton, stateless library, factory, etc.
- Client code may be cleaner without the
new
keyword - You can refactor to use instance-like state without clients needing to add the
new
keyword. - You can rely on all sorts of closure magic and global state by declaring functions and properties in various scopes within or outside of the function
- Destructuring and enumerating the properties will be restricted to exactly what you define, without any magic
class
properties lurking inside. - Allows you to use position instead of inheritance.
In the most concise syntax
() => ({})
With constructor-like params and closure access
(a,b,c) => ({
someA: a,
bAndC: b + c
})
A full example
let lastTotal
const randomPct = () => Math.random()*100
function SomeModule(a,b,c) {
const myTotal = a + b + c
lastTotal = myTotal
const getASquared = () => a * a
return {
total: a + b + c,
getTotal() {
return a + b + c
},
myTotal,
lastTotal,
randomPct,
getASquared,
}
}
A position example. You have the flexiblity to rename conflicts, or exclude any members of moduleA and moduleB, even conditionally.
const moduleA = useModuleA()
const moduleB = useModuleB()
const moduleC = (moduleA, moduleB) {
const exportedB = isAdmin() ? moduleB : {publicA: moduleB.publicA, publicB:moduleB.publicB}
return {
...moduleA,
...exportedB,
hello: 'World',
}
}
本文标签: javascriptClass vs function returning objectStack Overflow
版权声明:本文标题:javascript - Class vs function returning object - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741872172a2402257.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论