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.typecan 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 forObject.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 calledObject.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 metato 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 callingObject.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.
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
Both comments and pings are currently closed.
My ECMAScript 7 wishlist的更多相关文章
- JavaScript周报#184
This week’s JavaScript news Read this issue on the Web | Issue Archive JavaScript Weekly Issue 184Ju ...
- ECMAScript 6 扫盲
ECMAScript 6 目前基本成为业界标准,它的普及速度比 ES5 要快很多,主要原因是现代浏览器对 ES6 的支持相当迅速,尤其是 Chrome 和 Firefox 浏览器,已经支持 ES6 中 ...
- ECMAScript 5
2009年12月,ECMAScript 5.02011年6月,ECMAscript 5.1版发布2015年6月,ECMAScript 6正式通过,成为国际标准ES6第一个版本 ES2015,发布于20 ...
- ECMAScript 6入门
预计在2014年底,ECMAScript 6将会正式发布,他的草案在13年3月份被冻结,后续提出新特性将会移至ECMASript 7中.目前还没有哪款浏览器实现了ES6的全部内容,兼容性最强的一款要数 ...
- Javascript与ECMAScript
我们经常习惯性认为Javascript就是ECMAScript,但其实不是这样的. ECMAScript是一种脚本在语法和语义上的标准. 主要包括:语法.类型.语句.关键字.保留字.操作符.对象. 它 ...
- ECMAScript 6 开篇准备
1前言 该系列文章均为学习阮一峰老师<ECMAScript 6 入门>一书的学习笔记.原著:http://es6.ruanyifeng.com/ 各大浏览器的最新版本,对ES6的支持可以查 ...
- ECMAScript 5中属性的特性值
这是<JavaScript高级程序设计(第三版)>第六章相关内容的总结. ECMAScript中有两种属性:数据属性和访问器属性.每种属性都有四个特性值. 数据属性的四个特性值: [[Co ...
- SharePoint 2013 Excel Services ECMAScript 示例之明日限行
前言:最近遇到一个“明日限行”的功能,北京的交通啊,这个不在今天讨论范围内,暂不吐槽,想想代码开发,还要写WebPart部署,很麻烦,而且部署服务器,需要领导审批,想绕过这个麻烦事儿,就想到客户端了, ...
- JavaScript异步编程(1)- ECMAScript 6的Promise对象
JavaScript的Callback机制深入人心.而ECMAScript的世界同样充斥的各种异步操作(异步IO.setTimeout等).异步和Callback的搭载很容易就衍生"回调金字 ...
随机推荐
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(44)-工作流设计-设计表单
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(44)-工作流设计-设计表单 系列目录 设计表单是比较复杂的一步,完成一个表单的设计其实很漫长,主要分为四 ...
- docker 实战---部署tomcat\weblogic集群 (二)
安装tomcat镜像 准备好须要的jdk tomcat等软件放到home文件夹以下 启动一个虚拟机 docker run -t -i -v /home:/opt/data --name mk_tom ...
- ios从相册:摄像头中获取视频
ios从相册/摄像头中获取视频 如何从相册中获取视频 使用的是一个和获取照片相同的类UIImagePickerController //相册中获取视频 - (IBAction)clickViedoOF ...
- 编写android的widget
以前对这个东西很感兴趣,因为确实方便,如今有时间了来做一个例子 首先要定义一个layout(widgetview.xml)和一个配置文件(widgetconfig.xml) <?xml vers ...
- [转]C#中yield用法
yield 关键字向编译器指示它所在的方法是迭代器块.编译器生成一个类来实现迭代器块中表示的行为.在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值.这是一个返回值, ...
- 15、SQL Server 触发器
SQL Server 触发器 触发器是一种特殊的存储过程,只有当试图用数据操作语言DML来修改数据时才会触发,DML包含对视图和表的增.删.改. 触发器分为DML触发器和DDL触发器,其中DML触发器 ...
- @ManyToMany中间表附加字段设计
在使用@ManyToMany时,若中间表只有相应的外键字段可以直接建立两个对应的Entity 设置ManyToMany @ManyToMany 两个表多对多关联 但若是中间表有自己的附加字段,这需要为 ...
- 关于c++中的引用
引用是个别名. 1.引用是否占用空间 引用是否占用空间,此处是指广义上的占用内存空间,即为该对象新开辟一块内存.这个需要分不同的情况. 首先看一下常引用(const 引用). 这里关于常引用在c++ ...
- Wireshark提示没有一个可以抓包的接口
这是由于win下默认NPF服务是关闭的,需要以管理员的身份开启这个服务 Windows上安装wireshark时,会遇到NPF驱动无法启动的情况,一般如果采用管理员的方式就可以正常启动,或者是将NPF ...
- OC - 23.核心动画基础
概述 简介 核心动画提供了一组非常强大的动画API,通过该组API可以高效的实现绝大部分绚丽的动画效果 注意事项 核心动画的操作在子线程中执行,不会阻塞主线程 核心动画直接作用与CALayer对象上, ...