当浏览器开辟出供 js 执行的栈内存之后,代码并不是立即自上而下执行,而是需要先做一些事情:把当前作用域中带 var 和 function 的关键字进行提前的声明和定义(变量提升)
- var:只声明,未定义(不赋值)
- function:声明和定义(赋值)一起完成
匿名函数具名化
匿名函数具名化【规范函数执行的顺序】
1 | var fn = function sum() { |
匿名函数具名化可以让匿名函数实现递归调用
1 |
|
作用域中的变量提升
作用域链查找原则:首先会在当前作用域中查找,如果没有的话会沿着作用域链向上查找, 直至全局作用域
- 在全局作用域获取不到,报错:
... is not defined
- 如果是赋值语句,就相当于给全局作用域添加了这样一个属性名和属性值
全局变量
在全局作用域(默认会提供一个最大的 window 对象)中声明的变量
- 函数没有形参,且函数体里没有 var 声明
1 | var a = 6; |
答案:
1 | // 6 |
私有变量
函数执行的时候形成的作用域是私有的,保护里面的变量不受外界干扰
形参
函数有形参,但没有传实参,且打印在赋值之前
1
2
3
4
5
6
7var a = 6;
function fn(a) {
console.log(a);
a = 1;
}
fn();
console.log(a);答案:
1
2// undefined
// 6在私有作用域中声明的变量
函数没有形参,函数体里有 var 声明
1
2
3
4
5
6
7
8
9
10
11
12console.log(a, b);
var a = 12,
b = 12;
function fn() {
// 变量提升 var a;a 是私有的, b是 windows 全局作用域
console.log(a, b);
// 下面语句相当于 b=13; var a=b
var a = b = 13;
console.log(a, b);
}
fn();
console.log(a, b);答案:
1
2
3
4// undefined undefined
// undefined 12
// 13 13
// 12 13复合(形参 + 私有作用域中声明变量)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/*
私有作用域 fn(a)
1.形参赋值 a=12; a是私有的
2.变量提升 var b; b是私有的
只有 c 是全局作用域的
3.代码自上而下执行
*/
var a = 12,
b = 13,
c = 14;
function fn(a) {
console.log(a, b, c);
var b = c = a = 20;
console.log(a, b, c);
}
fn(a);
console.log(a, b, c);答案:
1
2
3// 12 undefined 14
// 20 20 20
// 12 13 20
块级作用域
- let 声明的变量不进行变量提升不能重复声明
1 | let a = 10, |
答案:
1 | // 20 20 |
引用类型数据问题
1 | /* |
答案:
1 | // [12,13] |
* 变量提升特殊性
* 判断条件
不论判断条件是否成立,都会进行变量提升
* 条件不成立
var:只声明不定义
1 | // 不管条件是否成立,都会进行变量提升,var a |
答案:
1 | // undefined |
function:
在老版本浏览器中:声明+定义
在新版本浏览器中:只声明不定义
可以把它理解成函数表达式
var fn = function(){}
,只声明var fn
delete fn
返回 false(var 变量
无法使用 delete 删除)
1 | // 在新版本浏览器中,判断条件中的function相当于只声明未定义,所以undefined |
答案:
1 | // undefined |
条件成立
- 判断条件成立,会对执行体中的 fn 进行变量提升(声明+赋值)
1 | console.log(fn); |
答案:
1 | // undefined |
特殊情况
判断条件成立,如果有 function 定义的变量,在这个 function 函数后面更改变量的值,更改的都是私有变量
可以把
if(){} 的
{ }
理解成块级作用域(特例: function(){} 的{ }
是私有作用域)
1 | var a = 0; |
自执行函数
- 自执行函数在当前作用域下不进行变量提升
新版浏览器:
- 在全局作用域中,没有变量提升
- 代码自上而下执行,
window.f = function(){}
和window.g = function(){}
- 进入自执行函数,走到 if 语句中,函数 g 声明提升,此时 g 只声明未定义,相当于 undefined,所以 g() 会报类型错误
1 | f = function () { |
答案:
1 | // TypeError: g is not a function |
老版浏览器:
在全局作用域中,没有变量提升
代码自上而下执行,
window.f = function(){}
和window.g = function(){}
进入自执行函数,走到 if 语句中,函数 g 声明提升并定义,
g() 为 true,参考下图优先级顺序,
==
优先级高于&&
g() && [] == ![]
,![] 转换为 false,再转换为0;[] 转换为 0;[] == ![]
返回 true
true && true
返回 true,进入循环f 进行重新赋值,f 指向
function(){return false}
,g 已经声明,不会再重复声明, g没有被修改答案:
1
2 // false
// false
赋值运算
- 只对等号左边进行变量提升(函数表达式)
1 | console.log(fn); |
答案:
1 | // undefined |
return 语句
- return 下面的代码虽然不能执行,但是可以进行变量提升(f2 进行变量提升)
- return 后面的代码不能进行变量提升(f1 不进行变量提升)
1 | function fn() { |
答案:
1 | // [Function: f2] |
重复变量名
- var 不会进行重复声明,但会重新赋值
1 | var num = 4; |
- function 在变量提升阶段 声明和定义是一同完成的,如果遇到重复声明定义的,会重新进行赋值
1 | /* |
答案:
1 | // 4 |