全局作用域
当浏览器加载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); // -> undefined
return function () {
console.log("test", num);
}
var num = 100;
}
fn();
在预解析时,如果变量名已经声明,不会执行重复声明,但是可以重新赋值。
// 预解析步骤如下:
// fn=xxxfff111 -> 声明+定义
// var fn; -> 声明(不会执行重复的声明,所以此时fn=xxxfff111)
// fn=xxxfff222 -> 声明(不会执行重复的声明)+定义(但是可以重新赋值),所以此时fn=xxxfff222
// -> 最终预解析结果是:fn=xxxfff222
fn(); // -> 222
function fn() { console.log(111) };
fn(); // -> 222
var fn = 10; // 会进行赋值操作,fn=10
fn(); // -> TypeError: fn is not a function
function 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
}();