函数参数默认值
(1) es5中模拟参数默认值:
function fn(name, count){
name = name || 'xiao ming'
count= count|| 1
console.log(name, count)
}
复制代码
这样可以为没有传入的参数,设置为默认值。这种方式的问题:如果count传入0,即使这个值是合法的,但也会被设置为默认值1。 改进后:
function fn(name, count){
name = typeof name !== 'undefined' ? name : 'xiao ming'
count = typeof count !== 'undefined' ? count : 1
console.log(name, count)
}
复制代码
(2) es6的做法:
function fn(name = 'xiao ming', count = 1){
console.log(name, count)
}
// name使用默认值
fn(undefined, 18)
复制代码
扫盲: null是一个有效参数,传入null并不会使用默认参数
(3) 命名参数与arguments
的关系
es5非严格模式下,修改命名参数,会同步更新到arguments对象中; 而在es6或者严格模式中,命名参数和arguments是分离的
function fn1(name, count) {
name = 'li si'
count = 5
console.log(arguments[0])
console.log(arguments[1])
}
fn1('xiao wang', 4)
// 非严格模式下
// li si
// 5
// 严格模式下或es6环境中
// xiao wang
// 4
复制代码
(4) 函数默认表达式
function count(){return 6}
function fn1(name, count = count()) {
console.log(name)
console.log(count)
}
fn1('xiao wang')
// 'xiao wang'
// 5
复制代码
扫盲: 1. 参数、参数默认值、参数默认表达式都在同一个独立的作用域内; 2. 参数作用域中存在临时性死区,即后面的参数可以引用到前面的参数,反之不可以; 3. 参数作用域引用不到函数体内的变量
证明独立作用域的例子:
function fn(a, cb = () => { a.b = 1; console.log(a) }) {
var a = { a: 1 }
cb()
console.log(a)
}
fn({ c: 3 })
// { c: 3, b: 1 }
// { a: 1 }
复制代码
cb
执行时,获取到的对象a
并不是函数体内新定义的,而是传入的参数a
,所以他们是在同一独立作用域的
无名参数
(1) es5中使用arguments获取不定数量的参数
// 如果没有注释,很难发现函数需要传入无命名参数
function fn(arr){
// i 从 1开始,需要跳过 arr这个命名参数
for(let i = 1, len = arguments.length; i < len; i++){
arr.push(arguments[i])
}
return arr;
}
console.log(fn([3],4,5,6,7)) // [3,4,5,6,7]
复制代码
存在的问题:
- 并不容易发现函数,需要传入不定量参数;
- 如果有命名参数,arguments中既包含命名参数也包含无名参数,还需要拆分获取
(2) es6中,使用不定参数
在参数前加三个点...
,表明这是一个不定参数,这个参数为数组,包含着自它以后传入的参数。 并且解决了上述es5
中的问题
function fn(arr, ...items){
for(let i = 0, len = items.length; i < len; i++){
arr.push(items[i])
}
return arr;
}
console.log(fn([3],4,5,6,7)) // [3,4,5,6,7]
复制代码
不定参数的限制:
- 每个函数只能定义一个不定参数,而且只能处于所有参数末尾。错误示范:
function fn(a, ...b, c){}
- 对象的
setter
函数不能使用不定参数。错误示范:let obj = {set name(...value){}}
扫盲:
- 无论是否使用不定参数,
arguments
总是包括所有传入的参数- 函数的**
length
属性**,表示命名参数的数量,跟不定参数无关
函数的name
属性
-
function fn(){}
name为fn
-
let fn = function (){}
name为fn
-
let fn = function fn1(){}
name为fn1
,因为函数名比被赋值的变量权重高 -
let obj = {get firstName(){}, sayName(){}}
。obj.firstName
为getter函数,所以name为get firstName
;obj.sayName
name为sayName
- 通过
bind()
创建的函数,name会带有bound
前缀。fn.bind()
name为bound fn
- 通过
Function()
创建的函数,name为anonymous
。new Function()
name为anonymous
扫盲:函数name属性不一定能获取对函数的引用,它只是协助调试用的额外信息
函数的多重用途
箭头函数
相关语法就不再赘述
与普通函数的区别:
- 没有
this、super、arguments
绑定,这些值都由外层的非箭头函数决定- 不能使用new关键词调用;没有原型;函数体内没有
new.target
。因为箭头函数没有[[construct]]
方法,所以不能当做构造函数调用。- 不可以改变this指向。箭头函数的this指向,只跟函数定义时的环境有关,跟使用环境无关,
call apply bind
也无力改变。
- 再也不用缓存
this
了,再也不会有_this self that
等各式各样的变量了。- 说到这里,突然想到了箭头函数可以在react中绑定事件处理函数的this到组件实例 ,以及 普通函数的this与执行环境相关
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
- 不支持重名参数。es6与es5严格模式下都不支持函数参数重名。
// es5非严格模式下,参数重名情况
function fn(a,b,a){
console.log(a)
console.log(b)
console.log(arguments[0])
console.log(arguments[1])
console.log(arguments[2])
a = 4
console.log(arguments[0])
console.log(arguments[1])
console.log(arguments[2])
}
fn(1,2,3)
//3
//2
//1
//2
//3
//1
//2
//4
// 即 arguments[0]与 a解除了相关性
复制代码
双冒号运算符
函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
复制代码