函数
函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。通俗的说,函数是利用特定语法,将一段代码打包在一起,每次调用函数就可以让这个代码块内的代码全部执行,复用代码。
要注意的是,函数跟循环不相似,循环是重复一定次数地执行代码,函数虽然可以重复执行代码,但是它很灵活,可以任意决定它调用的时机。
1.声明函数
声明函数有两种方式:
// 方式一
let fn1 = function () {
// 代码块
}
// 方式二
function fn2 () {
// 代码块
}
fn1和fn2都是函数名字,这是两种不同的声明方式。
函数体内的代码,在声明时是不会执行的。必须在调用函数后,这些代码才会执行。
2.调用函数
函数调用只需要将函数名字加括号即可,一个函数可以被重复调用无数次。
let fn = function () {
// 代码块
}
fn()
调用后,函数内的代码块就会被执行。
例如:计算1 + 2的值,并将值输出。
let add = function () {
let res = 1 + 2
console.log(res)
}
// 第一次调用
add() // 此时函数调用,函数体内的代码执行,输出3
// 还可以再次调用
add() // 此时再次调用,继续执行代码,输出3
...
这样我们就可以得到一个能自动计算1+2值的函数。
3.函数参数
为了不让函数内部的代码全部都固定死、每次执行都是固定结果,所以函数在调用时,可以从外部注入数据到内部使用,这种行为是通过函数参数完成的。
想要使用函数参数功能,从外部注入数据,那么函数内就需要提前有占位的符号,这种符号就类似变量功能,称为形参,而注入的数据成为实参。
// 例如计算,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
通过上面例子,发现形参和实参通常是在个数确定的时候使用,如果数量不确定使用起来就很不方便。
4.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) {}
// 这三种都是错的。
5.返回值
上面的所有函数,数据只能从外传入内,然后在内部使用。无法将函数内的运算结果返回出外部使用。这是因为函数没有设置返回值。
函数设置返回值使用return命令,return的作用是停止函数执行,并立刻返回后面的值
let add = function () {
let res = 1 + 3
return res
}
add() // 4
函数内一旦执行到return所在的行,就会立刻返回值。下一行以及之后的代码将永远不会执行。
6.箭头函数
箭头函数是function函数在某些情况下的特殊写法
let fn = function () {
return 1 + 3
}
// 上面fn函数执行后,立刻返回1+3的值,对应的箭头函数写法
let fn = () => 1 + 3
箭头函数=>
之后的内容就相当于function
函数return之后的内容。但是这种写法只适合上面的情况。
let fn = function () {
let a = 3
let b = 2
let c = a ** b
return c
}
// 上面的函数,函数体内有多条语句时,需要使用下面的写法
let fn = () => {
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函数是完全一致的。
7.函数在对象内的简洁写法
如果一个函数是对象内的某个属性的值时,可以有简洁的写法
let o = {
fn: function () {
}
}
// 以上是正常写法,跟下方写法完全等价
let o1 = {
fn () {
}
}
8.其他注意事项
立即执行函数(定义时就立马使用):不是函数的一种,而是函数执行的一种。主要是创建一个单独的作用域,防止让变量成为全局变量,从而污染全局。
(function(){}())
(function(){})()
+function(){}()
-function(){}()
~function(){}()
!function(){}()
函数声明:函数可以先使用后声明(变量提升)且不会报错,不过最好还是先声明后使用。
形参的命名: 见名知意。
形参没有接收到实参的话则形参的值为undefined。
关于形参个数和实参个数的几种情况:
①没有形参,只有实参
不作反应,只运行
②有形参,不传实参
形参接收undefined
③有形参,有实参
Ⅰ.个数一致 按顺序接收
Ⅱ.个数不一致
⑴形参多于实参
先按顺序一一对应接收,没有接收到实参的就是undefined
⑵实参多于形参
先按顺序接收,多余的实参就不去管了
一般需要多少形参和实参就写多少形参和实参
...rest接收剩余参数,其中rest可以自定义名字即 ...名字
箭头函数:
(形参) => {代码块}
就算没有形参,括号也必须要写
只有一个形参,括号可以不写
有多个形参,括号必须写
只需要return一个数据时则可以省略{}和return
箭头函数的this是固化的,或者说箭头函数根本就没有this,其this就是外层对象的this;如果外层对象也没有this,则其this指向window顶层对象
形参的默认值:
function zhuque(val,val2 = 2){
// 如果val2可以接受到 实参 则val2 = 实参值 如果接收不到 则val2 = 2
}
例:如果val2可以接受到实参,则val2 == 实参值;如果接收不到,则val2 = 2。
不需要声明形参:
形参不需要声明,声明的话会报错:重复声明。
修改this指向:
window.a = 0
let obj1 = {
a: 1
}
let obj2 = {
a: 2
}
function fn(num1, num2, num3) {
console.log(this.a); // 0
console.log(num1, num2, num3);
}
call():第一个参数为this将要修改指向的对象,有其他参数时在后面用逗号 一一跟上即可,该函数可以主动执行。
fn.call(obj1, 1, 2, 3)
apply():第一个参数为this将要修改指向的对象,有其他参数时需要用数组的[]符号包一下,该函数可以主动执行。
fn.apply(obj2, [1, 2, 3])
bind():第一个参数为this将要修改指向的对象,有其他参数时在后面用逗号一一跟上即可,该函数不会主动执行,但会return函数本体,此时再加上一个括号即可执行。
fn.bind(obj1, 1, 2, 3)()
返回值:如果没有手动给函数定义return的内容,那么函数执行完毕后默认return一个undefined;
return可以返回任意数据类型的值;
return后面用逗号表达式接上了几条数据时只会返回最后一条数据;
return 1, "ysc","ysc666"; //此时返回ysc666
如果要返回多条数据,可根据情况将数据放置在数组或者{}对象里;
return后可以接运算表达式,此时会将表达式运算到最简结果后再把结果返回出去。
this:this就近指向调用函数的对象。如果没有调用函数的对象,则指向window对象。this指向与函数在哪声明无关,而是和调用环境以及调用者有关;事件函数里this指向事件触发者。