一, 函数参数的默认值
1. 基本用法
- function point(x = 0,y = 0){
- this.x = x;
- this.y = y;
- }
- const p = new point();
- p;// point {x: 0, y: 0}
2. 注意问题
(1) 参数变量是默认声明的, 在函数体内, 不能用 let 或 const 再次声明, 否则会报错.
- function foo(x = 5,y = 5){
- let x = 1;
- };
- foo();//Identifier 'x' has already been declared
- function foo(x = 5,y = 5){
- const y = 0;
- };
- foo();//Identifier 'y' has already been declared
(2) 使用参数默认值时, 函数不能有同名参数.
- function foo(x = 5,x,y){
- };
- foo();// SyntaxError: Duplicate parameter name not allowed in this context
(3) 参数默认值不是传值的, 而是每次都重新计算默认值表达式的, 即参数默认值是惰性求值的.
二, 参数默认值与解构赋值默认值结合使用
1. 基本用法
- function foo({x,y=5}){
- console.log(x,y);
- }
- foo({});// undefined 5
- foo({x:1});// 1 5
- foo({x:1,y:2});// 1 2
- foo();//TypeError: Cannot destructure property `x` of 'undefined' or 'null'.
上例中, 只使用了对象的解构赋值, 没有使用函数参数的默认值. 只有当 foo 函数的参数是一个对象时, 变量 x 和 y 才会通过解构赋值生成. 如果函数 foo 调用时没有提供参数, 变量 x 和 y 就不会生成, 从而报错. 通过提供函数参数的默认值, 可以避免这种情况.
- function foo({x,y = 5} = {}){
- console.log(x,y);
- }
- foo();// undefined 5
2. 参数默认值与解构对象的默认值
- // 写法一
- function m1({x = 0, y = 0} = {}){
- return [x, y];
- }
- // 写法二
- function m2({x, y} = {x: 0, y: 0}){
- return [x, y];
- }
上例中两种写法的区别是: a. 写法一: 参数的默认值是一个空对象, 但设置了解构对象的两个属性默认值 b. 写法二: 参数的默认值是一个有具体属性的对象, 但没有设置解构对象的属性默认值
- // 上面的例子调用结果
- // 函数没有参数
- m1();//[0, 0] // 解构赋值
- m2();//[0, 0] // 使用默认参数
- // x,y 都有值
- m1({x: 3 ,y: 8});//[3, 8] 使用实际传的参数
- m2({x: 3 ,y: 8});//[3, 8] 使用实际传的参数
- //x 有值, y 无值
- m1({x: 3});//[3, 0] 解构赋值 y
- m2({x: 3});//[3, undefined] 解构赋值 y
- //x,y 都无值
- m1({});// [0,0] // 解构赋值
- m1({});// [undefined,undefined] // 解构赋值
- m1({z: 3}) // [0, 0]
- m2({z: 3}) // [undefined, undefined]
三, 参数默认值的位置
通常情况下, 定义了默认值的参数应该是函数的尾参数, 因为这样比较容易看出来, 到底省略了哪些参数. 如果非尾部的参数设置了默认值, 那实际上这个参数是没办法忽略的.
- function f(x = 1, y){
- return [x, y];
- }
- f();//[1, undefined]
- f(2);//[2, undefined]
- f(,2);//SyntaxError: Unexpected token ,
- f(undefined, 2);[1, 2]
- function f(x, y = 5, z){
- return [x, y, z];
- }
- f();//[undefined, 5, undefined]
- f(1);//[1, 5, undefined]
- f(1, ,2);//SyntaxError: Unexpected token ,
- f(1, undefined, 2) // [1, 5, 2]
- f(1, null, 2) // [1, null, 2]
上例中, 有默认值参数不是尾参数的, 这时无法只省略该参数, 而不省略它后面的其他参数, 除非显式输入 undefined. 如果输入 undefined, 将触发该参数等于默认值, null 则没有这个效果.
四, 函数的 length 属性
指定了默认值后, 函数的 length 将返回没有指定默认值的参数个数. 即指定了默认值后, length 属性将失真.
- function f(a, b, c = 5){}
- f.length;// 2
上例中, length 的返回值是函数的参数个数减去指定了默认值的参数个数. 这是因为 length 属性的含义, 该函数预期传入的参数的个数. 某个参数指定了默认值后, 预期传入的参数个数就不包括这个参数了. 注: 如果指定了默认值的参数不是尾参数, 那么该参数后面的参数也不计入 length 属性了.
- function f(c = 5, a, b){}
- f.length;//0
五, 参数作用域
一旦设置了参数的默认值, 函数进行声明初始化时, 参数会形成一个单独的作用域. 等到初始化结束, 这个作用域就会消失. 这种语法行为, 在不设置参数默认值时不会出现.
- 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 defined
这样写也会报错:
- var x = 1;
- function foo(x = x) {
- //...
- };
- foo() // ReferenceError: x is not defined
上例中, 参数 x = x, 形成一个单独作用域. 实际执行的是 let x = x, 由于暂时性死区的原因, 会报 "x 未定义".
六, rest 参数
ES6 引入 rest 参数 (形式为 ... 变量名 ), 用于获取函数的多余参数, 这样就不需要使用 arguments 对象了. rest 参数搭配的变量是一个数组, 该变量将多余的参数放入数组中.
- function add(...values){
- let sum = 0;
- for (var val of values){
- sum += val;
- }
- return sum;
- }
- add(2, 5, 3);//10
注: a. rest 参数之后不能再有其他参数, 否则会报错. b. 函数的 length 属性不包括 rest 参数.
- function f(x, y, ...arg){
- console.log(x + y);
- console.log(arg);
- }
- f(1,1,2,3);// 2 [2, 3]
- f.length;// 2
七, 严格模式
从 ES5 开始, 函数内部可以设定为严格模式. ES6 做了修改, 规定只要函数参数使用了默认值, 解构赋值, 或者扩展运算符, 那么函数内部就不能显示设定为严格模式, 否则或报错.
- function f(a, b = a){
- 'use strict';
- }
- // SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list
- function f(...a){
- 'use strict';
- }
- //Illegal 'use strict' directive in function with non-simple parameter list
这样规定的原因是, 函数内部的严格模式, 同时适用于函数体和函数参数. 但是, 函数执行的时候, 先执行函数参数, 然后再执行函数体. 这样就有不合理的地方, 只有执行到函数体才知道是否该以严格模式执行, 但是参数却先与函数体执行.
两种方法可以规避这种限制: a. 第一种: 设置全局性的严格模式
- "use strict"
- function f(a, b = a) {
- console.log(b);
- }
- f(1);// 1
b. 第二种: 把函数包在一个无参数立即执行的函数里面
- var f = (function () {
- 'use strict';
- return function(value = 42) {
- return value;
- };
- })();
- f();//42
八, name 属性
函数的 name 属性, 返回该函数的函数名. ES6 将其写入标准, 作出了修改: a. 如果将一个匿名函数赋值给一个变量, ES5 的 name 属性会返回空字符串. ES6 的 name 属性会返回实际的函数名.
- //ES6
- var f = function () {}
- f.name;// "f"
b. 如果将一个具名函数赋值给一个变量, 则 ES5 和 ES6 的属性都会返回这个具名函数原本的名字.
- const bar = function baz() {};
- // ES5
- bar.name; // "baz"
- // ES6
- bar.name; // "baz"
c. Function 构造函数返回的函数实例, name 属性的值为 anonymous
(new Function).name;// "anonymous"
d. bind 返回的函数, name 属性值会加上 bound 前缀
(function(){}).bind({}).name;//"bound"
来源: http://www.qdfuns.com/article/46690/fb98785425aad748868583f3984ede30.html