函数是对象
定义函数
- 具名函数
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}
let f4={x,y}=>({name:x,age:y}) //不能直接返回对象,需要加个圆括号
- 构造函数
let f=new function('x','y','return x+y')
函数自身和函数调用
let fn=()=>console.log('hi')
let fn2=fn;
fn2()
fn保存了匿名函数的地址,这个地址被复制给fn,fn2()调用了匿名函数。fn和fn2都是匿名函数的引用而已,真正的函数既不是fn也不是fn2。
函数的要素
调用时机
let a=1
function fn(){
console.log(a)
}
fn()
1
let a=1
function(){
console.log(a)
}
a=2
fn()
2
let a=1
function fn(){
console.log(a)
}
fn()
a=2
1
let a=1
funcion fn(){
setTimeout()=>{
console.log(a)
},0)
}
fn()
a=2
2
作用域
每个函数都默认创建一个作用域
function fn(){
let a=1
}
console.log(a)
a不存在,就算fn执行了,也访问不到作用域里面的a
全局变量,在顶级作用域声明的变量是全局变量,window的属性是全局变量,其他的都是局部变量。
作用域规则
- 如果多个作用域有同名的变量a,就向上取最近的作用域,简称就近原则。查找a的过程与函数执行无关,但a的值与函数执行有关。
闭包
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()
如果这个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包。
上面的a和f3组成了闭包。
形式参数
- 形式参数的意思就是非实际参数
- 形参可认为是变量声明
function add(x,y){
return x+y
}
等价于
function add(){
var x=arguments[0]
var y=arguments[1]
return x+y
}
形参只是参数取名字,形参可多可少。
返回值
- 每个函数都有返回值
- 函数执行完了之后才会返回
- 只有函数有返回值
调用栈
JS引擎是在调用一个函数前,需要把函数所在的环境push到一个数组里,这个数据叫做调用栈。等函数执行完了,就会把环境pop出来,然后return到之前的环境,继续执行后续代码。
递归函数
function f(n){
return n!==1?n*f(n-1):1
}
函数提升
function fn(){}
不管把具名函数声明在哪里,它都会跑到第一行。
let fn=function(){}
赋值语句,右边的匿名函数声明不会提升。
arguments和this
除了箭头函数不含arguments和this,其余函数都有。
没有前提的话 this默认指向window
- 如何传arguments
调用fn即可传arguments
fn(1,2,3)那么arguments就是[1,2,3]伪数组 - 如何传this
目前可以用fn.call(xxx,1,2,3)传this和arguments,而且xxx会被自动转化成对象。
使用use strict this传的就不会变成对象
this
如果this传的不是对象js会帮你把它封装成对象
假设没有this
let person={
name:'Kacey',
sayHi(){
console.log('你好,我叫'+person.name)
}
}
我们可以直接保存了对象地址的变量获取'name',这个方法叫引用
person如果改名,引用就没办法使用。并且我们在声明类时,如果没有声明对象,那么对象的引用也没办法使用。此时就可以使用this。
JS在每个函数里加了this,这样每个函数都能用this获取一个位置对象的引用了。
let person={
name:'frank',
sayHi(/*this*/){
console.log('你好,我叫`+this.name')
}
}
person.sayHi=()
函数调用时有两种方法:
- person,sayHi() 会自动把person传到函数里,作为this
- person.sayHi.call(person) 需要自己手动把person传到函数里,作为this
function add(x,y){
return x+y
}
add.call(undefined,1,2)
为什么要写undefined的原因是,第一个参数要作为this,但代码里没有this,所以只能通过undefined占位。用null也可以。
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
console.log(this,p1,p2)
}
let f2=f1.bind({name:'kacey'})
f2就是f1绑定了this之后的新函数 f2()等价于f1.call({name:'kacey'})