看完这篇文章,你将了解:
1.js 函数的五种声明方式
2.js 函数的本质
3.this 和 arguments 是什么
4.树与作用域
5.其他小知识


一. 函数的5种声明方式

1:具名函数

function f(x,y){ }

2:匿名函数

var f = function (x,y){ }

3:具名+匿名

var f = function z(x,y){ }

注:这种方式结合了前两种的写法,注意它有一个隐藏的坑(见后文)

4:对象构造

var f = new Function('x', 'y', 'return x+y')

5:=>

var f = (x,y) => { }

二. 函数的本质

一句话:函数是一个可以执行代码的对象
为说明函数本质是一个对象,我们甚至可以自己构造一个“函数”对象,这个对象具有函数功能。

三. this和arguments是什么?

前面讲过,函数调用本质上,就是通过函数对象对call方法的调用,该方法参数表如下:

    Function.call(this, arguments[0], arguments[1], ...)

可见,this和arguments,都是Function.call( )的参数

this是call的第一参数;

arguments是call的第二参数起所有参数组成的伪数组,也是函数实际调用时,我们传入的参数表。

事实上,我们每次调用函数

    f(1, 2)

相当于执行方法

    f.call(undefined, 1, 2)

实例:

注:arguments是复数形式,不要忘了s

注2:一些Java学习者,看见this难免见名生义,认为它就是“当前对象”;但通过以上分析我们可以看到,JavaScript中this并非对象,它就是一个参数而已。

四. 树与作用域

与作用域有关问题容易出错,我们可以画一棵树来描述作用域关系。

1.预备知识a:变量提升

var a = 1在代码中做了两件事:既声明(var a),又赋值(a = 1)。等价于

    var a    //声明
        a = 1    //赋值

变量提升就是将这两件事拆开做:保持a = 1位置不变,将var a提升到所属作用域的最上方。

2.预备知识b:一个函数就是一块作用域,变量的访问遵循就近原则

3.用树分析作用域逻辑

在全局范围下,先做一次变量提升,然后把所有声明变量与函数名维护为一个变量数组,这个数组就是一个根节点,里面的变量都是全局变量。

然后从根节点的所有函数出发,每个函数里继续做变量提升,维护变量数组 .... 递归地执行类似操作,就构成了一棵作用域"树"

作用域“树”的用法:

每当我们访问一个变量时,先看所在作用域是否有该变量,有→直接访问,没有→从父亲结点的作用域访问,依此类推。

q:如果顺着父亲结点一直没有找到访问变量,该怎么办呢?

a:分两种情况,无 var 声明它会声明全局变量并赋值;有 var 声明它会在当前作用域生成局部变量

  1. 4个作用域问题

Q1:

a1:

Q2:

a2:

Q3:

a3:

Q4:

a4:


五. 其他小知识

1.函数相关的一个坑

下图中,我们能访问y,不能访问y2

2.控制台按住"shift + 回车"可以不执行结果换行,继续写代码。

3.什么叫做“stack overflow”?

就是栈上溢,指压栈次数超过最大限制。


比如:递归引发栈上溢

4.什么是闭包?

如果一个函数,使用了它范围外的变量,那么(这个函数 + 这个变量)就是闭包。