4. Function扩展
NOxONE 2/14/2022 ES6
ES6 对 Function 做一些扩展,引入很多新特征,下面来逐个介绍
# 1. 函数默认参数
函数定义时,可以指定默认参数,在未传入参数或参数为undefined
时,会使用该默认参数
function Obj(id = 107, name = 'jack') {
this.id = id
this.name = 'jack'
}
const obj = new Obj() // {id: 107, name: 'jack'}
function add(x, y = 1) {
return x + y
}
add(1) // 2
add(1, 3) // 4
但是要小心,当指定默认函数参数,在函数调用时传参会发生隐式解构
,即let [arg1,...,arg2] = [...arguments]
,不能再此使用let
或const
重定义
function fn(x = 5) {
// 隐式解构:let [x=5] = [...arguments]
let x = 1 // error
}
// 下面这个可以
function fn(x = 7) {
if (x) {
let x = 1 // 不在一个语句块里
console.log(x) // 1
}
console.log(x) // 7
}
惰性求值
let x = 99
function foo(p = x + 1) {
alert(p)
}
foo() // 100
x = 100
foo() // 101
指定了默认值后,函数的length
属性会失真,因为 length 属性含义为函数预期传入
的参数个数
;(function (a, b, c = 5) {}).length(
// 2
function (a, b = 1, c) {},
).length // 1 只记录默认值前面的
# 2. rest 参数
rest 参数形式为...args
,会将获取函数的多余参数并将其放进数组中
function(a,...b){}
// 等价于
function(){
let args = [...arguments]
let a = args[0]
let b = args.slice(1)
}
// 注意rest只能是传参的末尾,放在其他位置会报错
function(a,...b,c){} // 报错
应用
function add(n, ...values) {
let sum = 0
for (let val of values) {
sum += val
}
return n * sum
}
add(10, 1, 2, 3) // 60
# 3. name 属性
函数的name
属性返回该函数的函数名
function fn(){ ... }
fn.name // 'fn'
var bar = function foo(){ ... }
bar.name // 'foo'
// 特殊的
var fn = function(){ ... }
fn.name // 'fn'
// bind()返回的函数会加上‘bound’前缀
function fn(){ ... }
fn.bind(null).name // 'bound fn'
# ☆4. 箭头函数
# 4.1 箭头函数语法
箭头函数是 ES6 对函数进行的最大扩展,以下是箭头函数的语法
var fn = (n) => n + 1
// 等价于
var fn = function (n) {
return n + 1
}
var fn = (a, b) => a + b
var fn = (a, b) => {
return a + b
}
若直接返回一个对象有必要时需要加个括号
var getId = (id) => ({ id: id, name: 'jack' })
// 等价于
var getId = (id) => {
return { id: id, name: 'jack' }
}
若箭头函数只有一行语句,且不需要返回值,可以采用void
(慎用,会被同事特殊关怀滴~)
var fn = (arr) => void arr.push(1)
var arr = [3, 2]
fn(arr) // [3,2,1]
# 4.2 箭头函数和参数解构结合
var obj = {
id: 107,
name: 'jack',
age: 18,
}
var getIdAndName = ({ id, name }) => name + ':' + id
getIdAndName(obj) // jack:107
# 4.3 箭头函数与 rest 参数结合
let numbersToArr = ...num => num
numbersToArr(1,2,3) // [1,2,3]
let fn = (head,...end) =>[head, end]
fn(1,2,3) //[1,[2,3]]
# ☆4.4 箭头函数注意点
简单记忆就是:4 个 "达咩"
- 不存在
this
,标准的来说箭头函数执行上下文中无this
,实际来自于沿作用域链访问到的 this - 不存在
arguments
- 不可以
new
,即不可以作为构造器函数使用 - 不可以
yield
详细说明一下箭头函数的 this
// 用Babel编译ES6代码来解释this
// ES6
function fn() {
let f = () => {
console.log('id:', this.id)
}
}
// ES5
function fn() {
var _this = this
var f = function () {
console.log('id:', _this.id)
}
}
由于箭头函数作用域中没有自己的 this,故call、apply、bind
对其无效
function fn() {
return () => {
return () => {
return () => {
console.log('id:', this.id)
}
}
}
}
var f = fn.call({ id: 1 })
// 指定fn上下文的this为{id:1},f为return出来的箭头函数,这时候this一直为fn上下文中的this,
// 故箭头函数没有真正意义上的this,也意味着bind、call、apply对它无效!
var t1 = f.call({ id: 2 })()() // id: 1
var t2 = f().call({ id: 3 })() // id: 1
var t3 = f()().call({ id: 4 }) // id: 1
下面的例子能帮助你很好的理解箭头函数里的this
,前提你得了解VO
,AO
,执行上下文
等概念《一文搞懂执行上下文、VO、AO、Scope、[[scope]]、作用域链、闭包》 (opens new window)
var obj = {
name: 'jack',
sayName: () => alert(this.name),
sayName2() { alert(this.name) }
}
obj.sayName2() // 'jack'
obj.sayName() // undefined, WTF???
// 冷静详细分析一下
// 在全局执行obj.sayName2()和obj.sayName()
// 1.创建全局上下文gContext
gContext = {
VO:{
obj:{ ... },
this: window
}
Scope:[ gContext.VO ],
}
// 2.创建sayName2Context
sayName2Context = {
AO:{
arguments:{ length:0 },
this:obj // 普通函数执行上下文有this
},
Scope:[ sayName2Context.AO, gContext.VO ]
}
// 执行函数,从Scope作用域链查询this,
// 在sayName2Context.AO中找到this为obj,故打印obj.name:'jack'
// 3.创建sayNameContext
sayNameContext = {
AO:{
arguments:{ length:0 },
// 箭头函数执行上下文无this(区别在这里!!!)
},
Scope:[ sayNameContext.AO, gContext.VO]
}
// 执行函数,从Scope作用域链查询this,
// 在gContext.VO中找到this为window,故打印window.name:undefined