Appearance
变量提升机制
变量提升(hoisting)
当栈内存(作用域)形成,JS 代码自上而下执行之前,浏览器首先会把所有带 “VAR”/“FUNCTION” 关键词的进行提前 “声明” 或者 “定义” ,这种预先处理机制称之为 “变量提升”
- 声明 (declare):var a (默认值 undefined)
- 定义 (defined):a=12 (定义其实就是赋值操作)
- 带 VAR 的在变量提升阶段只是声明,带 FUNCTION 声明和定义在变量提升阶段都完成了
基本数据类型
- 带“VAR”的只声明未定义
- 带“FUNCTION”的声明和赋值都完成了
- 变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的进行提升,因为此时函数中存储的都是字符串而已)
遇到函数如何执行
- 在全局作用域下声明的函数或者变量是“全局变量”,同理,在私有作用域下声明的变量是“私有变量” 【带 VAR/FUNCTION 的才是声明】
- 浏览器很懒,做过的事情不会重复执行第二遍,也就是,当代码执行遇到创建函数这部分代码后,直接的跳过即可(因为在提升阶段就已经完成函数的赋值操作了)
带 VAR 和不带的区别
在全局作用域下声明一个变量,也相当于给 WINDOW 全局对象设置了一个属性,变量的值就是属性值(私有作用域中声明的私有变量和 WINDOW 没啥关系)
用 var 定义 a
javascript
console.log(a); // => undefined
console.log(window.a); // => undefined
console.log("a" in window); //=> TRUE 在变量提升阶段,在全局作用域中声明了一个变量A,此时就已经把A当做属性赋值给WINDOW了,只不过此时还没有给A赋值,默认值UNDEFINED in:检测某个属性是否隶属于这个对象
var a = 12;
1
2
3
4
2
3
4
映射机制
javascript
var a = 12; // => 全局变量值修改,WIN的属性值也跟着修改
console.log(a); // => 全局变量A 12
console.log(window.a); // =>WINDOW的一个属性名A 12
a = 13;
console.log(window.a); // => 13
window.a = 14;
console.log(a); // => 14
// => 全局变量和WIN中的属性存在 “映射机制”
// => 不加VAR的本质是WIN的属性
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
不带 var 定义
javascript
console.log(a); // => Uncaught ReferenceError: a is not defined
console.log(window.a); // => undefined
console.log("a" in window); // =>false
a = 12; // => window.a=12
console.log(a); // =>12
console.log(window.a); // => 12
1
2
3
4
5
6
2
3
4
5
6
定义多个变量
定义多个后,直接调用
javascript
console.log(a, b); // =>undefined undefined
var a = 12,
b = 13; //=> 这样写B是带VAR的
1
2
3
2
3
用等号赋值
javascript
var a = (b = 12); // => 这样写B是不带VAR的 => var a=12; b=12;
1
私有作用域中的区别
私有作用域中带 VAR 和不带也有区别
- 带 VAR 的在私有作用域变量提升阶段,都声明为私有变量,和外界没有任何的关系
- 不带 VAR 不是私有变量,会向它的上级作用域查找,看是否为上级的变量,不是,继续向上查找,一直找到 window 为止(我们把这种查找机制叫做:”作用域链”),也就是我们在私有作用域中操作的这个非私有变量,是一直操作别人的
javascript
var a = 12,
b = 12;
function fn() {
// 私有作用域
console.log(a, b); // =>undefined 12
var a = (b = 13); // => var a=13; b=13;=
console.log(a, b); // =>13 13
}
fn();
console.log(a, b); //=>12 13
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11

等号左边变量提升
javascript
/*
* 变量提升:
* var fn; =>只对等号左边进行变量提升
* sum = AAAFFF111;
*/
sum();
fn(); // => Uncaught TypeError: fn is not a function
//=>匿名函数之函数表达式
var fn = function () {
console.log(1);
}; //=>代码执行到此处会把函数值赋值给FN
fn();
//=>普通的函数
function sum() {
console.log(2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
条件判断下的变量提升
- 在当前作用域下,不管条件是否成立都要进行变量提升
- 带 VAR 的还是只声明
- 带 FUNCTION 的在老版本浏览器渲染机制下,声明和定义都处理,但是为了迎合 ES6 中的块级作用域,新版浏览器对于函数(在条件判断中的函数),不管条件是否成立,都只是先声明,没有定义,类似于 VAR
全局作用域下声明
var a;
=> 在全局作用域下声明的全局变量也相当于给 WIN 设置了一个属性 window.a=undefined
javascript
console.log(a); // =>undefined
if ("a" in window) {
var a = 100;
}
console.log(a); //=>100
1
2
3
4
5
2
3
4
5
在私有作用域下声明
javascript
/*
* 变量提升:无
*/
f = function () {
return true;
}; //=>window.f=...(TRUE)
g = function () {
return false;
}; //=>window.g=...(FALSE)
~(function () {
/*
* 变量提升:
* function g; //=>g是私有变量
*/
if (g() && [] == ![]) {
// =>Uncaught TypeError: g is not a function (此时的g是undefined)
//=>[]==![]:TRUE
f = function () {
return false;
}; //=>把全局中的f进行修改 window.f=...(FALSE)
function g() {
return true;
}
}
})();
console.log(f());
console.log(g());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
重名问题的处理
- 带 VAR 和 FUNCTION 关键字声明相同的名字,这种也算是重名了(其实是一个 FN,只是存储值的类型不一样)
javascript
var fn = 12;
function fn() {}
console.log(fn); // 12
1
2
3
2
3
- 关于重名的处理:如果名字重复了,不会重新的声明,但是会重新的定义(重新赋值)【不管是变量提升还是代码执行阶段皆是如此】
javascript
/*
* 变量提升:
* fn = ...(1)
* = ...(2)
* = ...(3)
* = ...(4)
*/
fn(); // =>4
function fn() {
console.log(1);
}
fn(); // => 4
function fn() {
console.log(2);
}
fn(); //=>4
var fn = 100; // => 带VAR的在提升阶段只把声明处理了,赋值操作没有处理,所以在代码执行的时候需要完成赋值 FN=100
fn(); //=>100() Uncaught TypeError: fn is not a function
function fn() {
console.log(3);
}
fn();
function fn() {
console.log(4);
}
fn();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
查找上级作用域
当前函数执行,形成一个私有作用域 A,A 的上级作用域是谁,和他在哪执行的没有关系,和他在哪创建(定义)的有关系,在哪创建的,它的上级作用域就是谁
javascript
var a = 12;
function fn() {
//=>arguments:实参集合
//=>arguments.callee:函数本身FN
//=>arguments.callee.caller:当前函数在哪执行的,CALLER就是谁(记录的是它执行的宿主环境),在全局下执行CALLER的结果是NULL
console.log(arguments.callee.caller);
}
function sum() {
var a = 120;
fn();
}
function aa() {
fn();
}
aa();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15