这两个函数的行为不同,主要是因为 JavaScript 中的变量声明提升(hoisting)和作用域规则。让我们详细分析一下这两种情况。
情况 1:带有 var 声明的局部变量
var x = 10;
function f() {
console.log(x);
var x = 20;
}
f();
解释:
- 变量声明提升:在 JavaScript 中,使用
var声明的变量会被提升到其所在作用域的顶部,但初始化不会被提升。 - 作用域:在函数
f内部,var x声明了一个局部变量x,它遮蔽了外部的全局变量x。 - 执行顺序:
- 当进入函数
f时,局部变量x被声明并初始化为undefined(因为变量声明提升)。 - 执行
console.log(x)时,由于局部变量x已经声明但尚未赋值,所以输出undefined。 - 然后执行
x = 20,给局部变量x赋值为20。
- 当进入函数
因此,这段代码的输出是 undefined。
情况 2:没有 var 声明的局部变量
var x = 10;
function f() {
console.log(x);
x = 20;
}
f();
解释:
- 无
var声明:在函数f内部,x = 20没有使用var关键字声明,这意味着它会尝试修改当前作用域链中的变量x。如果没有找到局部变量x,则会修改全局变量x。 - 执行顺序:
- 进入函数
f时,没有新的局部变量x被声明。 - 执行
console.log(x)时,由于没有局部变量x,JavaScript 会在当前作用域链中查找,找到全局变量x,其值为10,所以输出10。 - 然后执行
x = 20,这会修改全局变量x的值为20。
- 进入函数
因此,这段代码的输出是 10。
总结对比
-
情况 1:由于
var x在函数内部声明,局部变量x遮蔽了全局变量x,并且由于变量声明提升,console.log(x)输出的是undefined。var x = 10; function f() { console.log(x); // undefined var x = 20; // 局部变量 x 被声明并赋值为 20 } f(); -
情况 2:由于没有使用
var声明局部变量x,console.log(x)访问的是全局变量x,输出10,然后将全局变量x修改为20。var x = 10; function f() { console.log(x); // 10 x = 20; // 修改全局变量 x 的值为 20 } f();
主要学习于王福朋博客:www.cnblogs.com/wangfupeng1…
一:执行上下文、作用域
- 这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”:
- 变量、函数表达式——变量声明,默认赋值为undefined;
- this——赋值;
- 函数声明——赋值;
****并且准备资源的顺序是有先后的,先预编译 函数 > 变量
等价于
所以,图中汇报错: a is not a function
执行上下文环境:通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。
函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域(如下图)
函数每被调用一次,都会产生一个新的执行上下文环境。(this的取值是执行上下文环境的一部分)
- this
- 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。其实这是一个压栈出栈的过程——执行上下文栈。如下图:
- javascript除了全局作用域之外,只有函数可以创建作用域。
作用域在函数定义时就已经确定了。而不是在函数调用时确定
- 在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。
自由变量取值:要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记。
上面描述的只是跨一步作用域去寻找。
如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。
这个一步一步“跨”的路线,我们称之为——作用域链。
闭包
定义:闭包是指有权访问另外一个函数作用域中的变量的函数。
(闭包是指那些能够访问自由变量的函数,这里的自由变量是指外部作用域中的变量)
你只需要知道应用的两种情况即可——
1.函数作为返回值
2.函数作为参数传递
第一,函数作为返回值
bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值。
第二,函数作为参数被传递
fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100(因为max是自由变量)。
函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。使用闭包会增加内容开销,现在很明显了吧!
应用:节流,防抖
优点:私有化数据,在私有化数据的基础上保持数据
缺点:可能会导致内存泄漏,内部的变量不会被自动回收掉
- g