JS 更新快,所以基于 JS 的 RN 语法更新也挺大。在阅读别人代码或项目开发时经常会碰到 EMACScript 新旧格式语法写的程序,给我们的理解造成困扰。所以总结了一些新旧 JS 语法的对比。
1. 模块
1.1 引用
ES5 中,通过 require 引入外部模块(相当于 C 中的 #include
1.2 导出单个类
ES5 中,通过 module.exports 导出某个类给别的模块用
var ES5Syntax = React.createClass({
...
});
module.exports = ES5Syntax;
ES6 中,通过 export default 导出单个类
export default class ES6Syntax extends Component {
...
};
2. 组件
2.1 定义组件
ES5 中,通过React.createClass来定义一个组件类
var ES5Syntax = React.createClass({
render: function() {
return (
<View style={styles.container}>
... // View 的子组件写在该处
</View>
<View /> // 没有子组件可以写为一行
);
},
});
ES6 中,通过定义一个继承自React.Component的class来定义一个组件类
export default class ES6Syntax extends Component {
render() {
return (
<View />
);
}
}
2.2 定义组件方法
ES5 中,组件方法定义为 functionName: function(param1, ...) {...},
,定义属性的标准写法,属性之间用逗号,
隔开,方法也是属性。
var ES5Syntax = React.createClass({
componentWillMount: function(){
},
render: function() {
return (
<View />
);
},
});
ES6 中,组件方法定义为 functionName(param1, ...) {...}
方法之间没有逗号分隔。
export default class ES6Syntax extends Component {
compoentWillMount() {
}
render() {
return (
<View />
);
}
}
2.3 定义组件的属性类型和默认属性
ES5 中,属性类型和默认属性分别通过 propTypes 成员和 getDefaultProps 方法来实现。由于 JS 是极弱类型语言,赋值的数据类型不同,变量的类型也会随之改变,比如初始为 number 类型的变量居然可以用 string 类型赋值。。。,所以就需要对传入或赋值的数据进行类型规范,便于数据有效性检查。注意:数据有效性检查只在开发时候有用。
var ES5Syntax = React.createClass({
getDefaultProps: function() {
return {
name: 'ES5Syntax',
year: 2014,
label: 'ClickMe',
};
},
propTypes: {
name: React.PropTypes.string.isRequired,
year: React.PropTypes.number.isRequired, // isRequired:本属性必须被包含,否则会产生 warning
label: React.PropTypes.string
},
render: function() {
return (
<View />
);
},
});
ES6+ 中,属性类型和属性默认值属于类本身的静态属性,所以可以用 static 成员来实现
export default class ES6Syntax extends Component {
static defaultProps = {
name: 'ES6Syntax',
year: 2015,
label: 'ClickMe',
}; // 注意这里有分号,用 `class` 定义的类内部的属性需要用分号 `;` 间隔开,方法不需要。
static propTypes = {
name: React.PropTypes.string.isRequired,
year: React.PropTypes.number.isRequired,
label: React.PropTypes.string.isRequired,
}; // 注意这里有分号
render: function() {
return (
<View />
);
},
});
2.4 初始化组件的 state
ES5 中,跟属性初始化类似
var ES5Syntax = React.createClass({
getInitialState: function() {
return {
esVersion: `${this.props.name} v1.0`,
clickCounts: 0,
};
},
});
ES6 中,有两种写法
export default class ES6Syntax extends Component {
state = {
esVersion: `${this.props.name} v1.0`,
clickCounts: 0,
};
});
在构造函数中对 state 进行初始化(推荐这种方法)
export default class ES6Syntax extends Component {
constructor(props) {
super(props);
// Operations usually carried out in componentWillMount go here
this.state = {
esVersion: `${this.props.name} v1.0`,
clickCounts: 0,
};
};
};
3. 将方法作为回调函数/胖箭头函数 =>
ES5 中,React.createClass 会把所有的方法都 bind 一遍,这样可以提交到任意的地方作为回调函数,而this不会变化。但官方现在逐步认为这反而是不标准、不易理解的。
var ES5Syntax = React.createClass({
buttonClick: function(e) {
// Here, 'this' refers to the component instance.
this.setState({clickCounts: this.state.clickCounts + 1});
},
render: function() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={this.buttonClick}>
<Text>{this.props.label}</Text>
</TouchableHighlight>
</View>
);
},
});
在ES6下,你需要通过bind来绑定this引用,或者使用箭头函数(它会绑定当前scope的this引用)来调用
// 方法1. 在 constructor 中手动绑定
export default class ES6Syntax extends Component {
constructor(props) {
super(props);
this.buttonClick = this.buttonClick.bind(this);
this.state = {
esVersion: `${this.props.name} v1.0`,
clickCounts: 0,
};
// Operations usually carried out in componentWillMount go here
};
buttonClick(e) {
this.setState({clickCounts: this.state.clickCounts + 1});
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight
onPress={this.buttonClick}>
<Text>{this.props.label}</Text>
</TouchableHighlight>
</View>
);
}
}
// 方法2. 利用箭头函数自动绑定
export default class ES6Syntax extends Component {
buttonClick = (e) => {
// Here, 'this' refers to the component instance.
this.setState({clickCounts: this.state.clickCounts + 1});
// this.state.clickCounts += 1; // state 必须用 this.setState() 修改
}; // 注意这里有分号
render() {
return (
<View style={styles.container}>
<TouchableHighlight
onPress={this.buttonClick}>
<Text>{this.props.label}</Text>
</TouchableHighlight>
</View>
);
}
}
箭头函数实际上是在这里定义了一个临时的函数,箭头函数的箭头 =>
之前是一个空括号、单个的参数名、或用括号括起的多个参数名,而箭头之后可以是一个表达式(作为函数的返回值),或者是用花括号括起的函数体(需要自行通过return来返回值,否则返回的是undefined)
4. Rest & Spread 运算符: ...
Rest 剩余参数运算符,在函数被调用时,剩余参数表示为一个数组名,该数组包含了那些没有对应形参的,长度不确定的剩余实参
Spread 展开运算符可以将一个可迭代的对象在函数调用的位置展开成为多个参数,或者在数组字面量中展开成多个数组元素.
Rest 将多个参数压缩为一个数组,Spread 是 Rest 的逆操作,将一个”数组内的元素”展开。
可以利用 Rest & Spread 运算符一次获取或者传递 N 个特定的 props
class CustomText extends Component {
render() {
var {
className,
...otherProps,
} = this.props; // ...otherProps: 获取除 className 外所有的其他的 props
return (
<Text {...otherProps}>{className}</Text> // 向 Text 传递所有otherProps 包含的 props
)
}
}
5. template strings
ES6 中引入了 template strings (模板字符串),允许嵌入表达式,并且支持多行字符串和字符串插补特性。模板字符串使用反引号 () 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${expression})的占位符,占位符内可以含有表达式,类比 swift 中的占位符 (expression)。 template strings 使得字符串操作更为方便、简洁。
ES5 中
esVersion: this.props.name + " v1.0",
ES6 中
esVersion: `${this.props.name} v1.0`,