箭头函数你想知道的都在这里

51次阅读

共计 4170 个字符,预计需要花费 11 分钟才能阅读完成。

1、基本语法回顾
我们先来回顾下箭头函数的基本语法。ES6 增加了箭头函数:
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: “Temp”};

// 不报错
let getTempItem = id => ({id: id, name: “Temp”});
下面是一种特殊情况,虽然可以运行,但会得到错误的结果。
let foo = () => { a: 1};
foo() // undefined
上面代码中,原始意图是返回一个对象{a: 1},但是由于引擎认为大括号是代码块,所以执行了一行语句 a: 1。这时,a 可以被解释为语句的标签,因此实际执行的语句是 1;,然后函数就结束了,没有返回值。
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’]
上面代码中,箭头函数没有自己的 this,所以 bind 方法无效,内部的 this 指向外部的 this。
3、没有 arguments
箭头函数没有自己的 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 的方式调用,会报错。
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
5、没有原型
由于不能使用 new 调用箭头函数,所以也没有构建原型的需求,于是箭头函数也不存在 prototype 这个属性。
var Foo = () => {};
console.log(Foo.prototype); // undefined
5、不适用场合
第一个场合是定义函数的方法,且该方法内部包括 this。
const cat = {
lives: 9,
jumps: () => {
this.lives–;
}
}
上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用 cat.jumps()时,如果是普通函数,该方法内部的 this 指向 cat;如果写成上面那样的箭头函数,使得 this 指向全局对象,因此不会得到预期结果。第二个场合是需要动态 this 的时候,也不应使用箭头函数。
var button = document.getElementById(‘press’);
button.addEventListener(‘click’, () => {
this.classList.toggle(‘on’);
});
上面代码运行时,点击按钮会报错,因为 button 的监听函数是一个箭头函数,导致里面的 this 就是全局对象。如果改成普通函数,this 就会动态指向被点击的按钮对象。
6、使用场景
下面这个是我们开发经常遇到的。我们一般会通过 this 赋值给一个变量,然后再通过变量访问。
class Test {
constructor() {
this.birth = 10;
}
submit(){
let self = this;
$.ajax({
type: “POST”,
dataType: “json”,
url: “xxxxx” ,//url
data: “xxxxx”,
success: function (result) {
console.log(self.birth);//10
},
error : function() {}
});
}
}
let test = new Test();
test.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}>Click Me</button>;
}
}
在 react 中我们这样直接调用方法是有问题的,在 handleClick 函数中的 this 是有问题,我们平时需要这么做
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(‘Click happened’, this);
this.setState({a: 1});
}
render() {
return <button onClick={this.handleClick}>Click Me</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}>Click Me</button>;
}
}
箭头函数中 this 的值是继承自 外围作用域, 很好的解决了这个问题。除此之外我们还可以用箭头函数传参(这个不是必须的),而且会有性能问题。更多信息请查看
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>
)
}
}
最后更多系列文章请看

ES6 学习(一)之 var、let、const
ES6 学习(二)之解构赋值及其原理
ES6 学习(三)之 Set 的模拟实现
ES6 学习(四)之 Promise 的模拟实现

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star 对作者也是一种鼓励。

正文完
 0