您的位置:首页 > 其它

函数声明 VS. 函数表达式

2017-08-12 14:14 176 查看
昨晚室友扔我一道前端JS面试题,我仔细看了一下,给出了答案,果不其然,有两个地方答错了,这里就记录一下。

题目

function Foo(){
getName = function(){alert(1)};
return this;
}
Foo.getName = function(){alert(2)};
Foo.prototype.getName = function(){alert(3)};
var getName = function(){alert(4)};
function getName(){alert(5)};
//请写出一下输出结果
Foo.getName()
getName()
Foo().getName()
getName()
new Foo.getName()
new Foo().getName()
new new Foo().getName()


解析

这里先说一下我做错的两项:二和三。

做第二个的时候,我的思路为:getName()这肯定是要调全局的getName函数,这时候要纠结一下了,我们有一个getName函数表达式,还有一个getName函数声明,那到底该调用哪个呢?这时候我的脑海里出现了一句话“在JavaScript中函数是一等公民”,so,我把getName函数表达式中的getName当作一个变量,getName函数声明中的getName当作函数,所以结果理所当然的认为是5。

我的思路从一开始就不对,拿到这道题,首先应该想一下考的知识点是什么?很明显,这就是考察变量提升(Hoisting)。就拿上面代码的后两行来说,先定义的getName函数表达式(1),然后是getName函数声明(2),首先要明确,
JS中,函数声明和函数表达式都是用来定义函数的
。所以前面我的理解都错了(函数表达式的左操作数是变量)。由于(1)和(2)都是定义了一个名为getName的函数,那调用时,到底执行的是哪个函数呢?这就要说到变量提升了。

变量提升

一切源于JS只有函数作用域和全局作用域
。在一个作用域中的代码执行之前,函数声明(函数本身也是一种变量–foo = function(){},所以也存在变量提升的现象)和变量定义通常会被解释器移到所在作用域的最顶部。

这里补充一下,在JavaScript中,变量进入一个作用域可以通过下面四种方式:

语言自定义变量:所有的作用域中都存在this和arguments这两个默认变量

函数形参:函数的形参存在于函数作用域中

函数声明:funciton foo(){}

变量定义:var foo

可以这样理解:在开始执行作用域中的代码时,

给当前
作用域对象(我自己起的名字)
添加一个arguments属性,属性值为调用该函数时,传递的参数列表。

给当前作用域对象添加属性名为函数的形参,属性值为函数的实参的属性。

把作用域中的函数声明提前,也就是给作用域对象添加一个属性名为函数名,属性值为函数声明的属性。

把作用域中的变量定义提前,相当于给作用于对象添加一个属性名为变量名,属性值为undefined的属性。

上面这一段仅仅是我个人的理解,网上有篇博文描写的很准确

当进入执行上下文(代码执行之前)时,VO(变量对象)里已经包含了下列属性:

函数的所有形参(如果我们是在函数执行上下文中)

— 由名称和对应值组成的一个变量对象的属性被创建;没有传递对应参数的话,那么由名称和undefined值组成的一种变量对象的属性也将被创建。


所有函数声明(FunctionDeclaration, FD)

— 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建;如果变量对象已经存在相同名称的属性,则完全替换这个属性。


所有变量声明(var, VariableDeclaration)

— 由名称和对应值(undefined)组成一个变量对象的属性被创建;如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。


我觉得有个例子能用来考察是否真正理解了变量提升

var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);


结果输出:1

变量和函数声明的函数名相同时,谁的优先级会更高

我是一个搬运工-–-

答案是看情况:

同样是一个 x 标识

如果在进行函数 function x() {}; 声明的同时,只声明 var x; 而不对 x 进行赋值操作,那么此时函数声明优先级更高,函数声明会覆盖掉变量声明

如果在进行函数 function x() {}; 声明的同时,不仅声明 var x; 而且同时还对 x 进行赋值操作,即 var x = 1;,那么此时,变量声明会被 保存 ,不会被覆盖。变量声明的优先级更高, 会覆盖掉函数声明。只有当函数声明及对函数的操作出现在变量声明之前时,函数声明才不会被覆盖。

function a() {}
console.log(a); //function a() {}
var a;
console.log(a); //function a() {}


function a() {}
console.log(a);  //function a() {}
var a = 1;
console.log(a); // 1


var a = 1;
function a() {}
console.log(a); // 1


看完代码上面的解释,相信以上三段代码大家都能得出正确的输出结果。

回到之前的面试题,第三个和第二个是同一个考点。这里就不再赘述了。

最后说一下开始的JS面试题的结果:2411233

参考文献

函数声明 VS 函数表达式https://changxiupeng.github.io/2016/10/29/%E5%87%BD%E6%95%B0%E5%A3%B0%E6%98%8E%E5%92%8C%E5%87%BD%E6%95%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F/

Javascript作用域和变量提升https://segmentfault.com/a/1190000003114255

js闭包http://www.cnblogs.com/zhjjNo1/archive/2011/02/12/1951905.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: