您的位置:首页 - 教程 - JavaScript - 正文
JavaScript函数之美

JavaScript函数之美~

  这篇文章,我将就以下几个方面来认识JavaScript中的函数。

  • 函数为什么是对象,如何定义函数
  • 如何理解函数可以作为值被传递
  • 函数的属性和方法与函数内部的对象

 

 

第一部分:函数为什么是对象,如何定义函数

  JavaScript中最有意思的恐怕是函数了,因为和其他语言不同,在JavaScript中,每个函数都是Function类型的实例,而我们知道:Object是一个基础类型,其他所有类型都是从Object继承了基本的行为。也就是说Function也是从Object引用类型继承而来的,那么作为Function类型的实例,函数也就是对象这一点就不难理解了。

  那么如何定义一个函数呢?一般来说,有三种方式可以来定义函数。

  第一:函数声明。这种语法和其他语言的形式一样,是我们最为常见的一种方式。如下:

function add(num){
    return num+10;
}

  第二:函数表达式。如下:

var add=function(num){
    return num+10;
};

  我们可以注意到函数表达式的定义函数方法把函数看作了一个表达式,因此最后要以分号;结尾,并且在function之后没有函数名,那么怎么调用呢?实际上,通过add即可引用了。(实际上,这里的add是全局对象global在浏览器中表现为window对象的一个属性或方法)

  第三:使用Function构造函数。如下:

var add=new Function("num","return num+10");

  Function构造函数可以接收任意多的参数,其中最后一个是函数体,前面所有的是函数的参数。这种方法是我们所不推荐的,因为它会导致解析两次代码(第一次是解析常规的ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响了性能。但是这种方法有利于我们理解:函数是对象,函数名是指针。

 

  由于我们不推荐第三种方法来创建函数,并且在实际中也用的很少。那么前两种方法又有什么区别呢?

    实际上,区别仅仅在于是否函数声明的方法会使得代码在开始执行之前,解析器就已经通过了函数声明提升读取并将函数添加到了执行环境中去,于是在代码求知识,JavaScript引擎会将它们放到源代码树的顶部,而函数表达式则不会。这里可能不好理解,看下面例子:

a

function say(){
	console.log("I like coding");
}
say();

  此时在控制台输出了I like coding.

b

var say=function (){
	console.log("I like coding");
}
say();

  同样,这时在控制台也输出了I like coding.

c

say();
function say(){
	console.log("I like coding");
}

  这里我们将say()这个调用函数的语句放在最上面了,同样控制台也输出了I like coding.

d

say();
var say=function (){
	console.log("I like coding");
};

  然而,这里却出现了错误。让我们捕获以下错误。

try{	
	say();
	var say=function (){
		console.log("I like coding");
	};
}catch(err){
	console.log("错误是:"+err);
}

  控制台提示:错误是:TypeError: say is not a function。以上代码之所以会在运行期间产生错误,是因为函数没有位于一个函数声明中,而是位于一个初始化的语句中,这样如何没有执行到该语句,那么变量sum就不会保存有对函数的引用。而使用函数声明,JavaScript引擎将函数声明提升到了最顶端,那么函数名sum就保存了对函数的引用。

  

 

 

  为了更深刻的理解函数是对象,函数名是指针,我们可以以下面的例子讲解:

  

       function add(num){
		return num+10;
	}	
	console.log(add(10));//20
	var addCopy=add;  //这时我们把add这个指针赋值给addCopy,于是addCopy也指向了同一个函数对象
	console.log(addCopy(10));//20 
	sum=null;  //null 的一大作用就是用于保存空的对象,这时sum指向了一个空的对象
	console.log(sum(10));//Uncaught TypeError: sum is not a function(…) sum是一个空对象,因此会出现语法错误
	console.log(addCopy(10));//20  而addCopy指向了那个函数对象,便可以得到正确的答案
    

   也正是因为函数是对象,其函数名(指针)可以有多个(指向的是同一个对象),因此也不难理解函数没有重载

 

第二部分:如何理解函数可以作为值来传递。

   一般,函数中的参数都是变量。而因为函数名是指针,也是变量,因此我们就可以把函数作为值来使用了。

   如下:

	 	function onefunc(antherfunc,argum){
	 		return antherfunc(argum);
	 	}
	 	function antherfunc(str){
	 		return str+"I am js";
	 	}
	 	console.log(onefunc(antherfunc,"ha "));//ha I am js

  除此之外,一个函数也可以作为另一个函数的结果返回。如下:

function createComparisonFunction(propName){
	 	return function(object1,object2){
//这是一个比较函数,虽然有形参,但是不需要传递实参,只是为了便于下面调用
	 			var value1=object1[propName];
	 			var value2=object2[propName];
//这里使用方括号操作符的好处在于:当定义一个对象数组时,里面的对象是用对象字面量方法来创建的,于是属性名可以加引号,这时属性名即使不规范也不会影响结果,应当注意的是,这时,最后调用的时候还是需要使用方括号调用。。
	 			if(value1<value2){
	 				return -1;
	 			}else if(value1>value2){
	 				return 1;
	 			}else{
	 				return 0;
	 			}

	 		};
//注意:虽然这里不用分号也可以运行,但是最好写上,这样才规范。
	 	}
	 	var data=[{name:"zhuzhen",age:21},{name:"heting",age:18}];
//这里表示按照何种属性排序
	 	data.sort(createComparisonFunction("name"));
	 	console.log(data[0].name);//heting
	 	data.sort(createComparisonFunction("age"));
	 	console.log(data[0].age);//18

第三部分:函数的属性和方法

  我们可以先来总结一下函数一共有哪些属性和方法且函数有哪些对象。

  函数的属性:length prototype caller  

  函数的方法:继承自Object的有toString0(),valueOf(),toLocaleString()。函数自身添加的方法有call() apply() bind() 

  函数的内部对象:this arguments(它还有一个callee属性)


评论: