27. JS 函数

27. JS 函数

四种方式定义函数

函数是对象,函数是一种特殊的对象

定义一个函数

具名函数

function 函数名(形式参数1,形式参数2){
	语句
	return 返回值
}

匿名函数

上面的具名函数,去掉函数名就是匿名函数

let a = function(x,y){return x + y}

也叫函数表达式

以下代码能打印出结果吗?不能会报"Uncaught ReferenceError:fn is not defined at :1:1"

let a = fucntion fn(x,y){return x + y}
fn(1,2)

因为=右边的函数,作用域只在右边,只能通过a来调用,除非把=去掉,变成全局作用域。

箭头函数

如果超过两个语句,需要加{}return

如果要返回一个对象,对象两边要加(),否则会被当作label对待

构造函数(没人用)

基本没人用,但是能让你知道函数是谁构造的

所有函数都是Function构造出来的

包括Object,Array,Function也是。

函数自身和函数调用

fn和fn()的区别,函数和函数调用

函数自身

结果:不会有任何结果,因为fn没有执行。

有圆括号才是调用fn()就能打印出hi

再进一步

结果

fn保存了匿名函数的地址

这个地址被复制给了fn2

fn2()调用了匿名函数

fn和fn2都是匿名函数的引用而已

真正的函数既不是fn也不是fn2

函数的要素

每个函数都有这些东西

  • 调用时机

  • 作用域

  • 闭包

  • 形式参数

  • 返回值

  • 调用栈

  • 函数提升

  • arguments(除了箭头函数)

  • this(除了箭头函数)

调用时机

调用时机不同,结果不同

例子1

问:上面这段代码打印出多少?

答:不知,因为没有调用代码

例子2

问:上面这段代码打印出多少?

答:1

例子3

问:上面这段代码打印出多少?

答:2

例子4

问:上面这段代码打印出多少?

答:1

例子5

问:上面这段代码打印出多少?

答:2

setTimeout是在整个程序执行完之后才会执行,那么等整个程序全部执行完了,a已经是2了。

例子6

问:上面这段代码打印出多少?

答:不是0,1,2,3,4,5,而是6个6。因为setTimeout会在循环结束之后才会打印出来

例子7

问:上面这段代码打印出多少?

答:是0,1,2,3,4,5, 因为JS在for和let一起用的时候会加东西,每次循环会多创建一个i。

作用域

每个函数都会默认创建一个作用域。

例子1

问:是不是因为fn没执行导致的

答:就算fn执行了,也访问不到作用域里面的a

例子2

全部变量V.S局部变量

在顶级作用域声明的变量是全局变量,window的属性是全局变量,其他都是局部变量。

函数可嵌套

作用域也可嵌套

例子3

Screen Shot 2022-01-20 at 12.04.09 AM

作用域规则

如果多个作用域有同名变量a

  • 那么查找a的声明时,就向上取最近的作用域

  • 简称“就近原则”

  • 查找a的过程与函数执行无关,这种叫做静态作用域,也叫词法作用域(编译原理知识),相反的就叫动态作用域

  • 但a的值与函数执行有关

例子4

闭包

闭包刚刚讲过了,讲过了吗,

重看例子4

Screen Shot 2022-01-20 at 12.12.40 AM

形式参数

形式参数的意思就是非实际参数

形式参数的本质是变量声明

形式参数可多可少,形式参数只是给参数取名字

返回值

每个函数都有返回值

函数执行完了后才会返回

只有函数有返回值

1+2返回值为3

1+2值为3

调用栈

什么是调用栈

JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组里,这个数组叫做调用栈。等函数执行完了,就会把环境弹(pop)出来。然后return到之前到环境,继续执行后续代码。

举例

递归函数

阶乘

理解递归

递归函数的调用栈

递归函数的调用栈很长,请画出阶乘6的调用栈

调用栈最长有多少

Chrome 12578

Firefox 26773

Node 12536

爆栈:如果调用栈中压入的帧过多,程序就会崩溃。

函数提升

什么是函数提升

不管你把具名函数声明在哪里,它都会跑到第一行

什么不是函数提升

这是赋值,右边的匿名函数声明不会提升

arguments 和this

每个函数都有,除了箭头函数

箭头函数

没有arguments和this

里面的this就是外面的this

就算你加call都没有

立即执行函数

只有JS有的变态玩意,现在用得少

原理

ES5时代,为了得到局部变量,必须引入一个函数,但是,这个函数如果有名字,就得不偿失,于是这个函数必须是匿名函数。声明匿名函数,然后立即价格()执行它。但是JS标准认为这种语法不合法,所以JS程序员寻求各种办法,最终发现,只要在匿名函数前面价格运算符即可。!,~,(),+,-都可以,但是这里面有些运算符会往上走,所以方方推荐永远用!来解决。

为什么不要使用括号?

这段代码是整个JS里面唯一需要加分号的地方,因为不加分号,console.log()return的是undefined,最后就会变成如下形式,所以会报错。

最后更新于

这有帮助吗?