代码改变世界

Javascript的声明

2012-11-05 09:40  Justany_WhiteSnow  阅读(2987)  评论(11编辑  收藏  举报
Quiz

 

下面Javascript代码为什么能运行?

hello();

function hello(){
    alert("Hello, world!");
}

但对于C,这么写会报错:

#include "stdio.h"

void main(){
    hello();
}

void hello(){
    printf("Hello, world\n");
}

由于hello没有被预先声明,代码“hello()”被认为是隐式声明,而隐式声明返回类型是int,所以提示hello类型错误。

通过预先声明或者将main函数放在hello函数的后面可以很容易解决这个问题。

那么对于Javascript却能运行,这代表了什么呢?

 

被提升的声明

Javascript引擎会先对代码解释,将声明提升,然后再执行。例如为了判断一个变量定义与否,如果我们如此写是会出引用错误的:

if(someVar === undefined){
    alert("someVar未定义");
}

但如果这样却不会出错:

if(someVar === undefined){
    var someVar = 1;
    alert("someVar未定义");
}

可见声明被提升了,但只有声明被提升了,因为someVar依然等于undefined,而不是1。

值得一提的是,这样子写也会报错:

if(someVar === undefined){
    someVar = 1;
    alert("someVar未定义");
}

这证明在解释阶段,隐式声明是没有作用的!而且为了让代码逻辑清晰,还是用显式声明吧!

 

chrome浏览器的诡异现象

 chrome浏览器(至少在本文签写时的最新版本22.0.1229.94 m依然是如此)有个诡异的现象:

var name = 20;
alert(typeof(name));    //string
name += 12;
alert(name);    //2012

所以请不要把name定义为除字符串的其他类型,当然可以的话也尽量避免使用这个全局变量吧。

 

函数的两种创建方式

函数申明:

function 函数名 (参数可选){ 函数体 }

函数表达式:

function 函数名可选 (参数可选){ 函数体 }

Syntax
FunctionDeclaration :
function Identifier ( FormalParameterListopt ) { FunctionBody }
FunctionExpression :
function Identifieropt ( FormalParameterListopt ) { FunctionBody }

——Standard ECMA-262 ECMAScript Language Specification  . ECMA

为什么函数要分声明(FunctionDeclaration)和表达式(FunctionExpression)呢?

译注:实际上FunctionDeclaration和FunctionExpression都是函数产生的语法,这和一般编程语言中函数声明只是定义接受参数数量及类型与返回值类型不太相同。或者说,这根本不是字面意义上的函数声明。我们可以理解对于Javascript的函数声明(FunctionDeclaration)其实是声明式(即需要提升的)函数产生语法,而函数表达式(FunctionExpression)则是表达式式(即执行时产生)函数产生语法。

——WhiteSnow

主要为了区分函数的创建是否需要被提升,如果是函数声明就需要被提升创建,但如果是表达式,那么可以在执行时再创建。这样是很必要的,比如我们可以利用表达式动态定义函数:

var foo;

if(Condition){
    foo = function(){
        //do something
    }
}else{
    foo = function(){
        //do anything else
    }
}

再比如对于匿名函数:

(function(){
    //do something
})();

声明提升也没什么意义,因为它不会在别的地方被引用。

 

如何判断函数声明与函数表达式

  • 匿名函数必然是函数表达式
  • 如果有名字的函数作为赋值表达式的一部分那么他也是一个表达式
  • 如果有名字的函数被括号“()”括住,那么他也是一个表达式

本文不准备深入命名函数表达式(named function expressions),具体可参见参考文献,不过一般应当避免命名函数表达式的使用,因为大部分功能都可以用匿名函数找到替代方案,或者说实际使用中不必考虑命名函数表达式。

 

匿名函数立刻执行

我们经常希望匿名函数定义好后立刻执行,但这么写会抛出语法错误:

function(){
    alert(1);
}();

正确写法如下。

  • 声明一个函数对象,然后执行它:
(function(){
    alert(1);
})();
  • 用括号强制执行:
(function(){
    alert(1);
}());
  • 使用void操作符:
void function(){
    alert(1);
}();

 

总结

  • Javascript中声明会被提升;
  • 对于变量显式声明提升的仅仅是声明,赋值并未被提升;
  • 对于函数声明由于其赋值和声明是一体的,所以提升的是整个函数的定义;
  • 变量隐式声明和函数表达式不会被提升。

 

思考题

1.  如果我们用函数表达式来创建函数,而不是用函数声明来创建,刚开始的题目会如何呢?

hello();

var hello = function(){
    alert("Hello, world!");
}

 2.  下面是一个Button类,并创建了一个他的实例,我们可以在浏览器中看到一个按钮,但是为什么单机按钮时alert出来的数值不是13,而是空的呢?如何能alert出我们设置的数值?

function Button(clickFunction) {
    this.button = document.createElement("button");
    this.button.appendChild(document.createTextNode("Test"));
    document.body.appendChild(this.button);
    this.button.onclick = function(){alert(this.value);}
}
var bt = new Button(13);    //单击这个button的时候alert出空

 

 

参考资料

 

Standard ECMA-262 ECMAScript Language Specification  . ECMA . June 2011

Named function expressions demystified . Juriy "kangax" Zaytsev . June 17, 2009

函数式JavaScript编程指南 . ShiningRay(译) . 2008/01/02