javascript_function

Function类型

函数其实是对象,每个函数都是Function类型的实例,一样具有属性和方法,因此,函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

函数对象与其他用户所定义的对象有着本质的区别,这一类对象被称之为内部对象,例如日期对象(Date)、数组对象(Array)、字符串对象(String)都属于内部对象。这些内置对象的构造器是由JavaScript本身所定义的:通过执行new Array()这样的语句返回一个对象,JavaScript内部有一套机制来初始化返回的对象,而不是由用户来指定对象的构造方式。

函数的声明

javascript //函数声明 function sum(num1,num2){ return num1+num2 } 或者javascript //函数表达式 var sum=function(num1,num2){ return num1+num2; }; 或者javascript var sum=new Function("num1","num2","return num1+num2"); //不推荐

函数声明和函数表达式是有区别的,解析器会率先读取函数声明,并使其置于任何代码之前(自动提升);而函数表达式,则必须等到解析器执行到它所在的代码行,才被执行

javascript alert(sum(10,10)); //这是正确的,20 function sum(num1,num2){ return num1+num2; }
alert(sum(10,10)); //这是错误的 var sum=function(num1,num2){ return num1+num2; }

JavaScript中函数是没有重载的,这正是由于函数名其实是一个指针,因此,同名的函数会把之前的指针覆盖掉:

javascript funtion Add(num){ return num + 100; } funtion Add(num){ return num + 200; } var result=Add(100); //300.

在JavaScript中,函数对象对应的类型是Function,正如数组对象对应的类型是Array,日期对象对应的类型是Date一样,可以通过new Function()来创建一个函数对象,也可以通过function关键字来创建一个对象。为了便于理解,我们比较函数对象的创建和数组对象的创建。先看数组对象:下面两行代码都是创建一个数组对象myArray:

以下是引用片段:

var myArray=[];
//等价于
var myArray=new Array();
同样,下面的两段代码也都是创建一个函数myFunction:
function myFunction(a,b){
return a+b;
}
//等价于
var myFunction=new Function("a","b","return a+b");

通过和构造数组对象语句的比较,可以清楚的看到函数对象本质,前面介绍的函数声明是上述代码的第一种方式,而在解释器内部,当遇到这种语法时,就会自动构造一个Function对象,将函数作为一个内部的对象来存储和运行。从这里也可以看到,一个函数对象名称(函数变量)和一个普通变量名称具有同样的规范,都可以通过变量名来引用这个变量,但是函数变量名后面可以跟上括号和参数列表来进行函数调用。

JavaScript引入Function类型并提供new Function()这样的语法是因为函数对象添加属性和方法就必须借助于Function这个类型。

做为值的函数

因为JavaScript中的函数名本身就是变量,所以函数也可以当作值来使用。也就是说,不仅可以想传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。

function callSomeFunction(someFunction, someArgument){
return someFunction(someArgument); } function add10(num){
return num + 10; }
var result1 = callSomeFunction(add10, 10); alert(result1); //20
function getGreeting(name){
return "Hello, " + name; }
var result2 = callSomeFunction(getGreeting, "Nicholas"); alert(result2); //"Hello, Nicholas

这里的callSomeFunction()函数是通用的,即无论第一个参数传递进来是一个什么函数,它都会返回执行执行第一个参数后的结果。当然,大家应该知道,要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对括号

函数的本质是一个内部对象,由JavaScript解释器决定其运行方式。通过上述代码创建的函数,在程序中可以使用函数名进行调用。本节开头列出的函数定义问题也得到了解释。注意可直接在函数声明后面加上括号就表示创建完成后立即进行函数调用,例如:

var i=function (a,b){
return a+b;
}(1,2);
alert(i);

这段代码会显示变量i的值等于3。i是表示返回的值,而不是创建的函数,因为括号“(”比等号“=”有更高的优先级。这样的代码可能并不常用,但当用户想在很长的代码段中进行模块化设计或者想避免命名冲突,这是一个不错的解决办法。

函数的内部属性

arguments.callee

在函数内部,有两个特殊的对象:arguments和this,arguments是一个数组对象,包含传入的所有参数,arguments的主要作用是保存函数参数,但这个对象还有个叫callee的属性,该属性是一个指向拥有这个arguments对象的函数,这有利于实现无名函数的递归或者保证函数的封装性,我们看一下下面非常经典的阶乘算法:

function factorial(num){ if(num<=1){ return 1; }else{ return num * factorial(num-1);//与函数名factorial耦合性太高了,不灵活 } }

上述代码与函数名耦合性太高,一换函数名就不行了,就可以采用以下方法:

function factorial(num){ if(num<=1){ return 1; }else{ return num * arguments.callee(num-1); //这样无论用什么名字都能完成递归调用 } }
使用递归来计算1到n的自然数之和:
var sum=function(n){
if(1==n)return 1;
else return n+sum(n-1);
}
alert(sum(100));

this

函数内部的另一个特殊对象就是this,其行为和java和C#中的this基本类似。换句话说,this引用的是函数据以执行的环境对象(当在网页全局作用域中调用函数时,this对象引用的就是window),来看下面的例子:

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

上面这段代码,当在全局作用域中调用sayColor()时,this对象引用的是全局对象window.换句话说,对this.color的求值会转换成对window.color的求值,于是结果返回了"red"。但是当把这个函数赋给对象o,并调用o.sayColor()的时候,this引用的是对象o,因此对this.color的求值会转换成对o.color的求值,于是结果返回"blue"

函数的属性和方法

length和prototype属性

每个函数都包含两个属性,length和prototype,length属性表示函数接收的命名参数的个数。

function sayName(name){ alert(name); } function sum(num1, num2){ return num1 + num2; } function sayHi(){ alert("hi"); } alert(sayName.length); //1 alert(sum.length); //2 alert(sayHi.length); //0

prototype属性十分耐人寻味,这个属性将在面向对象和继承的时候再讲解;

call()和apply()方法

这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象指向的值,两者仅在定义参数的方式有所区别:

Function.prototype.apply(thisArg,argArray);
Function.prototype.call(thisArg[,arg1[,arg2…]]);

从函数原型可以看到,第一个参数都被取名为thisArg,即所有函数内部的this指针都会被赋值为 thisArg,这就实现了将函数作为另外一个对象的方法运行的目的。两个方法除了thisArg参数,都是 为Function对象传递的参数。下面的代码说明了apply和call方法的工作方式:

//定义一个函数func1,具有属性p和方法A
function func1(){
this.p="func1-";
this.A=function(arg){
alert(this.p+arg);
}
}
//定义一个函数func2,具有属性p和方法B
function func2(){
this.p="func2-";
this.B=function(arg){
alert(this.p+arg);
}
}
var obj1=new func1();
var obj2=new func2();
obj1.A("byA"); //显示func1-byA
obj2.B("byB"); //显示func2-byB
obj1.A.apply(obj2,["byA"]); //显示func2-byA,其中[“byA”]是仅有一个元素的数组,下同
obj2.B.apply(obj1,["byB"]); //显示func1-byB
obj1.A.call(obj2,"byA"); //显示func2-byA
obj2.B.call(obj1,"byB"); //显示func1-byB

可以看出,obj1的方法A被绑定到obj2运行后,整个函数A的运行环境就转移到了obj2,即this指 针指向了obj2。同样obj2的函数B也可以绑定到obj1对象去运行。代码的最后4行显示了apply和call函 数参数形式的区别。

时间: 2024-08-30 04:40:40

javascript_function的相关文章

javascript_function(闭包Closure)

javascript闭包(Closure) 所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 上面是官方的解释,但这解释只会让人头晕.要理解闭包,首先理解两点:变量的作用域以及作用域链,这两个在前面都已经介绍过了,并且举了简单了列子,来回顾一下: var color = "blue"; function changeColor(){ var anotherColor = "red"; funct

js函数表达式和函数声明的区别

我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义"隐 藏"起来,外部作用域无法访问包装函数内部的任何内容. 例如: var a = 2; function foo() { // <-- 添加这一行 var a = 3; console.log( a ); // 3 } // <-- 以及这一行 foo(); // <-- 以及这一行 console.log( a ); // 2 虽然这种技术可以解决一些问题,但是它并不理想,因为会导致一些额外的