1、函数的默认值
ES6 中函数的参数是可以设置默认值的,同时,参数可以是一个表达式。
函数的参数,不能再函数内再次使用 let 和 const 再次声明
1 // ES6 写法 2 function log(x, y = "World") { 3 console.log(x, y) 4 } 5 6 // ES5 写法 7 function log(x, y) { 8 y = y || 'World'; 9 console.log(x, y); 10 } 11 12 // 13 let x = 99; 14 funtion foo(Y = x + 1) { 15 console.log(Y) 16 } 17 foo() // 100 每次调用都会重新计算 X + 1
参数的默认值可以与解构赋值一起使用
1 // 1, 参数必须是一个对象 2 function foo({x, y = 5}) { 3 console.log(x, y); 4 } 5 6 foo({}) // undefined 5 7 foo({x: 1}) // 1 5 8 foo({x: 1, y: 2}) // 1 2 9 foo() // TypeError: Cannot read property 'x' of undefined 10 11 // 2, 第二种方法可以解决,第一种方法不传参会报错的现象 12 // 这种方法是解构后 参数使用默认值 13 function foo({x, y = 5} = {}) { 14 console.log(x, y); 15 } 16 17 foo() // undefined 5 18 19 // 3, 默认值是一个有具体属性的参数,直接解构赋值参数 20 function m2({x, y} = { x: 0, y: 0 }) { 21 return [x, y]; 22 }
参数默认值的位置。 函数传参,参数是一一对应的,除了定义了默认值的尾参数可以省略,如果非尾部设置默认值,则参数不可以省略
1 function f(x, y = 5, z) { 2 return [x, y, z]; 3 } 4 5 f() // [undefined, 5, undefined] 6 f(1) // [1, 5, undefined] 7 f(1, ,2) // 报错 8 f(1, undefined, 2) // [1, 5, 2] 9 10 // 上面代码中,有默认值的参数不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。 11 12 // 只有传参是 undefined 的时候才能出发函数使用默认值
函数的 length 属性 // 如果参数没有指定默认值是,返回函数的参数,如果指定了默认值,则返回默认值之前参数的个数
1 // 返回函数参数的个数 2 (function (a) {}).length // 1 3 4 // 返回 默认值参数之前参数的个数 5 (function (a = 1) {}).length // 0 6 (function (a, b, c = 1) {}).length // 2 7 (function (a = 0, b, c) {}).length // 0 8 (function (a, b = 1, c) {}).length // 1 9 10 // ...rest, ...args 不计入参数的个数 11 (function(...args) {}).length // 0
作用域 // 当函数参数设置了默认值时,函数的参数就会有着独特的作用域,当函数初始化后,此作用域就会消失。此作用域为暂时性死区相当于{ let x }
var x = 1; function f(x, y = x) { // 此处的 x 为参数,实参赋值给形参 x, x 赋值给 y, 此时参数形参的作用域相当于 {let x; y = x} console.log(y); } f(2) // 2
1 let x = 1; 2 3 function f(y = x) { // 参数形参的作用域中 x 未定义,因此 x 指向外层作用域的 x 4 let x = 2; 5 console.log(y); 6 } 7 8 f() // 1
1 function f(y = x) { // 此时 参数形成的作用域及window下 x 均为定义会报错 2 let x = 2; 3 console.log(y); 4 } 5 6 f() // ReferenceError: x is not defined
var x = 1; function foo(x = x) { // 参数形成的作用域相当于 {let x = x} // ... } foo() // ReferenceError: x is not defined
1 let foo = 'outer'; 2 3 function bar(func = () => foo) { // 参数为函数,同上,参数形成的作用域 { func = () => foo} 此作用域内没有 foo 变量就要往上一层作用域中找 4 let foo = 'inner'; 5 console.log(func()); 6 } 7 8 bar(); // outer
var x = 1; function foo(x, y = function() { x = 2; }) { // 参数形成的作用域相当于 { let x;function(){ x = 2}}; y 函数执行,x 的赋值,只是给作用域的x赋值,并不会影响 全局和函数内的变量x var x = 3; y(); console.log(x); } foo() // 3 x // 1
1 var x = 1; 2 function foo(x, y = function() { x = 2; }) { // 参数形成的作用域同上,但函数内的 x 指向参数x,因此 x = 3 赋值给参数 x, 当 y函数执行时,有给参数 x赋值为2,因此打印x为2 3 x = 3; 4 y(); 5 console.log(x); 6 } 7 8 foo() // 2 9 x // 1
总结:函数的作用域链 { global { 传参 { 函数体 } } }
2、 rest 参数
ES6 引入 rest参数(...rest) 这样就不用使用arguments了,rest 是一个数组,而arguments 是一个伪数组,因此rest可以调用数组的使用方法
注意:rest 可以不是第一个参数,但是只能是最后一个参数
3、 name 属性 // 函数的 name属性, 返回函数名
1 // 当匿名函数赋值给变量的时候,返回的是 变量 2 var f = function () {}; 3 4 // ES5 5 f.name // "" 6 7 // ES6 8 f.name // "f" 9 10 // 当具名函数赋值给变量的时候,返回的是具名函数的名 11 const bar = function baz() {}; 12 13 // ES5 14 bar.name // "baz" 15 16 // ES6 17 bar.name // "baz" 18 19 // Function构造函数返回的函数实例,name属性的值为anonymous。 20 (new Function).name // "anonymous" 21 22 // bind返回的函数,name属性值会加上bound前缀。 23 function foo() {}; 24 foo.bind({}).name // "bound foo" 25 26 (function(){}).bind({}).name // "bound "
4、箭头函数 // ES6 允许使用 箭头( => ) 来定义函数
当一个函数不需要或者需要多个参数时, 就使用一个括号来代表参数部分,箭头后没有 {} 表示返回( return ) 箭头后的部分
var f = () => 5; // 等同于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };
当箭头后只有一条语句,同时需要返回时,就可以省略函数的大括号。否则就需要用大括号把它们括起来。
主要注意的是,返回对象的时候不能省略函数的大括号,因为浏览器会将对象的大括号解析成函数的大括号,会报错。当返回一个对象是,
可以省略大括号,而是用小括号括起来表示返回一个对象。
// 报错 let getTempItem = id => { id: id, name: "Temp" }; // 不报错 let getTempItem = id => ({ id: id, name: "Temp" });
当函数只有一条语句同时不需要返回值时,可以用 button.onclick = () => void doSomething();
void 运算符在箭头函数中避免泄露,箭头函数标准中,允许在函数体不使用括号来直接返回值。 如果右侧调用了一个原本没有返回值的函数,其返回值改变后,则会导致非预期的副作用。 安全起见,当函数返回值是一个不会被使用到的时候,应该使用 void
运算符,来确保返回 undefined
,这样,当 API 改变时,并不会影响箭头函数的行为。(这样doSomething 函数执行不管是否有返回,button的点击事件都不会有返回值)
箭头函数很大程度上简化了代码
1 // 正常函数写法 2 [1,2,3].map(function (x) { 3 return x * x; 4 }); 5 6 // 箭头函数写法 7 [1,2,3].map(x => x * x);
使用箭头函数注意点:
1、箭头函数中是没有自己的this, 而是引用外层的 this,一般情况下,函数的this是可变的,但箭头函数的this的指向是固定的
2、不可以当作构造函数,也就是说,不可以使用new
命令,否则会抛出一个错误。
3、不可以使用arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
4、不可以使用yield
命令
1 function Timer() { 2 this.s1 = 0; 3 this.s2 = 0; 4 // 箭头函数 5 setInterval(() => this.s1++, 1000); // this 的指向是 Timer中的 this指向 6 // 普通函数 7 setInterval(function () { // this 的指向是 window 8 this.s2++; 9 }, 1000); 10 } 11 12 var timer = new Timer(); // new 操作符 改变了 Timer中 this的指向,使 this指向 Timer 13 14 setTimeout(() => console.log('s1: ', timer.s1), 3100); 15 setTimeout(() => console.log('s2: ', timer.s2), 3100); 16 // s1: 3 17 // s2: 0
箭头函数不宜使用的地方:
1、定义对象中的方法,一般函数,this是指向这个对象,但是使用箭头后,this就会指向window
2、箭头函数的this是固定的,因此当需要动态的this时,不能用箭头函数
1 const cat1 = { 2 lives: 9, 3 jumps: () => { 4 this.lives--; 5 } 6 } 7 const cat2 = { 8 lives: 9, 9 jumps: function() { // 动态this,谁调用指向谁 10 this.lives--; 11 } 12 } 13 cat1.jumps() // this 指向 window 14 cat2.jumps() // this 指向 cat2
5、尾调用优化 // https://es6.ruanyifeng.com/#docs/function#%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96
6、尾逗号 // ES2017 允许函数的最后一个参数有尾逗号,这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。
此前,函数定义和调用时,都不允许最后一个参数后面出现逗号,否则会报错。
7、Function.prototype.toString() // ES2019 对函数实例的toString()
方法做出了修改。toString()
方法返回函数代码本身,以前会省略注释和空格。修改后的toString()
方法,明确要求返回一模一样的原始代码。
1 // 修改前,会省略注释和函数名和()之间的空格 2 function /* foo comment */ foo () {} 3 4 foo.toString() 5 // function foo() {} 6 7 // 修改后,返回原代码 8 function /* foo comment */ foo () {} 9 10 foo.toString() 11 // "function /* foo comment */ foo () {}"
8、 try...catch 语句参数省略
1 // 以前明确要求catch命令后面必须跟参数,接受try代码块抛出的错误对象。 2 try { 3 // ... 4 } catch (err) { 5 // 处理错误 6 } 7 8 // ES2019 做出了改变,允许catch语句省略参数。 9 try { 10 // ... 11 } catch { 12 // ... 13 }
-----不生产代码,只是代码的搬运工