前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你真的了解ES6函数特性么?

你真的了解ES6函数特性么?

作者头像
JowayYoung
发布2021-04-30 16:09:19
4470
发布2021-04-30 16:09:19
举报
文章被收录于专栏:JowayYoung谈前端

hello大家好,又见面了。 假期转瞬即逝,年后开工的第一天,早上是真的不想起床吖,为了不迟到闭着眼睛就穿衣服。 好啦好啦,步入正题啦,打起精神哦!

前言

函数是所有编程语言中重要的组成部分,在Es6出现之前 JavaScript的函数语法一直没有太大的变化,从而遗留了很多问题和隐晦的做法,导致实现一些功能需要编写很多代码。

函数形参默认值

JavaScript函数有一个特别的地方,就是无论在函数形参里定义了多少参数,都可以传入任意数量的参数,但是有的情况下,我们的参数只是可填,这样的话我们还在函数体呢写一堆逻辑从而导致代码冗余,还好Es6版本出现了函数默认值。

我们用Es5和Es6代码来比对一下

Es5处理默认参数
代码语言:javascript
复制
function person(name, age) {
    name = typeof(name) != "undefined" ? name : `蛙人${+ new Date()}`
    age = typeof(age) != "undefined" ? age : 24
    
}
person()

上面example中是Es5这样处理默认参数值的,假如我们参数多的话,这么写代码的话会造成非常冗余的,于是Es6就出现函数参数默认值。

Es6处理默认参数
代码语言:javascript
复制
function person(name = "蛙人", age = 24) {
    console.log(name, age)
}
person()  // 蛙人 24
person("张三", 30) // 张三 30
person(null, null) // null null

上面example是Es6中处理的默认参数,可以看到代码非常简化,上面代码可以看到参数传入了null,对于默认参数null也是一个合法值,这种情况下只有函数参数为undefined时才会使用默认值。

函数参数表达式

关于默认参数值,最有趣的特性可能就是非原始值传参了,也可以把默认参数定义为函数or 变量

代码语言:javascript
复制
function defaultName() {
    return "蛙人"
}
function person(name = defaultName()) {
    console.log(name)
}
person("张三") // 张三
person() // 蛙人

需要注意的是,默认参数的表达式不是一创建函数就立刻执行的,而是当该函数person被调用的时候并且没有传入参数才会执行。

上面example中,如果不传参才会调用默认值的defaultName函数。

下面来看一下默认参数传入变量。

代码语言:javascript
复制
let defaultName = "蛙人"
function person(name = defaultName) {
     console.log(name)
}
person("张三") // 张三
person() // 蛙人
代码语言:javascript
复制
function person(name, nickName = name) {
     console.log(name, nickName)
}
person("张三") // 张三 张三
person("蛙人", "掘金蛙人") // 蛙人 掘金蛙人

上面example中,第一个代码块的里面我们都能看的懂,只不过把之前的函数换成了变量。看第二个代码块里的代码,我们把nickName参数默认值设置成了第一个参数name参数,这是在引用参数默认值的时候,只允许引用前面参数的值,相当于函数参数就是定义的变量,我们后面的变量可以访问前面变量的,但是只限制在于当前作用域中,这个函数形参里就是当前作用域。我们再看一个例子。

代码语言:javascript
复制
function person(name = nickName, nickName) {
     console.log(name, nickName)
}
person("张三") // 张三 undefined

上面example中,第一个参数默认值是第二个参数,这时运行会抛出一个错误,因为这时在定义第二个变量前去访问,会造成暂时死区,如果不明白暂时死区的可以去看我的上一篇文章。《一看就懂的var、let、const三者区别》

函数参数默认值对arguments的影响

当使用函数默认参数时,arguments对象的行为会与以往不同

Es5非严格模式下使用arguments

Es5非严格模式下,函数命名参数的变化会体现在arguments对象上,arguments获取的是当前函数的实参,arguments在非严格模式下它跟形参是映射关系,就是形参有变化arguments跟着变。

代码语言:javascript
复制
function test(a, b) {
    console.log(a == arguments[0]) // true
    console.log(b == arguments[1]) // true
    a = "a"
    b = "b"
    console.log(arguments) // ["a", "b"]
}
test(1, 2)

上面example中,在非严格模式下,命名参数的变化会同步更新到arguments对象中。当a参数的变化,会映射到arguments[0]对象上。

Es5严格模式下使用arguments

下面我们再来看一下严格模式下的arguments

代码语言:javascript
复制
 function test(a, b) {
    'use strict';

    console.log(arguments)  // [1, 2]
    b = 10
    console.log(arguments) // [1, 2]
}
test(1, 2)

上面example是严格模式下的,可以看出当我们改变参数b时,再次打印arguments对象,它还是初始化值。在严格模式下JavaScript中取消了arguments对象这个令人困惑的行为,无论参数如何变化,arguments对象不再随之改变。

Es6中使用默认参数值对arguments的影响

在Es6中,如果一个函数使用了默认参数值,那么arguments对象的行为都将与JavaScript中的严格模式下保持一致。

代码语言:javascript
复制
 function test(a, b = 2) {
    a = 12
    b = 10
    console.log(arguments) // [1]
}
test(1)

上面example中,arguments对象打印出[1]是因为arguments对象获取的是实参,我们可以看到实参参数就传了一个值,所以arguments对象就只有一个值。再看第二点,ab的参数都改变了值,但是arguments对象还是没有改变,这就是上面说的,如果一个函数使用了默认参数值,那么arguments对象的行为都将与JavaScript中的严格模式下保持一致

处理无命名参数

在js中函数参数数量是任意的,当传入更少的数量,默认参数的特性可以有效的简化函数声明的代码。当传入更多的数量,Es6也同样提供了更好的方案。

Es5中获取无命名参数
代码语言:javascript
复制
function test(a, b, c) {
    console.log(arguments) // [1, 2, 3]
}
test(1, 2, 3)

上面example中,arguments对象虽然也可以实现获取所有的参数,但是呢如果我们想获取第二个参数之后的所有参数,那么还得循环去排除。

Es6中获取无命名参数
代码语言:javascript
复制
function test(...parmas) {
    console.log(params) // [1, 2, 3, 4]
}
test(1, 2, 3, 4)
代码语言:javascript
复制
function test(a, b, ...params) {
    console.log(params)
}
test(1, 2, 3, 4)

上面example中,第一个代码块里实现了在Es6中获取全部的参数,可是还不满足我们的需求。那么看第二个代码块里的代码就实现了,我们获取第二个参数后面所有的参数。

Es6获取无命名参数弊端

首先,每一个函数只能声明一个获取不定参数,而且只能放在函数的末尾,否则会报错。

代码语言:javascript
复制
function test(...params, a, b) {
 
}
test()

上面example中,会抛出错误,声明了不定参数数之后,就不能继续在后面声明参数。 还有一点,不定参数不能定义在对象字面量的setter中,因为setter函数只接收一个函数,写成不定参数之后就会是一个数组,这样就会导致程序异常。

代码语言:javascript
复制
let obj = {
  set name(...params) {

  }
}

函数name属性

JavaScript中所有的函数都有一个name属性,该属性保存的是该函数名称的字符串。没有名称的函数也仍然有name属性,该name属性值为空字符串。

代码语言:javascript
复制
function person() {}
let test = function() {}

console.log(person.name) // person
console.log(test.name) // test

上面example中,person函数name属性值为"person",对应着声明时的函数名称。匿名函数表达式test函数的name名称,对应着被赋值为匿名函数的变量。

name属性的特殊情况

我原来以为每个函数的name名称都是对应着当前的函数名,后来发现并不是这么回事。下面来看一下函数的特殊情况

代码语言:javascript
复制
var person = {
    get getName() {
        return "蛙人"
    }
}
console.log(Object.getOwnPropertyDescriptor(person, 'getName').get.name)  // get getName

function test() {}
console.log(test.bind().name) // bound test

上面example中,person.getName是一个取值函数getter,所以它的函数名称get getName,如果是setter函数的话那么名称会有带有前缀set。通过bind创建的函数,它的名称带有"bound"前缀。

箭头函数

Es6中箭头函数是其中最有趣的特性,箭头函数是一种使用箭头=>定义函数的新语法,但是它与传统的JavaScript函数有些不同,具体看下面几点。

  • 没有thissuperarguments
  • 不能通过new关键字调用
  • 没有原型prototype
  • 不可以改变this指向
  • 不支持重复的命名参数

箭头函数和传统函数一样都有一个name属性,这一点是不变的。

箭头函数语法
代码语言:javascript
复制
let person = () => "蛙人"

// 相当于下代码

function person() {
 return "蛙人"
}

上面example中,当箭头函数右侧的表达式求值后会立即返回。

箭头函数参数
代码语言:javascript
复制
let getName = val => val

// 相当于下代码

function getName(val) {
    return val
}

当箭头函数只有一个参数时,就可以省略括号,直接写参数名。如果要传入两个或多个参数,则就需要带上括号。看下面例子

代码语言:javascript
复制
let sum = (a, b) => a + b

// 相当于下代码

function sun(a, b) {
    return a + b
}

如果你想返回一个对象字面量,可以这样写

代码语言:javascript
复制
let getObj = () => ({name: "蛙人", age: 24}) // {name: "蛙人", age: 24}

// 相当于下代码

function getObj() {
    return {
     name: "蛙人",
        age: 24
    }
}
箭头函数没有this

箭头函数的this值,取决于函数外部非箭头函数的this值,如果上一层还是箭头函数,那就继续往上找,如果找不到那么this就是window对象

代码语言:javascript
复制
let person = {
    test: () => {
        console.log(this)
    },
    fn() {
        return () => {
            console.log(this)
        }
    }
}
person.test()  // window
person.fn()()  // person对象

上面example中,可以清楚的看到箭头没有this,那么它的this只会去找外层的非箭头函数的函数。

箭头函数没有arguments对象

同样箭头函数也没有arguments对象,但是如果它外层还有一层非箭头函数的话,就会去找外层的函数的arguments对象, 如下

代码语言:javascript
复制
let test1 = () => console.log(arguments)  // 执行该函数会抛出错误


function test2(a, b, c) {
    return () => {
        console.log(arguments) // [1, 2, 3]
    }
}
test2(1, 2, 3)()

上面example中,可以清楚的看到当前的箭头函数没有arguments对象,然而就去它的外层去找非箭头函数的函数。注意:箭头函数找arguments对象只会找外层非箭头函数的函数,如果外层是一个非箭头函数的函数如果它也没有arguments对象也会中断返回,就不会在往外层去找了。看下面例子

代码语言:javascript
复制
function test(a) {
    return function() {
        return () => {
            console.log(arguments) // []
        }
    }
}
test(1)()()

上面example中可以看到,里面的箭头函数往外层找非箭头函数的函数,然后不管外层这个函数有没有arguments对象都会返回。只有它是非箭头函数就可以,如果外层是箭头函数还会继续往外层找。

箭头函数不能用new关键字声明
代码语言:javascript
复制
let test = () => {}
new test() // 抛出错误,找不到constructor对象
箭头函数没有原型prototype

切记,箭头函数没有原型,有可能面试官会问,JavaScript中所有的函数都有prototype属性吗

代码语言:javascript
复制
let test = () => {}
test.prototype // undefined
箭头函数不能改变this指向
代码语言:javascript
复制
let person = {}
let test = () => console.log(this)

test.bind(person)()
test.call(person)
test.apply(person)

上面example中,改变this指向的方法都不会抛出错误,但是都无效,都不能改变this指向。

箭头函数不能重复命名参数
代码语言:javascript
复制
let sum = (a, a) => {} // 抛出错误,参数不能重复

觉得写的不错那就点个赞叭!

结语

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IQ前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 函数形参默认值
    • Es5处理默认参数
      • Es6处理默认参数
      • 函数参数表达式
      • 函数参数默认值对arguments的影响
        • Es5非严格模式下使用arguments
          • Es5严格模式下使用arguments
            • Es6中使用默认参数值对arguments的影响
            • 处理无命名参数
              • Es5中获取无命名参数
                • Es6中获取无命名参数
                  • Es6获取无命名参数弊端
                  • 函数name属性
                    • name属性的特殊情况
                    • 箭头函数
                      • 箭头函数语法
                        • 箭头函数参数
                          • 箭头函数没有this
                            • 箭头函数没有arguments对象
                              • 箭头函数不能用new关键字声明
                                • 箭头函数没有原型prototype
                                  • 箭头函数不能改变this指向
                                    • 箭头函数不能重复命名参数
                                      • 觉得写的不错那就点个赞叭!
                                      • 结语
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档