函数的扩展

1773 字
9 分钟
函数的扩展

函数参数的默认值#

ES6 允许给函数参数赋值初始值

基本用法#

通过给形参赋初始值,具有默认值的参数, 一般位置要靠后(潜规则),只有当传入的参数的值为undefined时才会使用默认值

function log(x, y = "World") {
console.log(x, y)
}
log("Hello") // Hello World
log("Hello", "China") // Hello China
log("Hello", "") // Hello

使用参数默认值时,函数不能有同名参数

// 不报错,但是先传入的同名参数会被后面覆盖
function fun(x, x, y) {
// ...
}
// 报错
function fun(x, x, y = 1) {
// ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

与解构赋值默认值结合使用#

参数默认值可以与解构赋值的默认值,结合起来使用

function fun({ x, y = 5 }) {
console.log(x, y);
}
fun({}); // undefined 5
fun({ x: 1 }); // 1 5
fun({ x: 1, y: 2 }); // 1 2
fun(); // TypeError: Cannot read property 'x' of undefined

上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值

只有当函数fun的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数fun调用时没提供参数,变量x和y就不会生成,从而报错

function fun({ x, y = 5 } = {}) {
console.log(x, y)
}
fun() // undefined 5

上面代码指定,如果没有提供参数,函数fun的参数默认为一个空对象

例:

function fetch(url, { body = "", method = "GET", headers = {} }) {
console.log(method);
}
fetch("https://example.com", {});
// "GET"
fetch("https://example.com");
// 报错

上面代码中,如果函数fetch的第二个参数是一个对象,就可以为它的三个属性设置默认值。 这种写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。

function fetch(url, { body = "", method = "GET", headers = {} } = {}) {
console.log(method);
}
fetch("https://example.com");
// "GET"

上面代码中,函数fetch没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method才会取到默认值GET

问:以下代码有何区别

// 写法一
function m1({ x = 0, y = 0 } = {}) {
return [x, y]
}
// 写法二
function m2({ x, y } = { x: 0, y: 0 }) {
return [x, y]
}

上面两种写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值 写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值

// 函数没有参数的情况
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]
m2({ x: 3 }) // [3, undefined]
// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

函数的 length 属性#

指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了

如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了

(function (a, b = 1, c) {}).length // 1

作用域#

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失

let x = 1
function fun(x, y = x) {
console.log(y)
}
fun(2) // 2

上面代码中,参数y的默认值等于x,调用fun()时,此时形成了封闭的作用域,默认值x指向第一个参数x,也就是2

let x = 1
function fun(y = x) {
let x = 3
console.log(y)
}
fun() // 1

上面代码中,函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。

如果此时,全局变量x不存在,就会报错

rest 参数#

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。 rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中

// ES5 获取实参的方式
function fun(){
console.log(arguments)
}
fun('a','b','c')
// rest 参数
function fun(x, ...args){
console.log(args);// filter some every map
}
fun('a','b','c') // b c

rest 参数必须要放到参数最后,否则会报错

function fun(a, ...args, b) {}
// SyntaxError: Rest parameter must be last formal parameter

函数的length属性,不包括 rest 参数

(function (...args) {}).length // 0
(function (a, b, ...args) {}).length // 2

箭头函数#

ES6 允许使用“箭头”(=>)定义函数

基本用法#

let fun = function (a) {
return a * a
}
// 等价于
let fun = (a) => {
return a * a
}

如果箭头函数当形参有且只有一个的时候,可以省略() 当代码体只有一条语句的时候, 可以省略{},但此时 return 必须省略,return 返回该条语句执行结果

let fun = (a) => {
return a * a
}
//等价于
let fun = a => a * a

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错

// 报错
let getTempItem = id => { id: id, name: "Temp" }
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" })

注意事项#

  • this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值

    function getName() {
    console.log(this.name)
    }
    let getName2 = () => {
    console.log(this.name)
    }
    window.name = "window"
    const obj = {
    name: "obj"
    }
    // 直接调用
    getName() //window
    getName2() //window
    // call 方法调用
    getName.call(obj) //obj
    getName2.call(obj) //window

    上面代码中都是定义在全局作用域中,而箭头函数getName2() this始终指向全局作用域,不随 call() 的调用而改变

    如在回调函数中使用 let div = document.getElementById(“test-div”) div.onclick = function () { // setTimeout(function () { // console.log(this) // 此时this是window // this.style.background = “red” // },2000)

    setTimeout(() => {
    // 此时this是外部作用域div
    this.style.background = "red"
    }, 2000)
    }
  • 不能作为构造实例化对象,也就是说,不可以使用new命令,否则会抛出一个错误

    let Person = (name, age) => {
    this.name = name
    this.age = age
    }
    let me = new Person('tom',30)
    //Uncaught TypeError: Person is not a constructor
    console.log(me)
  • 不能使用 arguments 变量,该对象在函数体内不存在。如果要用,可以用 rest 参数代替

    let fun = () => {
    console.log(arguments);
    }
    fun(1, 2)
    // ReferenceError: arguments is not defined

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

函数的扩展
https://sukksa.github.io/posts/han-shu-de-kuo-zhan/
作者
Sukksa
发布于
2021-11-09
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Sukksa
记录前端学习与日常
公告
博客刚刚上线,内容还在慢慢补齐。欢迎随便逛逛~
分类
标签
最新动态
站点统计
文章
69
分类
16
标签
17
总字数
178,517
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录