你的位置:首页 > Java教程

[Java教程]JavaScript:Function类型


在JavaScript中,函数实际是对象。每个函数都是Function类型的实例,而且都与其他类型一样具有属性和方法。函数声明方式:

// 第一种:使用函数声明语法定义function sum (num1, num2) {  return num1 + num2;}// 第二种:使用函数表达式定义函数var sum = function(num1, num2) {  return num1 + num2;};// 第三种:构造函数var sum = new Function("num1", "num2", "return num1+num2");

由于函数是对象,因此函数名实际上是一个指向函数的指针,不会与某个函数绑定。函数名与包含对象指针的其他变量没有什么不同,也就是说,一个函数可能有多个名字:

function sum(num1, num2) {  return num1 + num2; }alert(sum(1,2));  // 3var anotherSum = sum;alert(anotherSum(3,4)); // 7sum = null;alert(anotherSum(4, 5));  // 9

需要注意的是sum = null将sum变量与函数取消了关系,但是仍然可以正常调用anotherSum()。

没有重载

JavaScript中并没有类似于java语言的重载机制。

function addNum(num) {  return num + 1;}function addNum(num) {  return num + 2;}var result = add(0);  // 2

上面的例子声明了两个函数,而结果显示后面的函数覆盖了前面的函数。上面的代码等价于:

function addNum(num) {  return num + 1;}function addNum(num) {  return num + 2;}var result = add(0);  // 2

函数声明与函数表达式

JavaScript解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可用;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。

alert(sum(1, 2));function sum(num1, num2) {  return num1 + num2;}

上面的代码可以正常执行。这是因为在代码执行之前,解析器就已经通过名为函数声明提升的过程,读取并将函数声明添加到执行环境中。对代码求值时,JavaScript引擎也能把函数声明提升到顶部。

如果把函数声明改为函数表达式的形式,就会在执行期间导致错误。

alert(sum(1, 2));var sum = function(num1, num2) {  return num1 + num2;};

以上代码会运行错误。因为函数位于初始化语句中,在执行到函数所在的语句之前,变量sum中不保存对函数的引用。

作为值的函数

在JavaScript中的函数本身就是变量,所以函数也可以作为值来使用。这就是说,可以把函数当作一个参数传递给另一个变量。

function sum(add1, arg) {  return add1(arg);}

sum函数接收两个参数。第一个是参数是一个函数,第二个参数应该是要传递给该函数的一个值。

// add1执行加1操作function add1(num) {  return num + 1;}var result = sum(add1, 2);alert(result); // 3function sayHello(name) {  return "hello, " + name;}var result1 = sum(sayHello, "world");alert(result1);   // "hello, world"

注意:这里sum()函数是通用的,无论第一个参数传进来的是什么函数,都返回这个函数的结果。sum(add1, 2);和sum(sayHello, "world");中传递的都是add1()方法和sayHell0()方法,而不是执行它们返回的结果。

可以从一个函数中返回另一个函数。例如前面提到的数组的sort()方法的比较函数,需要传入两个参数。

function createComparisionFunction(propertyName) {  return function(obj1, obj2) {    var value1 = obj1[propertyName];    var value2 = obj2[propertyName];    if (value1 < value2) {      return -1;    } else if (value1 > value2) {      return 1;    } else {      return 0;    }  };}

上面定义了一个函数,这个函数中嵌套了一个函数,内部函数接收到propertyName参数后,会使用方括号表示法来取得给定属性的值,然后根据值来定义排序规则。

var data = [{name:"zhangsan", age:18}, {name:"lisi", age:19}];data.sort(createComparisionFunction("name"));alert(data[0].name);  // lisidata.sort(createComparisionFunction("age"));alert(data[0].name);  // zhangsan

函数内部属性

在函数内部,有两个特殊对象:arguments和this。

arguments

arguments是一个类数组对象,包含传入函数中的所有参数。arguments主要用于保存函数参数,除此之外还有一个callee属性,callee是一个指向拥有arguments对象的函数。

下面看一个阶乘的例子:

function factorial(num) {  if (num <= 1) {    return 1;  } else {    return num * factorial(num - 1);  }}

这种定义方法是没有问题的,缺点在函数的运行与函数名factorial紧密耦合在一起了,为了解决这个问题,可以使用arguments.callee。

function factorial(num) {  if (num <= 1) {    return 1;  } else {    return num * factorial(num - 1);  }}

这样,无论引用函数时是什么名字都不影响函数的正常运行。例如:

var newfactorial = factorial;factorial = function () {  return 0;}alert(newfactorial(5)); // 120alert(factorial(5));  // 0

this

this引用的是函数据以执行的环境对象。

window.color = "red";var obj = {color: "blue"};function sayColor() {  alert(this.color);}sayColor();   // "red",this指window对象obj.sayColor = sayColor;  obj.sayColor(); // "blue", this指obj对象

在调用函数之前this的值并不确定,this在运行过程中会指代不同对象。在调用sayColor();时,this引用的是全局对象window,this.color相当于window.color;当把sayColor赋值给obj对象并调用obj.sayColor()时,this引用的是obj对象,因此对this.color即obj.color。

caller

ECMAScript5提供了另一个函数对象的属性:caller。它保存着调用当前函数的函数的引用,如果在全局作用域中调用当前函数,值为null。

function outer() {  inner();}function inner() {  alert(inner.caller);}outer();

上面的代码会显示outer()函数的源码。因为outer()调用了inner(),所以inner.caller就执行outer()。

函数属性和方法

前面已经说过,在JavaScript中函数时对象,因此函数也有属性和方法。每个函数。每个函数都包含两个属性:length和prototype。

length属性保存的是调用数组时的参数个数,而不是声明时定义的参数个数。对于JavaScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问。在ECMAScript5中,prototype属性是不可枚举的,因此,使用for-in无法发现。

每个函数都包含两个非继承而来的方法:apply()call()。这两个方法都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

apply()方法

apply()方法接收两个参数:第一个参数是在其中运行函数的作用域;另一个是参数数组,它可以是Array的实例,也可以是arguments对象。

function sum(num1, num2) {  return num1 + num2;}function callSum1(num1, num2) {  return sum.apply(this, arguments); // 传入arguments}function callSum2(num1, num2) {  return sum.apply(this, [num1, num2]);  // 传入数组对象}alert(callSum1(1, 2)); // 3alert(callSum2(1, 2)); // 3

call()方法

call()方法与apply()方法的作用相同,它们的区别仅在于接收的参数不同。第一个参数也是this,其余参数都直接传递。

function sum(num1, num2) {  return num1 + num2;}function callSum(num1, num2) {  return sum.call(this, num1, num2);}alert(callSum(1, 2));  // 3

在使用call()方法的情况下,callSum()必须明确传入每一个参数,返回值都一样。

apply()和call()最大的作用在于能够扩充函数赖以运行的作用域。

window.color = "red";var obj = {color: "blue"};function sayColor() {  alert(this.color);}sayColor();       // redsayColor.call(this);  // redsayColor.call(window); // redsayColor.call(obj);   // blue

在上面的例子中,sayColor()方法是定义在全局作用域中的,在全局作用域中this.color会转换为window.color,所以sayColor()的结果为red。而sayColor.call(this)sayColor.call(window)显式地在全局作用域中调用函数,结果必然为热点。当运行sayColor.call(obj)时,函数的运行环境就变成了obj,当执行sayColor()时,this.color会转换为obj.color,因此结果为blue。

bind()方法

bind()方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

window.color = "red";var obj = {color: "blue"};function sayColor() {  alert(this.color);}var objSayColor = sayColor.bind(obj);objSayColor(); // blue

在上面的例子中,sayColor()调用bind()并传入对象。创建了objSayColor()函数。objSayColor()函数的this值为obj,因此,即使在全局作用域下调用objSayColor(),结果也是blue。