Skip to the content.

标识符,作用域,预解析,闭包

###1.作用域 定义:用来管理标识符可以在那里被访问的一套规则,或者说是标识符可以被访问的范围。

作用域分类:全局作用域和 局部作用域

全局作用域:在代码中任何地方都能访问到拥有全局作用域

局部作用域:在js中只能使用函数来划分作用域

if和for循环不能划分作用域。

2.作用域链

函数嵌套的时候,如果一个作用域中找不到一个变量,他会去找父集作用域,而不会去找子集作用域。如果父集找不到就会再往外找。如果父集和父集的父集都有该变量,会找父集的变量。而不是父集的父集。

3.js预解析

当js代码执行之前,会有一个预解析的过程,程序会把当前作用域中的变量名和函数的声明提前解析,js会把当前申明的变量名和函数提前当前作用域的最开始处理。 > 预解析只会发生在通过**var**定义的变量和**function**上。

函数表达式:只会把变量名提到当前作用域的最前面。所以不能在函数表达式赋值前调用。

var声明的变量和function声明的函数在预解析的区别

var声明的变量和function声明的函数在预解析的时候有区别,var声明的变量在预解析的时候只是提前的声明,function声明的函数在预解析的时候会提前声明并且会同时定义。也就是说var声明的变量和function声明的函数的区别是在声明的同时没有同时进行定义

查找标识的时候是从定义的位置查找的,不是调用的位置。

1、

var c = 100;
function func(){
	console.log(c);
	var c = 1;
}

上面打印的是c是undefined。

2、

var a=b=10;
function fn(){
	var c=d=20;//赋值先解析右边,因为d没有var,所以d变成全局变量,
}
console.log(d)

上面打印的是d,结果为20,并不是undefined。因为d变成了全局变量。

如果声明多个变量的时候,可以使用一个var,用逗号隔开, var a=1,b=2,c=3,d=4;

3. var , function声明的变量提升

if( false ){
    var aa = 20;
    var bb = 30;
}

function AA(){};
function BB(){};

//var定义的aa,bb以及function定义的AA(),BB()都会被变量提升到window对象下面

4、 函数声明与函数表达式在预解析的区别

AA();
function AA(){};

BB();
var BB = function(){};

//AA();不会报错,因为是以function的变量提升,BB()会报错,因为是以var的变量提升,到其相当于 BB(); var BB = undefined; BB = function(){};

 首先,我们知道解析器会对function定义的函数(也就是函数声明)在代码开始执行之前对其实行函数声明提升(function declaration hoisting),所以在函数声明之前调用该函数是不会在执行期间报错,但是函数表达式不同,函数表达式用 var 声明,也就是说解析器会对其变量提升,并对其赋值为undefined,然后在执行期间,等到执行到该var 变量的时候再将其变量指向一个function函数,所以在函数表达式之前执行该函数是会报错的。

5. function 覆盖

AA();   // 输出 I am AA_2;
function AA(){
   console.log('I am AA_1');
};

AA();  // 输出 I am AA_2;
function AA(){
  console.log('I am AA_2');
}

若定义了两个同名的函数,则在预解析期间后面一个会覆盖签名一个

6.预解析把变量或函数解析到其运行时的环境中

``` aa = “I am aa”; (function(){ console.log(aa); // 输出 aa 是 undefined var aa = “I am aa in a function”; console.log(aa); //输出 aa 是 I am aa in a function })();

// 这里 aa 被变量提升,但是aa 没有被变量提升到 window下面,而是被提升到其运行的环境 (function(){ })() 中去,也就是等同于

// aa = “I am aa”; //(function(){ // var aa;
// console.log(aa); // 输出 aa 是 undefined // aa = “I am aa in a function”; // console.log(aa); //输出 aa 是 I am aa in a function //})();

// 下面代码等同于上面,目的是为了若看不懂上面,则可看下面。 aa = “I am aa”; function AA(){ console.log(aa); var aa = “I am aa in a function”; console.log(aa); } AA();

> 解析器将变量提升并不是将所有的变量都提升到window对象下面,其提升的原则是提升到变量运行的环境中去

#### 7. JavaScript“预解析”分段进行

AA(); // 输出 AA2; function AA(){ console.log(‘AA1’); }

function AA(){ console.log(‘AA2’); }

function AA(){ console.log(‘AA3’); }

//上面例子说明function函数声明是分块的,然而至于var变量的提升经过反复验证是不分块的( 此处如有不同意见请指教 )。

> 所谓分段进行是按照< script >标签来分块进行预解析

#### 8. 变量和函数重名的时候会是什么情况

alert(test); //function(){ return “ this is function”}
var test=”this is test”;
function test(){
return “this is function”;
}
alert(test); // this is test
alert(test()); // this is function

> 当函数名和变量名一样的时候,alert(test);调用的是函数function test() 的指针,而不是调用的变量var test; 所以function的预解析 优先级 高于var(个人觉得)

**变量和函数重名的情况列举:**

var fn = 13;
function fn() {
console.log(‘ok’);
}
fn(); // Uncaught TypeError: fn is not a function

fn(); // -> 2
function fn() {console.log(1);}
fn(); // -> 2
var fn = 10; // -> fn = 10
fn(); // -> 10() Uncaught TypeError: fn is not a function function fn() {console.log(2);}
fn();


#### 9. 按script块解析的顺序和结果
    alert(msg);  
    //test();  
  
  
    var msg ="test";  
    /* function test(){ 
     alert("this is function"); 
     }*/  
- 这里有两个script块,他们是按照顺序进行预解析的,我们在第二个script块中定义的var msg,而在第一个script块中alert(msg)

- 显然在第一个script快中并没有预解析到一个叫msg的变量或者函数,而js又不会跨到第二个script块去解析,所以就报错msg未定义

**如果把两个script块调换一下位置,结果就不一样了**

var msg =”test”;
function test(){
alert(“this is function”);
}

alert(msg); // test
test(); // this is function

- script块顺序进行预解析,当第一个script块预解析完,会解析到var msg 和function test,

- 当再第二个script块中调用alert(msg);和test();时上面的js程序已经执行完毕了,自然会弹出 test 和this is function
#### 10. 函数带参数的解析

var a = 1; function fn1(a){ alert(a); a = 2; }; fn1(); // undefind。 alert(a); // a 值为: 1。

**解析过程如下:**

var a; // a 变量 提前 当前值:undefind。(全局变量) function fn1(a){ alert(a); a = 2; }; // fn1 函数块提前 值为: 整个函数 // 该提前的都提前的现在开始 逐行 解读代码, //有人会说那 参数 呢? 哈哈哈 不要着急还没到它呢。 var a = 1; // a = 1 赋值表达式 改变 变量 a 值为:1。 function fn1(a){
var a; //参数被当成变量 解析到当前函数 顶部。 alert(a); // a 当前函数内 a 值为:undefined. a = 2; // a = 2 复制表达式 无用 }; // 函数块 没有遇到调用 无用。 fn1(); // 现在遇到函数fn1 的调用了 咱们就去它里面看看
// 调用成功后 弹 undefind。 alert(a); // 这个 还是调用 全局的变量 a 值 为 1。

**如果把参数换成c不是a:**

var a = 1; function fn1(c){ alert(a); a = 2; }; fn1(); // 1 相当于一个匿名函数调用,函数中没有a到上一级找。 alert(a); // a 值为: 2

#### 11. 有作用域链的解析

console.log(total); //undefined; var total = 0; function func(num1, num2) { console.log(total); //undefined; var total = num1 + num2; console.log(total); //300; } func(100 , 200); console.log(total) //0;

**如果函数体内没有声明变量total**

console.log(total); //undefined; var total = 0; function func(num1, num2) { console.log(total); //0; total = num1 + num2; console.log(total); //300; } func(100 , 200); console.log(total) //300;

**上面所有的total都是全局变量**

#### 11. 全局作用域下带var和不带var的区别

console.log(num1); //undefined; var num1 = 12;

console.log(num2); //报错! num2 = 12;

> 在全局作用域中声明变量带var可以进行预解析,所以在赋值的前面执行不会报错;声明变量的时候不带var的时候,不能进行预解析,所以在赋值的前面执行会报错。

- `num2 = 12; `相当于给window增加了一个num2的属性名,属性值是12
- `var num1 = 12;` 相当于给全局作用域增加了一个全局变量num1,但是不仅如此,它也相当于给window增加了一个属性名num1,属性值是12
#### 12. 自执行函数:定义和执行一起完成

(function (num) { console.log(num); })(100);

> 自执行函数定义的那个function在全局作用域下不进行预解析,当代码执行到这个位置的时候,定义和执行一起完成了。

**其他定义自执行函数的方式:**

~ function (num) {}(100)

###4.闭包 1、当一个函数申明嵌套另外一个函数申明的时候,就会产生一个闭包环境函数嵌套函数,内层函数可以访问外层函数中的变量

2、函数形成一个新的私有的作用域,保护了里面的私有变量不受外界的干扰(外面修改不了私有的,私有的也修改不了外面的),这也就是闭包的概念。