请听题:
var c = 1;
function c(c) {
console.log(c);
var c = 3;
}
c(2);
复制代码
运行结果:
// 直接给你报错
TypeError: c is not a function
复制代码
解析:
-
第一个坑(预处理):
js语言本身具有预处理机制,js引擎在预处理期对所有声明的变量和函数进行处理,就是先把变量进行声明并读到内存里。也就是收集用var声明的变量信息和函数声明信息。变量和函数的优先顺序:先变量后函数。当变量名和函数名一致时后者会覆盖前者,来个小?:
function b() { }; var b ; console.log(typeof b) // function 复制代码
看啥看,就是
function
服不服? 接着再来个小?:function b() { }; var b = 1 ; console.log(typeof b) // number 复制代码
-
第二个坑(作用域域预处理):
js中存在两种作用域,
全局作用域
和函数作用域
, 预处理过程中的变量和函数提升都是在作用域上下文内进行。废话不多说,再来吃个?:var a = 2 function fn() { console.log(a) var a = 3 } fn() // undefined 复制代码
为什么是
undefined
呢? 全局作用域和fn
函数作用域内变量、函数分别进行提升。当fn
调用时,外层全局作用域的变量a
值未2
但是这并没什么卵用,因为在fn
的函数作用域内也声明了一个变量a
,函数作用域内的变量a
也会进行提升,当console.log(a)
语句执行的时候,fn
作用域内的a
变量声明但未赋值,所以输出undefined
。 -
回到本文?: 当本文中的例子
预解析
各种提升
完成以后,实际执行时的环境是这样的:var c ; c = function (c) { console.log(c); var c = 3; }; c = 1 ; c(2); // TypeError: c is not a function 复制代码
补充:
在
es6+
中,还存在块作用域
,结合let
、const
等关键字进行变量的生命能够避免很多问题,在本文中不再进一步讨论,有兴趣的同学可以去查一下。话又说回来,如果你没在ES6 前踩过这些坑,其实很难理解ES6 为什么会那么搞。