the property (if we used a string property called
or even
_email
, it would be
easy to carelessly access it directly).
This is a common pattern, and it works well, but it’s slightly more unwieldy than we
might like. Here’s an example of using this class:
const
u
=
new
User
();
u
.
setEmail
(
);
console.log(
`User email:
${
u
.
getEmail
()
}
`
);
While this works, it would be more natural to write:
const
u
=
new
User
();
u
.
=
;
console.log(
`User email:
${
u
.
}
`
);
Enter accessor properties: they allow us to have the benefits of the former with the
natural syntax of the latter. Let’s rewrite our class using accessor properties:
const
USER_EMAIL
=
Symbol
();
class
User
{
set
(
value
) {
if
(
!
/@/
.
test
(
value
))
throw
new
Error(
`invalid email:
${
value
}
`
);
this
[
USER_EMAIL
]
=
value
;
}
get
() {
return
this
[
USER_EMAIL
];
}
}
We’ve provided two distinct functions, but they are bundled into a single property
called
. If the property is assigned to, then the setter is called (with the assign‐
ment value being passed in as the first argument), and if the property is evaluated, the
getter is called.
You can provide a getter without a setter; for example, consider a getter that provides
the perimeter of a rectangle:
class
Rectangle
{
constructor
(
width
,
height
) {
this
.
width
=
width
;
this
.
height
=
height
;
}
get
perimeter
() {
return
this
.
width
*
2
+
this
.
height
*
2
;
}
}
We don’t provide a setter for perimeter because there’s no obvious way to infer the
rectangle’s width and height from the length of the perimeter; it makes sense for this
to be a read-only property.
302 | Chapter 21: Object Property Configuration and Proxies