1.基本语法回顾
我们先来回顾下箭头函数的基本语法。
var f = v => v
//等同于
var f = function (v){ return v;}
如果箭头函数不需要参数或者需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
//等同于
var f = function () {return 5};
var sum = (num1,num2) => num1 + num2;
//等同于
var sum = function(num1,num2) {
return num1 + num2;
}
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则就会报错
let getTempItem = id => {id : id, name : 'lcy' }; //报错
let getTempItem = id => ({id : id, name : 'lcy'}); //不报错
下面是一种特殊情况,虽然可以运行,但会得到错误的结果。
let foo = () => { a : 1};
foo() //undefined
上面代码中,原始意图是返回一个对象{a:1},但是由于引擎认为大括号是代码块,所以执行了一行代码a:1。这时,a可以被解释为语句的标签,因此实际执行的语句是1;然后函数就结束了,没有返回值。
关于作用域
箭头函数内定义的变量及其作用域
//常规写法
var greeting = () => {
let now = new Date()
return ("good" + ((now.getHours() > 17) ? "evening." : "day."))
}
greeting() // "good day."
console.log(now) //ReferenceError: now is not defined 标准的let作用域
//参数括号内定义的变量是局部变量(默认参数)
var preson = (now = new Date()) => "good" + (now.getHours() > 17 ? "evening." : "day.")
preson(); //good day.
console.log(now); // ReferenceError: now is not defined
//对比:函数体内{}不使用 var 定义的变量是全局变量
var preson = () => {now = new Date();
return ("good" + ((now.getHours() > 17) ? "evening." : "day."))
}
preson(); //"good day."
console.log(now); // Fri Dec 22 2017 10:01:00 GMT+0800 (中国标准时间)
// 对比:函数体内{}用var 定义的变量是局部变量
var preson = () => {
var now = new Data();
return ("good" +((now.getHours() > 17) ? "evenings" : "day."))
}
preson() //"good day."
console.log(now) //ReferenceError: now is not defined
2.关于this
2.1 默认绑定外层this
箭头函数没有this,所以需要通过查找作用域链来确定this的值。
这就意味着如果箭头函数被非箭头函数包含,this绑定的就是最近一层非箭头函数的this。
function foo(){
setTimeout(() => {
console.log(id,this.id);
},100);
}
var id = 21;
foo.call({id : 42}) //id:42
上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id:42})所以输入结果是42。
箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。
所以箭头函数转换成ES5的代码如下。
//es6
function foo(){
setTimeout(() => {console.log('id:',this.id);},100)
}
//es5
function foo(){
var _this = this;
setTimeout(function () {console.log('id:',_this.id)},100);
}
2.2 不能用call()、apply()、bind()方法修改里面的this
(function () {
return [
(() => this.x).bind({x:'inner'})() //无效的bind,最终this还是指向外层
]
}).call({x:'outer'}) //['outer']
上面代码中,箭头函数没有自己的arguments对象,这不一定是件坏事,因箭头函数可以访问外围函数的arguments对象:
3.箭头函数没有自己的arguments对象,这不一定是件坏事,因为箭头函数可以访问外围函数的arguments对象:
function constant(){
return () => arguments[0]
}
var result = constant(1);
console.log(result()); //1
那如果我们就是访问箭头函数的参数呢?
我们可以通过明明参数或者rest参数的形式访问参数:
let nums = (...nums) => nums;
4.不能通过new关键字调用
javascript函数有两个内部方法:[[Call]]和[[Construct]].
当通过new调出函数时,执行[Construct]方法,创建一个实例对象,然后再执行函数体,将this绑定到实例上。
当直接调用的时候,执行[[Call]]方法,直接执行函数体。
箭头函数并没有[[Construct]]方法,不能被用作构造函数,如果通过new的方式调用,会直接报错。
let foo1 = () => {};
let foo2 = new foo1(); //TypeError: foo1 is not a constructor
5.没有原型
由于不能使用new调用函数,所以也没有构建原型的需求,于是箭头函数也不存在prototype这个属性。
let foo = () => {};
console.log(foo.prototype); //undefined
不适用场景
第一个场景是定义函数的方法,且该方法内部包括this。
const cat = {lives:9,jumps:() => {this.lives--;}}
上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,这个方法内部的this指向cat;可是写成上面这样的箭头函数,使得this指向全局对象,因此不会得到预期的结果。
第二个场景是需要动态this的时候,也不应使用箭头函数。
var btn = document.getElementById('submit');
btn.addEventListener('click', () => {
this.classList.toggle('on');
})
上面代码运行时,点击按钮会报错,因为btn的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。
6.使用场景
下面这个是我们开发中经常遇到的代码,我们一般会通过this赋值给一个变量,然后再通过变量访问。
class Test{
constuctor(){
this.birth = 10;
}
submit(){
let self = this;
$.ajax({
type:"POST",
dataType:"json",
url:"xxxx",
data:"xxxxx",
success:function(result){
console.log(self.birth); //10
},
error:function(){}
})
}
}
let text = new Test()
text.submit() // undefined
这里我们就可以通过箭头函数来解决
.......
success:(result) => {
console.log(this.birth) //10
}
......
箭头函数在react中的运用场景
class foo extends Component{
constructor(props){
super(props)
}
handleClick(){
console.log('Click happened',this);
this.setState({a:1})
}
render(){
return <button onClick={this.handleClick}>点击我</button>
}
}
这里通过this.handleClick.bind(this)给函数绑定this。但是这样写起来有些麻烦,有没有简单的方法呢?这时候箭头函数就该出场了
class foo extends Component{
// Note: this syntax is experimental and not standardized yet.
handleClick = () => {
console.log('Click happened',this);
this.setState({a:1})
}
render(){
return <button onClick={this.handleClick}>点击我</button>
}
}
箭头函数中this的值是继承自身外围作用域,很好的解决了这个问题
除此之外我们还可以用箭头函数传参(这个不是必须的),而且会有性能问题。具体可参考这里:reactjs.org/docs/faq-fu…
const A = 65 // ASCII character code
class Alphabet extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
justClicked: null,
letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i)).
};
}
handleClick(letter) {
this.setState({ justClicked: letter });
}
render() {
return (
<div>
Just clicked: {this.state.justClicked}
<ul>
{this.state.letters.map(letter =>
<li key={letter} onClick={() => this.handleClick(letter)}>
{letter}
</li>
)}
</ul>
</div>
)
}
}
总结:
- 没有this
- 不能绑定arguments, 用rest参数 ... 解决
- 捕捉其所在的上下文的this值,作为自己的this
- 箭头函数没有原型属性
- 箭头函数不能当作Generator函数,不能使用yield关键字
箭头函数与传统的函数表达式有很大不同。它们的一些特性,在作为回调函数时非常好用。但是它们很难用做对象方法和构造函数。还有一些其他的区别,例如,箭头函数不能是生成器。可以看去MDN上看到更多细节。