代码改变世界

函数表达式与函数声明的一点区别

2013-09-26 16:25  二当家的  阅读(225)  评论(0编辑  收藏  举报

面试的时候面试官出了一道题,是关于JavaScript中函数表达式与函数声明的,可惜当时答错了,以前对这方面的虽然有些认识,但还是不够深刻,上网查了下相关的资料,才明白这期中的一些区别。

原题:

var x = 1;

x = function(){

  alert(“x”);

}

var y = 1;

function y(){

  alert(“y”);

}

说出执行下面的代码的执行结果

x();

y();

原以为的结果:

x()//输出字符x

y()//输出字符y

想法误区:

误以为在声明变量之后,无论是用函数表达式赋值,还是用函数声明一个与变量同名的函数,因为代码的执行顺序,都会覆盖之前的变量声明,因此到执行x(),y()时,x,y都是函数,是可以执行的,还一个劲的以为题目是不是出错,万分囧....

实际结果:

x()//输出字符x

y()//报错,number is not a function

结果分析:

从报的错误上看,显然是y仍然是一个number,说明函数声明没有起到应有的作用。

这里的主要原因是跟JavaScript执行时在处理上下文代码的机制有关。

题中所有函数以及变量都在全局环境中,在JS刚执行时,全局中的变量对象(VO)最开始包括:

  1. 所有函数声明
  2. 所有变量声明
  3. (如果执行环境是一个函数,则还包括函数的所有形参)

进入全局上下文时,VO表现如下:

VO(global) = {

    x: undefined,

    y: <reference to FunctionDeclaration>

    //这里也可以看出函数表达式与函数声明的不同,函数声明会影响到上下文的变量对象,而函数表达式不会

};

当代码进一步执行时,VO中的x会被赋值,

x = 1

接着

x = function(){alert(“x”);}

因为js中函数也是一个对象,所以是可以作为参数传递给另一个函数或者作为值赋给另外一个变量的。所以,这里赋值是成功的,因此x到最后就是一个函数而不是number了,因此执行x()后自然输出字符x。

而另一个结果的由来是因为,在进入一个上下文时,变量声明在顺序上跟在函数声明和形式参数声明之后,而且在这个进入上下文阶段,变量声明不会干扰VO中已经存在的同名函数声明或形式参数声明,因此,在进入上下文时,VO的结构如下:

VO(global) = {

x: undefined,

y: <reference to FunctionDeclaration>

}

而JavaScript在解析到 var y = 1;时,

// 如果function "x"没有已经声明的话

// 这时候VO['y'] = undefined>

// 但是这道题里因为已经有函数声明了,所以变量声明不能影响同名的function的值

所以

VO['y'] = <the value is not disturbed, still function>

在此申明:赋值操作不是在进入上下文第一时间完成的,而是在代码执行阶段

至此,进入环境上下文的(暂时称为)初始化操作完成了, 接下是执行代码:

因为有 x = function (){};var y = 1;

所以VO变化为:

VO(global) = {

x: <reference to function>,

y: 1

}

所以最后执行x(),y()的话,就很容易知道结果了。

x()//输出字符x

y()//报错,number is not a function

最后:

题目主要考察的是函数表达式与函数声明在JavaScript中的一些区别,汗...第一眼看到题目的时候还以为是考察JS中变量的预解析机制,也知道,函数声明在顺序上是优先于变量声明的,虽然也靠近了,但就是没注意到一个是函数表达式的赋值,一个是函数声明。

参考资料:

汤姆大叔的博客:深入解析JavaScript系列之变量对象篇 http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html