1. var定义的变量只会变量提升
console.log(a) // undefined
var a = 1
a = 2
console.log(a) // 2
2. function 关键字定义的变量会变量提升和定义
func()
function func () {
console.log('ok')
}
会输出 ok
func()
var func = function () {
console.log('ok')
}
上述代码执行会报错Uncaught TypeError: func is not a function
3. 匿名函数具名化,在函数外部无法访问
A()
var func = function A () {
console.log('ok')
}
A()
匿名函数具名化不论是在函数定义之前还是之后都不能访问。
为什么匿名函数具名化在函数外部无法访问呢?
因为当前上下文中不会创建这个名字。这个函数执行时,在形成的私有上下文中,会把这个具名化的名字作为私有上下文的变量,对应的值就是这个函数。
函数具名化有什么作用呢?
递归调用,如果没有具名化想在函数内部递归调用,非严格模式下只能通过arguments.callee(严格模式下不支持)。
4. 条件判断无论是否成立,条件判断里面的 var和function 关键字都会进行变量提升
console.log(a) // undefined
if (false) {
var a = 1
}
var foo = 1
function bar() {
if (!foo) {
console.log('a')
var foo = 10
}
console.log(foo)
}
bar()
下面这道题很多人第一眼会做错,因为他们认为函数bar的if判断里面foo的值是1,这其实是错误的。函数bar的if体用 var 定义了 foo,会进行变量提升,且在if判断里面foo的值是undefined。
5. 形参赋值对变量提升的影响
var 关键字声明的变量和形参变量名一样,那么var 变量提升时,不会重复声明
var a = 1
function fn(a) {
/*
* EC(FN)
* 作用域链:<EC(FN), EC(G)>
* 形参赋值:a = 1
* 变量提升:
* var a
* a -> 0x001 [[scope]]: EC(FN) // 不会重复声明
* ----------------------------
* 函数体代码执行
*/
console.log(a) // 1
var a = 2
}
fn(a)
function 关键字声明的函数名和形参变量名一样,会重新定义(function 关键字的变量提升声明和定义是一起的)
var a = 1
function fn(a) {
/*
* EC(FN)
* 作用域链:<EC(FN), EC(G)>
* 形参赋值:a = 1
* 变量提升:
* var a
* a -> [Function: a]: EC(FN) // 不会重复声明,但是需要重新赋值(定义)
* ----------------------------
* 函数体代码执行
*/
console.log(a) // [Function: a]
function a () {}
}
fn(a)
6. 变量提升的一些变态机制
前置知识:除函数、对象等大括号外,其他大括号中出现了 let/const/function 则会单独形成块级上下文。
新版本浏览器要兼容ES3/ES5,也要兼容ES6,所以产生了一些变态机制:出现在其他大括号中的 function 不再是声明 + 定义,而是只声明。
console.log(foo) // undefined
{
function foo() {} // 在块级作用域内,已经在变量提升时处理过了,此时在块级作用域内跳过,但是全局作用域内也有引用到,会把函数之前所做的操作给全局一份
foo = 1
}
console.log(foo) // [Function: foo]
练习一下下面三道题,来检测一下自己对上述知识点的理解吧!
习题1
习题1是对上面1,2两知识点的综合考察
fn() // 输出5
function fn() { console.log(1) }
fn() // 输出5
function fn() { console.log(2) }
fn() // 输出5
var fn = function() { console.log(3) }
fn() // 输出3
function fn() { console.log(4) }
fn() // 输出3
function fn() { console.log(5) }
fn() // 输出3
我们需要牢记的是,var定义的变量只会变量提升,function 关键字定义的变量会变量提升和定义。所以前三个fn执行的时候输出5,第三个fn执行完之后,var 声明的 fn,会进行赋值,所以后面三个fn执行的时候输出3。
习题2
习题2是对知识点5的考察
var foo = 'hello';
(function (foo) {
/**
* EC(AN)
* 作用域链:<EC(AN), EC(FN)>
* 形参赋值:foo = 'hello'
* 变量提升:var foo(foo 已经有了,不会重复声明)
*/
console.log(foo) // hello
var foo = 'world'
console.log(foo) // world
})(foo)
console.log(foo) // hello
习题3
习题3是对知识点6的考察
console.log(foo) // undefined
{
console.log(foo) // [Function: foo]
function foo() {}
foo = 1
function foo() {}
console.log(foo) // 1
foo = 2
console.log(foo) // 2
}
console.log(foo) // 1