JS 变量提升简单了解,
window 属性 和 var 定义的全局变量 有 映射关系
1. 变量提升以及私有作用域
console.log(a, b); //1: undefined, undefined, 变量提升(var 只声明), var a; var b; fn => 普通函数,声明 + 定义
var a = 12, b = 12;
function fn1 () {
/*fn1() 私有作用域下
* => 带var就是私有变量;
* => 不带不是私有变量: 向上级作用域查找,找到就停止,否则 一直找到 window 为止(这种查找机制叫做 "作用域链")
* 注意: 也就是说, 在私有作用域中操作的非私有变量, 是一直操作的别人的变量.
*/
console.log(a, b) // 3. 变量提升, var a; a = undefined; b = 12;
var a = b = 13; // 等价于 var a = 13; b = 13;
console.log(a, b) // 4: a = 13; b = 13;
}
fn1(); // 2: 跳过函数创建的代码, 直接执行
console.log(a, b) // 5: a = 12; b = 13; 这里的 a 是全局的变量, 和 函数内私有变量 a 不冲突;
2. 作用域链的拓展
function fn2 () {
// console.log(b2) // Uncaught ReferenceError: b2 is not defined
b2 = 13;
console.log('fn 内 b2: ', b2, 'b2' in window);
// 'b2' in window: true => 在作用域链查找的过程中, 如果找到 window 也没有这个变量, 相当于给 window 设置了一个属性 b(window.b = 13)
}
fn2();
console.log('fn 外 b2: ', b2);
console.groupEnd();
3. 只对等号左边变量提升
console.group("3. 只对等号左边变量提升");
// fn3(); // Uncaught TypeError: fn3 is not a function
var fn3 = function () {
console.log('fn3')
}
fn3(); // fn3
4. 条件判断下的变量提升
/*当前作用域下, 不管条件是否成立, 都要进行变量提升
* => 带 var 的还是只 声明
* => 带 function 的在老版本浏览器渲染机制下, 声明 + 定义 都处理;
* 新版本浏览器为了迎合ES6的块级作用域, 对于函数(条件判断中的函数), 不管条件是否成立, 都只 声明, 没有定义, 类似于 var;
*/
console.log(a4); // undefined
if ('a4' in window) {
var a4 = 100
}
console.log(a4); // 100
// 匿名函数, 变量 没有变量提升
f4 = function () { return true }; // window.f4 = func...
g4 = function () { return false }; // window.g4 = func...
~function () {
/* 变量提升 func g4, g4 = undefined, 是私有变量 =>
* 老版本浏览器可以通过;
* 新版本浏览器报错: g4() => Uncaught TypeError: g4 is not a function
*/
if (g4 === undefined && [] == ![]) {
// if (g4() && [] == ![]) {
f4 = function () { return false }; // 把全局下的 f4 修改
function g4 () { return true }; // 私有变量, 不影响外部的 g4
}
} ()
console.log(f4());
console.log(g4());
[] == ![] // 结果: true, (null, undefined, NaN, ‘’, 取反为 true)
5. ES6中基于 let/const 定义的变量或者函数
/* 1. 不存在变量提升机制;
* 2. 切断了 全局变量与 window 属性的映射关系;
* 3. let 不允许声明相同名字的变量;
* 注意: 当前作用域代码自上而下执行之前, 浏览器会做一个重复性检测(语法检测), 自上而下查找当前作用域下所有变量,
* 一旦发现重复的,直接抛出异常,代码也不会执行(虽然没有提前声明, 但是浏览器已经记录了所有的变量).
*/
let a5 = 10, b5 = 10;
let fn5 = function () {
// console.log(a5, b5); // can't access lexical declaration 'a5' before initialization
let a5 = b5 = 12;
console.log(a5, b5)
}
fn5();
console.log(a5, b5)