4. Function扩展

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],不能再此使用letconst重定义

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
    一定要爱着点什么,
    恰似草木对光阴的钟情。
    红莲华
    x
    loading...