### 函数与箭头函数
* 函数function
* 箭头函数 =>{ }
* call、apply、bind、::
* rest参数与扩展运算符
* * * * *
#### **函数function**
> 函数对于任何一门编程语言来说都是核心概念,通过函数可以封装多条语句,而且可以在任何地方任何时候去调用,es5中通过function关键字来声明函数,后面跟一组参数以及函数体。在javascript中函数在定义时不必指定是否有返回值,但实际上任何函数都可以通过return语句后面跟上药返回的值来实现定义返回值,同时函数中也可以嵌套函数,调用函数或者返回一个函数。案例如下:
~~~
function s(){
let b =1
return b
}
~~~
> 上面代码我们可以通过 s() 来调用,返回 1,这个函数执行完return语句后会立即退出,因此return后面的语句永远不会被执行,例如:
~~~
function s(){
let b =1
return b
console.log(2) //这里永远不会被执行
}
~~~
> 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。如下:
~~~
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
~~~
> 需要注意的是:参数变量是默认声明的,所以不能用let或const再次声明。而且函数默认值的作用域只在函数体中,外部不可使用。
> ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中,同时也可以使用map和set这种类似数组的值。案例如下:
~~~
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
~~~
> 从ES5开始,函数内部可以设定为严格模式。《ECMAScript 2016标准》做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
#### **箭头函数 =>{ }**
> ES6允许使用“箭头”(=>)定义函数。如下:
~~~
let f = v => v
// let f 定义函数名,=v 指定了箭头函数的参数v , =>指定了箭头函数的返回值 v
~~~
> 上面的箭头函数等同于:
~~~
let f = function(v) {
return v
}
~~~
> 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。如下:
~~~
let f = () => 5
f() // 5
~~~
> 如果箭头函数内部有语句需要执行,可以使用 { } 来将它们括起来,并指定return语句,如下:
~~~
let f = (a,b) => {
a = 1
b = 2
return a+b
}
f() // 3
~~~
> 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。如下:
~~~
let getTempItem = id => ({ id: id, name: "Temp" });
~~~
> 下面是rest参数与箭头函数结合的例子:
~~~
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]
~~~
#### **箭头函数使用注意点:**
* 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
* 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
* 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。(arguments对象是es5中获取函数参数的对象,本文档不会详细解释,因为用的少,而且ES6已经提供了rest参数)
* 不可以使用yield命令,因此箭头函数不能用作Generator函数。(javascript进阶中会讲到Generator函数和yield命令)
> 上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。代码如下:
~~~
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
let id = 21;
foo.call({ id: 42 });
// id: 42
~~~
> 上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
> 另外,由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。(关于call,apply,bind这三个改变this指向的方法下面有讲到)
> 同时,箭头函数内部也可以嵌套箭头函数,除this指向外与function中嵌套function无异
#### **call、apply、bind、::(注意⚠️:敲黑板)**
* * * * *
#### call
> 在javascript编程中,this指向的是当前对象/调用对象,call方法可以指定函数内部this的指向(即函数执行时的作用域)然后在指定的作用域中调用该函数,如下:
~~~
function a(name,price){
this.name = name
this.price = price
}
function b(name,price){
a.call(this,name,price) // 如果没有使用call,那么当运行到这句代码时this指向的是a
this.category = "B"
}
let c = new b("andy",6000000000000000)
console.log(c)
//{b:{category:"B",name:"andy",price:6000000000000000,__proto__:Object}}
~~~
> call方法的第一个参数就是this所要指向的哪个对象,后面的参数是函数运行时所需要的参数。
> 上面代码中,方法b中调用了a方法,在调用a方法时又使用了call指定了执行作用域为this,此时this指向的是b方法,如果没有使用call,那么在运行这行代码时this指向的是方法a
#### apply
> apply方法的作用域call类似,也是改变this指向,然后再调用该函数,唯一的区别就是,它接收一个数组作为函数执行时的参数,如下:
~~~
function y (o,i){
console.log(o+i)
}
f.call(null , 1 ,1) //2
f.apply(null,[1,2]) //3
~~~
#### bind
> bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数,如下:
~~~
let n ={
name:"andy",
age:22
}
function c(){
let name = "tom"
let age = "25"
console.log(this.name)
console.log(this.age)
}
let f = c.bind(n)
f() // andy 22
~~~
#### :: (注意这两个冒号)
> 这两个冒号可不是我失误打上去的,而是ES7中新增的一种改变this指向的语法,用于替代上面的call、apply、bind,代码如下:
~~~
let foo = {}
let bar = {}
bar::foo //将foo的this指向绑定到bar对象上
~~~
#### **rest参数与扩展运算符**
#### rest参数
> ES6引入了rest参数(‘...a//自定义的变量’),用于获取函数的多余参数,这样就不需要使用arguments对象了,rest参数搭配的自定义变量是一个数组,该变量将多余的参数放入这个数组中。如:
~~~
function add(...a){
let n = 0
for(let i of a){
n+=i
}
return n
}
add(4,6,10) // 20
~~~
> 上例add函数是一个求和函数,利用rest参数可以想该函数传入任意数目的参数。rest参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量,类似的数组操作如下:
~~~
function push(array,..items){
items.forEach(function(item){
array.push(item)
console.log(item)
})
}
let a = []
push(a,1,2,3,4,5,6)
console.log(a) // [1,2,3,4,5,6]
~~~
> 在使用rest参数时一定要注意,rest参数后不可以再有任何其他参数,即:“rest参数只能是函数的最后一个参数”,否则会报错。同时函数的length属性中也不包括rest参数。
#### 扩展运算符
> 扩展运算符是三个点(...),它好比是rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。如:
~~~
console.log(...[1,2,3]) // 1 2 3
console.log(1,...[2,3,4],5) // 1 2 3 4 5
~~~
> 扩展运算符也可以用来合并数组,如:
~~~
//ES5合并数组写法
[1,2,3].concat([4,5]) //[1,2,3,4,5]
//ES6扩展运算符写法
[1,2,3,...[4,5]] //[1,2,3,4,5]
~~~
> 也可以在解构运算中使用扩展运算符,如:
~~~
let [a,...b] = [1,2,3,4]
console.log(a) // 1
console.log(b) // [2,3,4]
~~~
> 在使用扩展运算符对数组赋值时,扩展运算符只能放在最后一位,否则会报错。
> 除了数组以外,扩展运算符也可以将一个字符串/对象/Map/Set等数据结构转为数组。
> 在javascript中,函数只能返回一个值,如需返回多个值,只能返回数组或对象,扩展运算符提供了一种变通的方法,可以将函数返回的数组或对象通过扩展运算符直接传入其他函数。