定义一个函数:
具名函数
function 函数名(形参1,形参2){
语句
return 返回值
}
匿名函数
let a = function(x,y){return x+y}
//也叫函数表达式
箭头函数
let f1 = x => x*x
let f2 = (x,y) => x+y //圆括号不能省
let f3 = (x,y) => {return x+y} //写了return 花括号不能省
let f4 = (x,y) =>({name: x , age:y}) //返回对象时需要加圆括号
用构造函数
let f = new Function('x','y','return x+y')
//基本没人用,但能让人知道函数是由谁构造的
//所有函数都是Function 构造出来的
//包括Object,Array,Function
函数自身和其调用
//函数自身
let fn =() => console.log('hi')
fn
//结果:不会有任何结果,因为fn没有执行,得到值是函数本身
//函数调用
let fn =()=>console.log('hi')
fn()
//结果:打印出hi,值是函数的返回值undefined
//有圆括号才是调用
//进一步衍生
let fn = ()=> console.log('hi')
let fn2 =fn
fn2()
//结果:打印出hi,值为函数的返回值undefined
/**
注:
fn保存了匿名函数的地址,这个地址被复制,然后赋值给了fn2
故fn2()也是调用了那个匿名函数
fn和fn2都是匿名函数的引用而已,真正的函数既不是fn也不是fn2
**/
函数的要素
每个函数都有以下这些东西
- 调用时机
- 作用域
- 闭包
- 形式参数
- 返回值
- 调用栈
- 函数提升
- arguments(除了箭头函数)
调用时机
时机不同,结果不同 ```javascript let a =1 function fn(){ setTimeout(()=>{ console.log(a) },0) } //问:打印出多少 //答:1
let i = 0 for(i=0;i<6;i++){ setTimeout(()=>{ console.log(i) },0) } //问:打印出多少 //答:6 6 6 6 6 6 //理解为执行了六遍console.log(i),但 for 形成的块级作用域内并没有声明变量i, //块级作用域内是{console.log(i)},因此向上一级去找i,而这个时候i早已等于6
for(let j=0; j<6; j++){ setTimeout(()=>{ console.log(j) },0) } //问:打印出多少 //答:0,1,2,3,4,5 //for形成的块级作用域是{let j=0;console.log(j)}……{let j=5;console.log(j)}
<a name="xEamf"></a>
### 作用域
离语句最近的{}就是它的作用域(除去window的属性)<br />变量分为:全局变量和局部变量<br />在顶级作用域声明的变量是全局变量,window的属性是全局变量,其他的都是局部变量<br />函数可以嵌套。
<a name="cY2N7"></a>
#### 作用域规则:
如果多个作用域有同名变量a<br />那么查找a的声明时,就向上取最近的作用域<br />简称“就近原则”<br />查找a的过程与函数执行无关,但a的值与函数执行有关
```javascript
function f1(){
let a =1
function f2(){
let a =2
function f3(){
console.log(a)
}a=22
f3()
}console.log(a)
a =100
f2()
}
f1()
问:输出结果是什么
答: 1 和 22
闭包
概念:如果一个函数用到了其作用域外部的变量,那么这个函数+这个外部变量就叫做闭包
作用:私有化变量
要求:暴露出操作函数
缺点:在IE浏览器里可能会导致内存泄漏
形式参数
返回值
调用栈
调用栈是解释器追踪函数执行流的一种机制,当执行环境中调用了多个函数时,能够追踪到哪个函数正在执行,执行的函数体中又调用了哪些函数。
解释器会把执行函数压进调用栈,发现执行函数还调用了其他函数,那新函数也会被压进调用栈。
压栈出栈顺序为:先进后出FILO
函数提升
arguments
是所有非箭头函数中可用的局部变量,是由传入参数构成的一个伪数组,可以通过索引访问到传入的参数