函数是一种对象

定义一个函数

具名函数
function 函数名(形式参数1,形式参数2){
语句
return 返回值
}
匿名函数(函数表达式)
let a = function (x,y){
return x+y
}

箭头函数

左边是输入参数,右边是输出参数
如果直接返回一个对象,要加上圆括号
let fn = x =>({name:x})
代码:let fn = () => console.log(‘hi’)
let fn2 = fn
fn2()
结果:fn保存了匿名函数的地址
这个地址被复制给了fn2
fn2()调用了 匿名函数
fn和fn2都是匿名函数的引用而已
真正的函数既不是fn也不是fn2

函数的要素

每个函数都有这些东西
调用时机、作用域、闭包、形式参数、返回值、调用栈、函数提升、arguments(除了箭头函数)、this(除了箭头函数)

调用时机

//打印出6个6
image.png
//打印出0、1、2、3、4、5
image.png
原因:JS在for和let一起用的时候,每次循环会多创建一个i

作用域

每个函数都会默认创建一个作用域
在顶级作用域声明的变量是全局变量,window的属性是全局变量,其他都是局部变量
函数可嵌套(作用域也可嵌套)
规则:
如果多个作用域有同名变量a
那么查找a的声明时,就向上取最近的作用域【简称:就近原则】
查找a的过程与函数执行无关
但a的值与函数执行有关

闭包

如果一个函数用到了外部的变量,那么这个函数加这个变量就叫闭包
参数和返回值
形式参数:非实际参数
function add(x, y){
return x+y
}
//其中 x 和 y就是形参,因为并不是实际参数
add(1,2)
//调用add时,1和2是实际参数,会赋值给想 x y
形参可认为是变量声明
function add(){
var x = arguments[0]
var y = arguments[1]
return x+y
}
形参可多可少,只是给参数取名字

返回值:每个函数都有返回值

function hi(){ console.log( ‘ hi ‘ )} //没写return,返回值是undefined
function hi(){return console.log( ‘hi’ )} hi()
返回值为console.log(’hi’)的值 即为undefined
返回值 不等于 打印值
只有函数才有返回值,函数执行完了才会返回

调用栈

什么是调用栈
JS引擎在调用一个函数前
需要把函数所在的环境push到一个数组里
这个数组叫做调用栈
等函数执行完了,就会把环境弹(pop)出来
然后return到之前的环境,继续执行后续代码
爆栈
如果调用栈中压入的帧过多,程序就会崩溃

函数提升

function fn(){}
不管你把具名函数声明在哪里,它都会跑到第一行
let fn = function(){}
这是赋值,右边的匿名函数声明不会提升
arguments 和 this
每个函数都有,除了箭头函数
function fn(){
console.log(arguments)
console.log(this)
}
如何传arguments
调用fn即可传arguments
fn(1,2,3)那么arguments就是[1,2,3]伪数组
如何传this
可以用fn.call(xxx,1,2,3) xxx就是this 后面是伪数组
xxx会自动转化为对象
this是隐藏参数
arguments是普通参数
如果你不给任何的条件,那么this默认指向window
如果你传的this 不是对象,那么JS会自动帮你封装成对象 new XXX()
函数内,严格模式下 ‘ use strict ‘ , 你传的什么this就是什么
一个变量保存了一个对象的地址,我们把它称为引用

立即执行函数

原理:ES5时代,为了得到局部变量,必须引入一个函数
但是这个函数如果有名字,就得不偿失
于是这个函数必须是匿名函数
声明匿名函数,然后加()执行它
匿名函数前面加个运算符都可以
!、~、()、+、-
推荐永远用!来解决