函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。通俗的说,函数是利用特定语法,将一段代码打包在一起,每次调用函数就可以让这个代码块内的代码全部执行,复用代码。
要注意的是,函数跟循环不相似,循环是重复一定次数的执行代码,函数虽然可以重复执行代码,但是它很灵活,可以任意决定它调用的时机
声明函数
声明函数有两种方式
// 方式一
let fn1 = function () {
// 代码块
}
// 方式二
function fn2 () {
// 代码块
}
fn1和fn2都是函数名字,这是两种不同声明方式写的位置不同。
函数体内的代码,在声明时不会执行的。必须在调用函数后,才能执行
调用函数
函数调用只需要将函数名字加括号即可,一个函数可以重复调用无数次
let fn = function () {
// 代码块
}
fn()
调用后,函数内的代码块就会执行
例如:计算1 + 2的值,并且输出
let add = function () {
let res = 1 + 2
console.log(res)
}
// 第一次调用
add() // 此时函数调用,函数体内的代码执行,输出3
// 还可以再次调用
add() // 此时再次调用,继续执行代码,输出3
...
这样我们就可以得到一个能自动计算1+2值的函数,但是这样很蠢…因为每次都只会计算1+2,计算其他值,就需要再写函数,很麻烦。所以…看下面
函数参数
为了不让函数内部的代码全部都固定死、每次执行都是固定结果,所以函数可以在调用时,可以从外部注入数据到内部使用,这种行为是通过函数参数完成的。
想要使用函数参数功能,从外部注入数据,那么函数内就需要提前有占位的符号,这种符号就类似变量功能,称为形参,而注入的数据成为实参
// 例如计算,1 + n的结果
// 此时函数内需要一个n作为占位符,那么n需要提前声明
let add = function (n) { // 这个n是声明函数内会使用n(n就是形参)
let res = 1 + n // 这里使用n占位
console.log(res)
}
// 调用时,可以传入不同的数据,作为此次调用n的实际数据,
add(5) // 此时n=5,则执行后,输出:6 (传入的5就是此次传入的实参)
add(10) // 输出:11
...
add(-10) // -9
这样就得到一个能计算1+n的函数,如果需要的参数很多,依次写上占位符,将实参一次写在调用的括号内即可
实参和形参的顺序是对应的,第一个形参接收第一个实参…以此类推
形参需要遵守变量命名规范
let fn = function (a,b,c,d,e) {}
fn(1,2,3,4,5)
虽然形参和实参可以让函数使用起来非常灵活,但是这样并不是万能的。
例如,需要计算所有传入的实参的和。
let add = function (a, b, c){
// 如果实参个数不确定,那么形参就不好写,因为写少了,多出来的实参就无法接收,写多了,可能又会没有值传入
let res = a + b + c
}
add(1, 2) // 此时传入2个实参,形参3个,那么最后一个c就没有值就是undefined,此时进行运算会得到NaN
通过上面例子,发现形参实参通常实在个数确定的时候使用,如果数量不确定就很尴尬…所以请看下面
Rest参数
rest是剩余的意思,rest参数是用于函数参数不确定,每次传入的数量都不同,或者函数参数过多,但是又不想写那么多形参的情况,那么rest可以一次性全部接收
// 例如,计算传入的所有数据
let add = function (...rest) {
let res = 0
for (let value of rest) {
res += value
}
console.log(res)
}
add(1, 2) // 实参2个,此时rest接收到就是两个数据, 输出 3
add (3, 5, 4) // 实参3个,rest内就是3个数据。输出 12
通过上面例子,可以看出,rest非常灵活,可以用于接收所有实参
如果需要接收部分参数,也可以这么做
let add = function (a, b, ...rest) {}
add(1,2,3,4,5) // 此时 a = 1 b = 2 rest = [3,4,5]
所以,rest是用于接收剩余没有被形参所接收的全部参数的。如果一个形参都没有,只有rest,那么rest接收所有,如果rest前面有形参,则先优先其他形参接收,剩余没有被接收的都在rest内。所以rest的名字非常形象【剩余参数】
请注意的是,rest必须是最后一个参数,rest参数后面不能写其他形参。下面的写法都是错误的
let fn = function (...rest, a){}
let fn1 = function (a, ...rest, b){}
let fn2 = function (...rest, ...rest2) {}
// 这三种都是错的。
返回值
上面的所有函数,数据只能从外传入内,然后在内部使用。无法将函数内的运算结果返回出外部使用。这是因为函数没有设置返回值。
函数设置返回值使用return
命令,return
的作用是停止函数执行,并立刻返回后面的值
let add = function () {
let res = 1 + 3
return res
}
add() // 4
函数内一旦执行到return
的行,就会立刻返回值。下一行之后的代码都永远不会执行。
this
函数的this指向,指向调用函数的对象,如果没有,则指向window
this是一个关键字,js赋予它特殊的意义,最终决定this指向的是调用对象,而不是执行环境.其实函数的调用是这样的window.fn() 只不过window可以省略,函数的this指向可以通过方法人为改变
function fn(){
console.log(this)//指向window
function zq(){
console.log(this)//指向window
}
zq()
}
// 自执行this指向window
fn()
let o = {
a:{
b:{
c:function(){console.log(this)}
}
}
}
o.a.b.c() // this指向 {c:f} 这个对象
let fn = o.a.b.c // 把地址交出来 就和o没啥关系了
fn() //this指向 window
修改this指向
call(需要this指向的对象,实参1,实参2)
apply(需要this指向的对象,[实参1,实参2])
前者会自动执行函数
bind(需要this指向的对象,实参1,实参2) 返回函数
bind不会自动执行 任然需要再加一个() 执行函数
window.a = 1
let obj = {
a:2
}
let obj1 = {
a:2
}
function fn(a, b, c){
console.log(this.a)
}
fn.call(obj, 1, 2, 3)
fn.apply(obj1, [1, 2, 3])
fn.bind(obj, 1, 2, 3)()
箭头函数
箭头函数是function
函数在某些情况的特殊写法
let fn = function () {
return 1 + 3
}
// 上面fn函数执行后,立刻返回1+3的值,对应的箭头函数写法
let afn = () => 1 + 3
箭头函数=>
之后的内容就相当于function
函数return之后的内容。但是这种写法只适合上面的情况。
let fn = function () {
let a = 3
let b = 2
let c = a ** b
return c
}
// 上面的函数,函数体内有多条语句时,需要下面写法
let afn = () => {
let a = 3
let b = 2
let c = a ** b
return c
}
通过上面两个例子可以发现,如果箭头函数内有多条语句时,需要写花括号。没有花括号的适合,箭头后只能写一条语句,并且语句的结果会默认被return,有花括号时,如果需要返回值,需要明确写上return
如果需要传参时:
let add = function (a, b, ...rest) {
let res = a + b
for(let value of rest) {
res += value
}
return res
}
// 以上函数等价于下面箭头函数
let fn = (a, b, ...rest) => {
let res = a + b
for(let value of rest) {
res += value
}
return res
}
所以参数写法跟function函数是完全一致的。
函数在对象内的简洁写法
如果一个函数是对象内的某个属性的值,可以有简洁写法
let o = {
fn: function () {
}
}
// 以上是正常写法,跟下方写法完全等价
let o1 = {
fn () {
}
}