说起来ECMAScript中什么最有意思,我想那莫过于函数了——而有意思的根源,则在于函数实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常是使用函数声明语法定义的,如下面例子所示:
a.使用函数声明语法定义函数
function sum(num1, num2){ return num1+num2; }
b.使用函数表达式定义函数
var sum = function(num1, num2){ return num1+num2; };
以上代码定义了变量sum并将其初始化为一个函数。有读者可能会注意到,function关键字后面没有函数名。这是因为在使用函数表达式定义函数时,没有必要使用函数名——通过变量sum即可以引用函数。另外,还要注意函数末尾有一个分号,就像声明其他变量时一样。
c.最后一种定义函数的方式是使用Function构造函数。Function构造函数可以接受任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举除了新函数的参数
var sum = new Function(“num1”, “num2”, “return num1+num2”);//为什会解析2次呢?
从技术角度讲,这是一个函数表达式。但是,我们不推荐读者使用这种方式定义函数,因为这种语法会导致解析两次代码(第一次是解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。
函数声明与函数表达式
1. 解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
2. 在函数内部,有两个特殊的对象:arguments和this。其中,arguments是一个类数组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
3. 函数内部的另外一个特殊对象是this,this引用的是函数据以执行的环境对象(当在网页的全局作用域中调用函数时,this对象引用的就是window)
<script type="text/javascript"> function add(){ if(window == this){//函数内部的另外一个特殊对象是this,在这里this指向的是全局执行环境 alert("window is this"); } } add(); </script> <script type="text/javascript"> var color = "red"; function sayColor(){ return this.color; } alert(sayColor()); var obj = {nane : "高伟刚", age : 25, color : "blue"};//这里如果删除了color,会返回undefined。对象中没有属性,会返回undefined obj.say = sayColor; alert(obj.say()); </script>
3.caller这个属性中保存着调用当前函数的函数的引用
<script type="text/javascript"> function say(args){ sayColor(args); } function sayColor(args){ alert(sayColor.caller); } say();//会弹出调用sayColor函数的函数 sayColor();//null,全局作用域调用了sayColor函数,所以sayColor.caller == null </script>
函数属性和方法
ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length和prototype。其中,length属性表示函数希望接收的命名参数的个数。
1. 在ECMAScript核心所定义的全部属性中,最耐人寻味的就要数prototype属性了。对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。换句话说,诸如toString()和valueof()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接受两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象
Call()方法与apply()方法的作用相同,它们的区别仅在于接受参数的方式不同
重点:事实上,传递参数并非apply()和call()真正的用武之地;它们正真强大的地方是能够扩充函数赖以运行的作用域。来看下面一个例子。
<script type="text/javascript"> function sum(num1, num2){ alert(num1);//12 alert(num2);//20 alert(this.num3);//60 return num1+num2; } var obj = {num3 : 60}; function callSum(obj){ return sum.apply(obj, arguments);//设置函数体sum中this对象的值为obj,apply和call用途都在特定的作用域中调用函数,实际上等级设置函数体内this对象的值 } var count = callSum(12, 20); </script>
每个函数继承的toLocaleString()和toString()方法始终都返回函数代码。返回函数代码的格式则因浏览器而异——有的返回的代码与源代码中的函数代码一样,而有的则返回函数代码的内部表示,即由解析器删除了注释并对某些代码作了改动后的代码。由于与源代码存在差异,我们无法根据这两个方法返回的结果来实现任何重要功能。另外一个继承的valueOf()方法同样也只能返回函数代码
Problem:
Javascript预编译
<script type="text/javascript"> /*1.JavaScript"预编译"会先将变量x进行声明,但不会进行赋值。并且在进行所有操作之前,将函数add会进行预定义 *2."预编译"是一段一段的JavaScript代码来进行的 */ alert(x);//undefined,在所有代码执行之前,x已经进行的"预编译" add(); //弹出undefined var x = 333;/*虽然在所有代码执行之前已经进行的"预编译"(即声明了x)了,但是并有对x赋值。当执行到这行时,才对x赋值,所以下一行调用add()函数时 会弹出333。而本上之上调用add(),由于并没有对x赋值,所以会弹出undefined */ add();//弹出333 function add(){ alert(x); } </script>