array like objects

# Mike Samuel (14 years ago)

It occurred to me after looking at the proxy strawman that it might help to nail down what "array-like" means in future drafts. It isn't directly related to the proxy stuff though so I thought I'd start a separate thread.

I've seen quite a bit of library code that does something like if (isArrayLike(input)) { // iterate over properties [0,length) in increasing order } else { // Iterate over key value pairs } but different libraries defined array-like in different ways. Some ways I've seen: (1) input instanceof Array (2) Object.prototype.toString(input) === '[object Array]' (3) input.length === (input.length >> 0)

etc.

The common thread with array like objects is that they are meant to be iterated over in series. It might simplify library code and reduce confusion among clients of these libraries if there is some consistent definition of series-ness. This committee might want to get involved since it could affect discussions on a few topics: (1) key iteration order (2) generators/iterators (3) catchall proposals (4) type systems

One strawman definition for an array like object: o is an array like object if o[Get] returns a valid array index or one greater than the largest valid array index.

The need to distinguish between the two in library code could be mooted if for (in) on arrays iterated over the array index properties of arrays in numeric oder order first, followed by other properties in insertion order, and host objects like NodeCollections followed suit.

# Yehuda Katz (14 years ago)

Your strawman would support:

{0: "strawman", length: 0}

A better definition might be:

o is an array like object if o[Get] returns a Number one greater than the largest numeric property, or 0 if no numeric properties exist.

Yehuda Katz Developer | Engine Yard (ph) 718.877.1325

# Allen Wirfs-Brock (14 years ago)

Curiously, I don't believe any of the "generic" functions for arrays and array-like objects in section 15.4.4 actually depend upon such an "array-like" test. They all simply use ToUint32 applied to the value of the length property. If the length property doesn't exist or its value isn't something that an a convertible representation of a number, the value 0 is used as the length and not much happens. By this definition, all objects are essentially array-like but many have no array-like elements.

It make one wonder why if this definition works satisfactory for the built-ins it should be used for all array-like situations.

Allen

From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Yehuda Katz Sent: Monday, December 07, 2009 5:40 PM To: mikesamuel at gmail.com Cc: es-discuss Subject: Re: array like objects

Your strawman would support:

{0: "strawman", length: 0}

A better definition might be:

o is an array like object if o[Get] returns a Number one greater than the largest numeric property, or 0 if no numeric properties exist.

Yehuda Katz Developer | Engine Yard (ph) 718.877.1325

On Mon, Dec 7, 2009 at 5:33 PM, Mike Samuel <mikesamuel at gmail.com<mailto:mikesamuel at gmail.com>> wrote:

It occurred to me after looking at the proxy strawman that it might help to nail down what "array-like" means in future drafts. It isn't directly related to the proxy stuff though so I thought I'd start a separate thread.

I've seen quite a bit of library code that does something like if (isArrayLike(input)) { // iterate over properties [0,length) in increasing order } else { // Iterate over key value pairs } but different libraries defined array-like in different ways. Some ways I've seen: (1) input instanceof Array (2) Object.prototype.toString(input) === '[object Array]' (3) input.length === (input.length >> 0)

etc.

The common thread with array like objects is that they are meant to be iterated over in series. It might simplify library code and reduce confusion among clients of these libraries if there is some consistent definition of series-ness. This committee might want to get involved since it could affect discussions on a few topics: (1) key iteration order (2) generators/iterators (3) catchall proposals (4) type systems

One strawman definition for an array like object: o is an array like object if o[Get] returns a valid array index or one greater than the largest valid array index.

The need to distinguish between the two in library code could be mooted if for (in) on arrays iterated over the array index properties of arrays in numeric oder order first, followed by other properties in insertion order, and host objects like NodeCollections followed suit.

# Mike Samuel (14 years ago)

2009/12/7 Yehuda Katz <wycats at gmail.com>:

Your strawman would support: {0: "strawman", length: 0} A better definition might be:    o is an array like object if o[Get] returns a Number one greater than the largest numeric property, or 0 if no numeric properties exist.

By that better definition the array [,] is not array like.

# Breton Slivka (14 years ago)

The one that I use is

function isArrayLike(i){ return (typeof i !=="string") && i.length !== void (0); }

It might not be perfect, but it allows me to make certain assumptions about the input that are useful enough. Keep in mind that an Array may have a length of 5, and all those values are undefined, so {length:4} could be used as a valid arrayLike, and that seems reasonable to me.

# Breton Slivka (14 years ago)

On Tue, Dec 8, 2009 at 1:29 PM, Breton Slivka <zen at zenpsycho.com> wrote:

The one that I use is

function isArrayLike(i){ return (typeof i !=="string") && i.length !== void (0); }

It might not be perfect, but it allows me to make certain assumptions about the input that are useful enough. Keep in mind that an Array may have a length of 5, and all those values are undefined, so {length:4} could be used as a valid arrayLike, and that seems reasonable to me.

On Tue, Dec 8, 2009 at 1:18 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:

Curiously, I don’t believe any of the “generic” functions for arrays and array-like objects in section 15.4.4 actually depend upon such an “array-like” test. They all simply use ToUint32 applied to the value of the length property. If the length property doesn’t exist or its value isn’t something that an a convertible representation of a number, the value 0 is used as the length and not much happens. By this definition, all objects are essentially array-like but many have no array-like elements.

You've encapsulated here some of the reasoning behind my earlier test- Anything that passes my test is acceptable to any of the array prototype functions, but filters out two situations that I've found I didn't intend an object to be treated as an array. My usecase is overloaded functions that may also accept objects, and strings, and arraylikes, but I want a way to distinguish array-likes from objects and strings.

# Mike Samuel (14 years ago)

2009/12/7 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:

Curiously, I don’t believe any of the “generic” functions for arrays and array-like objects in section 15.4.4 actually depend upon such an “array-like” test. They all simply use ToUint32 applied to the value of the length property.  If the length property doesn’t exist or its value isn’t something that an a convertible representation of a number, the value 0 is used as the length and not much happens.  By this definition, all objects are essentially array-like but many have no array-like elements.

Most of the code snippets I've seen use it to decide how to iterate over elements and whether to produce an array or Object as output, not to decide whether it's appropriate to apply Array generics.

The each function from code.jquery.com/jquery-latest.js does if ( args ) { if ( length === undefined ) { // iterate using for in } else for ( ; i < length; )

And then any library routines that delegate to each to build an output inherit the same definition. I

# Mike Samuel (14 years ago)

2009/12/7 Breton Slivka <zen at zenpsycho.com>:

The one that I use is

function isArrayLike(i){    return (typeof i !=="string") && i.length !== void (0);

I like that it's efficient. How about if (i !== null && typeof i === 'object') { var len = i.length; var ilen = len >>> 0; return (len === ilen && ilen >= 0 && ilen <= 0x80000000) } return false;

Less efficient, but doesn't fail for null, skips functions which do have a length, and requires length to be a valid array length only performing one get and one toNumber.

# Brendan Eich (14 years ago)

On Dec 7, 2009, at 6:24 PM, Mike Samuel wrote:

2009/12/7 Yehuda Katz <wycats at gmail.com>:

Your strawman would support: {0: "strawman", length: 0} A better definition might be: o is an array like object if o[Get] returns a Number
one greater than the largest numeric property, or 0 if no numeric
properties exist.

By that better definition the array [,] is not array like.

Holes, yuck. Bringing up holes makes me sad. Prototype element of the
same index shows through a hole :-(.

The need to distinguish between the two in library code could be mooted if for (in) on arrays iterated over the array index
properties of arrays in numeric oder order first, followed by other
properties in insertion order, and host objects like NodeCollections followed
suit.

+1 on this -- it has come up before on-list, most recently in a thread
where Maciej tested browser implementations extensively:

esdiscuss/2009-October/010060

# Erik Corry (14 years ago)

2009/12/8 Mike Samuel <mikesamuel at gmail.com>:

It occurred to me after looking at the proxy strawman that it might help to nail down what "array-like" means in future drafts.  It isn't directly related to the proxy stuff though so I thought I'd start a separate thread.

I've seen quite a bit of library code that does something like   if (isArrayLike(input)) {     // iterate over properties [0,length) in increasing order   } else {     // Iterate over key value pairs   }

This looks fairly broken to me. If the object has enumerable properties that aren't positive integers then they don't get iterated over just because some heuristic says it's array-like. If the heuristic says it's array-like then we iterate over portentially billions of indexes even if it is very sparse.

I think there are two different questions being asked here.

  1. Does the object have the special semantics around .length? This is potentially useful to know and the normal way to find out doesn't work. Instanceof is affected by setting proto, yet the special .length handling persists and instanceof doesn't work for cross-iframe objects.

  2. Is it more efficient to iterate over this object with for-in or is it more efficient (and sufficient) to iterate with a loop from 0 to length-1? You can't implement functions like slice properly without this information and there's no way to get it.

but different libraries defined array-like in different ways. Some ways I've seen:    (1) input instanceof Array    (2) Object.prototype.toString(input) === '[object Array]'    (3) input.length === (input.length >> 0) etc.

These all look like failed attempts to answer one or both of the two questions above.

The common thread with array like objects is that they are meant to be iterated over in series. It might simplify library code and reduce confusion among clients of these libraries if there is some consistent definition of series-ness. This committee might want to get involved since it could affect discussions on a few topics:   (1) key iteration order   (2) generators/iterators   (3) catchall proposals   (4) type systems

One strawman definition for an array like object:    o is an array like object if o[Get] returns a valid array index or one greater than the largest valid array index.

The need to distinguish between the two in library code could be mooted if for (in) on arrays iterated over the array index properties of arrays in numeric oder order first, followed by other properties in insertion order, and host objects like NodeCollections followed suit.

FWIW V8 does this for both arrays and objects.

But really for..in is pretty sick for arrays. It will convert every single index in the array to a string. That's not something to be encouraged IMHO.

# Mike Samuel (14 years ago)

2009/12/8 Erik Corry <erik.corry at gmail.com>:

2009/12/8 Mike Samuel <mikesamuel at gmail.com>:

It occurred to me after looking at the proxy strawman that it might help to nail down what "array-like" means in future drafts.  It isn't directly related to the proxy stuff though so I thought I'd start a separate thread.

I've seen quite a bit of library code that does something like   if (isArrayLike(input)) {     // iterate over properties [0,length) in increasing order   } else {     // Iterate over key value pairs   }

This looks fairly broken to me.  If the object has enumerable properties that aren't positive integers then they don't get iterated over just because some heuristic says it's array-like.  If the heuristic says it's array-like then we iterate over portentially billions of indexes even if it is very sparse.

All true. And yet it is not uncommon. See the bottom of this email for a quick survey of a number of libraries' uses of the array-like concept.

I think there are two different questions being asked here.

  1. Does the object have the special semantics around .length?  This is potentially useful to know and the normal way to find out doesn't work.  Instanceof is affected by setting proto, yet the special .length handling persists and instanceof doesn't work for cross-iframe objects.

I think I agree with the bit about length, but proto doesn't work on non mozilla interpreters and isn't likely to be standardized. I don't see how it's relevant to array-like-ness.

  1. Is it more efficient to iterate over this object with for-in or is it more efficient (and sufficient) to iterate with a loop from 0 to length-1?  You can't implement functions like slice properly without this information and there's no way to get it.

Efficiency is obviously important, but another important distinction is whether 'length' should be included in the set of keys iterated over, and whether iteration over array-index keys should be in numeric order.

but different libraries defined array-like in different ways. Some ways I've seen:    (1) input instanceof Array    (2) Object.prototype.toString(input) === '[object Array]'    (3) input.length === (input.length >> 0) etc.

These all look like failed attempts to answer one or both of the two questions above.

Agreed. Some more attempts below.

The common thread with array like objects is that they are meant to be iterated over in series. It might simplify library code and reduce confusion among clients of these libraries if there is some consistent definition of series-ness. This committee might want to get involved since it could affect discussions on a few topics:   (1) key iteration order   (2) generators/iterators   (3) catchall proposals   (4) type systems

One strawman definition for an array like object:    o is an array like object if o[Get] returns a valid array index or one greater than the largest valid array index.

The need to distinguish between the two in library code could be mooted if for (in) on arrays iterated over the array index properties of arrays in numeric oder order first, followed by other properties in insertion order, and host objects like NodeCollections followed suit.

FWIW V8 does this for both arrays and objects.

But really for..in is pretty sick for arrays.  It will convert every single index in the array to a string.  That's not something to be encouraged IMHO.

Also agreed.

-- Erik Corry

Prototype.js dodges this problem by adding an each method to Array.prototype and others. This obviously fails for cross-context Arrays, and also suffers from the large sparse array and missing non-array-index properties problems.

It does switch on arrays in places, and does so inconsistently though -- in one case (unnecessarily?) filtering out arguments objects? if (Object.isArray(item) && !('callee' in item)) { for (var j = 0, arrayLength = item.length; j < arrayLength; j++) array.push(item[j]); } else {

function flatten() { return this.inject([], function(array, value) { if (Object.isArray(value)) return array.concat(value.flatten()); array.push(value); return array;

dojo.clone does if(d.isArray(o)){ r = []; for(i = 0, l = o.length; i < l; ++i){ if(i in o){ r.push(d.clone(o[i])); } } }else{ // generic objects r = o.constructor ? new o.constructor() : {}; } dojo.isArray = function(/anything/ it){ // summary: // Return true if it is an Array. // Does not work on Arrays created in other windows. return it && (it instanceof Array || typeof it == "array"); // Boolean } dojo.isArrayLike = function(/anything/ it){ // summary: // similar to dojo.isArray() but more permissive // description: // Doesn't strongly test for "arrayness". Instead, settles for "isn't // a string or number and has a length property". Arguments objects // and DOM collections will return true when passed to // dojo.isArrayLike(), but will return false when passed to // dojo.isArray(). // returns: // If it walks like a duck and quacks like a duck, return true return it && it !== undefined && // Boolean // keep out built-in constructors (Number, String, ...) which have length // properties !d.isString(it) && !d.isFunction(it) && !(it.tagName && it.tagName.toLowerCase() == 'form') && (d.isArray(it) || isFinite(it.length)); } and dojo uses these definitions to do type coercion }else if(!dojo.isArray(content)){ //To get to this point, content is array-like, but //not an array, which likely means a DOM NodeList. Convert it now. content = dojo._toArray(content);

The closure library similarly defines both isArray and isArrayLike, but with differences ( code.google.com/p/closure-library/source/browse/trunk/closure/goog/base.js ): goog.isArrayLike = function(val) { var type = goog.typeOf(val); return type == 'array' || type == 'object' && typeof val.length == 'number'; }; which is then used in many places ( www.google.com/codesearch?q=goog.isArrayLike+package%3Ahttp%3A%2F%2Fclosure-library\.googlecode\.com&origq=goog.isArrayLike ), e.g. in iter.js goog.iter.forEach = function(iterable, f, opt_obj) { if (goog.isArrayLike(iterable)) {

MooTools is the only library I looked at that does not check array-likedness. They do something similar to prototype by monkey-patching Array, which will fail for cross-context arrays. They do extend the concept of length to their Hash class: getLength: function(){ var length = 0; for (var key in this){ if (this.hasOwnProperty(key)) length++; } return length; }

# Brendan Eich (14 years ago)

On Dec 8, 2009, at 10:10 AM, Mike Samuel wrote:

  1. Does the object have the special semantics around .length? This
    is potentially useful to know and the normal way to find out doesn't work. Instanceof is affected by setting proto, yet the special .length handling persists and instanceof doesn't work for cross- iframe objects.

I think I agree with the bit about length, but proto doesn't work on non mozilla interpreters

WebKit JSC implemented too :-/.

and isn't likely to be standardized.

You got that part right :-).

I don't see how it's relevant to array-like-ness.

Agreed, but Erik's point here was about instanceof. Not only mutable
proto, but mutable bindings for constructors, and the issue of
multiple global objects with their unrelated suites of builtin
constructors, make instanceof less than ideal for deciding "array-like".

Efficiency is obviously important, but another important distinction is whether 'length' should be included in the set of keys iterated over,

In ES1-5, length is not enumerable for Array, String, and arguments
objects. Anything array-like, same deal or it's not "like" enough.

and whether iteration over array-index keys should be in numeric order.

I'm suspicious of real code depending on insertion order. Typically
arrays are built front to back so the two are the same. Exceptional
cases may break existing for-in loops that inspect arrays and want
numeric order. This is something to investigate (google codesearch?).

But really for..in is pretty sick for arrays. It will convert every single index in the array to a string. That's not something to be encouraged IMHO.

Also agreed.

Yes, that is an ES1 change from my Netscape implementation. It is
costly enough that optimizing implementations use memoized or static
strings for small integer literals!

Yet for-in on an array-like is attractive, especially in light of
comprehensions and generator expressions. One way forward would be to
change to number type under opt-in Harmony versioning. Another option
is new syntax, but that requires opt-in versioning anyway, and it's
not clear to me that changing for-in on Arrays, or on all array-likes,
to return numbers for indexes, is an incompatibility that we could not
"get away with".

If anyone knows of real-world code that would be broken by the type of
the for-in loop variable not always being string, I'm all ears.

# Allen Wirfs-Brock (14 years ago)

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich ...

If anyone knows of real-world code that would be broken by the type of

the for-in loop variable not always being string, I'm all ears.

/be

I don't have a real world example or a compelling use case for the following, but it seems like it might (possibly inadvertently) occur on the web:

One place it makes a difference is if the + operator is used with the induction variable:

var a=[0,2,1]; var b=[]; for (var i in a) b[i+1] = a[i];

In ES3/5, b will end up with properties named '01', '11', '21' and length=22. Note that '01' is not an "array index" because of the leading '0'. If for-in was changed so that i has numeric values for array index property names then b would end up with property names '1','2','3', and length=4.

The changed behavior is probably(??) what would be intended by somebody who did this, but changing it might still break apps that are currently perceived to be working.

My personal feeling is that rather than trying to "fix" various things about for-in (including pinning down enumeration order) it would be better to introduce a new more carefully defined enumeration statement(s) (for keys/for values??) and leave for-in alone.

# Juriy Zaytsev (14 years ago)

On Dec 8, 2009, at 1:10 PM, Mike Samuel wrote:

2009/12/8 Erik Corry <erik.corry at gmail.com>:

2009/12/8 Mike Samuel <mikesamuel at gmail.com>:

It occurred to me after looking at the proxy strawman that it might help to nail down what "array-like" means in future drafts. It isn't directly related to the proxy stuff though so I thought I'd start a separate thread.

I've seen quite a bit of library code that does something like if (isArrayLike(input)) { // iterate over properties [0,length) in increasing order } else { // Iterate over key value pairs }

This looks fairly broken to me. If the object has enumerable properties that aren't positive integers then they don't get iterated over just because some heuristic says it's array-like. If the heuristic says it's array-like then we iterate over portentially billions of indexes even if it is very sparse.

All true. And yet it is not uncommon. See the bottom of this email for a quick survey of a number of libraries' uses of the array-like concept.

[...]

Prototype.js dodges this problem by adding an each method to Array.prototype and others. This obviously fails for cross-context Arrays, and also suffers from the large sparse array and missing non-array-index properties problems.

It does switch on arrays in places, and does so inconsistently though -- in one case (unnecessarily?) filtering out arguments objects?

If you look just a bit below those lines, you will see that this whole concat method is only assigned to Array.prototype.concat when CONCAT_ARGUMENTS_BUGGY is truthy.

CONCAT_ARGUMENTS_BUGGY feature test looks like this:

(function() { return [].concat(arguments)[0][0] !== 1; })(1,2);

and is essentially a workaround to an "issue" with Opera, which, contrary to specs, makes arguments instanceof Array, yet fails to implement proper concat algorithm for arguments (as feature test demonstrates).

As far as isArray in Prototype.js, we just test for [[Class]] === "Array" (which is good across frames, but still fails across windows in IE).

There's, of course, no silver bullet isArray solution for a library. It's all about specific requirements, and as long as users do not fully understand language specifics, there will be broken expectations; some will expect "arrays" to inherit from Array.prototype (and so have any of Array.prototype methods available); some will expect arrays to have special [[Put]] and lengthbehavior; and others will expect array-like host objects (that neither inherit from Array, nor have special [[Put]] or [[Class]] === "Array") to be considered arrays as well.

It's all about context ;)

[...]

# Brendan Eich (14 years ago)

Ok, let's think about a new loop construct.

Adding new syntax makes for-in a boat-anchor implementations lug
around, in the best case. But realistically it will be years before
developers can count on the new loop construct. And as support comes
online, developers will have to choose which to use, and people
learning JS will have to cope with two similar kinds of loops.
Migration involves either a translator, or writing loops twice (a non- starter in practice), or using a source to source translator.

This all costs. It seems to me that the possibility of breakage under
opt-in versioning is not worse than the costs listed above. But again,
I welcome real-world examples (your example indicts polymorphic + as
much as string-ifying for-in, but no one is proposing a new "concat"
operator :-P)

Opt-in versioning will introduce new keywords including let and
possibly yield, which constitute breaking changes. We know this from
reality-based testing (Firefox 2 betas, JS1.7). While we haven't tried
leaving for-in-iterated indexes as numbers in years (we used to, since
ES1 made the change but Netscape's implementation did not for a
while), I suspect the breaking-ness is less than the new-keywords- under-opt-in issue. But we are introducing new keywords, they're
reserved in ES5 strict.

So I'll poke around with Google codesearch a bit later today.

# Mike Samuel (14 years ago)

2009/12/8 Brendan Eich <brendan at mozilla.com>:

On Dec 8, 2009, at 10:10 AM, Mike Samuel wrote:

  1. Does the object have the special semantics around .length?  This is potentially useful to know and the normal way to find out doesn't work.  Instanceof is affected by setting proto, yet the special .length handling persists and instanceof doesn't work for cross-iframe objects.

I think I agree with the bit about length, but proto doesn't work on non mozilla interpreters

WebKit JSC implemented too :-/.

and isn't likely to be standardized.

You got that part right :-).

I don't see how it's relevant to array-like-ness.

Agreed, but Erik's point here was about instanceof. Not only mutable proto, but mutable bindings for constructors, and the issue of multiple global objects with their unrelated suites of builtin constructors, make instanceof less than ideal for deciding "array-like".

Ah ok. I agree with Erik then. Even moreso, instanceof doesn't work cross context.

Efficiency is obviously important, but another important distinction is whether 'length' should be included in the set of keys iterated over,

In ES1-5, length is not enumerable for Array, String, and arguments objects. Anything array-like, same deal or it's not "like" enough.

Maybe I'm imagining things, but I'm pretty sure that on some browsers length is enumerable on arr in var arr = [1,2,3] arr.length = 2;

# Brendan Eich (14 years ago)

On Dec 8, 2009, at 11:36 AM, Brendan Eich wrote:

Adding new syntax makes for-in a boat-anchor implementations lug
around, in the best case. But realistically it will be years before
developers can count on the new loop construct. And as support comes
online, developers will have to choose which to use, and people
learning JS will have to cope with two similar kinds of loops.
Migration involves either a translator, or writing loops twice (a
non-starter in practice), or using a source to source translator.

Oops, I repeated the translator alternative when what I meant to say
was "people will stick with the old form, forever (for the life of
that content)."

On the other hand if we evolve for-in, then new code can be written to
normalize the type of the index if necessary, and the normalization
code can go away later, or be tolerated if lightweight enough:

for (let i in arraylike) { i |= 0; ... }

This is very cheap in some optimizing implementations if i is already
a number that happens to fit in a machine int.

New syntax could be nice but for-in has its own gravitational pull,
not just due to precedent but due to similarity to Python and other
languages. But mainly it won't be usable for years and in the mean
time, we'll have missed a lower-cost migration path that evolves (or
reforms, as in "recovers the good Platonic form" ;-) for-in.

# Brendan Eich (14 years ago)

On Dec 8, 2009, at 11:38 AM, Mike Samuel wrote:

Agreed, but Erik's point here was about instanceof. Not only mutable proto, but mutable bindings for constructors, and the issue of
multiple global objects with their unrelated suites of builtin constructors,
make instanceof less than ideal for deciding "array-like".

Ah ok. I agree with Erik then. Even moreso, instanceof doesn't work cross context.

Yes (what I meant by "multiple global objects").

In ES1-5, length is not enumerable for Array, String, and arguments
objects. Anything array-like, same deal or it's not "like" enough.

Maybe I'm imagining things, but I'm pretty sure that on some browsers length is enumerable on arr in var arr = [1,2,3] arr.length = 2;

That would be a straight-up conformance bug:

js> a = [1,2] [1, 2] js> a.length

2 js> for (i in a)print(i)

0 1 js> a.length = 0

0 js> for (i in a)print(i)

js>

Name names, take prisoners! Which browser(s)?

# Erik Corry (14 years ago)

2009/12/8 Brendan Eich <brendan at mozilla.com>:

On Dec 8, 2009, at 11:36 AM, Brendan Eich wrote:

Adding new syntax makes for-in a boat-anchor implementations lug around, in the best case. But realistically it will be years before developers can count on the new loop construct. And as support comes online, developers will have to choose which to use, and people learning JS will have to cope with two similar kinds of loops. Migration involves either a translator, or writing loops twice (a non-starter in practice), or using a source to source translator.

Oops, I repeated the translator alternative when what I meant to say was "people will stick with the old form, forever (for the life of that content)."

On the other hand if we evolve for-in, then new code can be written to normalize the type of the index if necessary, and the normalization code can go away later, or be tolerated if lightweight enough:

for (let i in arraylike) {    i |= 0;

If this pattern catches on then we could optimize for it without changing the language. I think returning integers from for-in is a complete non-starter but I'm not volunteering to build a browser that does it just so I can check what breaks.

# Brendan Eich (14 years ago)

On Dec 8, 2009, at 11:51 AM, Erik Corry wrote:

for (let i in arraylike) { i |= 0;

If this pattern catches on then we could optimize for it without changing the language.

Yeah, but some day there will be only "new" versions in which i is
numeric for arraylikes and that becomes pointless. On that fine day
programmers can stop writing the ugly extra statement. That's worth
something too, even as a new loop construct "could be nicer".

Do we want nicer with high migration tax and greater total language
complexity (new loop construct), or nicer with lower tax (reformed for- in under new opt-in version), or not-so-nice with perpetual type-fixup
kludge (what you seem resigned to :-P).

I think returning integers from for-in is a complete non-starter but I'm not volunteering to build a browser that does it just so I can check what breaks.

I'll handle that chore...

# Mike Samuel (14 years ago)

2009/12/8 Brendan Eich <brendan at mozilla.com>:

On Dec 8, 2009, at 11:38 AM, Mike Samuel wrote:

Agreed, but Erik's point here was about instanceof. Not only mutable proto, but mutable bindings for constructors, and the issue of multiple global objects with their unrelated suites of builtin constructors, make instanceof less than ideal for deciding "array-like".

Ah ok.  I agree with Erik then. Even moreso, instanceof doesn't work cross context.

Yes (what I meant by "multiple global objects").

In ES1-5, length is not enumerable for Array, String, and arguments objects. Anything array-like, same deal or it's not "like" enough.

Maybe I'm imagining things, but I'm pretty sure that on some browsers length is enumerable on arr in  var arr = [1,2,3]  arr.length = 2;

That would be a straight-up conformance bug:

js> a = [1,2] [1, 2] js> a.length 2 js> for (i in a)print(i) 0 1 js> a.length = 0 0 js> for (i in a)print(i) js>

Name names, take prisoners! Which browser(s)?

Sorry. I can't repeat it on anything I have installed. Maybe just a ghost of late-night-debugging-sessions past.

# Mike Samuel (14 years ago)

2009/12/8 Brendan Eich <brendan at mozilla.com>:

On Dec 8, 2009, at 11:51 AM, Erik Corry wrote:

for (let i in arraylike) {   i |= 0;

If this pattern catches on then we could optimize for it without changing the language.

Yeah, but some day there will be only "new" versions in which i is numeric for arraylikes and that becomes pointless. On that fine day programmers can stop writing the ugly extra statement. That's worth something too, even as a new loop construct "could be nicer".

Do we want nicer with high migration tax and greater total language complexity (new loop construct), or nicer with lower tax (reformed for-in under new opt-in version), or not-so-nice with perpetual type-fixup kludge

Well, we have "use strict," and then this would give us "use integer," so all that leaves would be an excuse to shoehorn "use vmsish" into the language :)

# Brendan Eich (14 years ago)

On Dec 8, 2009, at 12:52 PM, Mike Samuel wrote:

2009/12/8 Brendan Eich <brendan at mozilla.com>:

On Dec 8, 2009, at 11:51 AM, Erik Corry wrote:

for (let i in arraylike) { i |= 0;

If this pattern catches on then we could optimize for it without changing the language.

Yeah, but some day there will be only "new" versions in which i is
numeric for arraylikes and that becomes pointless. On that fine day
programmers can stop writing the ugly extra statement. That's worth something too,
even as a new loop construct "could be nicer".

Do we want nicer with high migration tax and greater total language complexity (new loop construct), or nicer with lower tax (reformed
for-in under new opt-in version), or not-so-nice with perpetual type-fixup
kludge

Well, we have "use strict," and then this would give us "use integer," so all that leaves would be an excuse to shoehorn "use vmsish" into the language :)

I remember VMS. Not as good as TOPS-20 :-P.

Seriously, opt-in versioning will not be via too many "use mumble"
pragmas, rather whole-edition selection a la RFC 4329. The "use
strict" idea is good but we don't want endless feature knobs (we =
everyone, TC39, developers, implementors).

More, while in-language version selection may yet happen, I don't see
how it helps with new syntax. You'll get syntax errors from old
browsers without any way to handle them (no fallback or alt content
for <script>).

More controllable (for authors) is something like the sketch here:

proposals:versioning

linked from

strawman:versioning

Your thoughts and edits are welcome on this last page.

# Mike Wilson (14 years ago)

I think Breton mentions something important here; the desire to actually detect if something is an array or arraylike to be able to branch to different code that does completely different things for array[likes] and objects. If we just provide a better generic iteration construct then this part is lost, as I might even not be doing iteration in one of the branches.

Certainly, solving this isn't easy, but it would be good if there was some direction taken by the WG to provide a standard way to detect arraylikeness without having to do iteration, so JS runtimes, browser host objects and JS libraries can follow this precedent.

Best Mike Wilson

# Mark S. Miller (14 years ago)

On Fri, Dec 11, 2009 at 2:27 AM, Mike Wilson <mikewse at hotmail.com> wrote:

I think Breton mentions something important here; the desire to actually detect if something is an array or arraylike to be able to branch to different code that does completely different things for array[likes] and objects. If we just provide a better generic iteration construct then this part is lost, as I might even not be doing iteration in one of the branches.

Certainly, solving this isn't easy, but it would be good if there was some direction taken by the WG to provide a standard way to detect arraylikeness without having to do iteration, so JS runtimes, browser host objects and JS libraries can follow this precedent.

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)
  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)
  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

then I suggest:

function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

Since getting 'length' may have side effects, this is written a bit weird so that this get only happens after earlier tests pass.

And yes, I'm aware that this usage of Object.prototype.propertyIsEnumerable implies that catchalls must virtualize it in order for a proxy to be able to pass this test :(.

# Mike Wilson (14 years ago)

Mark S. Miller wrote:

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)
  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)
  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

then I suggest:

function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

Thanks Mark, this is the right way to approach it I think. String objects will be considered array-like above which may not be desired, but either way, I think it is this type of guideline that can prove valuable, so host objects and libraries can make sure to adhere (or make sure not to adhere) to that pattern. I'm not sure where it should be put, but maybe there should be an official ES "duck" page with committee approved patterns ;-).

Best Mike

# Mark S. Miller (14 years ago)

[+commonjs]

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

Thanks Mark, this is the right way to approach it I think. String objects will be considered array-like above which may not be desired, but either way, I think it is this type of guideline that can prove valuable, so host objects and libraries can make sure to adhere (or make sure not to adhere) to that pattern. I'm not sure where it should be put, but maybe there should be an official ES "duck" page with committee approved patterns ;-).

I like the idea of collecting consensus patterns for coordinating conventions outside the jurisdiction of the specs. I wonder whether this is best done at wiki.ecmascript.org or at wiki.commonjs.org ? The CommonJS community is already functioning as an informal open standards body for such conventions.

# David-Sarah Hopwood (14 years ago)

Mark S. Miller wrote:

On Fri, Dec 11, 2009 at 2:27 AM, Mike Wilson <mikewse at hotmail.com> wrote:

I think Breton mentions something important here; the desire to actually detect if something is an array or arraylike to be able to branch to different code that does completely different things for array[likes] and objects. [...]

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)
  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)
  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

then I suggest:

function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

Since getting 'length' may have side effects, this is written a bit weird so that this get only happens after earlier tests pass.

If you want to avoid side effects:

function isArrayLike(obj) { if (!obj || typeof obj !== 'object') return false; var desc = Object.getPropertyDescriptor(obj, 'length'); if (desc) { var len = desc.value; return !desc.enumerable && (len === undefined || len >>> 0 === len); } }

This allows any length getter without checking that it will return an array index, but it still satisfies all of the above requirements.

However, I don't see why the check on the current value of length is necessary. For it to make any difference, there would have to be a nonenumerable length property on a non-function object, with a value that is not an array index. How and why would that happen?

And yes, I'm aware that this usage of Object.prototype.propertyIsEnumerable implies that catchalls must virtualize it in order for a proxy to be able to pass this test :(.

Same with Object.getPropertyDescriptor in the above.

# Mike Wilson (14 years ago)

David-Sarah Hopwood wrote:

Mark S. Miller wrote:

function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

If you want to avoid side effects:

function isArrayLike(obj) { if (!obj || typeof obj !== 'object') return false; var desc = Object.getOwnPropertyDescriptor(obj, 'length'); if (desc) { var len = desc.value; return !desc.enumerable && (len === undefined || len >>> 0 === len); } }

An advantage with Mark's code is that it doesn't rely on ES5 API. I think it's good to establish a standard for array-likeness that can be matched by ES3 code as well.

Best Mike

# Mark S. Miller (14 years ago)

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

[...] I'm not sure where it should be put, but maybe there should be an official ES "duck" page with committee approved patterns ;-).

Without claiming any "approvals" per se, I have started a "conventions:" namespace conventions:conventions

on the wiki and placed some initial suggestions there, including isArrayLike(). Thanks for the suggestion!

# Mike Samuel (14 years ago)

2009/12/12 Mike Wilson <mikewse at hotmail.com>:

David-Sarah Hopwood wrote:

Mark S. Miller wrote:

function isArrayLike(obj) {   var len;   return !!(obj &&             typeof obj === 'object' &&             'length' in obj &&             !({}).propertyIsEnumerable.call(obj, 'length') &&             (len = obj.length) >>> 0 === len); }

Nits:

Array length is specified as being in [0, 0x8000_0000], but the range of >>> is [0, 0x1_0000_0000).

On the String defect, we could repair that with && ({}).toString.call(obj) !== '[object String]' Cons: An extra function call in the likely case Strings are arguable array-like Pros: Strings are inconsistently indexable : (new String('foo'))[0] is undefined on IE 6

If you want to avoid side effects:

function isArrayLike(obj) {   if (!obj || typeof obj !== 'object') return false;   var desc = Object.getOwnPropertyDescriptor(obj, 'length');

What will getOwnPropertyDescriptor do for proxied arrays under the current proxy proposal?

# Mike Wilson (14 years ago)

Mark S. Miller wrote:

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

[...] I'm not sure where it should be put, but maybe there should be an official ES "duck" page with committee approved patterns ;-).

Without claiming any "approvals" per se, I have started a "conventions:" namespace conventions:conventions

on the wiki and placed some initial suggestions there, including isArrayLike(). Thanks for the suggestion!

Perfect! Thanks for putting this work in :-)

Best Mike

# Mark S. Miller (14 years ago)

On Sat, Dec 12, 2009 at 10:36 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/12 Mike Wilson <mikewse at hotmail.com>:

David-Sarah Hopwood wrote:

Mark S. Miller wrote:

function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

Nits:

Array length is specified as being in [0, 0x8000_0000], but the range of >>> is [0, 0x1_0000_0000).

As David-Sarah suggested, the current proposal avoid testing the value of

length at all, dodging this issue.

On the String defect, we could repair that with && ({}).toString.call(obj) !== '[object String]' Cons: An extra function call in the likely case Strings are arguable array-like Pros: Strings are inconsistently indexable : (new String('foo'))[0] is undefined on IE 6

Yes, this test would work. Before incorporating it, we should discuss

whether String wrapper objects should indeed be considered array-like. My inclination is that they should not. Opinions?

If you want to avoid side effects:

function isArrayLike(obj) { if (!obj || typeof obj !== 'object') return false; var desc = Object.getOwnPropertyDescriptor(obj, 'length');

What will getOwnPropertyDescriptor do for proxied arrays under the current proxy proposal?

It will return undefined, since (by design) it reveals that a trapping

proxy does not actually have any own properties. Therefore, the if-test below will not execute its then-part since desc will be falsy.

# Garrett Smith (14 years ago)

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

Mark S. Miller wrote:

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)

No native ES objects?

  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)

What takes bounded-by-constant time? The object's existence would not take any time.

  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

Can you please clarify what the problem area is a little more?

then I suggest: function isArrayLike(obj) {   var len;   return !!(obj &&             typeof obj === 'object' &&             'length' in obj &&             !({}).propertyIsEnumerable.call(obj, 'length') &&             (len = obj.length) >>> 0 === len); }

Is looks like "array like" is defined as an an object where "length" is non-enumerable and numeric. What about [[Get]], [[HasProperty]] for numeric property names?

And why must the - length - property be an own property? Why could length not be a getter in the prototype chain?

Indeed many Mozilla DOM collections work this way:

javascript: var cn = document.links; alert([cn.length, ({}).hasOwnProperty.call( cn, "length")]);

Mozilla elerts "80, false"

Garrett

# Mike Wilson (14 years ago)

Mark S. Miller wrote:

On Sat, Dec 12, 2009 at 10:36 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

On the String defect, we could repair that with && ({}).toString.call(obj) !== '[object String]' Cons: An extra function call in the likely case Strings are arguable array-like Pros: Strings are inconsistently indexable : (new String('foo'))[0] is undefined on IE 6

Yes, this test would work. Before incorporating it, we should discuss whether String wrapper objects should indeed be considered array-like. My inclination is that they should not. Opinions?

I don't think strings should trigger array-like, and this seems to be the position held by most JS libraries (see f ex dojo.isArrayLike). The normal use of these methods is to identify things like Arguments and NodeLists, ie sequences of distinct objects/values, not character sequences (text). Anyhow, whatever is decided here I think primitive strings and String wrappers should be handled the same way in this respect.

Best Mike

# Mark S. Miller (14 years ago)

On Sat, Dec 12, 2009 at 11:21 AM, Garrett Smith <dhtmlkitchen at gmail.com>wrote:

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

Mark S. Miller wrote:

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)

No native ES objects?

Native array and (currently) String wrapper objects do pass this test. No other ES3R natives objects can. Which native objects are you concerned about?

  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)

What takes bounded-by-constant time? The object's existence would not take any time.

The execution time of this predicate. Actually, that's not necessarily

true. Depending on implementation, the

'length' in obj

test may take time O(the depth of obj's inheritance chain). Likewise the propertyIsEnumerable test. I was mistakenly assuming bounded depth inheritance chains. I'll fix the text.

  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

Can you please clarify what the problem area is a little more?

then I suggest: function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

Is looks like "array like" is defined as an an object where "length" is non-enumerable and numeric. What about [[Get]], [[HasProperty]] for numeric property names?

And why must the - length - property be an own property? Why could length not be a getter in the prototype chain?

Indeed many Mozilla DOM collections work this way:

javascript: var cn = document.links; alert([cn.length, ({}).hasOwnProperty.call( cn, "length")]);

Mozilla elerts "80, false"

Not a problem. The proposed predicate does not test whether 'length' is own, only whether it is enumerable. A squarefree session on FF 3.5.5:

function isArrayLike(obj) { return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length')); }

var cn = document.links;

isArrayLike(cn); true

# Brendan Eich (14 years ago)

On Dec 12, 2009, at 11:36 AM, Mike Wilson wrote:

Mark S. Miller wrote:

On Sat, Dec 12, 2009 at 10:36 AM, Mike Samuel <mikesamuel at gmail.com>
wrote: On the String defect, we could repair that with && ({}).toString.call(obj) !== '[object String]' Cons: An extra function call in the likely case Strings are arguable array-like Pros: Strings are inconsistently indexable : (new String('foo'))[0] is undefined on IE 6

Yes, this test would work. Before incorporating it, we should
discuss whether String wrapper objects should indeed be considered
array-like. My inclination is that they should not. Opinions? I don't think strings should trigger array-like, and this seems to
be the position held by most JS libraries (see f ex
dojo.isArrayLike). The normal use of these methods is to identify
things like Arguments and NodeLists, ie sequences of distinct
objects/values, not character sequences (text).

A String object is indexable (to get unit-length strings -- not
characters) with length as fencepost above the maximum index.

A string or String object wrapping it has immutable .length and [i]
for i < length properties, OTOH -- it is not frozen, but this raises
the question: is a frozen Array instance no longer array-like?

Prototype's isArray wants 'splice' in obj && 'join' in obj, but that
rules out many objects considered array-like, without monkey-patching.

We need a definition of array-like that matches common practice, if
there is a common practice. Then we can see (without prejudgment)
whether Strings satisfy it.

From Mike Samuel's posts and my own quick reading, I don't see too
much common practice. A more detailed survey of popular Ajax libraries
would be great -- I'm out of time right now, but I'll try later today
unless someone else does it first.

Anyhow, whatever is decided here I think primitive strings and
String wrappers should be handled the same way in this respect.

Right.

# Brendan Eich (14 years ago)

On Dec 12, 2009, at 10:36 AM, Mike Samuel wrote:

2009/12/12 Mike Wilson <mikewse at hotmail.com>:

David-Sarah Hopwood wrote:

Mark S. Miller wrote:

function isArrayLike(obj) { var len; return !!(obj && typeof obj === 'object' && 'length' in obj && !({}).propertyIsEnumerable.call(obj, 'length') && (len = obj.length) >>> 0 === len); }

Nits:

Array length is specified as being in [0, 0x8000_0000],

Where? From the spec that implementors have had years to follow
(citing ES3 rather than ES5), Array length is defined by:

15.4.5.2 length

"The length property of this Array object is always numerically
greater than the name of every property whose name is an array index."

which refers to the definition of "array index" here:

15.4 Array Objects

"Array objects give special treatment to a certain class of property
names. A property name P (in the form of a string value) is an array
index if and only if ToString(ToUint32(P)) is equal to P and
ToUint32(P) is not equal to 2^32−1. Every Array object has a length
property whose value is always a nonnegative integer less than 2^32."

So length is in [0, 0xffffffff] and array indexes are in [0,
0xfffffffe].

String length has no such constraint.

# Garrett Smith (14 years ago)

On Sat, Dec 12, 2009 at 12:01 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 11:21 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

Mark S. Miller wrote:

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)

No native ES objects?

Native array and (currently) String wrapper objects do pass this test. No other ES3R natives objects can. Which native objects are you concerned about?

Any user defined native object e.g. a jquery object or something like:

{ x : 10, length : 1.5 }

  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)

What takes bounded-by-constant time? The object's existence would not take any time.

The execution time of this predicate. Actually, that's not necessarily true.

[snip]

Got it. That means that the isArrayLike test itself is constant time. That makes sense.

  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

Can you please clarify what the problem area is a little more?

then I suggest: function isArrayLike(obj) {   var len;   return !!(obj &&             typeof obj === 'object' &&             'length' in obj &&             !({}).propertyIsEnumerable.call(obj, 'length') &&             (len = obj.length) >>> 0 === len); }

[snip]

Is looks like "array like" is defined as an an object where "length" is non-enumerable and numeric. What about [[Get]], [[HasProperty]] for numeric property names?

And why must the - length - property be an own property? Why could length not be a getter in the prototype chain?

Indeed many Mozilla DOM collections work this way:

javascript: var cn = document.links; alert([cn.length, ({}).hasOwnProperty.call( cn, "length")]);

Mozilla elerts "80, false"

Not a problem. The proposed predicate does not test whether 'length' is own, only whether it is enumerable. A squarefree session on FF 3.5.5:

[snip]

Ah no, that is not correct, your isArrayLike does test to see whether "length" is own.

Object.prototype.propertyIsEnumerable does not consider properties in the prototype chain. In effect, propertyIsEnumerable is a test to see if an object has own property that is also enumerable.

We discussed this back in ES4 proposal days, and I think I remember that it was decided to keep the existing behavior (as unintuitive as it may be) to not break existing code (some already broken things YUI and some other libs were doing).

Also notice that the name propertyIsEnumerable deviates from standard boolean predicate method naming convention. Flash AS apparently has an isPropertyEnumerable (more standard boolean predicate name).

Garrett

# Mark S. Miller (14 years ago)

On Sat, Dec 12, 2009 at 12:29 PM, Garrett Smith <dhtmlkitchen at gmail.com>wrote:

Object.prototype.propertyIsEnumerable does not consider properties in the prototype chain. In effect, propertyIsEnumerable is a test to see if an object has own property that is also enumerable.

And just when I was thinking I was finally beginning to understand this language :(. I wonder what other gotchas I'm still missing?

# Mark S. Miller (14 years ago)

On Sat, Dec 12, 2009 at 12:29 PM, Garrett Smith <dhtmlkitchen at gmail.com>

wrote:

Object.prototype.propertyIsEnumerable does not consider properties in the prototype chain. In effect, propertyIsEnumerable is a test to see if an object has own property that is also enumerable.

At the end of < conventions:isarraylike> I have added

the issue:

Unfortunately, the propertyIsEnumerable test above, despite the name, actually tests whether a property is both enumerable and own. This means the above predicate will reject objects that inherit a non-enumerable length property. I can think of no reliable and portable ES3R test which tests only property enumerability, except by running a for-in loop and testing the output. Doing so would kill the bounded-by-constant time virtue.

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

# Garrett Smith (14 years ago)

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 12:29 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

Object.prototype.propertyIsEnumerable does not consider properties in the prototype chain. In effect, propertyIsEnumerable is a test to see if an object has own property that is also enumerable.

At the end of conventions:isarraylike I have added the issue:

Unfortunately, the propertyIsEnumerable test above, despite the name, actually tests whether a property is both enumerable and own. This means the above predicate will reject objects that inherit a non-enumerable length property. I can think of no reliable and portable ES3R test which tests only property enumerability, except by running a for-in loop and testing the output. Doing so would kill the bounded-by-constant time virtue.

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter. Why anyone would want to use |for in| for something that is arrayLike?

Can an arrayLike object have other properties? If you're talking about a DOM collection, you can be that it will have other properties and probably a mix of enumerable and non-enumerable proprietary properties.

A DOM collection (NodeList, for example) might very well have a length property that shows up in the body of a for in loop. There is no standard that specifies one way or another if a NodeList's length property should be enumerable. I can't see why anyone would care, either.

javascript: for(var p in document.childNodes) if(!isFinite(p)) alert(p);

Firefox 3.5, Safari 4, Chrome 2: "length" "item"

Opera 10: "length" "item" "namedItem" "tags"

IE7 "tags"

That example alone shows a few good reasons why using a for in loop would be a bad choice. In superficial testing in a few browsers, the length property showed up in the body of a for in loop. Other properties will show up, depending on the implementation and property name. Other collections will likely have even more properties.

if a DOM collection is considered arrayLike, then using for in loops over arrayLike can be considered to be a reckless, error-prone concept.

OTOH, If indexed properties are got off the object, as with your standard for loop, the enumerability of - length - is irrelevant. So again: Why do you care if |length| is enumerable?

Garrett

# Mark S. Miller (14 years ago)

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com>wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter. Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects. If

var obelisk = {
  width: 9,
  length: 16,
  height: 25
};

appears in some existing code, I don't want a predicate that decides the obelisk is array-like, as it was clearly not the intent. The reason I care about enumerability is that legacy ES3R code has no way to create an ordinary native object with a non-enumerable length property, thereby preventing false positives in against legacy ES3R objects.

OTOH, your examples of false DOM negatives below are sufficiently discouraging that perhaps this whole exercise is fruitless. Perhaps there is no coherent notion of array-like to be rescued from current practice.

# Mike Samuel (14 years ago)

2009/12/12 Brendan Eich <brendan at mozilla.com>:

On Dec 12, 2009, at 10:36 AM, Mike Samuel wrote:

2009/12/12 Mike Wilson <mikewse at hotmail.com>:

David-Sarah Hopwood wrote:

Mark S. Miller wrote:

function isArrayLike(obj) {  var len;  return !!(obj &&            typeof obj === 'object' &&            'length' in obj &&            !({}).propertyIsEnumerable.call(obj, 'length') &&            (len = obj.length) >>> 0 === len); }

Nits:

Array length is specified as being in [0, 0x8000_0000],

Where? From the spec that implementors have had years to follow (citing ES3 rather than ES5), Array length is defined by:

15.4.5.2 length

"The length property of this Array object is always numerically greater than the name of every property whose name is an array index."

which refers to the definition of "array index" here:

15.4 Array Objects

"Array objects give special treatment to a certain class of property names. A property name P (in the form of a string value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32−1. Every Array object has a length property whose value is always a nonnegative integer less than 2^32."

So length is in [0, 0xffffffff] and array indexes are in [0, 0xfffffffe].

Ah. Thanks.

# Mike Samuel (14 years ago)

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Fri, Dec 11, 2009 at 12:08 PM, Mike Wilson <mikewse at hotmail.com> wrote:

Mark S. Miller wrote:

If we're looking for a convention that is

  • does not admit any legacy ES3R non-array non-host objects (to prevent false positives)

No native ES objects?

  • does easily allow ES5 programmers to define new array-like non-array objects
  • takes bounded-by-constant time (i.e., no iteration)

What takes bounded-by-constant time? The object's existence would not take any time.

  • is a reasonably compatible compromise with the existing notions of array-like in legacy libraries as represented by previous examples in this thread

Can you please clarify what the problem area is a little more?

then I suggest: function isArrayLike(obj) {   var len;   return !!(obj &&             typeof obj === 'object' &&             'length' in obj &&             !({}).propertyIsEnumerable.call(obj, 'length') &&             (len = obj.length) >>> 0 === len); }

Is looks like "array like" is defined as an an object where "length" is non-enumerable and numeric. What about [[Get]], [[HasProperty]] for numeric property names?

I prefer "integral and non-negative" to "numeric." As Brendan pointed out, the definition of uint32 already matches up with length as specified for arrays. He also points out that there in no limit on string length, but if ulp(length) > 1 then you run into a lot of problems, so there is a

practical limit on length of 2**53.

I think any of the following definitions of length would be fine

  • uint32: (x === (x >>> 0) && isFinite(x))
  • non-negative integer: (x === +x && !(x % 1))
  • non-negative integer i such that ulp(i) <= 1: (x === +x && !(x %
  1. && (x - -1) !== x)
# Garrett Smith (14 years ago)

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's indexed properties, possibly filtering, mapping, etc, then allowing the algorithm to throw errors at Step 0 seems like a recipe for IE (a disaster).

[snip]

I am still a bit fuzzy on what your "arrayLike" means or is intended for. Allen pointed out on previous thread[1] that Array generic methods provide an implicit contract. What that contract is depends on the method and arguments.

Array.prototype.slice, when supplied with one argument, has the following implicit contract for the thisArg:

  1. [[Get]]
  2. [[HasProperty]] checks

An object that can do those two, but also has a - length - property has enough functionality so that a call to - ([]).slice.call( anObject ) - would be expected to return an array that has anObject's numeric properties, except for the specification allowing "anything goes" with Host object and we see "JScript Object Expected" errors in IE.

Allen has not yet commented on that.

Stronger wording for Host object in ES specification would provide stronger incentive for implementations (IE) to use host objects that have consistent functionality. IOW, if the Array.prototype.slice says:

| 2. Call the [[Get]] method of this object with argument "length".

[[Get]] works via property accessor operators, as in:-

k = anObject.length >>> 0;

  • then that step of the algorithm should succeed.

Once an Array is obtained, then the program can use the other Array.prototype methods on that object. Alternatively, if the object's functionality is known by the programmer and the functionality fulfills an implicit contract for a generic method, then the program should be able to use that method, host object or not. At least, I would like it to be that way.

| var anObject = document.body.childNodes; | function fn( obj ) { return /foo/.test(obj.className); }; | Array.prototype.filter.call( anObject, fn );

Or (Spidermonkey only):

| function fn( obj ) { return /foo/.test(obj.className); } | Array.filter( document.body.childNodes, fn );

ES5 allows the algorithm to terminate at step 0, so that approach is not viable.

A for loop could be used, however the downside to that now the program uses a user-defined makeArray function.

makeArray( anObject ). filter( function(obj) { return /foo/.test(obj.className); });

Which requires double iteration over the collection, plus the overhead of an extra call, plus the extra makeArray function, downloaded and interpreted and loaded in memory. This sort of thing is often mired in the bowels of today's popular libraries.

In contrast, the generic algorithm in question could be specified to execute each step, regardless of whether or not the argument is a host object.

Whether or not the execution of that step results in error depends on the particular object's implementation. This way, if an object supports [[Get]], an Array method that calls [[Get]] could be expected to succeed. However if the object has a readonly - length - property, or - length - is a getter with no setter, then it would be expected to throw an error.

Is this idea reasonable or feasable, from a spec standpoint or implementation standpoint?

This is a very very old issue these errors are still coming in IE8, but do not appear in other major browsers (AFAIK). Is stronger wording for host objects a good idea?

Garrett

[1]esdiscuss/2009-May/009310 [2]esdiscuss/2009-December/010241

# Brendan Eich (14 years ago)

On Dec 12, 2009, at 12:36 PM, Mark S. Miller wrote:

On Sat, Dec 12, 2009 at 12:29 PM, Garrett Smith <dhtmlkitchen at gmail.com

wrote: Object.prototype.propertyIsEnumerable does not consider properties in the prototype chain. In effect, propertyIsEnumerable is a test to see if an object has own property that is also enumerable.

And just when I was thinking I was finally beginning to understand
this language :(. I wonder what other gotchas I'm still missing?

See

bugzilla.mozilla.org/show_bug.cgi?id=57048

filed by David Flanagan on 2000-10-17. My comment 11 is from
2000-12-01. This was an ES3 design-by-committee botch, never
corrected. The spec originated the feature, no browser implemented
anything like it first. SpiderMonkey tried doing it right as the bug
shows, but no good deed goes unpunished.

# Mike Samuel (14 years ago)

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's

If programmers only wrote programs that would be true. But they also write libraries for consumption by other programmers, and many come from languages that encourage duck-typing : if it enumerates keys like a duck ...

indexed properties, possibly filtering, mapping, etc, then allowing the algorithm to throw errors at Step 0 seems like a recipe for IE (a disaster).

Please clarify a "recipe for IE"

[snip]

I am still a bit fuzzy on what your "arrayLike" means or is intended for. Allen pointed out on previous thread[1] that Array generic methods provide an implicit contract. What that contract is depends on the method and arguments.

It is meant to help library writers write generic code themselves that choose an appropriate iteration mechanism.

# Garrett Smith (14 years ago)

On Sun, Dec 13, 2009 at 10:31 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's

If programmers only wrote programs that would be true.  But they also write libraries for consumption by other programmers, and many come from languages that encourage duck-typing : if it enumerates keys like a duck ...

What well-designed piece of code has absolutely no idea what its input is?

indexed properties, possibly filtering, mapping, etc, then allowing the algorithm to throw errors at Step 0 seems like a recipe for IE (a disaster).

Please clarify a "recipe for IE"

IE tends to take allowances for things whenever allowed to do so. Where the spec says "implementation dependent", IE will do something either useless or harmful.

ES5: | Whether the slice function can be applied successfully to | a host object is implementation-dependent.

That note allows an implementation to throw an error if the thisArg is a Host object, without attempting to run the algorithm. That's a recipe for IE and IE is a disaster.

I am suggesting a change so that an ES algorithm will run as specified.

Other things IE will do is not implement, throw errors, or provide wacko results for [[HasProperty]], [[Get]], [[ToBoolean]] internal properties. It is worth noting that prior to ES5, this was allowable.

Fortunately, ES5 rectified that with:

| Every object (including host objects) must implement all | of the internal properties listed in Table 8.

Which includes properties [[Get]], [[HasProperty]], et al.

That is a change that I strongly endorse. Whether or not IE will change to comply with that requirement has yet to be seen.

[snip]

I am still a bit fuzzy on what your "arrayLike" means or is intended for. Allen pointed out on previous thread[1] that Array generic methods provide an implicit contract. What that contract is depends on the method and arguments.

It is meant to help library writers write generic code themselves that choose an appropriate iteration mechanism.

I'm getting fuzzier. What is the method doing? Do you have a real example?

Garrett

# Mike Samuel (14 years ago)

2009/12/13 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sun, Dec 13, 2009 at 10:31 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's

If programmers only wrote programs that would be true.  But they also write libraries for consumption by other programmers, and many come from languages that encourage duck-typing : if it enumerates keys like a duck ...

What well-designed piece of code has absolutely no idea what its input is?

Noone is talking about code that has absolutely no idea what its inputs are. Please see my mail in this thread from 10:10 PST of 8 Dec for examples of code that knows that an input is either an array-like collection or a map-style collection but that use different definitions of the former.

indexed properties, possibly filtering, mapping, etc, then allowing the algorithm to throw errors at Step 0 seems like a recipe for IE (a disaster).

Please clarify a "recipe for IE"

IE tends to take allowances for things whenever allowed to do so. Where the spec says "implementation dependent", IE will do something either useless or harmful.

ES5: | Whether the slice function can be applied successfully to | a host object is implementation-dependent.

That note allows an implementation to throw an error if the thisArg is a Host object, without attempting to run the algorithm. That's a recipe for IE and IE is a disaster.

I am suggesting a change so that an ES algorithm will run as specified.

Other things IE will do is not implement, throw errors, or provide wacko results for [[HasProperty]], [[Get]], [[ToBoolean]] internal properties. It is worth noting that prior to ES5, this was allowable.

Fortunately, ES5 rectified that with:

| Every object (including host objects) must implement all | of the internal properties listed in Table 8.

Which includes properties [[Get]], [[HasProperty]], et al.

That is a change that I strongly endorse. Whether or not IE will change to comply with that requirement has yet to be seen.

What IE should/does do seems orthogonal to what is array like and whether TC39 should comment on what is array like.

[snip]

I am still a bit fuzzy on what your "arrayLike" means or is intended for. Allen pointed out on previous thread[1] that Array generic methods provide an implicit contract. What that contract is depends on the method and arguments.

It is meant to help library writers write generic code themselves that choose an appropriate iteration mechanism.

I'm getting fuzzier. What is the method doing? Do you have a real example?

Please see the mail mentioned above for examples of code, and please see Mark's mail from 11:12 PST 11 Dec for a proposed convention for array like.

# Garrett Smith (14 years ago)

On Mon, Dec 14, 2009 at 9:34 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/13 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sun, Dec 13, 2009 at 10:31 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's

If programmers only wrote programs that would be true.  But they also write libraries for consumption by other programmers, and many come from languages that encourage duck-typing : if it enumerates keys like a duck ...

What well-designed piece of code has absolutely no idea what its input is?

Noone is talking about code that has absolutely no idea what its inputs are. Please see my mail in this thread from 10:10 PST of 8 Dec for examples of code that knows that an input is either an array-like collection or a map-style collection but that use different definitions of the former.

You mentioned several code examples earlier:

Please see the thread where I mentioned "mired in the bowels of today's javascript libraries".

Those examples are vague. They mostly try to address all possible cases, but don't do a very good job when the argument is a Host object. The fact that these things are considered reliable is an indication to the poor understanding of javascript.

Take a snip from one of your shining examples:

| dojo.isArray = function(/anything/ it){ | // summary: | // Return true if it is an Array. | // Does not work on Arrays created in other windows. | return it && (it instanceof Array || typeof it == "array"); // Boolean | }

That the method accepts anything' and returns anything. If - it - is falsy, such as (false, null, undefined, 0, ""), then the inupt itself is returned. OTherwise there is a check to - it instanceof Array - which will always be false cross-frame/window. Third, there is typeof it == "array", which will always be false. ALthough it is funny to see how many mistakes dojo can cram into a single line of code, the fact of the matter is that Dojo authors are not competent or knowledgeable of ECMAScript.

Keep going? OK...

| dojo.isArrayLike = function(/anything/ it){ | // summary: | // similar to dojo.isArray() but more permissive | // description: | // Doesn't strongly test for "arrayness". | // Instead, settles for "isn't a string or | // number and has a length property". | // Arguments objects and DOM collections | // will return true when passed to | // dojo.isArrayLike(), but will return false | // when passed to dojo.isArray(). | // returns: | // If it walks like a duck and quacks like a | // duck, return true | // return it && it !== undefined && // Boolean | // keep out built-in constructors (Number, String, ...) | // which have length properties. | !d.isString(it) && !d.isFunction(it) && | !(it.tagName && it.tagName.toLowerCase() == 'form') && | (d.isArray(it) || isFinite(it.length)); | }

So we have a function which again takes anything and passes anything to d.isString d.isFunction which can be assumed to be similar in quality to their isArray function. Next, exclude any object that does has a tagName property "form" (case-insensitive). Finally, return true if d.isArray || isFinite(it.length). That includes textNodes, comment nodes, and probably a ton of other Host objects.

The dojo.isArrayLike method tries to do something with anything, isn't very clear about what it does, and creates confusion by being posed as something competently written.

Copying such things is obviously a poor idea. Basing APIs on such things is part of the "death spiral". and I'm out of time.

,

Garrett

# Mike Samuel (14 years ago)

2009/12/14 Garrett Smith <dhtmlkitchen at gmail.com>:

On Mon, Dec 14, 2009 at 9:34 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/13 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sun, Dec 13, 2009 at 10:31 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's

If programmers only wrote programs that would be true.  But they also write libraries for consumption by other programmers, and many come from languages that encourage duck-typing : if it enumerates keys like a duck ...

What well-designed piece of code has absolutely no idea what its input is?

Noone is talking about code that has absolutely no idea what its inputs are. Please see my mail in this thread from 10:10 PST of 8 Dec for examples of code that knows that an input is either an array-like collection or a map-style collection but that use different definitions of the former.

You mentioned several code examples earlier:

Please see the thread where I mentioned "mired in the bowels of today's javascript libraries".

Those examples are vague. They mostly try to address all possible cases, but don't do a very good job when the argument is a Host object. The fact that these things are considered reliable is an indication to the poor understanding of javascript.

Take a snip from one of your shining examples:

This is just another of your strawmen. I specifically said that these were examples of code defining array like-ness in various ways. I never said they were examples of great code.

|       dojo.isArray = function(/anything/ it){ |               //      summary: |               //              Return true if it is an Array. |               //              Does not work on Arrays created in other windows. |               return it && (it instanceof Array || typeof it == "array"); // Boolean |       }

That the method accepts anything' and returns anything. If - it - is

Ok, so this goes back to your earlier strawman where you make the unsupported assertion that code that accepts anything is bad. What about the identity function, the cast operators defined in the standard (ToString, ToPrimitive, ToNumber, ToObject), operators like +, and some of your favorite array generics (Array.prototype.indexOf)? The more fundamental the operator, the fewer assumptions it can make about its inputs.

falsy, such as (false, null, undefined, 0, ""), then the inupt itself is returned.

That the author did not know this is an unsupported assertion and that this somehow makes the code bad is a non sequitur, but I'm not going to get sucked into a code purity pissing contest with you.

Please explain what this has to do with any of the following questions: (1) Should TC39 render an opinion on what is array like? (2) If (1), what is array like? (3) If (!1), should future EcmaScript drafts define iteration order for arrays as index order and possibly recommend to array like host objects that the define iteration order similarly.

# Breton Slivka (14 years ago)

On Tue, Dec 15, 2009 at 6:29 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

(3) If (!1), should future EcmaScript drafts define iteration order for arrays as index order and possibly recommend to array like host objects that the define iteration order similarly.

I would suggest that this change would be mostly useless to me in my day to day work. The fact that past ecmascripts made the mistake of enumerating over properties that should not be enumerated over (such as monkey patched functions) is a fact of life that spawned two patterns:

  1. The crockford iteration: for(var i in o) { if(o.hasOwnProperty(i)){ //iteration code } }
  2. The array-like iteration: for(var i = 0; i<o.length; i++) { //iteration code }

When I iterate over an array or array like, I want JUST the numbered indexes and none of the named indexes. I cannot think of a pattern that uses for( x in y) which can gaurantee that, and I can't figure out how I would use it in day to day work. The iteration code would need to be prepared for either a numeric i, or a string i, so I couldn't safely do arithmetic OR string operations on it without deciding which I was dealing with first. Even if I did so, I can't think of a situation where such code would be useful. not to mention, would not changing the order break compatibility with existing programs?

A far more useful direction for iteration is the generalization of the forEach, map, and filter array extras. As others have pointed out, there is an implicit contract in these methods. These are methods that actually make programming easier, rather than just being useless appendixes like some rigging of for-in iteration order would be. I have no opinion on whether they should be static methods or not.

My suggestion would be that if an object can function as the "this" argument within Array.prototype.slice, and slice returns a non empty array, then it is array like, and should be acceptable within any of the other array extras, as that's what I expect. This includes user defined objects such as the jQuery object, host collections, the arguments object, arrays from other frames, etc. This matches with patterns that I use, patterns used in jquery, the pattern of calling Array.prototype.slice on arguments objects, it renders moot the trouble with arrays from external frames, and so on.

The only pitfall, which I've already pointed out, is that it also includes strings, and may, if naively defined ( as I did earlier) include functions, or objects in the style of {width:20, height:40: length:60}. When I write templating functions, or dom tree walking functions, I often need to distinguish between a host collection, an array or array like, and a string, to decide whether I simply pass the object on as is, or iterate over it. (is it singular or multiple in nature?)

The fact is, javascript libraries, and programmers will use any language construct they can grab a hold of to make reasonable decisions that work within the program their writing. I'm not sure what value there would be in TC-39 decreeing a pattern as blessed, because programmers are just as likely to ignore it and just use what works for them, today, right now, no matter how technically wrong it is.

However, the discussion here has revealed one interesting thing: Both ES3 and ES5 are inadequate for efficiently determining one particular property of an object that would be useful in the accuracy of these decisions. That is, it is difficult to determine whether an object has numeric properties or not without iterating over [length] elements. If there was some flag in an object that gets set on insertion of a numeric property, a flag which is observable in some way from user code, or something that gets observed by a built in isArrayLike, that would, I believe, close this gap. Does it make sense to try and unset the flag once it is set? I don't think that would be necessary.

# Mike Samuel (14 years ago)

2009/12/14 Breton Slivka <zen at zenpsycho.com>:

On Tue, Dec 15, 2009 at 6:29 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

(3) If (!1), should future EcmaScript drafts define iteration order for arrays as index order and possibly recommend to array like host objects that the define iteration order similarly.

I would suggest that this change would be mostly useless to me in my day to day work.  The fact that past ecmascripts made the mistake of enumerating over properties that should not be enumerated over (such as monkey patched functions) is a fact of life that spawned two patterns:

  1. The crockford iteration: for(var i in o) {    if(o.hasOwnProperty(i)){       //iteration code    } }
  2. The array-like iteration: for(var i = 0; i<o.length; i++) {    //iteration code }

Does the performance of this on large sparse arrays pose problems for you in your day to day work? E.g. for (o = new Array(1e100)).

Do you use jquery in day to day work? I ask because that's one library that already seems to dynamically switch between the two.

When I iterate over an array or array like, I want JUST the numbered indexes and none of the named indexes. I cannot think of a pattern that uses for( x in y) which can gaurantee that, and I can't figure out how I would use it in day to day work.  The iteration code would need to be prepared for either a numeric i, or a string i, so I couldn't safely do arithmetic OR string operations on it without deciding which I was dealing with first. Even if I did so, I can't think of a situation where such code would be useful. not to mention, would not changing the order break compatibility with existing programs?

Can you think of any existing programs that rely on the current (undefined but something like insertion order) order for arrays?

A far more useful direction for iteration is the generalization of the forEach, map, and filter array extras. As others have pointed out, there is an implicit contract in these methods. These are methods that actually make programming easier, rather than just being useless appendixes like some rigging of for-in iteration order would be. I have no opinion on whether they should be static methods or not.

My suggestion would be that if an object can function as the "this" argument within Array.prototype.slice, and slice returns a non empty

I think basing the definition on what Array.prototype.slice does is a decent criterion.

array, then it is array like, and should be acceptable within any of the other array extras, as that's what I expect. This includes user defined objects such as the jQuery object, host collections, the arguments object, arrays from other frames, etc. This matches with

Only if those collections are not empty. Calling slice on an empty array obviously returns an empty array which would fail your test above.

patterns that I use, patterns used in jquery, the pattern of calling Array.prototype.slice on arguments objects, it renders moot the trouble with arrays from external frames, and so on.

The only pitfall, which I've already pointed out, is that it also includes strings, and may, if naively defined ( as I did earlier) include functions, or objects in the style of {width:20, height:40: length:60}. When I write templating functions, or dom tree walking functions, I often need to distinguish between a host collection, an array or array like, and a string, to decide whether I simply pass the object on as is, or iterate over it. (is it singular or multiple in nature?)

The fact is, javascript libraries, and programmers will use any language construct they can grab a hold of to make reasonable decisions that work within the program their writing. I'm not sure what value there would be in TC-39 decreeing a pattern as blessed, because programmers are just as likely to ignore it and just use what works for them, today, right now, no matter how technically wrong it is.

There are far fewer Javascript library writers than Javascript programmers so I'm not convinced that it's too large an audience to talk to.

However, the discussion here has revealed one interesting thing: Both ES3 and ES5 are inadequate for efficiently determining one particular property of an object that would be useful in the accuracy of these decisions. That is, it is difficult to determine whether an object has numeric properties or not without iterating over [length] elements. If there was some flag in an object that gets set on insertion of a numeric property, a flag which is observable in some way from user code, or something that gets observed by a built in isArrayLike, that would, I believe, close this gap. Does it make sense to try and unset the flag once it is set? I don't think that would be necessary.

This flag on insertion would again report that empty arrays are not array like, but I guess there's only one possible ordering of the elements of the empty set so it's moot.

# Breton Slivka (14 years ago)

On Tue, Dec 15, 2009 at 10:34 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/14 Breton Slivka <zen at zenpsycho.com>:

On Tue, Dec 15, 2009 at 6:29 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

(3) If (!1), should future EcmaScript drafts define iteration order for arrays as index order and possibly recommend to array like host objects that the define iteration order similarly.

I would suggest that this change would be mostly useless to me in my day to day work.  The fact that past ecmascripts made the mistake of enumerating over properties that should not be enumerated over (such as monkey patched functions) is a fact of life that spawned two patterns:

  1. The crockford iteration: for(var i in o) {    if(o.hasOwnProperty(i)){       //iteration code    } }
  2. The array-like iteration: for(var i = 0; i<o.length; i++) {    //iteration code }

Does the performance of this on large sparse arrays pose problems for you in your day to day work? E.g. for (o = new Array(1e100)).

No it does not. While I know that large sparse arrays are possible, I do not encounter them very often. The only time I've used a sparse array is in a representation of a timeline, where I did not expect to visit N indexes in greater than linear time. I would not consider this "large" on the order of your example, though. I have never encountered anything like that. My performance expectations are about the length of the array, not how many elements are properly stored in it. I lie a little bit here, there is one situation where I expect greater than linear time: Serializing the timeline into JSON, I convert the sparse array representation to an "array like" object representation before serializing (to save space). In this case I explicitly use for/in without identifying the type of the object first. I already have a reasonable assurance about what it's going to be since I created the object myself, it will either be an array, or undefined.

I will concede though, a way to efficiently iterate over a sparse array in numeric order might be useful in some cases. I suggest the forEach (and other) array methods for this though. The efficiency and implementation of that does not have to be visible to the user, as long as the interface remains the same, and does not need to involve the ecmascript committee.

Do you use jquery in day to day work?  I ask because that's one library that already seems to dynamically switch between the two.

Yes I do use jquery. I'm unsure about what you're referring to here when you say "the two" though.

Can you think of any existing programs that rely on the current (undefined but something like insertion order) order for arrays?

No I can't. I only know that standardising the order of iteration for objects was brought up as a compatiblity issue during the ES4/ES5 standardization process, since all popular interpreters behave in the same way. It would seem silly to me to then change the order after all that fuss.

A far more useful direction for iteration is the generalization of the forEach, map, and filter array extras. As others have pointed out, there is an implicit contract in these methods. These are methods that actually make programming easier, rather than just being useless appendixes like some rigging of for-in iteration order would be. I have no opinion on whether they should be static methods or not.

My suggestion would be that if an object can function as the "this" argument within Array.prototype.slice, and slice returns a non empty

I think basing the definition on what Array.prototype.slice does is a decent criterion.

array, then it is array like, and should be acceptable within any of the other array extras, as that's what I expect. This includes user defined objects such as the jQuery object, host collections, the arguments object, arrays from other frames, etc. This matches with

Only if those collections are not empty.  Calling slice on an empty array obviously returns an empty array which would fail your test above.

I'm not married to the nonempty criterion. What I mean here is to exclude objects like {width:10, height:10, length:10}, or function objects. The non empty criterion is simply a heuristic, a naive solution to this tricky logic problem. But the empty set is still a set after all. I am open to better solutions, and I don't even mind considering such an object array like. I just thought I would try, out of consideration of the arguments of other posters.

patterns that I use, patterns used in jquery, the pattern of calling Array.prototype.slice on arguments objects, it renders moot the trouble with arrays from external frames, and so on.

The only pitfall, which I've already pointed out, is that it also includes strings, and may, if naively defined ( as I did earlier) include functions, or objects in the style of {width:20, height:40: length:60}. When I write templating functions, or dom tree walking functions, I often need to distinguish between a host collection, an array or array like, and a string, to decide whether I simply pass the object on as is, or iterate over it. (is it singular or multiple in nature?)

The fact is, javascript libraries, and programmers will use any language construct they can grab a hold of to make reasonable decisions that work within the program their writing. I'm not sure what value there would be in TC-39 decreeing a pattern as blessed, because programmers are just as likely to ignore it and just use what works for them, today, right now, no matter how technically wrong it is.

There are far fewer Javascript library writers than Javascript programmers so I'm not convinced that it's too large an audience to talk to.

True.

However, the discussion here has revealed one interesting thing: Both ES3 and ES5 are inadequate for efficiently determining one particular property of an object that would be useful in the accuracy of these decisions. That is, it is difficult to determine whether an object has numeric properties or not without iterating over [length] elements. If there was some flag in an object that gets set on insertion of a numeric property, a flag which is observable in some way from user code, or something that gets observed by a built in isArrayLike, that would, I believe, close this gap. Does it make sense to try and unset the flag once it is set? I don't think that would be necessary.

This flag on insertion would again report that empty arrays are not array like, but I guess there's only one possible ordering of the elements of the empty set so it's moot.

You are correct, but I would like to take the opportunity to emphasize that I propose the flag only as a single piece of evidence, but not as the only piece of evidence for consideration in the isArrayLike decision.

# Garrett Smith (14 years ago)

On Mon, Dec 14, 2009 at 11:29 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/14 Garrett Smith <dhtmlkitchen at gmail.com>:

On Mon, Dec 14, 2009 at 9:34 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/13 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sun, Dec 13, 2009 at 10:31 AM, Mike Samuel <mikesamuel at gmail.com> wrote:

2009/12/12 Garrett Smith <dhtmlkitchen at gmail.com>:

On Sat, Dec 12, 2009 at 4:04 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Dec 12, 2009 at 3:38 PM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On Sat, Dec 12, 2009 at 2:59 PM, Mark S. Miller <erights at google.com> wrote:

Are we really this stuck? Can anyone think of a reliable, portable, and fast ES3R test that tests whether a property is enumerable, whether it is inherited or not?

Not stuck. Why do you care if |length| is enumerable?

If a standard |for| loop is used, it doesn't matter.  Why anyone would want to use |for in| for something that is arrayLike?

|for in| is not my concern. I wish a predicate that has little chance of false positives against legacy ES3 user constructed objects.

Why the need to distinguish between a user-defined object that is intended for iteration vs one that is not? The program should already know. If the needs of a program are to iterate over an object's

If programmers only wrote programs that would be true.  But they also write libraries for consumption by other programmers, and many come from languages that encourage duck-typing : if it enumerates keys like a duck ...

What well-designed piece of code has absolutely no idea what its input is?

Noone is talking about code that has absolutely no idea what its inputs are. Please see my mail in this thread from 10:10 PST of 8 Dec for examples of code that knows that an input is either an array-like collection or a map-style collection but that use different definitions of the former.

[...]

That the method accepts anything' and returns anything. If - it - is

Ok, so this goes back to your earlier strawman where you make the unsupported assertion that code that accepts anything is bad.

Did I write that a method that accepts anything is bad? I wrote:

| What well-designed piece of code has absolutely no idea what its input is?

However if you want to argue that a function that is expected to accept anything is going to be without problems, the examples you provided don't go very far for support that.

There is a reason I keep mentioning issue with IE's Host objects.

The type of problem that would be solved by code that would be using an isArrayLike method might also be solved by a way to create real array from an object (host or native). In order to compare that to isArrayLike, there should be a problem where isArrayLike is a good solution. The code examples posted were believed to be demonstration of a need for isArrayLike, without having shown usage pattern. The posted code examples do not look like a good solution for anything. I would strongly discourage any web standard from taking influence from such pieces of code.

By giving too much freedom with Host objects ECMAScript may be partly responsible for the allowance of this failure.

What

about the identity function, the cast operators defined in the standard (ToString, ToPrimitive, ToNumber, ToObject), operators like +, and some of your favorite array generics (Array.prototype.indexOf)?  The more fundamental the operator, the fewer assumptions it can make about its inputs.

I think I understood what you intended by "cast operators", though nonstandard terminology can be the source of confusion. I have no idea what you are thinking the Identity function is.

falsy, such as (false, null, undefined, 0, ""), then the inupt itself is returned.

That the author did not know this is an unsupported assertion and that this somehow makes the code bad is a non sequitur, but I'm not going to get sucked into a code purity pissing contest with you.

The code you posted has the comment:-

| If it walks like a duck and quacks like a duck, return true

  • so the author intends to return boolean, but instead returns 0, undefined, null, "". I would not be surprised if the author actually does not know what his own code does. We also have the typeof it == "array" test, and so the author doesn't know the possible results for typeof, either.

It is not a pissing contest; the code is far from being sufficient justification for any proposal. Most of the code in most of these libraries (Google closure, Dojo) is of poor quality.

Please explain what this has to do with any of the following questions: (1) Should TC39 render an opinion on what is array like? (2) If (1), what is array like?

What are you doing with it?

(3) If (!1), should future EcmaScript drafts define iteration order for arrays as index order and possibly recommend to array like host objects that the define iteration order similarly.

Why would you want that? So you can use for-in on a host object?

Garrett

# P T Withington (14 years ago)

On 2009-12-08, at 13:10, Mike Samuel wrote:

All true. And yet it is not uncommon. See the bottom of this email for a quick survey of a number of libraries' uses of the array-like concept.

FWIW, Here is the (separately/simultaneously invented) definition from OpenLaszlo:

function isArrayLike (obj:*):Boolean { if (obj && ((obj is Array) || (obj['length'] != (void 0)))) { var ol = obj.length; return ((typeof(ol) == 'number' || ol is Number) && ((ol|0) === ol) && (ol >= 0)); } return false; }

This only shows up in the debugger, and it is used to heuristicate when inspecting an object whether to portray it as an Array (possibly with non-numeric properties), or as an Object. It's an acknowledgement that users do create objects that they think of as arrays. OTOH, we don't do any magic beyond that. If a user intends an object to be treated as an array, they use Array operations on it (including for, not for in).

I once had the vain hope that I could say:

function MyArray () {} MyArray.prototype = [];

to create my own subclasses of Array. That might have lessened the need for isArrayLike.

# Jorge Chamorro (14 years ago)

On 15/12/2009, at 13:51, P T Withington wrote:

(...) I once had the vain hope that I could say:

function MyArray () {} MyArray.prototype = [];

to create my own subclasses of Array. That might have lessened the need for isArrayLike.

For that, we'd need an Array.create(). It might be a good thing... ¿?

# Brendan Eich (14 years ago)

On Dec 15, 2009, at 4:51 AM, P T Withington wrote:

On 2009-12-08, at 13:10, Mike Samuel wrote:

All true. And yet it is not uncommon. See the bottom of this email for a quick survey of a number of libraries' uses of the array-like concept.

FWIW, Here is the (separately/simultaneously invented) definition
from OpenLaszlo:

function isArrayLike (obj:*):Boolean { if (obj && ((obj is Array) || (obj['length'] != (void 0)))) {

ES4-ish "is" operator -- this must be LZX-JS?

 var ol = obj.length;
 return ((typeof(ol) == 'number' || ol is Number) &&
         ((ol|0) === ol) &&
         (ol >= 0));

Could combine the last two using >>>:

js> (-1|0)===-1

true js> (-1>>>0)===-1

false

} return false; }

This only shows up in the debugger, and it is used to heuristicate
when inspecting an object whether to portray it as an Array
(possibly with non-numeric properties), or as an Object. It's an
acknowledgement that users do create objects that they think of as
arrays. OTOH, we don't do any magic beyond that. If a user intends
an object to be treated as an array, they use Array operations on it
(including for, not for in).

I once had the vain hope that I could say:

function MyArray () {} MyArray.prototype = [];

to create my own subclasses of Array. That might have lessened the
need for isArrayLike.

If only internal methods were looked up by their (unnameable in the
language [1]) names, instead of being accessed via [[Class]] (which is
never delegated). Then lack of a custom [[Put]] in a (new MyArray)
would let the prototype [[Put]] shine through, but receive the (new
MyArray) instance as the object whose .length should be updated to be
one greater than the highest index.

But [[Class]] is a lightly abstracted vtable pointer; more's the pity.

/be

[1] strawman:names

# Brendan Eich (14 years ago)

On Dec 15, 2009, at 5:05 AM, Jorge Chamorro wrote:

On 15/12/2009, at 13:51, P T Withington wrote:

(...) I once had the vain hope that I could say:

function MyArray () {} MyArray.prototype = [];

to create my own subclasses of Array. That might have lessened the
need for isArrayLike.

For that, we'd need an Array.create(). It might be a good thing... ¿?

What would Array.create do?

Tucker is looking for a way to compose custom [[Put]] with prototype- based delegation. My reply suggested that if [[Put]] were a property
name, just like "length", and looked up along the prototype chain, so
that the default [[Put]] was in Object.prototype, the custom Array
[[Put]] that updates .length to be one greater than the highest index
were on Array.prototype (or on [] in Tucker's example, but in general
on the class prototype would suffice), etc., then we would not need
anything new like Array.create.

We would have one mechanism, prototype-based delegation, for finding
methods (internal or external), and a complementary private-name
scheme for generating and (implicitly, in the case of certain private
names built into the language) using names that can't be expressed
outside of an abstraction or module boundary.

That still seems winning to me, but I didn't implement anything like
it in 1995. It may have come up in ES1 days (I have vague memories),
but no one implemented it so it was a dark horse for standardization.

# Jorge Chamorro (14 years ago)

On 15/12/2009, at 16:21, Brendan Eich wrote:

On Dec 15, 2009, at 5:05 AM, Jorge Chamorro wrote:

On 15/12/2009, at 13:51, P T Withington wrote:

(...) I once had the vain hope that I could say:

function MyArray () {} MyArray.prototype = [];

to create my own subclasses of Array. That might have lessened the need for isArrayLike.

For that, we'd need an Array.create(). It might be a good thing... ¿?

What would Array.create do?

Array.create(prototypeObject) would return an [] instance whose prototype chain's first object is prototypeObject.

Currently, afaics, there's no way to do that (subclassing Array) not without resorting to .proto or manually extending instances via own properties/methods.

It's the same functionality provided by Object.create(), but for [] instead of {}.

Tucker is looking for a way to compose custom [[Put]] with prototype-based delegation. My reply suggested that if [[Put]] were a property name, just like "length", and looked up along the prototype chain, so that the default [[Put]] was in Object.prototype, the custom Array [[Put]] that updates .length to be one greater than the highest index were on Array.prototype (or on [] in Tucker's example, but in general on the class prototype would suffice), etc., then we would not need anything new like Array.create.

The way I see it, the subclass' prototype object ought to come -necessarily- before the Array.prototype object in the subclassed instance's prototype chain. If not, every other existing and future [] instances would be affected too. Also, because if not there would be no way to create two different [] subclasses.

If for some reason you wanted/needed to override/shadow any/some of Array.prototype's methods, isn't it that, in order to achieve that, the subclass' prototype object ought to come before the Array.prototype object in the instance's prototype chain ? That, or manually extending each and every instance with own properties ?

AFAICS, anything that comes -in the prototype chain- after Array.prototype would extend all the [] instances at once. But most likely I'm missing something...

We would have one mechanism, prototype-based delegation, for finding methods (internal or external), and a complementary private-name scheme for generating and (implicitly, in the case of certain private names built into the language) using names that can't be expressed outside of an abstraction or module boundary.

That still seems winning to me, but I didn't implement anything like it in 1995. It may have come up in ES1 days (I have vague memories), but no one implemented it so it was a dark horse for standardization.

Hmm. sorry, I don't quite follow.

# Brendan Eich (14 years ago)

On Dec 15, 2009, at 8:46 AM, Jorge Chamorro wrote:

It's the same functionality provided by Object.create(), but for []
instead of {}.

Ah, that helps -- thanks. This is not exactly what Tucker showed,
though.

Tucker is looking for a way to compose custom [[Put]] with
prototype-based delegation. My reply suggested that if [[Put]] were
a property name, just like "length", and looked up along the
prototype chain, so that the default [[Put]] was in
Object.prototype, the custom Array [[Put]] that updates .length to
be one greater than the highest index were on Array.prototype (or
on [] in Tucker's example, but in general on the class prototype
would suffice), etc., then we would not need anything new like
Array.create.

The way I see it, the subclass' prototype object ought to come - necessarily- before the Array.prototype object in the subclassed
instance's prototype chain. If not, every other existing and future
[] instances would be affected too. Also, because if not there would
be no way to create two different [] subclasses.

Sure. I was not describing Array.create or "subclasses", just
responding to what Tucker showed:

function MyArray () {} MyArray.prototype = [];

It may be that Array.create could fit his use-case instead, but if the
idea is to make new user-defined constructors such as MyArray, then
Array.create does not necessarily help. The user-defined constructor
would have to return a new Array.create(MyArray.prototype) instance,
and it would be stuck with the non-configurable "own" properties
defined on Array instances.

This might or might not be desired; anyway, it's not exactly the same
as what you proposed in reply.

If for some reason you wanted/needed to override/shadow any/some of
Array.prototype's methods, isn't it that, in order to achieve that,
the subclass' prototype object ought to come before the
Array.prototype object in the instance's prototype chain ?

I don't believe I wrote otherwise.

That, or manually extending each and every instance with own
properties ?

AFAICS, anything that comes -in the prototype chain- after
Array.prototype would extend all the [] instances at once. But most
likely I'm missing something...

My point was that if [[Put]], which is part of what makes an Array
instance "array-like", were delegated, along with the other internal
methods in ES5 (which are of course specification devices, not
concrete implementation artifacts), then what Tucker wrote -- exactly
what he wrote -- would "work".

The reason it doesn't work is evident from a bit more REPL citing:

js> function MyArray () {}

js> MyArray.prototype = []; [] js> var a = new MyArray

js> a.length

0 js> a[9] = "length should now be 10" "length should now be 10" js> a.length

0

What I proposed instead (not seriously; it's more a Self homage than
anything to add to a future JS/ES, at this point) was that setting
a[9] would look for [[Put]] in a, not find it, look in a. [[Prototype]], which is [] above, not find a custom [[Put]], then look
in [].[[Prototype]] which is Array.prototype, and finally find the ES- specified custom [[Put]] that advances length as needed.

When the Array [[Put]] was invoked, it would be passed a as the
directly referenced object, the receiver of the method call (aka | this|). Thus, 'length' would be set directly on a by the Array [[Put]]
internal method.

In ES specs and real implementations, internal methods and various
corresponding implementation hooks are called based on [[Class]] of
the directly referenced object, in contrast. No lookup for internal- method names that cannot be uttered in the source language is done.
But this ancient design decision means we have "host objects" that can
and do have inconsistent or even incorrect internal method suites.

# Mike Samuel (14 years ago)

2009/12/15 P T Withington <ptw at pobox.com>:

On 2009-12-08, at 13:10, Mike Samuel wrote:

All true.  And yet it is not uncommon.  See the bottom of this email for a quick survey of a number of libraries' uses of the array-like concept.

FWIW, Here is the (separately/simultaneously invented) definition from OpenLaszlo:

function isArrayLike (obj:*):Boolean {    if (obj && ((obj is Array) || (obj['length'] != (void 0)))) {

Please excuse my ignorance, but is "is" equivalent to "instanceof" and is the use of both obj['length'] and obj.length significant?

# David-Sarah Hopwood (14 years ago)

Brendan Eich wrote:

In ES specs and real implementations, internal methods and various corresponding implementation hooks are called based on [[Class]] of the directly referenced object, in contrast.

In ES specs, there's no indication that [[Class]] can or should be used for internal method lookup; I don't know where you got that idea.

As for implementation, [[Class]] could be derived from some other type tag that gives sufficient information to do such lookup, but [[Class]] by itself is not sufficient.

# P T Withington (14 years ago)

On 2009-12-15, at 13:59, Mike Samuel wrote:

2009/12/15 P T Withington <ptw at pobox.com>:

On 2009-12-08, at 13:10, Mike Samuel wrote:

All true. And yet it is not uncommon. See the bottom of this email for a quick survey of a number of libraries' uses of the array-like concept.

FWIW, Here is the (separately/simultaneously invented) definition from OpenLaszlo:

function isArrayLike (obj:*):Boolean { if (obj && ((obj is Array) || (obj['length'] != (void 0)))) {

Please excuse my ignorance, but is "is" equivalent to "instanceof" and is the use of both obj['length'] and obj.length significant?

As Brendan surmised, this is OpenLaszlo's variant on Javascript, inspired by both ES4 and AS3 (which is probably why it is not found by code search).

'is' is equivalent to 'instanceof' (but also operates on interfaces and mixins). obj['length'] is an idiom (used by our variant and AS3) to silence warnings (in debug mode) for referencing a non-existent property.

# Brendan Eich (14 years ago)

On Dec 15, 2009, at 11:18 AM, David-Sarah Hopwood wrote:

Brendan Eich wrote:

In ES specs and real implementations, internal methods and various corresponding implementation hooks are called based on [[Class]] of
the directly referenced object, in contrast.

In ES specs, there's no indication that [[Class]] can or should be
used for internal method lookup; I don't know where you got that idea.

Sorry, I wrote "called" where I meant "defined".

In the ES specs, [[Class]] and internal methods are matched in
invariant fashion, and the spec counts on this. ES1-3 throw TypeError
from certain built-in methods if the |this| parameter's [[Class]] is
not "RegExp", e.g. ES5 Array.isArray of course checks [[Class]]
directly.

ES doesn't use [[Class]] much other than for TypeErrors and
Object.prototype.toString, but it's aligned with the internal methods
intentionally. This was a topic in ES1 days, based on C++ and C
implementations.

As for implementation, [[Class]] could be derived from some other
type tag that gives sufficient information to do such lookup, but [[Class]] by itself is not sufficient.

I'm not sure what you mean. Sure, [[Class]] in the spec is string- valued, so it can't be a vtable pointer. But in implementations that
use C++, there is not only a class name-string associated with every
instance, but a suite of internal methods or hooks.

# David-Sarah Hopwood (14 years ago)

Brendan Eich wrote:

On Dec 15, 2009, at 11:18 AM, David-Sarah Hopwood wrote:

Brendan Eich wrote:

In ES specs and real implementations, internal methods and various corresponding implementation hooks are called based on [[Class]] of the directly referenced object, in contrast. [...] Sorry, I wrote "called" where I meant "defined".

As for implementation, [[Class]] could be derived from some other type tag that gives sufficient information to do such lookup, but [[Class]] by itself is not sufficient.

I'm not sure what you mean. Sure, [[Class]] in the spec is string-valued, so it can't be a vtable pointer. But in implementations that use C++, there is not only a class name-string associated with every instance, but a suite of internal methods or hooks.

Exactly: [[Class]] is associated with each instance and so are the other internal methods/properties, but that doesn't imply that other properties are "defined based on" [[Class]].

# Brendan Eich (14 years ago)

On Dec 15, 2009, at 12:32 PM, David-Sarah Hopwood wrote:

Exactly: [[Class]] is associated with each instance and so are the
other internal methods/properties, but that doesn't imply that other
properties are "defined based on" [[Class]].

The internal methods specified for built-in objects in the ES specs
are not just happenstance. That they correlate with [[Class]] as if
the latter were a nominal type id is no coincidence. You could say
[[Class]] was "defined based on" the internal methods, but that is as
backwards now as it was in the ES1 days when the spec was being
developed.

The point remains that host objects and the native objects specified
in Clause 15 act like nominal type instances. They do not delegate
[[Put]] or other internal methods to any object, instead they call
what is bound "in" the directly referenced instance. The idea of
delegated [[Put]] is not done in ES specs, in fact it is ruled out.
But delegating internal methods would allow composing array-ness via
prototyping, as far as I can see. It would also allow, e.g., an Object
instance to delegate to a Date prototype.

This is the road not taken, which opens the door for host object
madness. It is true that even with delegated "internal" methods, i.e.
no special nominal type-like binding of instances to "hooks", just
private method names, one could make an inconsistent suite of the meta- methods for a given object kind. But there would be only one object
system: prototype-based; instead of two: the hidden nominal type
system underlying builtins and host objects, and the pure-JS prototype- based object world.

Something more like Self, in other words. I still wonder if we can't
recover that lost form, enable prototype-based composition as Tucker
wanted, and banish host objects to the ninth circle of hell, in a
future edition.

# P T Withington (14 years ago)

On 2009-12-15, at 23:28, Brendan Eich wrote:

Something more like Self, in other words. I still wonder if we can't recover that lost form, enable prototype-based composition as Tucker wanted, and banish host objects to the ninth circle of hell, in a future edition.

We can dream! :)