箭头函数
function
函数都有个length
属性表示函数形参的长度。
function foo(a, b){}
console.log(foo.length); // 2
但是当函数的形参有默认值的时候,length
属性也会受到影响(只会计算默认值之前的长度)
function test(a, b, c = 1, d, e, f) {}
test(1, 2, 3);
console.log(test.length); // 2
另外当函数存在默认值的时候,arguments
的映射关系也将不存在。
// 函数不存在默认值
function foo(a, b, c, d, e, f) {
arguments[0] = 10;
b = 20;
console.log(arguments); // [10, 20, 3]
}
foo(1, 2, 3);
// 函数存在默认值
function test(a, b, c = 30, d, e, f) {
arguments[0] = 10;
b = 20;
console.log(arguments); // [10, 2, 3]
}
test(1, 2, 3);
函数参数的解构:
function foo({ x, y = 5 }) {
console.log(x, y);
}
foo({}); // undefind 5
foo({ x: 1 }); // 1 5
foo({ x: 1, y: 2 }); // 1 2
foo(); // 报错,默认不匹配, 可以以这样的方式赋值默认值 function foo({ x, y = 5 } = {})
接下来就要来聊聊ES6
新增语法「箭头函数表达式」:
:::info
语法:() => {}
:::
可以看到基本语法只有三个部分组成,实际上还可以更简洁。
当箭头函数只有一个参数的时候()
可以省略:
let foo = num => { console.log(num); }
foo(100);
当箭头函数内执行语句只有一行的时候,{}
也可以省略:
let foo = num => console.log(num);
foo(100);
箭头函数的{}
还具有return
的作用:
let foo = num => num; // 默认就是 return num;
console.log(foo(100)); // 100
// 和普通 function 一样,如果不指定返回的内容默认返回 undefind
let foo2 = num => {};
console.log(foo2(100)); // undefind
箭头函数的参数也可以进行解构:
var foo = ({ x, y } = {}) => x + y;
console.log(foo({ x: 1, y: 2 })); // 3
利用箭头函数可以更简洁的写出我们的需求:
// ES5
var arr = [1, 2, 3, 4, 5];
arr.sort(function(a,b){ return b - a });
console.log(arr); // [5, 4, 3, 2, 1]
// ES6
var arr = [1, 2, 3, 4, 5];
arr.sort((a, b) => b - a);
console.log(arr); // [5, 4, 3, 2, 1]
箭头函数中不存在**arguments**
:
var sum = (a, b) => {
console.log(arguments); // arguments is not defined
return a + b;
};
sum(1, 2);
箭头函数的本质
1、箭头函数本质上不是用function
关键字进行定义的,而是使用胖箭头=>
函数,所以箭头函数的**this**
指向是根据定义时外层函数的作用域来决定的。
function foo() {
console.log(this); // { name: "obj1" }
return (a) => {
// 箭头函数在定义的时候 this 指向外层作用域的 this
// this 指向外层的 this,也就表示 .call 无效
console.log(this); // { name: "obj1" }
};
}
var obj1 = { name: "obj1" };
var obj2 = { name: "obj2" };
var bar = foo.call(obj1);
bar.call(obj2);
const person = {
eat() {
console.log(this); // person{}
},
drink: () => {
// 箭头函数的 this 指向定义时外层作用域的 this
console.log(this); // window{}
},
};
person.eat();
person.drink();
箭头函数中并没有this
的机制,箭头函数的this
值固化的,所以就导致函数内部的this
就是外部的this
(也就是定义时候的this
),既然没有this
指向,所以不能当作构造函数使用、不能使用 bind
、appply
、call
都无法使用
function foo() {
return () => {
return () => {
return () => {
console.log(this);
};
};
};
}
var f = foo.call({ id: 1 });
var f1 = f.call({ id: 2 })()(); // { id: 1 }
var f2 = f().call({ id: 3 })(); // { id: 1 }
var f3 = f()().call({ id: 4 }); // { id: 1 }
2、箭头函数不能作为构造函数使用
同样因为箭头函数内部没有this
所以箭头函数不能进行实例化。
let Foo = ()=>{};
let foo1 = new Foo(); // Foo is not a constructor
3、箭头函数没有arguments
这个特点我们上面也说过了,可以使用...rest
运算符来获取实参
var test = () => {
console.log(arguments); // Uncaught ReferenceError: arguments is not defined
};
test()
但是箭头函数可以获取到外层作用域的arguments
:
function foo() {
setTimeout(() => {
// 闭包
console.log(arguments); // [1, 2, 3],foo 函数的 arguments
}, 1000);
}
foo(1, 2, 3);
4、yield
命令在胖箭头函数不能生效
箭头函数使用场景的总结: 1、简单的函数表达式,得出唯一的
return
计算值,函数内部没有this
的引用,且没有递归、事件绑定、借绑定的时候,用箭头函数比较合适 2、内层函数表达式需要调用this
,需要确保适当的this
指向的时候。 例如setTimeout
、oBtn.addEventlisntener("click", ()=>{ this.getData() }, false)
3、需要将arguments
对象转换为数组的时候。ES5
的写法:var args = Array.prototype.slice.call(arguments)
…rest 运算符
因为箭头函数没有arguments
,所以使用箭头的时候想要获取所有的实参需要使用...rest
运算符来获取
:::info
...rest
具有拓展和收集的功能。
拓展:用于将一个数组或类数组对象转换为逗号分隔的值序列。
收集:用于将以逗号分隔的值序列转换为数组。
:::
使用...rest
来收集参数:
var sum = (...args) => {
console.log(args); // [1, 2] 返回的是一个真正的数组,而不是类数组
};
sum(1, 2);
...rest
运算符将数组数据进行拓展:
function foo(x, y, z) {
console.log(x, y, z); // 1, 2, 3
console.log(arguments); // 1, 2, 3, 4, 5, 6
}
foo(...[1, 2, 3, 4, 5, 6]);
foo.apply(null, [1, 2, 3, 4, 5, 6]); // ES5 的时候需要用 apply 方法
...rest
还可以用来数组合并:
let a = [2, 3, 4];
let b = [1, ...a, 5];
console.log(b); // [1, 2, 3, 4, 5]
当函数使用...rest
收集参数的时候,只能放到()
的最后,否则就会报错:
function foo(a, b, ...c) {
console.log(a, b, c); // 1 2 [3, 4, 5, 6]
}
foo(1, 2, 3, 4, 5, 6);
和函数默认值一样,当函数使用...rest
后函数的length
属性将会受到影响:
console.log(function (a, b) {}.length); // 2
console.log(function (a, b, ...c) {}.length); // 2
如果详细来说**rest**
运算符是将函数的参数进行收集,而拓展运算符是将数据进行展开。
数组拓展运算符:ES6 入门教程
对象拓展运算符:ES6 入门教程