一、函数参数的默认值
- ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function method(x,y=10){
console.log(x, y);
}
method(20);
- 参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。
function fn(x=10){
let x=20;
console.log(x);
}
fn();
1. 与解构赋值默认值结合使用
function fn({x,y,z}){
console.log(x, y, z);
}
fn({x:1,y:2,z:3});
function foo([x,y=2,z]){
console.log(x, y, z);
}
foo([1,,3]);
foo([]);
foo([1,,]);
foo([2,3,4]);
foo();
function fetch(url,{body,method="get",headers={}}){
console.log(method);
}
fetch("http://www.baidu.com",{});
fetch("http://www.baidu.com");
- 如果函数fetch的第二个参数是一个对象,就可以为它的三个属性设置默认值。这种写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。
function fetch(url,{body,method="get",headers={}}={}){
console.log(method);
}
fetch("http://www.baidu.com",{});
fetch("http://www.baidu.com");
- 函数fetch没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method才会取到默认值get。
2. 参数默认值的位置
- 通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
function f(x=1,y){
return [x,y];
}
console.log(f());
console.log(f(2));
console.log(f(,1));
console.log(f(undefined, 1));
console.log(f(1, null));
function f(x,y=5,z){
return [x,y,z];
}
console.log(f());
console.log(f(1));
console.log(f(1,undefined,2));
console.log(f(1,null,2));
- 如果传入undefined,将触发该参数等于默认值,null则没有这个效果。
3. 函数的 length 属性
- 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
- 函数里面的length属性返回的是当前函数的形参个数。
console.log((function(a){}).length);
console.log((function(a=5){}).length);
console.log((function(a,b,c=5){}).length);
- length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了 3 个参数,其中有一个参数c指定了默认值,因此length属性等于3减去1,最后得到2。
- length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。
function f(...args){
console.log(args);
}
console.log(f.length);
- 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
4. 函数的作用域
- 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失
var x=1;
function f(x,y=x){
console.log(y);
}
f(2);
- 参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
let x=1;
function f(y=x){
let x=2;
console.log(y);
}
f();
- 函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。
如果全局变量x不存在,就会报错。
function f(y=x){
let x=2;
console.log(y);
}
f();
var x=1;
function f(x=x){
}
f();
- 参数x = x形成一个单独作用域。实际执行的是let x = x,由于暂时性死区的原因,这行代码会报错。
如果参数的默认值是一个函数,该函数的作用域也遵守这个规则
let foo='outer';
function bar(func=()=>foo){
let foo='inner';
console.log(func());
}
bar();
- 函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。函数参数形成的单独作用域里面,并没有定义变量foo,所以foo指向外层的全局变量foo,因此输出outer。
function bar(func=()=>foo){
let foo='inner';
console.log(func());
}
bar();
- 匿名函数里面的foo指向函数外层,但是函数外层并没有声明变量foo,所以就报错了。
var x=1;
function f(x,y=function(){x=2;}){
var x=3;
y();
console.log(x);
}
f();
console.log(x);
- 函数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();
console.log(x);
二、函数的rest 参数
- 等价于…values,获取函数多余的参数,使用的是扩展运算符。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。这样就不需要使用arguments对象了。
function info(...value){
console.log(arguments);
console.log(value);
for(let val of value){
console.log(val);
}
}
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);
- 注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function f(a,...b,c){}
console.log((function(a){}).length);
console.log((function(...a){}).length);
console.log((function(a,...b){}).length);
五、自己实现map映射方法
Array.prototype.fakeMap = function(fn,context){
console.log(context);
let arr = 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);
console.log(value, index, array);
return value*2;
});
console.log(obj);
function myArray(){
}
myArray.prototype.map = function(){
console.log(this);
}
let arr = new myArray();
arr.map();