js进阶学习笔记(二) -- 作用域以及变量提升
一 初步了解作用域
在了解作用域之前,先来看一个问题;
var xixi= 222;
在js处理这句话的时候,会发生什么呢?
-
参与这句代码处理过程的参与者
作用域参与.jpg -
代码处理过程
过程.jpg -
作用域是什么呢
我们将作用域定义为一套规则,用来管理引擎是如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找.
-
作用域嵌套
-
当一个函数嵌套在另一个函数中的时候,就发生了作用域的嵌套,因此,在当前作用域中无法找到某个变量的时候,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或者到达最外层作用域.
-
将作用域链比作建筑的话:一楼就代表当前的作用域,如果找不到变量,就会往上爬楼,依次寻找变量,直到最顶层仍找不到变量的话,就会报出错误.
作用域链.jpg
-
二 函数作用域
-
定义
可以将内部的变量和函数定义隐藏起来,外部作用域无法访问包装函数内部的任何内容
如果外部有一个变量xi,但是在函数作用域中也想用这个名字该怎么办呢?var xi = '外部'; function foo(){ var xi = '内部'; console.log(xi) //'内部' } foo() console.log(xi); //'外部'
但是所定义的变量foo可能污染了它当前所在的作用域,且执行这个函数,必须要再加一行代码foo()来调用;优化一下;
var xi = '外部'; (function foo(){ var xi = '内部'; console.log(xi) //'内部' })() console.log(xi); //'外部'
如果我们想在内部也访问外部的相同变量,该怎么区分和访问呢?
var xi = '外部';
(function foo(global){//这里可以将后面传来的参数重新命名;
var xi = '内部';
console.log(xi) //'内部'
console.log(global.xi) //内部
})(window) //这里只要把window对象作为一个参数传给封装函数就行了.
console.log(xi); //'外部'
**很多框架源码都用了这个方式来解决诸如防止undefined被篡改等情况**
**函数是js中最常见的作用域单元**
###三 提升
直觉上,我们会认为js在执行代码的时候是由上到下一步一步执行的,但是实际上并不完全是这样
我们先看看一段代码
```java
a = 2;
var a;
console.log(a); //2
这句话的代码等同于
var a;
a= 2;
console.log(2)
再来看一段代码:
console.log(a);//undefined
var a = 2;
这段代码等同于:
var a;
console.log(a);
a = 2;
在这段代码里到底发生了什么呢?
-
编译器
js的作用域基本上都是词法作用域;什么是词法作用域呢?就是定义在词法阶段的作用域,是由你在写代码的时候将变量和作用域写在哪来决定的.大部分情况下词法作用域是不变的.
我们之前提到过,编译器第一个工作就是把代码解析成词法单元,然后语法分析成语法树;在这期间,编译器基本能够知道标识符是在哪声明以及是怎么声明的.它会用合适的作用域将他们关联起来.
作用域编译.jpg
回到我们刚刚的代码;
console.log(a);//undefined
var a = 2;
在编译阶段,会执行var a;
;剩余的a = 2
会留在原地等待执行;因此等同于
var a;
console.log(a);
a = 2;
在这个阶段,变量声明会从他们代码所在的位置移动到最顶层更,这个过程就叫做提升;
-
函数优先
函数声明和变量声明都会被提升,但是,函数会首先被提升.然后才是变量;(值得注意的是,函数声明提升的时候,函数体一同被提升;函数表达式的函数体则不提升;)
foo(); // 1
var foo;//尽管此变量声明在function foo()之前,但是因为函数优先
//foo变量声明就会变成重复声明,忽略;
function foo(){
console.log(1);
}
foo = function(){
console.log(2);
}
这段代码会被引擎理解为:
function foo(){
console.log(1)
}
foo();
foo = function(){
console.log(2)
}
虽然var重复声明会被忽略,但是后面的函数声明会覆盖前面的
foo(); // 3
function foo(){
console.log(1);
}
var foo = function(){
console.log(2);
}
function foo(){ //覆盖了之前的function foo();
console.log(3);
}