开篇结论:函数是特殊的对象
具名函数
function 函数名(形式参数1,形式参数2){
语句
return 返回值
}
匿名函数
上面的具名函数,去掉函数名就是匿名函数
let a = function(x,y){return x+y}
也叫函数表达式
注意
let a = function fn(x,y){
return x+y
}
fn(1,2)//直接调用会报错,因为fn的作用域再等号右边,没有等于号赋值就是全局的
箭头函数
let f1 = x =>x*x //箭头左边的x为输入参数,x*x为输出参数
let f2 = (x,y) => x+y // 多个参数需要圆括号
let f3 = (x,y) =>{
console.log('hi')
return x+y
}//多个语句花括号不能省,因为不知道你要返回那一句
let f4 = (x,y) =>({name:X,age:y})//直接返回对象会出错,需要加个括号,因为默认第一层花括号是一个lable
构造函数
let f=new Function('x','y',return x+y)
//基本没人用,但是能让你知道函数是谁构造出来的
//所有函数都是Funciton构造出来的
//包括Object,Array,Function也是
函数自身VS函数调用

这个地址被复制给了fn2
fn和fn2都是匿名函数的引用而已
真正的函数既不是fn也不是fn2
函数的要素
每个函数的都有:调用时机,作用域,闭包,形式参数,返回值,调用栈,函数提升,arguments(除了箭头函数),this(除了箭头函数)
调用时机
调用时机不同结果不同

let i = 0
for(i = 0;i<6;i++){
setTimeout(()=>{
console.log(i)
},0)
}
//结果是6个6不是1,2,3,4...因为for循环已经执行完毕
for 循环let组合


作用域
在顶级作用域声明的变量是全局变量
window的属性是全局变量
其他的都是局部变量
let b = 1 //顶级作用域,b可全局访问
function f2(){
window.c = 2//将c挂到window上,也可全局访问,但前提是这个函数已经执行
let b //这里的b为局部作用域
}
f2()
function f1(){
console.log(c)//可访问c,因为已经挂到window上了
}
f1()


如果多个作用域有同名变量a
那么就查找a的声明时,就向上取最近的作用域
简称就近原则
查找a的过程与函数执行无关
但a的值与函数执行有关
闭包

形式参数
function add(x,y){
return x+y
}
//其中x和y就是形参,因为并不是实际的参数add(1,2)
//调用add时,1和2是实际参数,会被赋值给xy
//形参可认为是变量声明
//上面代码近似等价于下面的代码
function add(){
var x = arguments[0]
var y = arguments[1]
return x+y
}
//形参可多可少
返回值
每个函数都有返回值
function hi(){console.log('hi')}
hi()
//没有return,返回值为undefined
function hi(){return console.log('hi')}
hi()
//返回值为console.log('hi')的值,即undefined
//函数执行完才会返回,只有函数有返回值
调用栈
JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组中,这个数组叫做调用栈,等函数执行完了以后,就会把环境弹(pop)出,然后return到之前的环境,继续执行后续代码

函数递归
function f(n){
return n !== 1 ? n*f(n-1) :1
}
理解递归

function computeMaxCallStackSize(){
try{
return 1+computeMaxCallStackSize();
}catch(e){
//报错说明stack overflow
return 1;
}
}
//爆栈
//如果调用栈中压入的帧过多,程序就会崩溃
//Chrome栈长度大概在(11000-12600)区间
//firefox(26773左右)
//Node(12536)
函数提升
function fn(){}
//不管你把具名函数声明在哪里,它都会跑到第一行
//var语句会出现这样的问题,而let不会
arguments(除了箭头函数)
function fn(){
console.log(arguments)
}
//查看arguments
//调用fn即可传arguments,fn(1,2,3)那么arguments就是[1,2,3]伪数组
arguments是包含所有参数的伪数组(没有数组常用方法的数组称为伪数组)
this(除了箭头函数)
function fn(){
console.log(this)
}//查看this
//目前可以用fn.call(xxx,1,2,3)传递this和arguments
//而且xxx会被自动转化成对象(JS糟粕)
fn.call(1)//如果传递的不是对象,JS会自动封装成对象
//正常传参写法加'use strict'
function fn(){
'use strict'
console.log('this:'+this)
}
如果fn不传递参数,this默认指向小写的window
两种调用法
隐式传递
fn(1,2)//等价fn.call(undefined,1,2)
obj.child.fn(1)//等价于obj.child.fn.call(obj.child,1)
显示传递
fn.call(undefined,1,2)
fn.apply(undefined,[1,2])
小白调用法
person.sayHi()
会自动把person传到函数里,作为this
大师调用法
person.sayHi.call(person)
需要自己动手把person传到函数里,作为this
function add(x,y){
return x+y
}
add.call(undefined,1,2)//undefined是占位符
绑定this
function f1(p1,p2){
console.log(this,p1,p2)
}
let f2 =f1.bind({name:'frank'})
//那么f2就是f1绑定了this之后的新函数
f2()//等价于f1.call({name:'frank'})
//.bind还可以绑定其他参数
let f3 =f1.bind({name:'frank'},'hi')
f3()//等价于f1.call({name:'frank'},hi)
立即执行
! function (){
var a = 2
console.log(a)
}()//声明一个匿名函数直接调用