函数在Javascript中是一等公民。
因为,在Javascript中,凡是能使用变量的地方,就能使用函数,函数被当成了一个值,它与Javascript中基本类型值的地位不相上下。
不深入了解函数的特性,就不能把函数用的出神入化——我是这么觉得,所以我今天就来深入一下“一等公民”
创建函数
要使用函数必须得先创建函数(好像是废话...),而函数有好几种创建方式,你想想看,你知道几种?
函数声明
使用 function 关键字,后面跟上函数的名称,来声明一个函数。这种是最普通的创建函数的方式
function eat(){
console.log('eat an egg')
}
函数表达式
还是用 function 关键字来声明,但是后面不用跟函数名(这个叫匿名函数),然后把函数赋值给一个变量,这种方式用的挺多!
const myMoney = function (){
return 9999999999
}
箭头函数
箭头函数是个新东西,ES6提供的;箭头函数主要的作用是:当你需要处理一些比较复杂的 “小任务”(不需要考虑复用性),但又没必要去声明一个“大函数”来处理时,直接使用箭头函数来完成你的任务!它立即调用后会马上返回你要的结果,把值传递给你设定的接受变量以后,它就从内存中消失了,可谓 “用后即焚”。箭头函数的写法如下:
const MarchWage = 7000
const JulyWage = 8750
const OctuberWage = 11000
const myMoney = () => {
// 这里有一通操作,比如除去五险一金,除去税等等
let temp = MarthWage * (1 - 0.5%)
temp / (2000*0.13 - 177)
.....
.....
.....
return temp // 终于算完了你的工资
}
像上面这样的 “小任务” 只是计算比较复杂,一个表达式搞不定,专门声明一个函数又没有复用性可言(你只会在这个地方用到一次,后面就用不到了),所以直接使用箭头函数啦!方便、快捷、省事
其实箭头函数跟匿名函数差不多,它只是不用写 function 关键字。不过箭头函数存在一个问题:它没有自己的上下文执行环境—— this 对象。箭头函数中的 this 表示的是它外部作用域下的 this
函数的属性与内置方法
name
name 属性返回函数的名称,如果你需要判断一个作为参数传入的函数,它的名称是否是你需要的那个,就可以用上这个属性
function A(){
console.log('I am func A')
}
function B(f){
if(f.name == 'A'){
console.log('I find func A')
}else{
console.log('This is not the func A')
}
}
B(A)
length
length 属性返回函数定义的参数个数——形参,注意,是定义时的参数的个数,而不是你实际传入的参数的个数
function A(a, b){
console.log('my params')
}
console.log(A.length) // 2
length 属性在你需要实现函数重载的时候可能会用到,例如根据函数的入参个数来实现重载
arguments
arguments 属性是一个只能在函数内部使用的对象,它存储了函数的参数以及一个叫做 callee 和一个叫做 length 的属性。它可以通过[]的方式来访问函数参数:
function A(a, b){
console.log(arguments[0])
console.log(arguments[1])
}
A('p1', 'p2') // p1 p2
在函数内部怎样实现自己调用自己呢?答案是使用arguments.callee
function A() {
console.log(arguments.callee === A)
}
A() // true
前面说到函数的length 属性可以返回函数定义的参数个数,那么函数真正运行时的参数个数怎么取到呢? arguments.length 属性就能取到,它表示的就是函数的实参
function A(a, b){
console.log(arguments.length)
}
A('p1', 'p2', 'p3', 'p4') // 4
toString()
toString方法可以返回函数的源代码,是字符串的形式,这个方法好像没啥用处
函数的作用域
在 Javascript 中,函数的作用域是可以被动态指定的,什么意思呢?说的就是函数的作用域不一定必须是它声明时所处的作用域,也可以被指定为其他的作用域
举个例子,下面的函数用于打印出 name 的值
const name = 'Andy'
function say(){
console.log(name)
}
say() // Andy
现在改下需求,定义一个对象 Person ,同样需要打印出 Person 的 name 属性,怎么搞?
const Person = {
name: 'Jack',
say: function(){
console.log(this.name)
}
}
Person.say() // Jack
上面的写法可以,但是还可以用一种更好的方式,通过改变函数 say 的作用域,将其指定为对象 Person ,这样函数 say 就能访问到 Person 内部的 name 属性了
const Person = {
name: 'Jack'
}
function say(){
console.log(this.name)
}
say.call(Person) // Jack
这种方式其实就是前面提到过的,将函数作为对象的方法来调用,这样你就不用单独为对象创建一个同样的方法,你可以直接指定现有方法的作用域,能很好的实现函数复用
指定函数作用域可以使用 call、apply、bind这 3 个方法,具体的使用参见作用域
函数重载
函数重载就是根据传入参数的个数不同,在函数内部执行不同的逻辑,这样可以避免重复声明功能类似,但是又有一点点不同的函数
下面是最通俗易懂的函数重载实现,通过判断参数的个数,然后内部做 switch 判断来重载函数逻辑
function sayName(){
// arguments 是函数的属性,在内部可以访问到,表示函数的实参
switch(arguments.length){
case 0:
console.log('noting')
break
case 1:
console.log('firstName:', arguments[0])
break
case 2:
console.log('full name:', arguments[0], arguments[1])
}
}
sayName() // noting
sayName('Jack') // firstName: Jack
sayName('Jack', 'London') // full name: Jack London
上面的函数 sayName 就是一个实现了重载的函数,可以看到下面连续调用了 sayName 三次,但是每次的执行结果都不同,因为每次调用传入的参数个数是不同的