ES6函数的扩展
1.函数参数的默认值
es6之前,不能给函数的参数指定默认值,只能在函数体内使用变通的方法来实现;
function log(x,y){
y = y || '你是大傻子';
console.log(x,y);
}
log(1); //1,你是大傻子
log(1,''); // 1,你是大傻子
检查参数y是否赋值,没有则指定默认值
es6的写法:
function log (x, y = '哈哈哈'){
console.log(x,y);
}
log(1); //1,哈哈哈
log(1,''); // 1
由于es6内部使用 “===” 来判断是否有值, '' === undefined 返回false,
if(y === "undefined"){
y = '哈哈哈';
}
而在y= y || '你是大傻子';中 ;
y = '' || '你是 大傻子';
y = 0 || '你是大傻子';
y = null || '你是大傻子';
y = undefined || '你是大傻子';
返回值均为 :'你是大傻子'
所以 es6 之前的写法 log(1,''); 返回 1你是大傻子,
所以 es6 的写法中:log(1,''); 这里的y值不取默认值,而是取传进去的 ` '' ` 值,结果返回 1''; 即为 1;
eg2:
function Point(x = 0, y = 0){ //构造函数(首字母大写)
this.x = x;
this.y = y;
}
const p = new Point();
p // x = 0; y = 0;
这样写的优点:
1.简洁(不用在函数内部做判断等操作)
2.易读(读代码时,可以清楚看懂哪些参数可省略)
3.利于优化(因为有默认值的存在,即使后期不再使用该参数,也不会导致无法运行)
注意:
在给函数传参时,参数变量x 是默认声明的,根据es6变量、函数不能被重复声明的规定,在函数体内,变量x不能
使用let 或const 再次声明,否则会报错;
function log(x,y){
let x = 1; //error
const y = 0; // error
}
- 使用参数默认值时,函数不能有同名参数(不知道为啥)
function(x,x,y){
... //可以
}
function(x,x,y=1){
...//报错 不可以
}
- 参数默认值不是传值的,而是每次重新计算默认表达式的值。(惰性求值)
let x = 99;
funtion foo(p = x + 1){
console.log(p);
}
foo(); //p = 99+1 = 100
x = 200;
foo(); // p = 200 +1 = 201 //这里没用使用上次计算
这里参数p的默认值 是 x+1. 每次调用foo时,都是重新计算 x+1,而不是取上次的p = 100;
- 与解构赋值默认值结合使用
function foo({x,y = 5}){
console.log(x,y);
}
foo({});//传入x;undefined,y:undefined;y取默认值5,结果 :undefinde 5;
foo({x:1,y:2}); // 1 2
foo({x:1});// 1 5
foo();//此时传入的对象为undefined , foo函数中没有给 {x,y = 5}取默认值,所以在函数体内找不到x,y的值,返回:Uncaught TypeError: Cannot destructure(解构) property `x` of 'undefined' or 'null'.
该方法只使用了对象的结构赋值默认值,没有使用函数参数的默认值。只用参数是一个对象时,变量x和y才会通过解构赋值生成。通过提供函数参数的默认值,可以避免这种情况。如下:
function foo({x,y = 1} = {}){
console.log(x,y);
}
foo();//没有参数时,取{},传入值x:undefinde,y:undefinde; 返回结果:undefined 1;
如果没有提供参数,foo的参数默认是一个空对象,传入空对象时,y的默认值为1生效,所以输出 undefined 1;
- 解构赋值eg2(eg1的扩展)
function fetch(url,{body = '',method = 'GET',headers = {}}){
console.log(method);
}
fetch('//ossweb-imng.qq.com',{});// 返回method的默认值 GET
fetch('//ossweb-img.qq.com');// 参数匹配url,后面的对象对应的参数为空,fetch函数又没有指定默认参数,所以这里解构报错,错误同上;
如果函数fetch的第二个参数是对象,就可以为其三个属性设置默认值。这种写法不能省略第二个参数。如果和函数参数的默认值相结合,就可以省略第二个参数({});实现如下:
function fetch(url,{body = '', method = 'GET', headers = {}} = {}){
console.log(method);
}
fetch('//ossweb-img.qq.com',{});//GET
fetch('//ossweb-img.qq.com');//GET
此时,函数fetch没有第二个参数时,函数参数的默认值生效,然后解构赋值的默认值生效,变量method取到默认值GET。
?练习:写法的差别
//1
function m1({x = 0, y = 1} = {}){
return [x,y];
}
//2
function m2 ({x,y} = {x:1,y:2}){
return[x,y];
}
写法1中,给m1函数指定了参数默认值{};同时又设置了对象解构赋值的默认值{x = 0, y = 1};
写法2中,给m2函数只指定了参数默认值是一个具有属性的对象{x = 1, y = 2},而没有设置对象结构赋值的默认值;
//没有参数时
m1(); //取默认参数{},然后解构赋值默认值 x:0,y:0;
m2(); //取默认参数{x:0,y:0},返回 0 0
//参数齐全时
m1({x:3,y:8}); // 有参数,直接解构赋值给参数 x:3,y:8
m2({x:3,y:8}); //有参数, 直接解构赋值
x:3,y:8
//参数不齐时
m1({x:1}); //有参数,解构赋值时 y为undefined,此时y取默认值0 ,返回 1 0
m2({x:1}); // 有参数, 解构赋值时,y为undefined ,但此时y未设置默认值
返回 1 undefined
//都无值时
m1({}); //返回解构赋值后的默认值 0 0
m1({}); //有参,但参数未设置对象解构赋值的默认值, 此时 对象为{},x y 均为 undefined , 返回 undefined undefined
- 参数默认值的位置
定义了默认值的参数,应是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾参数设置默认值,实际上这个参数是无法省略的。
//eg1
functon f(x = 1, y){
return [x,y];
}
f(); // [1,undefined]
f(2); // [1,undefined]
f(,1) // 报错
f(undefined,1);//[1,1]
//eg2
function f(x,y = 5, z){
return [x,y,z];
}
f();//[undefined,5,undefined]
f(1);//[1,5,undefined]
f(1,,2);//undefined
f(1,undefined,2);//[1,5,2]
上面代码中,有默认值的参数都不是尾参数。此时,无法省略该参数,而不省略它后面的参数,除非显式的输入 undefined;
如果传入 undefined ,出发该参数等于默认值,非undefined 则不行(如 null,‘’,0);
funtion foo (x = 5, y = 7 ){
return [x,y];
}
foo(null,undefined); // null 7
==================
分割线 0410 23:30 未完待续
函数的 length 属性
0411 21:46分 ,续上:
- 函数的length属性返回函数的参数个数,但给参数指定了默认值后,只返回没有指定默认值的参数个数。
(指定了默认值后,函数的length属性会失真,返回不准确)
(function(a){}).length;//1
(function(a = 1){}).length;//0
(function(a,b,c = 1){}).length;//2
length属性的返回值,等于函数的参数个数,减去 指定了默认值的参数个数。
length 含义:函数预期传入的参数个数。 某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。 rest参数也不会计入length属性(还没看到)。
(function(...args){}).length;//0
如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。(length属性计算第一个设置默认值的参数之前的 参数个数);
(function(a,b = 1,c){}).length;//1
(function(a = 1,b,c){}).length;//0
(function(a,b,c = 1,d){}).length;//2
- 作用域
在设置参数默认值的情况下,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等初始化结束,作用域消失。 函数体内不能再声明参数中的变量。
在未设置参数默认值时,不会出现该作用域。
var x = 1;
function foo(x , y = x){ //参数y的默认值 等于 变量x
foo(2);// 2
调用函数f时,参数形成一个单独的作用域。在这个作用域里,默认值变量x 指向第一个参数x, 而不是全局变量 x //1;所以在调用 foo函数时, 参数x = 2; 所以 y = 2,输出为2;
eg2:
let x = 1;
function foo( y = x){
let x = 2;
console.log(y);
}
foo(); // 1
函数f被调用时, 设置了参数默认值 的 y = x 形成一个单独的作用域。 这个作用域里,变量x 未定义, 所以指向外层的全局变量x。 函数调用时,函数体内部的局部变量x 影响不到 默认值变量x ; 所以输出 1;
如果此时全局变量x不存在的话,就会报错。
function foo(y = x){
let x = 2;
console.log(y);
}
foo(); // x is not defined
let x = 1;
function foo(x = x){ //参数 x = x 形成一个单独作用域。 实际执行的是 let x = x, 由于暂时性死区的原因 ,会报 x 未定义错误。
....
}
foo();// x is not defined
参数的默认值 是 一个函数是, 函数的作用域也遵守这个规则。
let foo = 'outer';
function bar(func = () => foo){
let foo = 'inner';
console.log(func());
}
bar(); // outer
函数bar 的参数 func的默认值是一个匿名函数 ,返回值为全局变量foo,(函数参数形成的单独作用域里没有定义foo,所以foo指向外层的全局变量),所以输出 outer
同理,如果没有在外层定义全局变量foo,会报错:
function bar(func = () => foo){ //匿名函数内的foo变量指向外层,但函数外层没有声明foo,所以报错
let foo = 'inner';
console.log(func());
}
bar(); // foo is not defined
eg3:
var x = 1;
function foo(x, y = function(){x = 2}){
var x = 3; //此处使用var 声明函数体内部变量x, 使用let 会报错
y();
console.log(x);
}
foo(); // 3
x // 1
函数foo的参数形成一个单独作用域,作用域里,声明了变量x,y,y的默认值是一个匿名函数,匿名函数内部的变量取的是当前作用域的第一个参数x。foo函数内部又声明了一个内部变量x,该内部变量x 与 参数x 不是同一个作用域,所以不是同一个变量,执行 y 的默认值(匿名函数)后, 外部的全局变量x 未改变, foo函数内部的变量x也未变。
var x = 1;
functino foo(x, y = funciton(){x = 2}){
x = 3;
y();
console.log(x);
}
foo(); // 2
x //1
如果将 foo函数内部的 var x = 3;的var去除,函数foo的内部变量x 就指向第一个参数x,与匿名函数内部的x一致,最后输出
值为 2 ;foo内部 x =3 时, 参数x的值被设置为 3, 执行 y() 后,又被重新赋值为2 ,最后输出2;
- 作用域的应用
funtion throwIfMissing(){
throw new Error("Missing parameter");
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided;
}
foo(); // Error:Missing parameter;
foo函数调用时如果没有参数,就会调用默认值 throwIfMissing函数的运行结果(函数名throwIfMissing 之后有()),表明
参数的默认值不是在定义时执行,而是在运行时执行。(这里参数的默认值如果在定义时执行,那么缺少参数时,若想顺利抛出错误,需要执行该参数的默认值(throwIfMissing函数),有参数时则只需要返回参数值,这样就需要判断返回值是函数还是常量 。 所以在运行时执行 这样的写法更简洁)如果参数已赋值,默认值中的函数就不会运行。
另外,可将参数默认值设置为 undefined , 表明该参数可省略。
function foo(optional = undefined){
...
}
0411 23:13 抄完—— 函数扩展第一小节