mathiasbynens / Array.from

A robust & optimized ES3-compatible polyfill for the `Array.from` method in ECMAScript 6.

Home Page:https://mths.be/array-from

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support iterators

mathiasbynens opened this issue · comments

Since these polyfills were initially meant for ES5 and ES3 environments I didn’t implement support for ES6 iterators – any engines that support iterators probably support Array.from as well, right?

Well, just in case there is one that won’t, it would be nice to support iterators anyway, if only for this use case:

Array.from('foo\uD834\uDF06bar') // assuming ES6 StringIterator support
// [ 'f', 'o', 'o', '\uD834\uDF06', 'b', 'a', 'r' ]

Tests:

function makeIterator(array) {
    var nextIndex = 0;
    return {
        'next': function() {
            if (nextIndex < array.length) {
                return {
                    'value': array[nextIndex++],
                    'done': false
                };
            }
            return {
                'done': true
            };
        }
    };
}

var codePoints = ['a', '\uD834\uDF06', 'b'];
assertDeepEquals(Array.from(makeIterator(codePoints)), codePoints);
assertDeepEquals(Array.from.call(null, makeIterator(codePoints)), codePoints);
assertDeepEquals(Array.from.apply(null, [makeIterator(codePoints)]), codePoints);

We should absolutely add support for strings, with or without iterator support. (Edit: we already do have support for strings :-) )

However, until there exists an implementation with iterators, and without Array.from, we probably shouldn't try to shim that here.

However, until there exists an implementation with iterators, and without Array.from, we probably shouldn't try to shim that here.

Why not? Not shimming it means the polyfill is not future-compatible.

Why would we want to create any shim code for a scenario where it could never possibly be useful?

As it stands, Array.from('a\uD834\uDF06b') gives a different result depending on whether the environment supports StringIterator or not. This might break code.

If this polyfill supported iterators, the user could at least load a polyfill for the iterator they need (e.g. StringIterator) and then have it work the same way it would in a proper ES6 environment.

Ah, I hadn't thought about astral characters/code points. Fair enough :-)

One use use case is http://nwjs.io it already has ES6 Set and Map but it does not have Array.from

+1 -- I came here because wanted a polyfill that supported Array.from(set) in nodejs 0.12. Any chance of that here?

The Array.from implementation in https://github.com/paulmillr/es6-shim supports it just fine, but I'd love to see this one support it as well.

until there exists an implementation with iterators, and without Array.from, we probably shouldn't try to shim that here.

node 0.11.16 --harmony provides your use case

The --harmony flag is for unstable, likely broken features, and shouldn't ever be used in a production environment - while we could support it, it's only necessary to support unflagged, stock environments.

I notice about Array.from(Iterator/MapIterator) in someMapInstance.values()
someMapInstance.values() is MapIterator

And the problem in using angular2 in es5 is that angular2 use Array.from( someMapInstance.values() ) to get the array result
so i need to get the result like this

var from = function(arrayLike) {
    var C = this;
    if (arrayLike == null) {
        throw new TypeError('`Array.from` requires an array-like object, not `null` or `undefined`');
    }
    /**********************start hack code*********************/
    if(typeof arrayLike === 'object' && (arrayLike.toString() === '[object Map Iterator]' || arrayLike.toString() === '[object Iterator]'))
    {
        var A = new Array();
        var item;
        while( (item = arrayLike.next()).done === false ){
            A.push(item.value);
        }
        return A;
    }
    /**********************end hack code***********************/

    var items = Object(arrayLike);
    var mapping = arguments.length > 1;

    var mapFn, T;
    if (arguments.length > 1) {
        mapFn = arguments[1];
        if (!isCallable(mapFn)) {
            throw new TypeError('When provided, the second argument to `Array.from` must be a function');
        }
        if (arguments.length > 2) {
            T = arguments[2];
        }
    }

    var len = toLength(items.length);
    var A = isCallable(C) ? Object(new C(len)) : new Array(len);
    var k = 0;
    var kValue, mappedValue;
    while (k < len) {
        kValue = items[k];
        if (mapFn) {
            mappedValue = typeof T == 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
        } else {
            mappedValue = kValue;
        }
        defineProperty(A, k, {
            'value': mappedValue,
            'configurable': true,
            'enumerable': true,
            'writable': true
        });
        ++k;
    }
    A.length = len;
    return A;
};
defineProperty(Array, 'from', {
    'value': from,
    'configurable': true,
    'writable': true
});

after i modify this, my angular2 DI sample works

import {Inject, Injector, bind} from 'angular2/di'

class Engine {
    start(){
        return 'engine start';
    }
}

class Car {
    public engine;
    constructor(@Inject(Engine) engine) {
        this.engine = engine;
    }

    drive(){
        return this.engine.start() + '...' + 'begin drive...';
    }
}

var inj = Injector.resolveAndCreate([
    bind(Car).toClass(Car),
    bind(Engine).toClass(Engine)
]);
var car = inj.get(Car);
console.log(car.drive());

there are result in my console, aha

engine start...begin drive...

so, can you support Iterator?

The --harmony flag is for unstable, likely broken features, and shouldn't ever be used in a production environment

I work at a pretty well respected company and we're starting to use it in production. We're not alone https://github.com/search?q=node+--harmony&ref=searchresults&type=Code&utf8=%E2%9C%93 (angular jumps out at me as a fellow early adopter, but I'm sure if you clicked 'next' a few times you'd find some other familiar names... and remember this is only public repos.)

I actually ran into the same problem, trying to get all keys from a Map (node 0.12.2). Also, as an additional use case, if you're using a polyfill for BOTH Map and Array.from, it would be really neat if it all worked the way it should.

@wheresrhys the appeal to authority notwithstanding, it's still a terrible idea. Also, using node 0.11, which doesn't get security updates, is a horrible idea also.

How about this for string when it doesn't support Symbol?

> 'foo\uD834\uDF06bar'.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g)
["f", "o", "o", "𝌆", "b", "a", "r"]

@falsandtru That would skip lone surrogates.

Then it would be able to support by /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF]|[^\uD800-\uDFFF]/g.

Closed with #27.

v1.1.0 is now released which should contain all the latest fixes, and will iterate properly in every version of every engine.