google / traceur-compiler

Traceur is a JavaScript.next-to-JavaScript-of-today compiler
Apache License 2.0
8.18k stars 580 forks source link

Length enumerable on extended Arrays #372

Open rosshadden opened 10 years ago

rosshadden commented 10 years ago

I'm finding when I extend Array, the resulting class' length property is enumerable.

class Set extends Array {}

var a = new Array();
a.push(2, 4, 6, 8);

var s = new Set();
s.push(2, 4, 6, 8);

Object.keys(a); // ["0", "1", "2", "3"]
Object.keys(s); // ["0", "1", "2", "3", "length"]
rosshadden commented 10 years ago

cc @optikalefx

arv commented 10 years ago

This is because we do not do the @@create dance yet so the instance is not an array instance, therefore the [[Put]] done in push just adds a new property.

rosshadden commented 10 years ago

Is this possible when transpiling to ES5? Whenever I have tried subclassing Array myself (without traceur) I have always run into issues with length.

Are there any workarounds you suggest in getting length non-enumerable until one of us gets this implemented?

arv commented 10 years ago

There are workarounds, but it requires transpiling new expression. I have a patch for doing that somewhere but it slowed things down too much so I never submitted it.

rosshadden commented 10 years ago

In my situation I ended up using Object.defineProperty(this, "length", { enumerable: false }); in my constructor.

arv commented 10 years ago

That does not work correctly if you do s[2] = 2, s.length which should now return 2.

I just uploaded my branch to https://github.com/arv/traceur-compiler/compare/new-new-behavior. Unfortunately it makes make force 3x slower which is unacceptable but the following does work:

// Options: --experimental

class A extends Array {}

import {create} from '@reflect';

Array[create] = () => { return [] };

var a = new A();
a[2] = 2;
a.length
arv commented 10 years ago

I did some tweak to the runtime function and now it is only 70% slower :'(

yelvert commented 9 years ago

This is the only way I know to subclass array and other exotics.

class MyArray extends Array {
  constructor (...args) {
    let inst = new Array(...args)
    inst.__proto__ = MyArray.prototype
    return inst
  }
}
let array = new Array(1,2,3,4)
let myArray = new MyArray(1,2,3,4)
console.log(myArray, myArray[2], myArray.constructor.name)

This throws the following error with the experimental or memberVariables options Constructors of derived class must contain a super call when the memberVariables option is enabled, but works fine without those options.

arv commented 9 years ago

Your code is valid in ES6. I'm not sure how to combine this with the AtScript proposed feature to allow member variables. Member variables depends on super being called so that all super classes get their chance to add the instance properties.

@vicb