箭头函数

function函数都有个length属性表示函数形参的长度。

  1. function foo(a, b){}
  2. console.log(foo.length); // 2

但是当函数的形参有默认值的时候,length属性也会受到影响(只会计算默认值之前的长度)

  1. function test(a, b, c = 1, d, e, f) {}
  2. test(1, 2, 3);
  3. console.log(test.length); // 2

另外当函数存在默认值的时候,arguments的映射关系也将不存在。

  1. // 函数不存在默认值
  2. function foo(a, b, c, d, e, f) {
  3. arguments[0] = 10;
  4. b = 20;
  5. console.log(arguments); // [10, 20, 3]
  6. }
  7. foo(1, 2, 3);
  8. // 函数存在默认值
  9. function test(a, b, c = 30, d, e, f) {
  10. arguments[0] = 10;
  11. b = 20;
  12. console.log(arguments); // [10, 2, 3]
  13. }
  14. test(1, 2, 3);

函数参数的解构:

  1. function foo({ x, y = 5 }) {
  2. console.log(x, y);
  3. }
  4. foo({}); // undefind 5
  5. foo({ x: 1 }); // 1 5
  6. foo({ x: 1, y: 2 }); // 1 2
  7. foo(); // 报错,默认不匹配, 可以以这样的方式赋值默认值 function foo({ x, y = 5 } = {})

接下来就要来聊聊ES6新增语法「箭头函数表达式」: :::info 语法:() => {} ::: 可以看到基本语法只有三个部分组成,实际上还可以更简洁。

当箭头函数只有一个参数的时候()可以省略:

  1. let foo = num => { console.log(num); }
  2. foo(100);

当箭头函数内执行语句只有一行的时候,{}也可以省略:

  1. let foo = num => console.log(num);
  2. foo(100);

箭头函数的{}还具有return的作用:

  1. let foo = num => num; // 默认就是 return num;
  2. console.log(foo(100)); // 100
  3. // 和普通 function 一样,如果不指定返回的内容默认返回 undefind
  4. let foo2 = num => {};
  5. console.log(foo2(100)); // undefind

箭头函数的参数也可以进行解构:

  1. var foo = ({ x, y } = {}) => x + y;
  2. console.log(foo({ x: 1, y: 2 })); // 3

利用箭头函数可以更简洁的写出我们的需求:

  1. // ES5
  2. var arr = [1, 2, 3, 4, 5];
  3. arr.sort(function(a,b){ return b - a });
  4. console.log(arr); // [5, 4, 3, 2, 1]
  5. // ES6
  6. var arr = [1, 2, 3, 4, 5];
  7. arr.sort((a, b) => b - a);
  8. console.log(arr); // [5, 4, 3, 2, 1]

箭头函数中不存在**arguments**

  1. var sum = (a, b) => {
  2. console.log(arguments); // arguments is not defined
  3. return a + b;
  4. };
  5. sum(1, 2);

箭头函数的本质

1、箭头函数本质上不是用function关键字进行定义的,而是使用胖箭头=>函数,所以箭头函数的**this**指向是根据定义时外层函数的作用域来决定的。

  1. function foo() {
  2. console.log(this); // { name: "obj1" }
  3. return (a) => {
  4. // 箭头函数在定义的时候 this 指向外层作用域的 this
  5. // this 指向外层的 this,也就表示 .call 无效
  6. console.log(this); // { name: "obj1" }
  7. };
  8. }
  9. var obj1 = { name: "obj1" };
  10. var obj2 = { name: "obj2" };
  11. var bar = foo.call(obj1);
  12. bar.call(obj2);
  1. const person = {
  2. eat() {
  3. console.log(this); // person{}
  4. },
  5. drink: () => {
  6. // 箭头函数的 this 指向定义时外层作用域的 this
  7. console.log(this); // window{}
  8. },
  9. };
  10. person.eat();
  11. person.drink();

箭头函数中并没有this的机制,箭头函数的this值固化的,所以就导致函数内部的this就是外部的this(也就是定义时候的this),既然没有this指向,所以不能当作构造函数使用、不能使用 bindappplycall都无法使用

  1. function foo() {
  2. return () => {
  3. return () => {
  4. return () => {
  5. console.log(this);
  6. };
  7. };
  8. };
  9. }
  10. var f = foo.call({ id: 1 });
  11. var f1 = f.call({ id: 2 })()(); // { id: 1 }
  12. var f2 = f().call({ id: 3 })(); // { id: 1 }
  13. var f3 = f()().call({ id: 4 }); // { id: 1 }

2、箭头函数不能作为构造函数使用
同样因为箭头函数内部没有this所以箭头函数不能进行实例化。

  1. let Foo = ()=>{};
  2. let foo1 = new Foo(); // Foo is not a constructor

3、箭头函数没有arguments
这个特点我们上面也说过了,可以使用...rest运算符来获取实参

  1. var test = () => {
  2. console.log(arguments); // Uncaught ReferenceError: arguments is not defined
  3. };
  4. test()

但是箭头函数可以获取到外层作用域的arguments

  1. function foo() {
  2. setTimeout(() => {
  3. // 闭包
  4. console.log(arguments); // [1, 2, 3],foo 函数的 arguments
  5. }, 1000);
  6. }
  7. foo(1, 2, 3);

4、yield命令在胖箭头函数不能生效

箭头函数使用场景的总结: 1、简单的函数表达式,得出唯一的return计算值,函数内部没有this的引用,且没有递归、事件绑定、借绑定的时候,用箭头函数比较合适 2、内层函数表达式需要调用this,需要确保适当的this指向的时候。 例如setTimeoutoBtn.addEventlisntener("click", ()=>{ this.getData() }, false) 3、需要将arguments对象转换为数组的时候。 ES5的写法:var args = Array.prototype.slice.call(arguments)

…rest 运算符

因为箭头函数没有arguments,所以使用箭头的时候想要获取所有的实参需要使用...rest运算符来获取 :::info ...rest具有拓展和收集的功能。
拓展:用于将一个数组或类数组对象转换为逗号分隔的值序列。
收集:用于将以逗号分隔的值序列转换为数组。 :::

使用...rest来收集参数:

  1. var sum = (...args) => {
  2. console.log(args); // [1, 2] 返回的是一个真正的数组,而不是类数组
  3. };
  4. sum(1, 2);

...rest运算符将数组数据进行拓展:

  1. function foo(x, y, z) {
  2. console.log(x, y, z); // 1, 2, 3
  3. console.log(arguments); // 1, 2, 3, 4, 5, 6
  4. }
  5. foo(...[1, 2, 3, 4, 5, 6]);
  6. foo.apply(null, [1, 2, 3, 4, 5, 6]); // ES5 的时候需要用 apply 方法

...rest还可以用来数组合并:

  1. let a = [2, 3, 4];
  2. let b = [1, ...a, 5];
  3. console.log(b); // [1, 2, 3, 4, 5]

当函数使用...rest收集参数的时候,只能放到()的最后,否则就会报错:

  1. function foo(a, b, ...c) {
  2. console.log(a, b, c); // 1 2 [3, 4, 5, 6]
  3. }
  4. foo(1, 2, 3, 4, 5, 6);

和函数默认值一样,当函数使用...rest后函数的length属性将会受到影响:

  1. console.log(function (a, b) {}.length); // 2
  2. console.log(function (a, b, ...c) {}.length); // 2

如果详细来说**rest**运算符是将函数的参数进行收集,而拓展运算符是将数据进行展开。
数组拓展运算符:ES6 入门教程
对象拓展运算符:ES6 入门教程