定义
函数是一种对象
- 具名函数
function 函数名 (形式参数1,形式参数2){
语句
return 返回值
}
- 匿名函数 上面的具名函数去掉函数名,也叫函数表达式
let a = function(x,y){return x+y}
如果函数的声明是在等于号右边的,那他的作用域只在等于号右边成立,除了这里,其他地方在调用这个函数只能用声明的变量,若没有等于号则是全局使用
let a = function fn(x,y){return x+y}
fn(1,2) //错误❌
- 箭头函数 如果要返回对象,在对象{ }外面加()
let f1 = x => x*x
let f2 = (x,y) => x+y //圆括号不能省略
let f3 = (x,y) => {return x+y; console.log('你好')} //如果有两句必须自己家return
let f4 = (x,y) => ({name: x,age: y}) //直接返回对象会出错,需要加()
- 构造函数
fn和fn()
let fn = () => console.log('hi')
fn //不会有任何结果,因为fn没有执行
fn() //打印出hi,有()圆括号才是调用
let fn = () => console.log('hi')
let fn2 = fn
fn2()
fn保存了匿名函数的地址,这个地址被复制给了fn2,fn2()调用了匿名函数,fn和fn2都是匿名函数的引用而已,真正的函数既不是fn也不是fn2
函数的要素
每个函数都有这些东西
- 调用时机
- 作用域
- 闭包
- 形式参数
- 返回值
- 调用栈
- 函数提升
- arguments(除了箭头函数)
- this(除了箭头函数)
调用时机
时机不同结果不同
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
打印出6个6,循环先执行完i=6,在打印调用的6次,把当前的事情干完之后再去干
for( let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
打印出0、1、2、3、4、5,因为JS在for和let一起用时会加东西,每次循环会多创建一个i,循环执行后会创建6个不同的i
作用域
每个函数都会创建一个作用域,let的作用域,找到包住他最近的花括号
function fn(){
let a = 1
}
console.log(a) //a不存在
- 局部变量:只在这一块代码中可以生效
- 全局变量:在顶级作用域声明的变量或者把你的变量写在window上,如window.c = 1 如果多个作用域有同名变量a,那么查找a的声明时,就向上取最近的作用域,查找a的过程与函数执行无关,即作用域与函数执行无关,那么这种成为静态作用域,但a的值与函数执行有关
闭包
如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包
形式参数
形式参数的意思就是非实际参数,也可认为是变量声明而且形参声明可多可少;如add(x,y),x,y就是;但是add(1,2)1,2就是实际参数
返回值
每个函数都有返回值,返回值实在执行之后才有的,就算不写return返回值会默认是undefined
只有函数有返回值
调用栈
JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组里,这个数组就是调用栈,等函数执行完,就会把环境弹(pop)出来,然后return到之前的环境,继续执行后续代码
console.log(1)
console.log('1+2的结果为'+add(1,2))
console.log(2)
递归函数的调用栈很长
函数提升
function fn(){}不管你把具名函数写在哪里,它都会跑到第一行,可以在声明之前调用
let add = 1
function add(){}
let add = function fn(){} 会报错,这是赋值,右边的匿名函数声明不会提升
arguments
arguments是包含所有参数的伪数组,没有数组的共有属性
- 如何传arguments 调用fn即可传,fn(1,2,3)就是[1,2,3]伪数组
this
如果不给任何条件,this默认指向window,传一个undefined会变成全局变量Window
- 如何传this 目前可以用fn.call(xxx,1,2,3)传this和arguments,而且xxx会被自动转化成对象,即你传的不是对象,JS会自动帮你封装成对象,第一个xxx是this,后面的所有的是arguments,this是隐藏参数,arguments是普通参数
function fn(){
'use strict'
console.log(this)
}
fn.call(1) //打印出1,不是对象
需要一种办法拿到还没出现的对象,实现函数对那个对象的引用
- 用this获取那个对象
let person ={
name = 'frank'
sayHi(this){
console.log(`你好,我叫` + this.name)
}
}
person.sayHi() //正确
person.sayHi(person) //错误
person.sayHi.call({name:1}) //正确{}内传什么,this就是什么,
person.sayHi()相当于person.sayHi(person)然后person被传给了this(person是个地址) 每个函数都能用this获取一个未知对象的引用了
this的两种使用方法
- 隐式传递
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]) //后面的其他参数上面要加[]
绑定this
- 使用.bind可以让this不被改变
- .bind还可以绑定其他参数
立即执行函数
优先使用!
!function (){
var a = 2
console.log(a)
} ()
只要一个局部变量的方法,新版只需要
{
let a = 2
console.log(a)
}