Static Properties in ES Class
问题
ES6 的 class 支持 static property 吗?
static property, 即类(class,在 JS 中即构造函数 constructor)上的静态属性,代码示例如下:
class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // Prints '42'
}
}
背景
ES6,更合适的说法是 ES2015,所支持的 class 语法如下(来自 babel - Learn ES2015):
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
其中有 instance property(通过在 constructor 中赋值), instance method 以及 static method 的示例,但唯独没有 static property 的示例。而到底这一特性是否支持,不同来源的说法不太一致。
理论上,使用已知的 ES2015 语法,可以通过对 class 定义 getter 实现 static property,写法如下:
class MyClass {
get myStaticProp() {
return 42
}
}
但是这么写很傻,我们只是需要一个简单的 property 而已。
这个问题之所以重要,就要说到 react。react 的 component 定义支持 ES5 及 ES6 两种方式:
// The ES5 way
var HelloMessage = React.createClass({
render: function () {
return <div>Hello {this.props.name}</div>;
}
})
// the ES6 way
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
但是在 ES6 的写法下,initialState
, defaultProps
以及 propTypes
的声明与 ES5 写法下有较大区别,官方文档见此,ES5 的版本是:
// The ES5 way
var Counter = React.createClass({
getDefaultProps: function() {
return {
initialCount: 0
};
},
getInitialState: function() {
return {
count: this.props.initialCount
};
},
propTypes: {
initialCount: React.PropTypes.number
}
});
在 ES6 写法下,initialState
要求通过在初始化的时候给 this.state
赋值实现,而 defaultProps
及 propTypes
需要通过在构造函数上定义同名属性(Counter.defaultProps
及 Counter.propTypes
)实现,具体是这样的:
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };
类的实现代码分割成了两块,很丑陋,这也正是我一度不愿意使用 ES6 的写法的原因。可是,如果 ES6 的 class 支持 static property 的定义的话,我们就可以写成这样:
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
static propTypes = {
initialCount: React.PropTypes.number
};
static defaultProps = {
initialCount: 0
};
}
看起来会好很多。
答案
static property 并不在 ES2015 范围以内。目前是 stage-1 的提案。将来如果会通过,大概也是 ES7(ES2016),而不是 ES2015 的一部分了。这个提案同时也包含了 instance property (实例属性,在提案中被称为 instance fields) 的部分,如下的写法会在 ClassWithInits 的构造函数执行的时候给实例添加属性 myProp,值为 42:
class ClassWithInits {
myProp = 42;
}
这个提案对应的 babel plugin 是 transform-class-properties,可以通过在 babel 中配置这个 plugin 或者配置 presets 中添加 stage-1
(包含了 plugin transform-class-properties)以支持这一特性。
最后,得到的经验是,得益于 babel 的语法转换功能,在新的开发模式下,在考察语法特性时,不需要考虑是否某一规范(ES6、ES7,...)的组成部分,对应规范是否已正式发布,各大浏览器是否支持,只需要考虑是否可以通过对应的转换插件支持,在自己的开发中使用即可。代码要转换,思维方式更要转换。
参考
- https://github.com/jeffmo/es-class-fields-and-static-properties
- http://www.2ality.com/2015/02/es6-classes-final.html
- https://docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit#heading=h.kfc1yc110kdz
- http://babeljs.io/docs/learn-es2015/
- http://babeljs.io/docs/plugins/preset-stage-1/
- http://babeljs.io/docs/plugins/transform-class-properties/
- http://babeljs.io/blog/2015/06/07/react-on-es6-plus/
- https://facebook.github.io/react/docs/reusable-components.html
- http://react-china.org/t/es6-react-createclass/3441/3
- http://bbs.reactnative.cn/topic/15/react-react-native-%E7%9A%84es5-es6%E5%86%99%E6%B3%95%E5%AF%B9%E7%85%A7%E8%A1%A8