//函数表达式
var greeting = function(){
console.log("hello world");
}
//声明式函数
function greeting(){
console.log("hello world");
}
上面的两种函数定义方法是我在写代码的时候比较常用的,一般是一直使用一种方法,但考虑到如果同时使用两种定义函数的方法,而且定义的函数名字是一样,会是怎样的结果?
首先,我们要了解到这里涉及到的知识有函数提升,变量提升,两者提升的优先级,还有JS引擎在编译阶段是怎么处理函数提升、变量提升的
JS的提升
在编译阶段阶段,代码真正执行前的几毫秒,会检测到所有的变量和函数声明,所有这些函数和变量声明都被添加到名为[Lexical Environment]词汇环境的JavaScript数据结构内的内存中。所以这些变量和函数能在它们真正被声明之前使用。
两者提升的优先级
console.log(foo);//
function foo(){
console.log("函数声明");
}
var foo = "变量";
/**
答案:
function foo(){
console.log("函数声明");
}
*/
那么问题来了,既然变量提升优先级是在后面,为什么没有将foo函数覆盖为undefined呢?(因为var声明变量提升会在编译阶段将变量添加到词法环境中,然后赋值为undefined)。
JS引擎工作:
一个代码块{}中包含的代码就是一段,里面的变量都是先编译后执行
- 找些代码来调用一个函数
- 在执行
函数代码之前,创建执行上下文。 - 进入创建阶段
-
初始化
作用域链 -
创建
变量对象:-
创建
arguments对象,检查参数的上下文,初始化名称和值并创建引用的副本。 -
扫描上下文以获取函数声明:
- 对于找到的每个函数,在
变量对象(或活动对象)中创建一个属性,该属性是确切的函数名称,该函数具有指向内存中函数的引用指针。 - 如果函数名已存在,则将覆盖引用指针值。
- 对于找到的每个函数,在
-
扫面上下文以获取变量声明:
- 对于找到的每个变量声明,在
变量对象(或活动对象)中创建一个属性,该属性是变量名称,并将值初始化为undefined。 - 如果变量名称已存在于
变量对象(或活动对象)中,则不执行任何操作并继续扫描(即跳过)。
- 对于找到的每个变量声明,在
-
确定上下文中的this。
-
- 激活/代码执行阶段:
- 在上下文中运行/解释功能代码,并在代码逐行执行时分配变量值。
上面的步骤可以看出,函数提升优先级比变量提升要高。
练习题目
console.log(greeting)
//函数表达式
var greeting = function(){
console.log("函数表达式");
}
//声明式函数
function greeting(){
console.log("声明式函数");
}
/**
答案:
function greeting(){
console.log("声明式函数");
}
*/
//函数表达式
var greeting = function(){
console.log("函数表达式");
}
console.log(greeting)
//声明式函数
function greeting(){
console.log("声明式函数");
}
/**
答案:
function greeting(){
console.log("函数表达式");
}
console.log(greeting)
//函数表达式
function greeting(){
console.log("第一个声明式函数");
}
//声明式函数
function greeting(){
console.log("第二个声明式函数");
}
/**
答案:
function greeting(){
console.log("第二个声明式函数");
}
从练习题目可以得出,赋值之后,变量才会被覆盖,且在同名函数下,会被后面定义的同名函数覆盖
总结
-
函数提升优先级比变量提升要高,且在编译阶段不会被变量声明覆盖,JS引擎会选择跳过同名的变量,不做创建和初始化。
-
在执行阶段,与函数同名的变量会在赋值语句之后,将之前的同名函数覆盖
-
同名函数下,会被后面定义的同名函数覆盖