array like objects

Mike Samuel mikesamuel at gmail.com
Tue Dec 8 11:38:36 PST 2009


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;


>
>> 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.
>
> /be
>
>>
>>
>>> --
>>> 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 (
>>
>> http://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 (
>>
>> http://www.google.com/codesearch?q=goog.isArrayLike+package%3Ahttp%3A%2F%2Fclosure-library%5C.googlecode%5C.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;
>>        }
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>
>


More information about the es-discuss mailing list