函数参数的默认值
在ES6之前,不能直接为函数指定默认值。
eg:
function funcEs5(test){test = test || '默认值'console.log(test)}funcEs5() // 打印结果默认值,因为没有给值 // 当给值时function funcEs5(test){test = test || '默认值'console.log(test)}funcEs5('给定值') // 打印结果给定值
但是这样的话就会产生一个问题,JS的逻辑或运算符只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值,只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。而且它的true和false是存在隐式转化的,也就是说,如果你想在ES5中给参数传 0, false, 空字符串等可以隐私转换为false的参数,是会凉掉的。。
EG:
function funcEs5(count, bool, str){count = arguments[0] || '默认数字'bool = arguments[1] || '给它一个true'str = arguments[2] || '默认字符串'console.log(count, bool, str)}funcEs5(0, false, "")// 打印结果:默认数字 给它一个true 默认字符串
当然啦,肯定有解决的办法的啦
判断一下它是否为undefined,如果是,说明该参数没有赋值:
function funcEs5(count, bool, str){arguments[0] === undefined && '给它默认数字'arguments[1] === undefined && '给它一个true'arguments[2] === undefined && '默认字符串'console.log(count, bool, str)}funcEs5(0, false, "") // 打印结果:0 false ""
好啦,现在开始说ES6的允许为函数的参数设置默认值,大家把前面的忘了吧
非常简单,直接写在括号中的参数后面即可
function funcES6(test = '有本事你就给我赋值') {console.log(test) // 打印结果:有本事你就给我赋值}funcES6()
如果你给该参数赋值,则默认值不会生效,否则取默认值
function funcES6(test = '有本事你就给我赋值') {console.log(test) // 打印结果:我要赋值啦}funcES6('我要赋值啦')
与解构赋值默认值结合使用
关于解构赋值ES6对象的扩展(上)ES6对象的扩展(下)
先来看看使用对象的解构赋值默认值吧
let obj = {name: '我不是默认姓名',age: undefined}funcES6(obj)function funcES6({ name, age = '18' }) {console.log(name, age)}// 打印结果: 我不是默认姓名 18
我给age一个undefined,相当于age属性没有传值:
let obj = {name: '我不是默认姓名'}funcES6(obj)function funcES6({ name, age = '18' }) {console.log(name, age)} // 当该属性没有传值时,获取到的是undefined
同时,这种写法里的参数拿到的是传入对象的name属性和age属性,其它属性一律过滤,假设你ajax没有封装的话,{ data } => {} 一般可以直接拿到后端返回的数据(axios是这样)
当对象的解构赋值配合函数的默认值时
在上个例子中,我那种写法其实非常不严谨很low的一种写法,因为当没有传入参数时,它是会报类型错误的:
let obj = {name: '我不是默认姓名'}funcES6()function funcES6({name, age = '18'}) {console.log(name, age)} // Uncaught TypeError: Cannot match against 'undefined' or 'null'.
这是因为你没传的话,它就是undefined,它不是对象,它无法解构,所以一般,我们在遇到这种是,应该考虑到如果没有传入参数以及传入参数之后我们需要那些属性,实现方式:
funcES6()function funcES6({name, age = '18'} = {}) {console.log(name, age)} // undefined "18"
当我们没有传入参数时,我们使用ES6函数的扩展给它指定一个默认值,当传入时也可以用解构赋值来取到需要用到的属性名!
高逼格用法:
function fetch(url, { body = '', method = 'POST', headers = {} } = {}) {console.log(method);}fetch('http://es6.ruanyifeng.com/#docs/function') // 打印结果:POST
这种写法意思就是说,当你没有传递第二个参数时,先执行等号右边的(给它一个默认值),然后执行等号左边的对象的解构赋值(左边的是对象的解构赋值中给一个默认值),其实跟普通赋值一样,先执行等号右边,然后再是左边。
深入理解:
// 写法一(函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;)function f1({x = 0, y = 0} = {}) {console.log(x, y)}// 写法二(函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。)function f2({x, y} = { x: 0, y: 0 }) {console.log(x, y)}
当我们打印它们在不同情况下得到的结果时:
函数没有参数的情况
f1() // 0 0 f2() // 0 0
x 和 y 都有值的情况
f1(1, 2) // 1, 2
f2(1, 2) // 1, 2
x 有值,y 无值的情况(重点)
f1({x: 1}) // 1, 0f2({x: 1}) // 1, undefined
为什么会出现这种情况呢,这是因为,当我门没有给函数传参时是取默认值,当有参数时,则会覆盖默认值,上面传了单个参数之后,下面其实是变成这样:
// 写法一(函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;)function f1({x = 0, y = 0} = { x: 1 }) {console.log(x, y)}// 写法二(函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。)function f2({x, y} = { x: 1 }) {console.log(x, y)}
当我传入一个参数时,传入的参数会覆盖等号右边的函数默认值,这样的话就会导致函数2被覆盖掉,而函数1则不同,因为js是先执行等号右边的,然后回过头来再执行等号左边的,当对象{x:1}覆盖掉函数1的空对象之后,它会回过来去执行等号左边的对象的解构赋值,然后发现y没有,而对象的解构赋值中y=0!!!
函数的作用域
当一个函数中的参数设置了默认值之后,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
此段后续请阅读阮一峰大佬的:http://es6.ruanyifeng.com/#docs/function,
我能力有限,太菜了无法清晰的表达出来
rest 参数(argument)
跟对象的扩展运算符,数组的扩展运算符类似,函数也有属于它自己的扩展运算符(先这么认为吧,反正都是三个点)
![4fb7fc690ad98d84b3f4b147de660a19.png](https://i-blog.csdnimg.cn/blog_migrate/9424dc0f0bcc342e33784d9653091b6f.jpeg)
大家可以先把它当arguments(实参列表),不过这样是错的,因为...arg接收的是剩余参数,其次不同于arguments,它就是一个真正的数组,它有数组的所有方法!
建议大家以后都用...arg来代替arguments,因为ES6的箭头函数中,是没有arguments实参列表存在的!
![e45cb390c0256cee426057a8c0c28fa2.png](https://i-blog.csdnimg.cn/blog_migrate/d2f78213c88eb5ebe836241e663c141d.jpeg)
在以往如鱼得水的arguments在ES6中死的很干脆,大家都知道,语法是向前发展的,w3c敢这么玩肯定是有它的道理的!没错,函数扩展运算符完美干掉arguments!
![23062d68727dd86c7c3aeb4229b74519.png](https://i-blog.csdnimg.cn/blog_migrate/6eb13d3dbb833f77fb20a676637a5d40.jpeg)
老少通吃,不管是ES6,还是ES5,它都可以完美兼容!
arguments能做的它能做,arguments不能做的它也能做!
嗯,虽然它很厉害,但是大家要记下它是剩余参数列表,而不是全部参数列表(形参只写它一个,那它就是)
![73befe3a43ae1cd5d134fb408edb1619.png](https://i-blog.csdnimg.cn/blog_migrate/2b8056a647b68b2b03e210058c631738.jpeg)
箭头函数
ES6 允许使用“箭头”(=>)定义函数。(它不会改变this指向,建议大家在setTimeout()和setInterval()使用箭头函数)
![b585df78dbcc9ef982c1a14d376cfe85.png](https://i-blog.csdnimg.cn/blog_migrate/be6f43c20ea01c442f5ba6b53a7f995b.jpeg)
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
![718d5f1712d1f8135042e498c8ccfabe.png](https://i-blog.csdnimg.cn/blog_migrate/7e091ef2a618a5fb01872eed2719bf53.jpeg)
如果箭头函数的代码块部分只有一条语句,则可以省略 return
![8be7da5ccdfdc007012e01667555d0ff.png](https://i-blog.csdnimg.cn/blog_migrate/b8a76200adf6ed55b8e36e801063163f.jpeg)
不过返回的是对象的话,就不能这么写了,因为对象和块级作用域都是包个大括号表示,我们有两种方式来处理这种:
// 方式一let obj = {a: 999}let funcES6 = () => obj// 方式二let funcES6 = () => ({a: 999}) // 推荐方式二
如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。
![535ca152c56019bb6201d2f75e13aa26.png](https://i-blog.csdnimg.cn/blog_migrate/c662a070434f7bd264cfca1001d3cdc0.jpeg)
void 操作符指定要计算一个表达式但是不返回值。(执行函数代码,但是有没有return都是undefined)
箭头函数可以与变量解构结合使用。
![c1e3ceb7b675f1c4387703173b61006d.png](https://i-blog.csdnimg.cn/blog_migrate/d6e0e5ab733750410af44da6289c36f3.jpeg)
使用注意事项:
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象(不改变this指向)。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替(...arg)。
关于第一点,划重点!!!!
箭头函数的this指向,在函数生成的那一刻,已经绑定到定义时所在的作用域中,而不是跟普通函数一般,绑定在执行时所在的作用域!
eg: 1,我们先搓出来一个局部作用域,此时内部的this指向指向的是对象obj;
![c1405edcf932ec85c0da7eee78bd92a0.png](https://i-blog.csdnimg.cn/blog_migrate/bfc517685541e165ce3cb2d10d4061dd.jpeg)
es5之前的函数是谁调用它,this就指向谁,它的this作用域就是谁,也就说是,它的作用域是在函数执行之后才生成的,而es6的箭头函数作用域在函数生成之后便会随之生成!
![999e87a1f87822041654f9a7ad9b02d9.png](https://i-blog.csdnimg.cn/blog_migrate/b99b1b8181ffa0ef27ec730fe2fcc04a.jpeg)
如图所示,当这两个函数都没有被人调用时,默认为全局调用,所以es5的指向window,而es6的指向当前作用域,它的this指向没有被改变。
重点参考阮一峰大大的ES6标准入门,感兴趣的大佬可以看看(比我写的好太多啦):http://es6.ruanyifeng.com/#docs/function