一、函数参数的默认值
- ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function method(x,y=10){ console.log(x, y); } method(20);//20 10
- 参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。
function fn(x=10){ let x=20; console.log(x);//SyntaxError: Identifier 'x' has already been declared } fn();1. 与解构赋值默认值结合使用
function fn({x,y,z}){ console.log(x, y, z); } fn({x:1,y:2,z:3});//1 2 3 function foo([x,y=2,z]){ console.log(x, y, z); } foo([1,,3]);//1 2 3 foo([]);//undefined 2 undefined foo([1,,]);//1 2 undefined foo([2,3,4]);//2 3 4 foo();//TypeError: undefined is not iterablefunction fetch(url,{body,method="get",headers={}}){ console.log(method); } fetch("http://www.baidu.com",{});//get fetch("http://www.baidu.com");//TypeError: Cannot destructure property `body` of 'undefined' or 'null'
- 如果函数fetch的第二个参数是一个对象,就可以为它的三个属性设置默认值。这种写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。
function fetch(url,{body,method="get",headers={}}={}){ console.log(method); } fetch("http://www.baidu.com",{});//get fetch("http://www.baidu.com");//get
- 函数fetch没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method才会取到默认值get。
2. 参数默认值的位置
- 通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
function f(x=1,y){ return [x,y]; } console.log(f());//[1, undefined] console.log(f(2));//[2, undefined] console.log(f(,1));//报错SyntaxError: Unexpected token , console.log(f(undefined, 1));//[1, 1] console.log(f(1, null));//[1, null]function f(x,y=5,z){ return [x,y,z]; } console.log(f());//[undefined, 5, undefined] console.log(f(1));//[1, 5, undefined] //console.log(f(1,,2));//报错SyntaxError: Unexpected token , console.log(f(1,undefined,2));//[1, 5, 2] console.log(f(1,null,2));//[1, null, 2]
- 如果传入undefined,将触发该参数等于默认值,null则没有这个效果。
3. 函数的 length 属性
- 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
- 函数里面的length属性返回的是当前函数的形参个数。
console.log((function(a){}).length);//1 console.log((function(a=5){}).length);//0 console.log((function(a,b,c=5){}).length);//2
- length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了 3 个参数,其中有一个参数c指定了默认值,因此length属性等于3减去1,最后得到2。
- length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。
function f(...args){ console.log(args); } console.log(f.length);//0
- 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
4. 函数的作用域
- 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失
var x=1; function f(x,y=x){ console.log(y); } f(2);//2
- 参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
let x=1; function f(y=x){ let x=2; console.log(y); } f();//1
- 函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。
如果全局变量x不存在,就会报错。function f(y=x){ let x=2; console.log(y); } f();//ReferenceError: x is not definedvar x=1; function f(x=x){ } f();//ReferenceError: Cannot access 'x' before initialization
- 参数x = x形成一个单独作用域。实际执行的是let x = x,由于暂时性死区的原因,这行代码会报错。
如果参数的默认值是一个函数,该函数的作用域也遵守这个规则let foo='outer'; function bar(func=()=>foo){ let foo='inner'; console.log(func()); } bar();//outer
- 函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。函数参数形成的单独作用域里面,并没有定义变量foo,所以foo指向外层的全局变量foo,因此输出outer。
function bar(func=()=>foo){ let foo='inner'; console.log(func()); } bar();//ReferenceError: foo is not defined
- 匿名函数里面的foo指向函数外层,但是函数外层并没有声明变量foo,所以就报错了。
var x=1; function f(x,y=function(){x=2;}){ var x=3; y(); console.log(x); } f();//3 console.log(x);//1
- 函数f的参数形成一个单独作用域。这个作用域里面,首先声明了变量x,然后声明了变量y,y的默认值是一个匿名函数。这个匿名函数内部的变量x,指向同一个作用域的第一个参数x。函数f内部又声明了一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和外部全局变量x的值都没变。
- 如果将var x = 3的var去除,函数f的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,所以最后输出的就是2,而外层的全局变量x依然不受影响。
var x=1; function f(x,y=function(){x=2;}){ x=3; y(); console.log(x); } f();//2 console.log(x);//1
二、函数的rest 参数
- 等价于…values,获取函数多余的参数,使用的是扩展运算符。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。这样就不需要使用arguments对象了。
function info(...value){ console.log(arguments);//[1,2,3,4] console.log(value);//[1,2,3,4] for(let val of value){ console.log(val);//1 2 3 4 } } info(1,2,3,4);
- 注:箭头函数中写arguments对象会报错,因为箭头函数特点是上下文this指针保持一致,而window中不含arguments。
- arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.from先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
function push(array,...items){ items.forEach(function(item){ array.push(item); }); console.log(array); } var a=[]; push(a,1,2,3);//[1, 2, 3]
- 注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function f(a,...b,c){}//SyntaxError: Rest parameter must be last formal parameter
- 函数的length属性,不包括 rest 参数。
console.log((function(a){}).length);//1 console.log((function(...a){}).length);//0 console.log((function(a,...b){}).length);//1
三、ES6的严格模式
- 从 ES5 开始,函数内部可以设定为严格模式。
- 只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显示设定为严格模式,否则会报错。
详细的es5严格模式见下方链接- https://blog.csdn.net/qq_45806781/article/details/113791150?spm=1001.2014.3001.5501
- ES6做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
- 这样规定的原因是,函数内部的严格模式,同时适用于函数体和函数参数。但是,函数执行的时候,先执行函数参数,然后再执行函数体。这样就有一个不合理的地方,只有从函数体之中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。
四、函数的name属性
- 函数的name属性,返回该函数的函数名。
console.log(info.name);//info
- 如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。
console.log(f.name);//f //es5 console.log(f.name);//
- 如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。
五、自己实现map映射方法
Array.prototype.fakeMap = function(fn,context){ console.log(context);//undefined let arr = this;//this是当前对象数组 let temp = []; for(let i = 0;i<arr.length;i++){ let result = fn.call(context,arr[i],i,arr); temp.push(result); } return temp; } var st=[1,2,3,4,5]; var obj=st.fakeMap(function(value,index,array){ console.log(this);//window console.log(value, index, array);//输出值 索引 原数组 return value*2; }); console.log(obj);//[2, 4, 6, 8, 10];
- 原型链方法中的this指针指向当前对象。
function myArray(){ } myArray.prototype.map = function(){ console.log(this);//当前对象 } let arr = new myArray(); arr.map();
版权声明:本文为qq_45806781原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。