箭头函数
ES6标准新增了一种新的函数:Arrow Function(箭头函数)。
为什么叫Arrow Function?因为它的定义用的就是一个箭头:
x => x * x
上面的箭头函数相当于:
function (x) {
return x * x;
}
箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ … }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ … }和return:
x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
如果参数不是一个,就需要用括号()括起来:
// 两个参数:
(x, y) => x * x + y * y
// 无参数:
() => 3.14
// 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}
如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:
// SyntaxError:
x => { foo: x }
因为和函数体的{ ... }有语法冲突,所以要改为:
// ok:
x => ({ foo: x })
箭头函数不仅仅是编写简洁代码的“捷径”。它还具有非常特殊且有用的特性。
JavaScript 充满了我们需要编写在其他地方执行的小函数的情况。
例如:
- arr.forEach(func) —— forEach 对每个数组元素都执行 func。
- setTimeout(func) —— func 由内建调度器执行。
……还有更多。JavaScript 的精髓在于创建一个函数并将其传递到某个地方。在这样的函数中,我们通常不想离开当前上下文。这就是箭头函数的主战场。
箭头函数特征
箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。箭头函数没有 this。如果访问 this,则会从外部获取。
【01】箭头函数没有 prototype (原型),所以箭头函数本身没有this
// 箭头函数
let a = () => {};
console.log(a.prototype); // undefined
// // 普通函数
function a() {};
console.log(a.prototype); // {constructor:f}
【02】箭头函数不会创建自己的this
箭头函数没有自己的this,箭头函数的this指向在定义(注意:是定义时,不是调用时)的时候继承自外层第一个普通函数的this。所以,箭头函数中 this 的指向在它被定义的时候就已经确定了,之后永远不会改变。
let group = {
title: "Our Group",
students: ["John", "Pete", "Alice"],
//定义的时候,外层第一个函数的this是window
kell:()=>{
console.log(this,'this1')//这里的this是window
},
showList() {
console.log(this,'this2'); //this就是group
this.students.forEach(
// console.log(this,'this2');
student => console.log(this,'this3')//这里的this也是group
);
}
};
group.showList();
group.kell();
【03】call | apply | bind 无法改变箭头函数中this的指向
call | apply | bind方法可以用来动态修改函数执行时this的指向,但由于箭头函数的this定义时就已经确定且永远不会改变。所以使用这些方法永远也改变不了箭头函数this的指向。
var id = 10;
let fun = () => {
console.log(this.id)
};
fun(); // 10
fun.call({ id: 20 }); // 10
fun.apply({ id: 20 }); // 10
fun.bind({ id: 20 })(); // 10
【04】箭头函数不能作为构造函数使用
我们先了解一下构造函数的new都做了些什么?简单来说,
分为四步:
① JS内部首先会先生成一个对象;
② 再把函数中的this指向该对象;
③ 然后执行构造函数中的语句;
④ 最终返回该对象实例。
但是!!因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new调用时会报错!
let Fun = (name, age) => {
this.name = name;
this.age = age;
};
// 报错
let p = new Fun('dingFY', 24);// Uncaught TypeError: Fun is not a constructor
【05】箭头函数不绑定arguments,取而代之用rest参数…代替arguments对象,来访问箭头函数的参数列表
// 普通函数
function A(a){
console.log(arguments);
}
A(1,2,3,4,5,8); // [1, 2, 3, 4, 5, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 箭头函数
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
// rest参数...
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
【06】箭头函数不能用作Generator函数,不能使用yield关键字
什么是 Generator 函数?
在Javascript中,一个函数一旦开始执行,就会运行到最后或遇到return时结束,运行期间不会有其它代码能够打断它,也不能从外部再传入值到函数体内
而Generator函数(生成器)的出现使得打破函数的完整运行成为了可能,其语法行为与传统函数完全不同
Generator函数是ES6提供的一种异步编程解决方案,形式上也是一个普通函数,但有几个显著的特征:
– function关键字与函数名之间有一个星号 “*” (推荐紧挨着function关键字)
– 函数体内使用 yield 表达式,定义不同的内部状态 (可以有多个yield)
– 直接调用 Generator函数并不会执行,也不会返回运行结果,而是返回一个遍历器对象(Iterator Object)
– 依次调用遍历器对象的next方法,遍历 Generator函数内部的每一个状态
{
// 传统函数
function foo() {
return 'hello world'
}
foo() // 'hello world',一旦调用立即执行
// Generator函数
function* generator() {
yield 'status one' // yield 表达式是暂停执行的标记
return 'hello world'
}
let iterator = generator() // 调用 Generator函数,函数并没有执行,返回的是一个Iterator对象
iterator.next() // {value: "status one", done: false},value 表示返回值,done 表示遍历还没有结束
iterator.next() // {value: "hello world", done: true},value 表示返回值,done 表示遍历结束
}
上面的代码中可以看到传统函数和Generator函数的运行是完全不同的,传统函数调用后立即执行并输出了返回值;Generator函数则没有执行而是返回一个Iterator对象,并通过调用Iterator对象的next方法来遍历,函数体内的执行看起来更像是“被人踢一脚才动一下”的感觉