1. Working on Fetch domintro Boxes
This section focuses on WebIDL union types, the es-union algorithm, and stringifiers.
1.1. Fetch accepting multiple types of input
While working on the Fetch Standard’s domintro boxes (issue here) I
ran down a few rabbit holes. While reading the description of the request object I noticed it mentioned
that a request is the input to fetch. I had obviously used a regular string as the first
(and sometimes only) parameter to the exposed fetch()
fetch API, as have many people, so upon reading the spec I was
curious as to how this conversion might take place. I had also vaguely recalled seeing some application code use a URL
object as input to fetch
, which added to the curiousity.
A member of the WHATWG organization pointed out over IRC that step 2 of the fetch()
in the spec indicated that
whatever we passed in as input
always went through the Request()
constructor to sort of "sanitize" the input. This
means that the following calls to fetch:
fetch('https://domfarolino.com');
fetch(new URL('https://domfarolino.com'));
fetch(new Request('https://domfarolino.com'));
Are effectively:
fetch(new Request('https://domfarolino.com'));
fetch(new Request(new URL('https://domfarolino.com')));
fetch(new Request(new Request('https://domfarolino.com')));
1.2. The Request()
constructor accepts multiple types of input
At this point my confusion about being able to pass in string, URL
, and Request
objects was still with me
but had shifted focus to the Request()
constructor as opposed to the fetch()
API. What specifically in
spec-land allows us to handle this? When looking at the Request()
constructor, I noticed that step 5 handles
the case where input is a string, while step 6 handles the case where input is a Request
object. So here I
wondered how, if we accept string and Request
objects, are we able to accept something like a URL
object?
1.3. Hello stringifier
The short answer is that WebIDL stringifies everything that gets passed into a method taking a DOMString
or USVString
.
The URL
object happens to have a custom stringifier which returns the URL
's href
attribute upon
string coercian. This is nice because it spits out a type that the Request()
constructor is designed to take.
1.4. Converting an ECMAScript value to an IDL union type
I was then curious as to what stopped Request
objects being passed into the Request()
constructor from undergoing
the same stringification as URL
objects, since the string type is what we first look for. To understand this we have
to look at the WebIDL es-union algorithm. In short, this algorithm defines the steps to run when we convert an ECMAScript
value to one of several IDL types single targeted type. These types are specified in WebIDL as a union type. The Request
class IDL in the fetch spec defines its input parameter as an object of type RequestInfo
, which
is a union type of Request
or USVString
. The reason Request
objects are not stringified like URL
objects
is due to step 4, substep 1 of the es-union algorithm. In short, this algorithm will favor interface types that
ECMAScript object implements before trying to stringify.
2. Utility of Request
{"mode": "no-cors"}
and {"redirect": "manual"}
Note, more to come.
3. A bit on ECMAScript Object Essential Internal Methods
Note, this section may be generalized in the future to include more information on exotic objects, which would warrant the
title of this section changing to A bit on ECMAScript Object Internal Methods
, with Essential Internal Methods and Internal Slots being
a more specific subsection.
3.1. Essential Internal Methods and Internal Slots
The spec states that the semantics of objects in ECMAScript are specified by algorithms called internal methods. These internal methods
are invoked by other algorithms. Internal slots also exist to give internal state to objects housing them. Both internal methods and slots
are not exposed object properties, as they’re internal. Their names are enclosed in square brackets [[]]
.
A list and description of the Essential Internal Methods that every object has (implements) can be found under 6.1.7.2 Object Internal Methods and Internal Slots. What’s cool is that a corresponding algorithmic definition for each Essential Internal Method (in the context of an Ordinary Object) can be found in 9.1 Ordinary Object Internal Methods and Internal Slots.
That last part about the context of Ordinary Objects is important because Ordinary Objects provide the default definition of an Essential Internal Methods, while Exotic Objects may provide their own varying definition of some of the Essential Internal Methods (among other exotic-specific internal methods). For example, see Array Exotic Objects.
Array exotic objects provide an alternative definition for the [[DefineOwnProperty]] internal method. Except for that internal method, Array exotic objects provide all of the other essential internal methods as specified in 9.1.
3.2. [[Get]] and [[Set]] on accessor properties
Before the list of Essential Internal Methods, I noticed the description of property descriptors gave and explained attributes (think of attributes as internal slots that represent exposed Object properties, in this case, properties of property descriptors) associated with both data properties and accessor properties. It might be a little confusing that [[Get]] and [[Set]] attributes were described here before the [[Get]] and [[Set]] Essential Internal Methods. This made me think "Hmm, that must mean there can be at most like..two (different) [[Get]]s performed when accessing a property, right?".
The answer is yes, and let’s see why. We might want to invoke the Get abstract operation on some object, thus invoking it’s [[Get]]
internal method,
which would encounter a property descriptor. Said property descriptor may further be an accessor property, and if so, it will have a [[Get]] attribute
(internal slot) whose value is a function, F. We’ll then want to call F.[[Call]]
in order to invoke the getter function to retrieve the value we
should finally return.
Note, [[Call]]
is an Essential Internal Method that appears only on function objects.
Note, we don’t actually call F.[[Call]]
directly in [[Get]]
, but instead indirectly do so by first
invoking the Call abstract operation on F. I believe this is a common pattern so that internal methods can remain polymorphic
(for example, the object is exotic and provides its own version of some Essential Internal Method) but I’m not sure.
This all makes sense, and clears up any confusion that the description of the [[Get]]
Essential Internal Method might cause:
The bit saying "If any ECMAScript code must be executed to retrieve the property value, ..." might be confusing before realizing that the actual
definition of the [[Get]]
internal method indeed executes a function object in the case where the key was accociated with an accessor
property.