变量提升的处理机制还是比较重要的低层机制,虽然现在用ES6let
和const
来进行变量处理的时候已经不存在这个机制了,但是还不能完全的说ES6替代ES3,浏览器在ES6和ES3混入的时候一方面要向前兼容ES3一方面也要向后兼容ES6
# 变量提升
在当前上下文中(无论全局/私有/块级),JS代码自上而下执行之前,浏览器会提前处理一些事情(词法解析之前的一个环节,而词法解析一定发生在代码执行之前)。
会把当前上下文中所有带
var
、function
关键字进行提前声明或定义- 声明:是创建变量的过程
- 定义:是赋值的过程
带
var
只会提前的声明带
function
会提前的声明+定义
var a = 10
// 声明declare: var a
// 定义defined: a = 10
意义
让我们创建变量之前使用这个变量而不报错
# 案例
# var 提升
# 例1:
// 在代码执行之前:全局上下文中的变量提升
// var a 默认值是undefined
console.log(a) //=> undefined
var a = 12 //=> 创建值12 不需要声明a了(变量提升阶段完成,完成的事情不用重新处理 a=12)
a = 13 //=> 让全局的变量a = 13
console.log(a) //=> 13
# 例2:
// EC(G) 变量提升
console.log(a) //=> a is not defined
a = 13
console.log(a)
# 例3:
// EC(G) 变量提升 只有var/function会提升
console.log('ok') //=> 'ok'
console.log(a) //=> Cannot access 'a' before initialization 不能在let声明前使用变量
let a = 12
a = 13
console.log(a)
# function 提升
# 例1:
// 全局上下文中的变量提升
// func=函数 函数在这个阶段赋值都做了
// var a = 12在函数执行之前只是字符串
func()
function func(){
var a = 12
console.log('ok')
}
# 例2:
// 全局上下文中的变量提升
// func=函数 函数在这个阶段赋值都做了
// var a = 12在函数执行之前只是字符串
func() //=> Uncaught TypeError:func is not a function
var func = function () {
// 真实项目中建议用函数表达式创建函数,因为这样在函数变量提升的时候只会声明functopn不会赋值
console.log('OK')
}
func()
# 例3:
var func = function AAA(){
// 把原本作为值的函数表达式匿名函数“具名化”(虽然具名化,但是这个名字不允许外部调用 =>也就是不会再当前上下文中创建这个名字)
// 当函数执行,在形成私有上下文中,会把这个具名化的名字作为私有上下文中的变量来进行处理
console.log('OK')
console.log(AAA) //=> 当前函数
AAA() //递归调用,而不用严格模式下都不支持的 arguments.callee 了
}
func()
AAA() //=> Uncaught ReferenceError:AAA is not a function
# 例4:
// 基于var或者function在全局上下文中声明的变量(全局变量),会映射到GO(全局对象window)上一份,作为他的属性;而且接下来的是一个修改,另外一个也会同步修改
'use strict'
var a = 12
console.log(a) //=> 12 全局变量
console,log(window.a) // 12 映射到GO上的属性
window.a = 13
console.log(a) //=> 13 映射机制是一个修改另一个也修改
# 例5:
// EC(G):全局变量提升
// 无论条件是否成立,都要进行变量提升(细节点:条件中带function在新版本浏览器中,只会提升,不会再赋值了)
console.log(a,func) //=> [老版本] undefined function [新版本] undefined undefined
if(!('a' in window)){ //=> 检测a 是否为windeow的一个属性 !true => false
var a = 1
function func(){}
}
console.log(a) //=> undefined
# 例6:
// EC(G)变量提升
// fn=>1
// =>2
// var fn 已经声明过了
// =>4
// =>5
// 全局上下文中有一个全局变量fn,值是输出5的函数(此时window.fn=>5)
fn() //=> 5
function fn(){ console.log(1) } //=> 不再处理,变量提升阶段搞过了
fn() //=> 5
function fn(){ console.log(2) }
fn() //=> 5
var fn = function fn(){ console.log(3) } //=> var fn不用处理了,赋值在变量提升阶段没处理过,此处需要处理 fn = window.fn=>3
fn() //=> 3
function fn(){ console.log(4) }
fn() //=> 3
function fn(){ console.log(5) }
fun() //=>3
# 面试题
# 例1
var foo = 1
function bar(){
if(!foo){
var foo = 10
}
console.log(foo)
}
bar()
解析
另外一道题延展
# 例2
这道题挺难的,基本上99.99999%都做不对,这里分享里给大家一起看看
var a = 0
if(true){
a = 1
function a(){}
a = 21
console.log(a)
}
console.log(a)
解析
- 老版本浏览器

- 新版本浏览器
