ES6学习复习系列文章目录
ES6所有知识点学习复习文章链接地址- ES6字符串和数字格式新增的所有知识点(全面详细)
- ES6解构赋值(各种情形详解)
- ES函数默认值
- ES6的箭头函数应用和注意点
- ES6函数尾调用,尾递归及优化深入理解
- ES6数组之扩展运算符应用场景详解
- ES6数组的常用的拓展方法详细应用
- ES5,6数组的空位处理
ES函数参数默认值
函数参数设置默认值之后产生的作用域的变化是一个非常值得注意的点,本文详细举例分析此点。 箭头函数主要注意点就是this的指向问题,箭头函数虽然方便但是需要注意有些地方是不能使用的,譬如对象中的函数是不能使用箭头函数的正文内容
函数参数的设置
函数参数开始准许设置默认值
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。
这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值
不能显式设定为严格模式的情形
ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
这样规定的原因是,函数内部的严格模式,同时适用于函数体和函数参数。但是,函数执行的时候,先执行函数参数,然后再执行函数体。
这样就有一个不合理的地方,只有从函数体之中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。
所以在ES6中, 只要参数使用了默认值、解构赋值、或者扩展运算符,就不能显式指定严格模式。
参数默认值与解构赋值的默认值,结合起来使用。
上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。通过提供函数参数的默认值,就可以避免这种情况。
通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。
如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。
如果传入undefined,将触发该参数等于默认值,null则没有这个效果。
同样支持参数列表解构
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
指定默认值之后的length属性问题
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
这是因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。
函数的设置参数默认值作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
代码中,函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。
如果此时,全局变量x不存在,就会报错,如右侧代码。
看上面参数作用域的范围已经用红线划出来了
代码中,函数foo的参数形成一个单独作用域。这个作用域里面,首先声明了变量x,然后声明了变量y,y的默认值是一个匿名函数。这个匿名函数内部的变量x,指向同一个作用域的第一个参数x。函数foo内部又声明了一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和外部全局变量x的值都没变。
如果将var x = 3的var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,所以最后输出的就是2,而外层的全局变量x依然不受影响。
函数的rest参数设置
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组(是真实数组不是arguments那种的类数组),该变量将多余的参数放入数组中。
注意, rest 参数它就是一个真正的数组,数组特有的方法都可以使用。 rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
ES6常用的箭头函数
箭头函数的形式
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错
rest 参数与箭头函数结合的例子。
rest 参数与箭头函数结合的例子。
简化回调函数
箭头函数的关键注意点
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
普通函数中的this指向的是调用它的对象,如果没有直接调用对象,会指向undefined或者window,一般都会指向window,在严格模式下才会指向undefined
在使用new操作符新生成一个对象x中, 拥有一个属性a,值为22,同时也创了一个新的箭头函数,这个箭头函数是在新对象x中建立的, 所以函数内的this指向的就是对象x
setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。
但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。因为是在{id:42}这个对象创造的一个延时器, 所以延时器里面的箭头函数指向的就是{id:42}
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。因此,在下面的代码中,传递给setInterval的函数内的this与封闭函数中的this值相同:
箭头函数没有自己的this指针
由上面代码可以看出使用箭头函数后不用再定义一个that来指向父级作用域了
不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替
箭头函数没有prototype属性。
var Foo = () => {};
console.log(Foo.prototype); // undefined
与严格模式的关系
var f = () => { 'use strict'; return this; };
f() === window; // 或者 global
是否可以使用 yield 关键字
yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。
对象中的函数不能使用箭头函数的形式
第一个场合是定义对象的方法,且该方法内部包括this。
cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。
需要动态this的时候,也不应使用箭头函数。
上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。