一、函数类型
ECMAScript中包含三类函数,每一类都有各自的特性
1、函数声明:
- 拥有函数名
- 代码位置:全局上下文或者另外一个函数体中
- 创建时间:在进入上下文时创建
- 会影响变量对象
声明方式如下:
function add(a,b){ return a + b; }
这类函数的主要特性是:只有它们可以影响变量对象(存储在上下文VO中)。
2、函数表达式:- 代码位置位于表达式的位置
- 名字可有可无
- 不会影响变量对象
在代码执行阶段创建
这类函数的主要特性是:代码总是处于表达式的位置。最简单的示例如下:var foo = function(a,b){ return a + b; }
3、函数声明 VS 函数表达式
当函数表达式有名字的时候,它很难和FD区分。不过如果仔细看这两者定义的话,要区分它们还是很容易的:
函数表达式总是处于表达式的位置。//在括号(分组操作符)只可能是表达式 (function foo(){}); //数组初始化中,同样也只能是表达式 [function bar(){}]; //逗号操作符也只能是表达式 1,function baz(){}
定义中还提到函数表达式是在执行代码阶段创建,并且不是存储在变量对象上。
//不论是在定义前还是定义后,函数表达式都访问不到 //(因为它在代码执行的阶段创建) alert(foo);//"foo" is not defined (function foo(){}); //后面也没有用,因为它根本不存在VO中 alert(foo);//"foo" is not defined
4、立即执行函数
方式一: (function (){})(); 方式二: (function (){}());
二、函数创建的算法
如下所示使用伪代码表示函数创建的算法(不包含联合对象的步骤)。有助于理解ECMAScript中的函数对象,此算法对所有函数类型都是一样的。
F = new NativeObject(); //属性[[Class]] is "Function" F.[[Class]] = "Function" //函数对象的原型 F.[[Prototype]] = Function.prototype //对函数自身引用 //[[Call]]在函数调用F()激活 //同时创建一个新的执行上下文 F.[[Call]] = <reference to function> //内置的构造器 //[[Construct]]会在使用"new"关键字的时候激活 //事实上,它会为新对象申请内存 //然后调用F.[[Call]]来初始化创建的对象,将this值设置为新创建的对象 F.[[Construct]] = internalContructor //当前上下文(创建函数F的上下文)的作用域链 F.[[Scope]] = activeContext.Scope //如果是通过new Function()方式来创建,则 F.[[Scope]] = globalContext.Scope //形参个数 F.length = countParameters //通过F创建出来的对象的原型 __objectPrototype = new Object(); __objectPrototype.constructor = F F.prototype = __objectPrototype return F
要注意的是,F.[[Prototype]]是函数(构造器)的原型,而F.prototype是通过该函数创建出来的对象的原型。在有些文章中会将F.prototype叫做”构造器的原型”,这是错误的。