JavaScript高级编程学习4——Function is a object

      在.NET里函数是实现类的某一个功能,是类的一部分,其和类的关系是从属关系。但是在ECMAScript里面却不是这样的。在ECMAScript里面,函数是一个对象,用C#的语言表达就是ECMAScript里面的函数是一个引用类型。因此我们在前面可以看到,可以将一个函数赋值于一个通过var声明的变量或者将一个函数直接作为返回值而不会产生任何一个错误。

      函数的声明:

通常我们声明一个函数的代码如下:function Add(a,b)(){return a+b},其实这只是一个语法糖而已。ECMAScript声明function的方式有三种。

函数声明的基本框架为

function 函数名(参数列表)
{
  //执行代码体
}

其对应的函数表达式框架为

var 函数名=function(参数列表){
//执行代码体
}

而这两者在ECMAScript内部的原生创建方式对应代码如下:

var 函数名=new Function('参数列表','执行代码体')

在EMCA-262的正式定义方式为:

new Function(p1,p2,p3,......,pn,body);

所以在ECMAScript标准中,只定义了原生创建方式的构造流程。ECMA-262中对Function建立函数对象的过程描述比较繁琐,在本文中我将其简要总结如下:

1、如果没有传递给Function参数,则构造一个参数列表和执行代码体均为空的函数对象。

2、如果只传递了一个参数,则构造一个参数列表为空的函数对象,其执行代码体为其唯一传递过来的参数。

3、如果传递了一个以上的参数,最后一个解析为执行代码体,剩下的参数解析为参数列表。或者说"p1 – pn”解析成参数列表,而“body”解析成代码体。如果pi中存在字符“,”则将其解析为由“,”分割的多个参数。

4、以上过程中如果解析参数列表或代码体失败,抛出SyntaxError异常,否则以解析出的参数列表和代码体建立一个函数对象。

5、将执行创建此函数对象的执行环境的作用域链传入,作为此函数对象的Scope属性。

6、返回此函数对象的指针。

    函数的调用:

     在ECMAScript中函数可以通过两种方式被调用,一种就是普通的函数调用,直接通过函数名进行调用,一种是作为构造器的函数调用。之所以引入后面一种是因为ECMAScript没有class关键字。

     这里重点讲解作为构造器调用。注意作为构造器调用时在函数名前面加上new关键字,这个和上面讲的函数的声明new Function()不要混淆掉。由于在ECMAScript里面是没有类和关键字class的,因此如果我们想通过类似于C#中,通过类名来调用方法的话,则需要利用构造器函数的调用。

View Code
1 // 声明一个Person函数
2 function Person(name) {
3 this.ShowName=function() {
4 alert(name)
5 }
6 }
7 var person = new Person('wuxq');//这里用var来声明模拟的对象,而不是用function。
8 person.ShowName();


     这里有一点不明白,就是为什么ShowName为什么要声明为this.ShowName,如果没有这样声明则运行会出错?

     但是这样声明是有弊端的,在上面再附加上这段代码。下面的alert输出为false。

      var person2 = new Person("wuxiaoqian");
      alert(person.ShowName==person2.ShowName); 这样说明person.ShowName和person2.ShowName先内存中不是同一个引用。但是我们知道对于类,属性在不同的对象是不同的引用,而方法是共各个对象公用的。这样也不会浪费内存。但是从上面alert的输出可以得知person和person2的ShowName方法在内存中是不同的引用,这样显然不合理。于是就引出了原型。

       对于原型的定义是这样的:Prototype是每个函数都有的一个成员(注意上面已经提过函数是一个对象)。以上面的Person为例子。当var person=new Person('wuxq')的时候,并为person自动创建一个属性“__proto__”,此属性指向F的prototype。上面的代码修改后是这样。

View Code
1 function Person(name) {
2 this._name;
3 }
4 Person.prototype.ShowName = function () {
5 alert(this._name);
6 }
7 var person1 = new Person('wuxq');
8 var person2 = new Person('wuxiaoqian');
9 alert(person1.ShowName==person2.ShowName);

      注意Person.prototype是Person的一个属性。这个属性其实是一个prototype对象的引用。而这个prototype对象里面也有一个prototype属性。 

      但是也许我们会疑问person1和person2是怎么找到定义的prototype之上的ShowName的呢?ECMAScript的成员查找时基于其成员查找算法的,这个算法应用的数据结构是原型链。在ECMAScript中,当查找一个对象的某个成员时,首先在对象上查找,如果找不到则到其“__proto__”指向的原型对象上找,再找不到则到原型对象的“__proto__”指向的对象也就是原型的原型上找,直到找到;如果某个原型的“__proto__”为null并且此时还没有找到合适成员,则返回“undefined”。



(此图转载自:http://www.cnblogs.com/leoo2sk/archive/2011/01/12/ecmascript-function.html

posted @ 2011-03-14 21:16  雁北飞  阅读(307)  评论(0编辑  收藏  举报