JavaScript的运行阶段分为预编译阶段
和执行阶段
,今天要讨论的变量声明提高和函数声明提高,就是在这个阶段完成的。函数
在预编译阶段,JS引擎会作一件事情,那就是读取变量的定义
并肯定其做用域
即生效范围。code
变量定义ip
变量做用域作用域
先看一个简单示例:io
var name = 'ryan'; function say(){ console.log(name); //输出:undefined var name = 'zoe'; console.log(name); //输出:'zoe' } say();
解析:上述代码从结果看,say函数执行第一次打印name时,并未打印全局的name('ryan'),而是打印局部的name(undefined),这是由于在预编译阶段,say函数内部进行了变量声明提高,提高后的执行效果以下:console
var name = 'ryan'; function say(){ var name; //变量name声明提高至做用域顶部,但未赋值,故为undefined console.log(name); //存在局部name,则无视全局name name = 'zoe'; //变量赋值保持原位 console.log(name); //输出:'zoe' } say();
函数的两种建立方式:编译
函数声明:function
say(); //输出:'saying' function say(){ console.log('saying'); }
函数表达式:变量
say(); //报错:say is not a function var say = function(){ console.log('saying'); }
解析:一样地先执行函数,后建立函数,结果倒是不同。缘由在于,经过函数声明的方式,该函数声明(包括定义)
会被提高至做用域的顶部,而表达式的建立方式则只提高了变量say至做用域的顶部,此时的say其值为undefined,调用say()天然报错“say不是一个方法”。方法
再来看一个示例:
var say = function(){ console.log('1'); }; function say(){ console.log('2'); }; say(); //输出:'1'
解析:预编译阶段进行变量声明提高和函数声明提高后,上述代码执行效果等同于:
var say; //变量声明提高 function say(){ //函数声明提高 console.log('2'); } say = function(){ //变量赋值保持原位执行,say函数被覆盖 console.log('1'); }; say(); //输出'1'
总结:函数声明提高,会将函数的声明和定义全都提高至做用域顶部。
变量声明提高,只提高声明部分(未赋值状态),赋值部分保持原位置不动。
函数声明提高的优先级要高于变量声明提高。
先看一个简单示例:
console.log(say); //输出:[Function: say] function say(){ console.log('1'); }; var say = '2'; console.log(say); //输出'2'
解析:本例中声明的函数和变量同名都是say,且函数声明在先,变量声明在后,按理说第一次打印say值预期会是undefined,然而结果是[Function: say]。
预编译阶段进行变量声明提高和函数声明提高后,上述代码执行效果等同于:
var say = function (){ //函数声明(包括定义)提高 console.log('1'); }; var say; //只是声明,并不会覆盖say的值 console.log(say); //故输出:[Function: say] say = '2'; //此时say会被覆盖 console.log(say); //输出'2'
总结:同名状况下,函数声明提高优先级要高于变量声明提高,且提高后该函数声明定义不会被提高后的同名变量声明所覆盖,可是会被后续顺序执行的同名变量赋值所覆盖。