Here's the first draft explaining how enumeration works in ES4.
--larsTitle: Enumeration
Enumerability
NAME: "Enumerability" FILE: spec/language/enumerability.html CATEGORY: Object model SOURCES: ? SPEC AUTHOR: Lars DRAFT STATUS: DRAFT 1 - 2008-04-10 REVIEWED AGAINST ES3: NO REVIEWED AGAINST ERRATA: NO REVIEWED AGAINST BASE DOC: NO REVIEWED AGAINST PROPOSALS: NO REVIEWED AGAINST CODE: NO REVIEWED AGAINST TICKETS: NO IMPLEMENTATION STATUS: ? TEST CASE STATUS: ? OPEN ISSUES * The rationale for the decision not to enumerate fixture properties is a bit feeble. REFERENCES [1] proposals:iterators_and_generators [2] http://www.ecmascript.org/es4/spec/Object.html [3] proposals:bug_fixes [FOR.IN.LOOP.CREATION.ORDER] [4] ES3 spec 12.6.4 [5] proposals:bug_fixes [ITERATE.NULL.AND.UNDEFINED]
Synopsis
The purpose of the present spec is to define what it means for a
property of an object to be "enumerable", that is, what it means for
the property to be discovered when the properties of an object are
enumerated by for
-in
and for
-each
-in
loops [4].
This spec also describes in high level terms the facilities that are available to user programs to implement variations on enumeration.
Enumeration and itemization in ES4
According to [1], enumeration and itemization in ES4 is specified
in terms of the new iterator protocols using a system-defined helper
class called Enumerator
. A section below contains a brief
description of how the translation is performed. The central point is
that enumeration is implemented by an Enumerator
instance.
The constructor of
Enumerator
optionally takes some namespace values:
iterator class Enumerator.<T> { public function Enumerator(obj, f, deep, ...namespaces) { ... } public function next(): T { ... } }
An Enumerator
created on an object obj will produce (from
its next
method) a succession of the enumerable properties of
obj: those dynamic properties of obj whose "enumerable"
attribute [2] is set, filtered by the contents of namespaces. If
deep is true, then the objects in the prototype chain of obj
are included in the enumeration. Enumeration is always performed
starting at obj; properties are produced in the order they were
inserted into obj [3]; when the properties in obj are
exhausted the process is repeated on the immediate prototype of
obj, if any.
There is a distinct namespace in ES4 known as the "compatibility
namespace" or "no namespace". In code, this namespace is denoted by
the keyword public
.
When an Enumerator
is created with an empty set of namespaces,
it filters the enumerable attributes by the compatibility namespace,
and all property names produced are string
values -- the property
name without the namespace, because the namespace is always the
compatibility namespace.
When an Enumerator
is created with a nonempty set of
namespaces, it filters the enumerable attributes by those namespaces
only, and all property names produced are Name
values: namespace
and name pairs.
(Punchline.) Normal enumeration and itemization by the
for
-in
and for
-each
-in
loops result in the
creation of Enumerator
instances with an empty sets of namespaces
and deep set to true. Ergo, normal enumeration behaves as in
ES3.
Programs that needs to enumerate properties in specific
namespaces, or that only need to perform shallow enumeration, can
create custom Enumerator
classes. It is possible that ES4 ought
to provide utility functions to make this simple, but experimentation
will show.
Rationale and discussion
The rephrasing of enumeration in terms of iteration allows the
for
-in
and for
-each
-in
statements to be used for
iteration, which is exceptionally handy.
Regarding namespace filtering and enumeration by default of only
public properties: Namespaces are used by user programs for privacy
and integrity. For example, the private
attribute on properties
and methods of a class is just the name of a system-generated
namespace. It would be a breach of privacy for arbitrary code to be
able to enumerate arbitrary properties on an object, since the private
property names would be revealed in the form of Name
objects, from
which the private namespace could be exposed. Yet it is useful for
code to enumerate properties in namespaces controlled by that code.
So the compromise -- which is also right for compatibility with ES3 --
is that only public properties are enumerated by default; all
system-provided namespaces can be obtained by naming them (a class
method can evaluate the _expression_ private
to obtain the object
representing its private namespace); and custom Enumerator
instances can be constructed to enumerate names in namespaces other
than the public.
The reason for not enumerating fixture properties is that this fits in well with a certain style of programming. Consider constructing a prototype object:
Func.prototype = const { toString: function () ..., myMeth: function () ... }
The methods toString
and myMeth
are meant to be prototype
methods, hence not enumerable. But they are in the public namespace,
and therefore enumerable by default. However, the lock-down by the
const
prefix -- making them fixtures -- is taken as a signal that
they are not "normal" public properties. Hence it's probably right
not to enumerate them.
Primer: Translating enumeration into iteration
For the full story, go to [1].
The loop that is written like this in ES3:
for (i in o) ...has this meaning in ES4:
let IT = iterator::GET(o, true); while (true) { try { i = IT.next(); } catch (e : iterator::StopIterationClass) { break; } ... }
In the preceding fragment, IT
is a fresh variable, iterator::GET
is a system-supplied function shown below, and
iterator::StopIterationClass
is a class type. Both of the latter are
considered unforgeable (local rebindings do not shadow the original
meanings we consider here).
The function GET
and its auxiliary DEFAULT_GET
are defined
as follows:
iterator const function GET(start: !Object, deep: boolean): iterator::IteratorType.<*> { if (start like iterator::IterableType.<*>) return start.iterator::get(deep); else return iterator::DEFAULT_GET(start, deep); } iterator const function DEFAULT_GET(start: !Object, deep: boolean = false): iterator::IteratorType.<(string|Name)> new Enumerator.<(string|Name)>(start, function (id: (string|Name), obj: !Object): (string|Name) { return id }, deep);
In other words, if the object o
is iterable -- if it provides
its own iterator -- then its own iterator is used to control the loop,
otherwise the default behavior is invoked. It is the default behavior
that interests us here since it implements the ES3 enumeration and
itemization protocol.
The default behavior, implemented by DEFAULT_GET, creates a new
Enumerator
instance, and this instance behaves in the way outlined
earlier in this document.
The behavior for for
-each
-in
itemization is similar.
_______________________________________________ Es4-discuss mailing list Es4-discuss@mozilla.org https://mail.mozilla.org/listinfo/es4-discuss