箭头函数
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 5foo({ x: 1 }); // 1 5foo({ x: 1, y: 2 }); // 1 2foo(); // 报错,默认不匹配, 可以以这样的方式赋值默认值 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 一样,如果不指定返回的内容默认返回 undefindlet foo2 = num => {};console.log(foo2(100)); // undefind
箭头函数的参数也可以进行解构:
var foo = ({ x, y } = {}) => x + y;console.log(foo({ x: 1, y: 2 })); // 3
利用箭头函数可以更简洁的写出我们的需求:
// ES5var arr = [1, 2, 3, 4, 5];arr.sort(function(a,b){ return b - a });console.log(arr); // [5, 4, 3, 2, 1]// ES6var 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 definedreturn 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 指向定义时外层作用域的 thisconsole.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, 3console.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); // 2console.log(function (a, b, ...c) {}.length); // 2
如果详细来说**rest**运算符是将函数的参数进行收集,而拓展运算符是将数据进行展开。
数组拓展运算符:ES6 入门教程
对象拓展运算符:ES6 入门教程
