全局作用域
当浏览器加载HTML的时候,会提供一个供全局JS代码执行的环境 -> 全局作用域(global/window)
预解析(变量提升)
在当前的作用域中,JS代码执行之前,浏览器会把所有带var/function的代码进行提前声明(declare)或定义(defined)
理解声明和定义
var num = 12;// declare: var num; -> 告诉浏览器中全局作用域中有一个num变量了// defined: num = 12; -> 给变量进行赋值
var 和 function 预解析的区别
- var -> 在预解析时只是提前声明
- function -> 在预解析时提前声明+定义
全局作用域下带 var 声明和不带 var 的区别
- 不带var:num1 = 12; -> 相当于给 window 增加了一个叫 num1 的属性,属性值是12
- 带var:num2 = 12; -> 相当于给全局作用域增加了一个全局变量 num2,但是不仅如此,它也相当于给 window 增加了一个叫 num2 的属性,属性值是12
- 在局部作用域中,设置一个全局变量: function () { total = 100; } -> 相当于给 window 增加一个属性名叫 total,其属性值是100
预解析只发生在当前作用域下
一开始只对window的作用域下进行预解析,只有函数执行的时候才对会函数中的代码进行预解析。
预解析例子
var num = 12;var obj = {name: "jex", age: 16}function fn(num1, num2) {var total = num1 + num2;console.log(total);}
局部作用域预解析
如何区分局部变量和全局变量
- 在全局作用域下声明(预解析的时候)的变量是全局变量
- 在局部作用域中声明的变量和函数的形参
判断一个变量的值,首先要确认它是否为局部变量。如果是局部变量,和外面全局变量没有任何的关系;如果不是局部变量,则往当前作用域的上级作用域进行查找,一直找到window为止(作用域链)。
- 当函数执行时
首先会形成一个新的局部作用域,然后按照如下步骤执行:
1) 如果有形参,先给形参赋值;
2) 进行局部作用域中的预解析;
3) 作用域中的代码从上到下执行;
函数形成了一个新的局部作用域,保护了里面的私有变量不受外面变量的干扰(外面的修改不了私有变量,私有变量也修改不了外面的变量 -> “闭包”)
- 局部作用域预解析例子
console.log("global:", total)var total = 0;function fn(num1, num2) {console.log("scope:", total);var total = num1 + num2;console.log("calc:", total)}fn(100, 200)console.log("end:", total)

预解析机制中一些注意情况
预解析时,不管条件是否成立,都会把带 var 的代码进行提前声明
if(!("num" in window)) {var num = 12;}console.log(num) // undefined
预解析时只解析 “=” 左边的,不解析右边 ```javascript // Uncaught TypeError: fn is not a function fn(); // fn是函数表达式 var fn = function () { console.log(“ok”) }
// —————对比函数声明————— fn(); // -> “ok” function fn() { console.log(“ok”) }
- 自执行函数在全局作用域下不进行预解析```javascript// 自执行函数:定义和执行一起完成了(function (num) {console.log(num)})(100)
函数体中return后的代码不执行了,但会先进行预解析;return后的返回值,不会进行预解析。
function fn() {console.log(num); // -> undefinedreturn function () {console.log("test", num);}var num = 100;}fn();
在预解析时,如果变量名已经声明,不会执行重复声明,但是可以重新赋值。
// 预解析步骤如下:// fn=xxxfff111 -> 声明+定义// var fn; -> 声明(不会执行重复的声明,所以此时fn=xxxfff111)// fn=xxxfff222 -> 声明(不会执行重复的声明)+定义(但是可以重新赋值),所以此时fn=xxxfff222// -> 最终预解析结果是:fn=xxxfff222fn(); // -> 222function fn() { console.log(111) };fn(); // -> 222var fn = 10; // 会进行赋值操作,fn=10fn(); // -> TypeError: fn is not a functionfunction fn() {console.log(222) };fn()
所以结果是输出两次222后报错,停止运行程序。
查找上级作用域
上级作用域只跟函数在哪定义有关,跟函数在哪里执行没有任何关系。
var num = 12;function fn() {var num = 120;return function () {console.log(num)}}var f = fn;f(); // -> 120~function () {var num = 1200;f() // -> 120}();

