My ECMAScript 7 wishlist

With ECMAScript 6 now feature complete, any further changes to the core of JavaScript will happen in ECMAScript 7. I’m pretty excited about the changes coming in ECMAScript 6 and there are already some great ECMAScript 7 features such as Object.observe() and asynchronous functions. While the development of ECMAScript 7 continues, I just wanted to share my personal wishlist of things that would make writing JavaScript even better and are (seemingly) within the scope of consideration for ECMAScript 7.

Some notes about the wishlist items:

  • I haven’t found a good source of already-scheduled ECMAScript 7 features, so I don’t know if any of these are already on the roadmap or not.
  • I don’t actually care what the names of things are, I just care about the functionality.
  • I’m no expert in syntax. It’s entirely possible I suggested something here that isn’t feasible.

Arrays

I recently came to realize that I spend an inordinate amount of time working with arrays in JavaScript, moreso than anything else. I’ve had a growing list of annoying things about working with arrays that have been partially solved in ECMAScript 5 and 6. However, there still seems to be some low-handing fruit.

Array.prototype.last(), Array.prototype.first()

The number of times I write something like items[items.length - 1] each week drives me crazy. I just want a last() method that does it for me. The native equivalent of this:

Array.prototype.last = function() {
    return this[this.length - 1];
};

While I check the last item of arrays frequently, I also check the first item frequently. So I’d love to have first() as well:

Array.prototype.first = function() {
    return this[0];
};

With these two methods, a lot of my code would look cleaner:

//before 
if (items[0] === "(" && items[items.length - 1] === ")") {
    // do something
}

// after
if (items.first() === "(" && items.last() === ")") {
    // do something
}

Array.prototype.isEmpty()

Another thing I do with arrays a lot is check to see if it’s empty by comparing the length to zero. I’d much rather have a method to improve readability. Something like this:

Array.prototype.isEmpty = function() {
    return this.length === 0;
}

Function.empty

I find myself using empty functions frequently, especially in tests and callback-oriented functions where I don’t actually care to wait for the results. That means I usually write things like:

someAsyncMethod(function() {
    // noop
});

The // noop comment is there to make sure people understand I intentionally left this function empty. I’d much rather there be a predefined empty function that I can reuse whenever I want a throwaway function, such as:

someAsyncMethod(Function.empty);

// where...
Object.defineProperty(Function, "empty", {
    value: () => {},
    writable: false,
    configurable: false,
    enumerable: true
};

Object.deepPreventExtensions(), Object.deepSeal(), Object.deepFreeze()

ECMAScript 5 added Object.preventExtensions(), Object.seal(), and Object.freeze(). These serve to protect objects from certain types of modification, which is fantastic, except that these are shallow operations. For instance:

var data = {
    subdata: {
        type: "js"
    }
};

Object.freeze(data);

data.subdata = {};   // fails silently in nonstrict mode

data.subdata.type = "css";   // succeeds

This is working as intended, data.subdata cannot be overwritten but data.subdata.type can be since Object.freeze() only freezes the properties of the object that is passed. In most cases, that’s okay, but I’ve found myself needing to apply object protection deeply, and it would be great to have official methods that did this.

My primary use case is in reading in a JSON configuration and wanting to protect it throughout the lifetime of the application. It’s possible to implement this fairly easily in ECMAScript 6:

Object.deepPreventExtensions = function(object) {

    // for avoiding circular references
    var handled = new WeakSet();

    // recursive function
    function deepPreventExtensions(object) {

        // handle first level
        Object.preventExtensions(object);
        handled.add(object);

        Object.keys(object).filter(function(key) {
            // get keys for objects not already handled
            return object[key] && (typeof object[key] === 'object') && !handled.has(object[key]);
        }).forEach(function(key) {
            Object.deepPreventExtensions(object[key]);
        });
    }

    deepPreventExtensions(object);
};

The only tricky part is handling circular references, but that is made somewhat easier by using a WeakSet to track already-handled objects. The same basic pattern can be applied for Object.deepSeal() and Object.deepFreeze().

Defensive objects

I recently wrote a post about defensive objects. As a refresher, defensive objects are those that throw an error when you try to read a property that doesn’t exist. This is the way objects work in type safe languages and is the last missing capability for accurately creating classes in JavaScript that behave as they would in other languages.

Today, you can get pretty close:

class Person {
    
    constructor(name) {
        this.name = name;
        Object.seal(this);
    }
}

Using the ECMAScript 6 class syntax plus Object.seal(), you’re able to create an object that can’t have its properties removed or new properties added. However, accessing a nonexistent property will still just return undefined:

var me = new Person("Nicholas");
console.log(me.nme);      // unfortunate typo, returns undefined

Because the property nme doesn’t exist, it returns undefined when you try to access it. I recently spent a half hour tracking down a bug that was a typo of this nature and wished I had a way to prevent it from happening.

Adding this behavior would bring object properties inline with variables in terms of what will happen when you try to access something that doesn’t exist. An error is thrown when you try to read an undeclared variable; I’d like that same behavior when you try to read an undeclared property.

I propose a method that is similar to Object.preventExtensions(), perhaps called Object.preventUndeclaredGet() (probably not the best name) that would set an internal property on an object changing the [[Get]] behavior to throw an error when the given property doesn’t exist. For example:

class Person {
    
    constructor(name) {
        this.name = name;
        Object.seal(this);
        Object.preventUndeclaredGet(this);
    }
}

var me = new Person("Nicholas");
console.log(me.name);  // "Nicholas"
console.log(me.nme);   // throws error

Adding this capability allows you to create classes that correctly mimic classes in other languages. Also, if you don’t seal the object, you can add new properties whenever you want; as long as you set the property value before reading it, no error will occur.

Custom descriptor attributes

Property descriptors seem like a great way to add meta information to properties except that you cannot add unknown properties. JavaScript always returns only the spec-defined attributes when you try to store a custom piece of information:

var me = {};
Object.defineProperty(me, "name", {
    value: "Nicholas"
    type: "string"
});

var descriptor = Object.getOwnPropertyDescriptor(me, "name");
console.log(descriptor.value);    // "Nicholas"
console.log(descriptor.type);     // "undefined"

To me, the property descriptor is a great possible location for storing information related to a particular property. Besides the implications for storing type hints, you could also store relevant information about validation, data bindings, or more.

It wouldn’t make sense to allow just any arbitrary attributes on the descriptor, as the language might need to add more in the future. However, adding a single property that is designed for custom information could work. For instance, what if the spec declared a property called meta to contain user-defined information. That meta would be stored and could later be retrieved exactly as-is, without the possibility of affecting the other property descriptor values or risk naming collisions with future property descriptor attributes. For example:

var me = {};
Object.defineProperty(me, "name", {
    value: "Nicholas"
    meta: {
        type: "string"
    }
});

var descriptor = Object.getOwnPropertyDescriptor(me, "name");
console.log(descriptor.value);     // "Nicholas"
console.log(descriptor.meta.type); // "string"

Lightweight traits

In many ways, JavaScript has supported traits for a long time through the use of mixins. Traits are really the same thing: objects that provide a set of methods intended to be applied to another object. The Object.assign() method was added in ECMAScript 6 to aid in this endeavor. However, it can get quite messy to use this approach:

var trait1 = {
    method1: function() {}
};

var trait2 = {
    method2: function() {}
};

function MyObject() {
    // ...
}

Object.assign(MyObject.prototype, trait1, trait2, {
    method3: function() {}
});

There’s no way to easily do the same thing with ECMAScript 6 classes, so you’d be stuck calling Object.assign() in the constructor and applying it to each instance.

What I’d like to propose is some syntactic sugar to make this easier using object literals and classes. For object literals, it would look like this:

function MyObject() {
    // ...
}

// lightweight traits
MyObject.prototype = {

    use trait1,
    use trait2,

    method3: function() {}
};

// desugars to
MyObject.prototype = Object.assign({}, trait1, trait2, {
    method3: function() {}
});

A similar syntax can be used in ECMAScript 6 classes to specify traits for the prototype:

class MyObject {
    use trait1;
    use trait2;

    constructor() {}

    method3() {}
}

// desugars to

function MyObject() {
    // ...
}

Object.assign(MyObject.prototype, trait1, trait2, {
    method3: function() {}
});

It’s entirely possible that Object.assign() should actually be something else, perhaps something that also calls toMethod() so the super binding is correct, but I think this example illustrates my point.

Conclusion

I’m very excited to see where ECMAScript 7 is headed and hope that some of these ideas are worthwhile enough to pursue. Even if they aren’t, ECMAScript 6 is such a superior upgrade from ECMAScript 5 that I’m sure ECMAScript 7 will be a really great set of changes as well.

Comments

  1. Kyle Simpson

    For `Function.empty`, I believe you can already use (since way way back) `Function.prototype`. :)

  2. David Bruant

    Object.deepFreeze() => YES!

    re defensive objects
    "I recently spent a half hour tracking down a bug that was a typo of this nature and wished I had a way to prevent it from happening."
    => I'm sorry for repeating what I wrote in your previous blogpost, but TypeScript does prevents this type of bugs entirely. No need for an ES7 feature in that direction.
    And TypeScript can be used without buying into any TypeScript-specific feature since TypeScript is a superset of JavaScript. If you want, you can decide to only run the compiler with a watcher just for the type checking (ignoring the output).

    Custom descriptor attributes
    => From what I understand, it will never happen for regular objects (since that would break existing code strictly expecting the current behavior). It's likely to be possible for Proxies (it was sure until recently and came back to flux, but I think it's still very likely)

    Lightweight traits => YES <3

  3. Nicholas C. Zakas

    @David - no worries, we'll just have to agree to disagree on defensive objects. If an undeclared variable throws an error when used, I don't see why undeclared properties can't do the same.

    Bummer about property descriptors if it's true. Such a missed opportunity.

  4. Nicholas C. Zakas

    @Kyle - a little too magic looking for my taste. I can see that being easily misunderstood without a comment.

  5. Gil

    I think Array.prototype.firs should be like this:


    Array.prototype.first = function() {
    return this[0];
    };

    Great post, I fully agree.

  6. Gil

    Oh sorry, I've just seen you already fix it :)

  7. thorn

    As for arrays, what about Array.prototype.remove to remove elements by value? I hate writing indexOf and splice every time.

  8. Joan

    var data={subdata:{get type(){return "js"}}};
    Object.freeze(data);
    Read-only properties would work.

  9. Taron

    I would add a few things
    a proper way to know variable type, the typeof returns "object" for all type of Object, the instanceof is good, but it can be used only on checking case and you should know witch which you should compare fro example if(someVar instance of CustomType).
    It will be great to have some method defined on Object for example Object.getType and we can know type of any objects

    Object.getType(someVar) //returns "CustomType"

    or this method can be defined on object level like toString for example

    somVar.getType() //returns "CustomType"

  10. caridy

    Traits for classes, and mixins are a big pain today with the ES6 classes. `Decorators` seems to fit that bill, although it is in a very early stage, there was a deep dive on the preliminar API during TC39 april meetup, for some reason I can't find the link to Yehuda's presentation, but here is the link for the notes:
    https://github.com/rwaldron...

  11. Kevin Reed

    I would like to see Array.prototype.first() implemented like .First() in .NET's LINQ where it optionally accepts a filter function.

    This would return the first item that satisfies the condition.
    arr.first(function (item) { return item.prop === 'value'; });

    This would be different from Array.prototype.filter() in that it only returns one item from the array.

  12. Nicholas C. Zakas

    @Caridy - I know they're a pain, hence my wishlist item. :) Decorators don't solve the same problem (though they do solve the arbitrary metadata for properties problem).

  13. Nicholas C. Zakas

    @Kevin - I really like that idea!

  14. Cameron Knight

    @Kevin: That's ES6's `Array.prototype.find` (and also `Array.prototype.findIndex`)

  15. Kyle Simpson

    > a little too magic looking for my taste. I can see that being easily misunderstood without a comment.

    `Function.prototype` is already built into the language, and it is quite literally an empty do-nothing function that's there just to hold "inheritable" properties for all other functions. If you're looking for a built-in empty function, it's already there, we don't need another one.

    If you were really concerned about the name "prototype" being confusing, just do this:

    if (!Function.empty) {
    Function.empty = Function.prototype;
    }

    Bam!

  16. Rock

    > Array.prototype.last(), Array.prototype.first()
    That about second, third, penult and else? Add new functions? :) Be better add .get and .set like Map / WeakMap with support negative index:

    [1, 2, 3].get(0); // => 1
    [1, 2, 3].get(-1); // => 3
    [1, 2, 3, 4]
    .set(1, 'a')
    .set(-2, 'b'); // => [1, 'a', 'b', 4]

    and add support negative index to String.prototype.at proposal.

    > Function.empty
    Use Function.prototype.

    > Object.deepPreventExtensions(), Object.deepSeal(), Object.deepFreeze()
    NO! We've had enough of shallow versions of these ugly features.

    > Lightweight traits
    +

    @Kevin
    > This would return the first item that satisfies the condition.
    Google ES6 Array.prototype.find.

  17. Monthy Python

    @Nicholas:
    1) Thank you for the great post. Excellent discussion starter.
    2) Is there a public open forum to debate/vote on ES7 outside of members of TC39? I remember Angus Croll had started JSFfixed http://javascriptweblog.wor..., but it seems to have died off: https://twitter.com/jsfixed last tweet on June 7th, 2012. If not, would you start one?

    @Kyle:
    Debatable. But you have a good point about empty/Function.prototype

    @Kevin:
    +1 for first with filter. Let's also have last with a filter function!

    @Taron:
    +1 for getType

  18. Emil

    I have a very simple wishlist for ES7.

    Don't rush it.
    We have barely gotten ES6 yet, and that will be a major shift. All the things you can do today, do them and prove that they are popular. Thats when they should go in the language specc. (im looking at Array.prototype.first/last, just use them today).

    Traits syntax, that would be nice. But I think that a simple function, with no magic is a much better way to find out what a trait is, for most devs. Again wait until there is a real world requests it.

    (I would like generic monads. To solve much of the same problems that promises, generators and iterables solve, but im not suggesting it should be added)

    Let's wait and see which patterns we need, and add only them.

  19. Ray Bellis

    To go along with the "filter" variant of `.first`, how about `Function.true` and `Function.false`? (Yes, I know the latter could just be an alias for `Boolean`.

  20. Paolo

    A shortcut for lambdas, please. Endless occurrences of function(...) pollute Js code with noise as much as $this-> and array() pollute PHP. Use a -> as other languages do, or copy Ruby block passing. Anything would be an improvement.

  21. Nicholas C. Zakas

    @Kyle - I don't think passing Function.prototype into arbitrary places is safe. Who knows if the consumer is modifying the empty function in some way?

  22. Nicholas C. Zakas

    @Monty - no such mechanism exists.

  23. Nicholas C. Zakas

    @Emil - people are already using traits, that's exactly what mixins are. Adding some sugar would just make what people are already doing easier.

  24. Nicholas C. Zakas

    @Paolo - arrow functions are already in ECMAScript 6.

  25. Kirill Sukhomlin

    Array.prototype.last
    I use a.slice(-1)[0] for such cases, but descriptive methods are better.
    Actually, one can have subclassable built-ins in es6 and create custom collections based on Array.

    Function.empty
    +1 to Kyle Simpson.
    Some frameworks, namely ExtJS use such approach.

    Defensive objects
    There's already a const classes proposal. I'm afraid the ultimate solution to such problems is introducing types, though (Hello, TypeScript!).

    Traits
    Also was discussed somewhere on net. Currently one can do Object.assign() for Object's prototype. Cause es6 classes are nothing more than syntax sugar to common prototype inheritance.

  26. Tony Brown

    I just wanted to say thanks for all your work Nicolas, you are an inspiration to me. I have not had any formal training in CS, don't know any Java or C++ so learning JavaScript as a programming language has been a long journey, one I could not imagine without your books and talks.

    I pray in the name of Jesus that you will not suffer from lymes disease nor any other infirmities. ( I just read that post, and the comments are off).

    Be blessed

Understanding JavaScript Promises E-book Cover

Demystify JavaScript promises with the e-book that explains not just concepts, but also real-world uses of promises.

Download the Free E-book!

The community edition of Understanding JavaScript Promises is a free download that arrives in minutes.