JS 函数
函数的四种定义方法及组成
首先,要明确一点就是函数本身也是一个对象。
具名函数
function 函数名 (形式参数1,形式参数2){语句 return 返回值}
匿名函数
上面的具名函数去掉函数名就是匿名函数
例如
let a = function(x,y){return x+y}
也叫函数表达体
我们可以把匿名函数和具名函数结合起来使用,如 let a =function fn(x,y){return x+y}
,那么需要注意的是这时只能通过 a(x,y) 来调用函数,fn(x,y)不能调用函数,因为 fn 的作用域只在等号之后存在,在外部无法访问到 fn。
箭头函数
let f1 = x => x*x
let f2 = (x,y) => x+y // 包含两个及以上形参时圆括号不能省
f3 = (x,y) => {console.log('hi');return x+y} // 函数体包含两句以上语句时花括号不能省
let f4 = (x,y) => ({name:x, age: y})
//当箭头函数返回一个对象时,为了避免对象被识别为块从而出现报错的情况,这里的解决办法是使用一个圆括号将返回对象包起来
用构造函数
let f = new Function('x', 'y', 'return x+y')
很少使用,但是能让你知道函数是谁构造的
所有函数都是 Function 构造出来的
包括 Object、Array、Function 也是
每个函数都包含以下内容
- 调用时机
- 作用域
- 闭包
- 形式参数
- 返回值
- 调用栈
- 函数提升
- arguments(除了箭头函数)
- this(除了箭头函数)
执行时机
结合例子单独开一篇来讲
作用域&闭包
全局变量 VS 局部变量
- 在顶级作用域下声明的变量就是全局变量
- window 的属性是全局变量
- 除此之外的全部是局部变量
作用域的嵌套
如果多个作用域有同名变量 a
那么查找 a 的声明时,就向上取最近的作用域
简称「就近原则」
查找 a 的过程与函数执行无关
但 a 的值与函数执行有关
以上其实说的就是作用域链,只要记住就近原则这一点就能弄明白了
闭包
- 如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包详情以后再补充
参数和返回值
- 形式参数
- 形参的就是非实际参数
- 形参可以认为就是变量声明
- 返回值
- 每一个函数都有返回值
- 函数执行完了才有返回值
- 只有函数才有返回值
1+2 的返回值是 3- 1+2 的值是 3
递归、调用栈、爆栈与函数提升
什么是调用栈
JS 引擎在调用一个函数前
需要把函数所在的环境 push 到一个数组里(压栈)
这个数组叫做调用栈
等函数执行完了,就会把环境弹(pop)出来(弹栈)
然后 return 到之前的环境,继续执行后续代码
调用栈最长有多少?,使用如下函数可以测试调用栈有多长
1
2
3
4
5
6
7
8
9
10
11
12
13function computeMaxCallStackSize() {
try {
return 1 + computeMaxCallStackSize();
} catch (e) {
// 报错说明 stack overflow 了
return 1;
}Chrome 12578
Firefox 26773
Node 12536
爆栈:如果调用栈中压入的帧过多,程序就会崩溃
变量提升
什么是变量提升
- function fn(){}
- 不管你把具名函数声明在哪里,它都会跑到第一行
什么不是函数提升
let fn = function(){}
这是赋值,右边的匿名函数声明不会提升(let 没有变量提升)
arguments 与 this
arguments
是一个伪数组,使用 Array.from 转化为数组
this
如果没有任何指示,那么 this 就指向 window
目前可以用 fn.call(xxx, 1,2,3) 传 this 和 arguments
如果没有使用严格模式那么 xxx 会被自动转化成对象(JS 的糟粕),如果使用严格模式那么就是原本的 xxx
绑定 this
使用 bind() 可以让 this 不再改变
1
2
3
4
5
6function f1(p1, p2) {
console.log(this, p1, p2);
}
let f2 = f1.bind({ name: 'frank' });
// 那么 f2 就是 f1 绑定了 this 之后的新函数
f2(); // 等价于 f1.call({name:'frank'}).bind 还可以绑定其他参数
1
2let f3 = f1.bind({ name: 'frank' }, 'hi');
f3(); // 等价于 f1.call({name:'frank'}, hi)
箭头函数
没有 arguments 和自己的 this
立即执行函数
原理
ES 5 时代,为了得到局部变量,必须引入一个函数
但是这个函数如果有名字,就得不偿失
于是这个函数必须是匿名函数
声明匿名函数,然后立即加个 () 执行它
但是 JS 标准认为这种语法不合法
所以 JS 程序员寻求各种办法
最终发现,只要在匿名函数前面加个运算符即可
!、~、()、+、- 都可以
但是这里面有些运算符会往上走
所以推荐永远用 ! 来解决
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!