函数是一种特殊的对象
定义一个函数
- 具名函数
function 函数名(形参1,形参2){
语句
return 返回值
}
- 匿名函数(去掉函数名)
let a =function(x,y){
return x+y
}//也叫函数表达式
note:
let a =function fn(x,y){
return x+y
}
fn(1,2)//会报错,因为fn函数在等号右边,则作用域范围只在等号右边
- 箭头函数
let f1 = x =>x*x//箭头左边为输入,右边为输出
f1(9)//输出81
let f2 = (x,y) => x*y//若箭头左边有两个输入参数,需要括号括起来
f2(8,9)//输出72
let f3 = (x,y) => {
console.log('hi')
return x*y
}//若箭头右边有多个语句,必须加花括号和return 返回值
let f4 = x =>({
name:x
})//若要在箭头右边直接返回一个对象,需要加括号
- 构造函数(很少用)
let fn1 = new function('x','y',
'console.log(\'hi\');
return x*y'
)
所有函数都是Function构造出来的,包括Object,Array,Function都是。
函数自身v函数调用
- 函数自身
let fn = () =>console.log('hi')
fn//没有结果,因为fn没有执行(调用)
- 函数调用
let fn = () =>console.log('hi')
fn()//打印出hi,有圆括号才是调用
let fn = () =>console.log('hi')//fn只保存了匿名函数的地址
let fn2 = fn//地址复制给fn2
fn2()//fn和fn2都是匿名函数的引用而已
函数的要素
-
调用时机
调用时机不同,结果不同
let a = 1
function fn(){
console.log(a)
}
a = 2
fn()//打印出2
let a = 1
function fn(){
setTimeout(() => {
console.log(a)
},0)
}
fn()
a = 2//先执行完程序,再打印出2
let i= 0
for(i = 0;i < 6;i++){
setTimeout(() =>{
console.log(i)
},0)
}//打印出6个6,而不是0,1,2,3,4,5
for(let i = 0;i < 6;i++){
setTimeout(() =>{
console.log(i)
},0)
}//打印出0,1,2,3,4,5,因为JS在for和let一起用时会加东西,每次循环会多创建(复制)一个i(迎合新手想法)。
-
作用域
- 全局变量和局部变量
在顶级作用域声明的变量就是全局变量,window的属性是全局变量,其他都局部变量。
- 函数嵌套
function f1(){ let a = 1 function f2(){ let a = 2 console.log(a) } console.log(a) a = 3 f2() } f1()//打印出1,2当多个作用域有同名变量a,那么查找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()//打印出1,2如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包,如上面的a和f3组成了闭包。
-
形式参数
function add(x,y){
return x+y
}//x和y为形参,因为不是实际的参数
add(1,2)//调用add时,1和2为实参,会被赋值给 x,y
function add(){
var x = arguments[]
var y = arguments[]
return x+y
}//形参其实可认为是变量声明
- 返回值
每个函数都有返回值
function hi(){
console.log('hi')
}
hi()//返回值为undefined,因为没写return
function hi(){
return console.log('hi')
}
hi()//返回值为console.log('hi')的值,即undefined,只是打印了hi
函数执行完后才会返回,只有函数才有返回值。
- 调用栈
-
JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组里,数组即为调用栈。
-
等函数执行完后,就会把环境pop出来。
-
然后return到之前的环境,继续执行后续代码。
-
递归函数
-
阶乘
function f(n){ return n === 1 ? 1 : n*f(n-1) } f(100)//压100次栈 -
递归的调用栈最长为
Chrome:12578
Firefox:26773
Node:12536
-
爆栈
调用栈中压入得帧过多,程序会崩溃。
-
-
- 函数提升
function fn(){}
不管将具名函数声明在哪,都会跑到第一行。
let fn = function(){}//为赋值,右边的匿名函数声明不会提升到第一行
- arguments(箭头函数没有)
伪数组
Array.from()//可以将伪数组变成数组
-
this(箭头函数没有) 如果不给任何条件,this默认指向window,如果传的不是对象,JS会自动封装成对象。
- 传this
fn.call(xxx,1,2,3)//传this或arguments如果非要传数字,不想自动封装成对象
function fn(){ 'use strict' console.log('this:'+this) }this是一个隐藏参数, arguments是普通参数。
-
假如没有this
let person = { name: 'frank', sayHi(){ console.log(`你好,我叫` + person.name) } } //可以直接保存对象地址的变量获取'name',简称引用- 如果person改名了,sayHi函数就挂了。
- 或者sayHi函数有可能在另一个文件里。
- 而JS中person.sayHi()会隐式地person作为this传给sayHi,方便sayHi通过this获取person对应的对象。
-
新手调用法
person.sayHi()
//会自动把person传到函数里,作为this
-
老手调用法
person.sayHi.call(person) //要手动把person传到函数里,作为this例如:
function add(x,y){ return x+y } add.call(undefined,1,2)//3第一个参数要作为 this,而原函数没有 this,只能用 undefined 占位,null 也可以。
再如:
Array.prototype.forEach2 = function(fn){ for(let i=0;i<this.length;i++){ fn(this[i],i) } } //因为使用forEach2时总会用arr.forEach2,所以arr就被自动传给了forEach2 array.forEach2.call(array,(item)=>console.log(item)) //1,2,3 array.forEach2.call({0:'a',length: 1 },(item)=>console.log(item)) //this不一定是数组- this 的两种使用方法
- 隐式传递
fn(1,2) obj.child.fn(1)- 显示传递
fn.call(undefined,1,2)或fn.apply(undefined,[1,2]) //使用apply需要在参数上加中括号[](需要数组形式) obj.child.fn.call(obj.child,1)- 绑定 this
- 使用.bind 可以让 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') -
箭头函数(没有 this 和 arguments)
console.log(this)
//window
let a = () => console.log(this)
//箭头函数的this就是外部window的this,一个普通的变量
a.call(1)//箭头函数的this不能指定,还是外部的this,除非外部this改变
- 立即执行函数(现在用的很少)
获取局部变量,使用()立即执行,只需要在匿名函数前加个运算符即可,推荐感叹号!
! function (){
var a =2
console.log(a)
} ()//2,+,—可以,1* 也可以,只要加个运算
新版 JS 只需要 let 外加{ }即可
{
let a = 2
console.log(2)
}//2
Note:
function 外有()时,才会用;分隔开,也是唯一需要加分号的地方。
console.log('hi');
(function (){
var a =2
console.log(a)
} ())