Skip to content

Commit c757c94

Browse files
marcoscaceresmartinthomsonjyasskin
authored
Add 'Choose the Appropriate WebIDL Construct for Data and Behavior' (#567)
* Add 'Choose the Appropriate WebIDL Construct for Data and Behavior' * Apply suggestions from code review Co-authored-by: Martin Thomson <[email protected]> * Apply suggestions from code review Co-authored-by: Martin Thomson <[email protected]> * Update index.bs * Update index.bs * Apply suggestions from code review * Update index.bs * Update index.bs Co-authored-by: Martin Thomson <[email protected]> * Update index.bs * Update index.bs * Update index.bs Co-authored-by: Martin Thomson <[email protected]> * Apply suggestions from code review Co-authored-by: Martin Thomson <[email protected]> * Update index.bs Co-authored-by: Martin Thomson <[email protected]> * Update index.bs Co-authored-by: Martin Thomson <[email protected]> * Update index.bs Co-authored-by: Martin Thomson <[email protected]> * Apply suggestions from code review Co-authored-by: Martin Thomson <[email protected]> * Bikeshed is too picky Co-authored-by: Jeffrey Yasskin <[email protected]> * Apply suggestions from code review * Update index.bs * Update index.bs * Update index.bs * Update index.bs --------- Co-authored-by: Martin Thomson <[email protected]> Co-authored-by: Jeffrey Yasskin <[email protected]>
1 parent 278da73 commit c757c94

File tree

1 file changed

+151
-1
lines changed

1 file changed

+151
-1
lines changed

index.bs

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,157 @@ like {{WeakRef}} or {{FinalizationRegistry}},
15331533
set accurate author expectations about
15341534
the interaction with garbage collection.
15351535

1536-
<h2 id="api-surface">JavaScript API Surface Concerns</h2>
1536+
<h2 id="api-surface">Designing JavaScript APIs</h2>
1537+
1538+
<h3 id="webidl-dictionaries-interfaces-namespaces">Use WebIDL dictionaries, interfaces, and namespaces appropriately</h3>
1539+
1540+
Use the appropriate WebIDL mechanisms for new APIs.
1541+
1542+
WebIDL defines multiple constructs for defining Web APIs.
1543+
Dictionaries, interfaces, and namespaces
1544+
each have different properties suited to different purposes.
1545+
1546+
The goal is to ensure ergonomic, consistent APIs that feel natural to Web developers
1547+
while avoiding pitfalls like "fake classes" or classes that provide no functionality.
1548+
1549+
<h4 id="dictionaries-for-configuration" class="no-num no-toc">Use Dictionaries for “Configuration” or “Input-Only” Data</h4>
1550+
1551+
Choose a dictionary when the part of the API represents data that is transient,
1552+
especially when an API accepts a set of parameters, configuration, or options.
1553+
1554+
Dictionaries are ideal for when the data doesn't get stored or mutated; it's just used it at the time of the call.
1555+
1556+
For example, the [`ShareData`](https://www.w3.org/TR/web-share/#dom-sharedata) member from Web Share [[WEB-SHARE]]:
1557+
1558+
```WebIDL
1559+
dictionary ShareData {
1560+
USVString title;
1561+
USVString text;
1562+
USVString url;
1563+
};
1564+
```
1565+
1566+
And how it's commonly used:
1567+
1568+
<pre class="highlight">
1569+
await navigator.share({text: "Text being shared" });
1570+
</pre>
1571+
1572+
Dictionaries are easily extensible and makes it easy to add optional fields later as needed.
1573+
Members of a dictionary are optional by default, but can be marked as `required` if needed.
1574+
1575+
Dictionaries are also highly idiomatic (i.e., natural to use in JavaScript).
1576+
Passing `{ ... }` inline is the most natural way to supply configuration in JavaScript.
1577+
1578+
Dictionaries, because of how they are treated by user agents, are also relatively future-proof.
1579+
Dictionary members that are not understood by an implementation are ignored.
1580+
New members therefore can be added without breaking older code.
1581+
1582+
Dictionaries are best used for objects that don't need to be distinguished by type in their lifecycle (i.e., `instanceof` checks are mostly meaningless because it's always `Object`).
1583+
1584+
Dictionaries are "passed by value" to methods (i.e., they are copied).
1585+
Browsers engines strip unknown members when converting from JavaScript objects to a WebIDL representation.
1586+
This means that changing the value after it is passed into an API has no effect.
1587+
1588+
Again, taking the [`ShareData`](https://www.w3.org/TR/web-share/#dom-sharedata) dictionary as an example:
1589+
1590+
```JS
1591+
const data = {
1592+
"text": "Text being shared",
1593+
// Ignored by a browser that does not include a "whatever" parameter.
1594+
"whatever": 123,
1595+
};
1596+
let p = navigator.share(data);
1597+
1598+
// Changing this after calling .share() has no effect
1599+
data.text = "New text";
1600+
```
1601+
1602+
<h4 id="interface-for-functionality-state-identity" class="no-num no-toc">Choose an Interface for Functionality, State, and Identity</h4>
1603+
1604+
Interfaces are roughly equivalent to classes in JavaScript.
1605+
Use an interface when a specification needs to bundle state--
1606+
both visible properties and internal "slots"--
1607+
with operations on that state (i.e., methods).
1608+
1609+
Unlike dictionaries, interfaces:
1610+
1611+
* can have instances with state,
1612+
* can have need read-only properties,
1613+
* can exhibit side-effects on assignment, and
1614+
* provide the ability to check object's identity
1615+
(i.e., one can check if it is an `instanceof` a particular class on the global scope),
1616+
1617+
Defining an interface also exposes it on the global scope, allowing for the specification of static methods.
1618+
For example, the `canParse()` static method of the URL interface.
1619+
1620+
```JS
1621+
if (URL.canParse(someURL)) {
1622+
// Do stuff...
1623+
}
1624+
```
1625+
1626+
Give stateful interfaces a constructor, if possible.
1627+
Do not add a constructor if a class has no state.
1628+
Doing so is considered *bad practice*, as it is effectively creating a "fake class":
1629+
that is, instances of the interface do nothing that a static method couldn't do.
1630+
[`DOMParser`](https://html.spec.whatwg.org/#domparser) or [`DOMImplementation`](https://dom.spec.whatwg.org/#domimplementation) are examples of fake classes.
1631+
1632+
<h4 id="interface-serializer" class="no-num no-toc">Provide a serializer to make interface data more accessible</h4>
1633+
1634+
Add serializers to transform instances of an interface into a form that is expected to be used by many applications.
1635+
1636+
A `.toJSON()` method allows an instance of an interface to produce a useful JSON serialization.
1637+
A `.toBlob()` method can be used to extract a binary representation of an interface.
1638+
1639+
This makes object natural to use with APIs.
1640+
For example, [`GeolocationPosition`](https://www.w3.org/TR/geolocation/#dom-geolocationposition)
1641+
provides a `toJSON()` method:
1642+
1643+
```JS
1644+
const position = await new Promise((resolve, reject) => {
1645+
navigator.geolocation.getCurrentPosition(resolve, reject);
1646+
});
1647+
1648+
const message = JSON.stringify({
1649+
user: userId,
1650+
time: Date.now(),
1651+
position, // .stringify() calls .toJSON() automatically
1652+
});
1653+
```
1654+
1655+
<h4 id="namespace-to-avoid-fake-classes" class="no-num no-toc">Choose a namespace to Avoid “Fake Classes” for Behavior-Only Utilities</h4>
1656+
1657+
A namespace exists to group a set of static properties and methods.
1658+
A namespace does not have a prototype.
1659+
Examples include the `Math`, `Intl`, `Atomics`, and `Console` objects in JS.
1660+
1661+
With one or two small static functions, a whole new namespace may be overkill.
1662+
Attaching them to an existing object might be more appropriate.
1663+
1664+
Conversely, a namespace that grows too large or too broad may need better organization or separate logical partitions.
1665+
1666+
<h4 id="pseudo-namespaces" class="no-num no-toc">"Pseudo-namespaces"</h4>
1667+
1668+
As WebIDL interfaces can have attributes,
1669+
it is possible to create "pseudo-namespaces"
1670+
by defining non-constructable interfaces as attributes.
1671+
1672+
A common example are all the attributes attaches to the `navigator` object.
1673+
The `navigator` object has an interface definition ([`Navigator`](https://html.spec.whatwg.org/#navigator)),
1674+
which itself contains other attributes that gives access to further functionality
1675+
through interface instances.
1676+
1677+
For example:
1678+
1679+
* `navigator.credentials` - The `CredentialsContainer` interface of the Credentials Management API.
1680+
* `navigator.geolocation` - The `Geolocation` interface of the Geolocation specification.
1681+
* `navigator.permissions` - The `Permissions` interface of the Permissions specification.
1682+
1683+
Pseudo-namespaces are useful for when, as a spec author,
1684+
you need to refer to a specific instance of a thing
1685+
(such as the permissions that apply to a specific browsing context, for `navigator.permissions`)
1686+
even if that thing does not have any visible state exposed to the page.
15371687

15381688
<h3 id="attributes-like-data">Attributes should behave like data properties</h3>
15391689

0 commit comments

Comments
 (0)