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]]、作用域链、闭包》
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