在ECMAScript中,Function(函数)类型实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针。
一.函数的声明方式
1.普通的函数声明
function sum (num1,num2) {
return num1+num2;
}
2.使用变量初始化函数
var sum = function (num1,num2) {
return num1+num2;
}
3.使用Function构造函数
var box= new Function('num1', 'num2' ,'return num1 + num2');
PS:第三种方式我们不推荐,因为这种语法会导致解析两次代码(第一次解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。但我们可以通过这种语法来理解"函数是对象,函数名是指针"的概念。
函数名仅仅是指向函数的指针,因此函数名和包含对象指针的其他变量一样。
也就是说,不同的函数名可以指向同一个函数。
function hcd (a,b) {
return a+b;
}
alert(hcd(1 ,2 ));
var h = hcd;
alert(h(3 ,2 ));
hcd = null ;
alert(hcd(0 ,1 ))
首先定义了一个函数hcd,将h设置为与hcd相等,(注意:使用不带圆括号的函数名是访问函数指针,而非调用函数结果),这时hcd和h就指向同一个函数。
因为函数的根源是对象,所以将hcd设置为空对象null,就可以将hcd和函数“断绝关系”。
但是这并不影响h指向函数。
二.函数的特点
1. 没有重载
function h (a) {
return a+1 ;
}
function h (b) {
return b+2 ;
}
alert(h(2 ));
其实和这个是一样的:
var h = function (a) {
return a+1 ;
}
h = function (b) {
return b+2 ;
}
alert(h(2 ));
2. 函数声明和函数表达式
函数声明:
解析器会先读取函数声明,并使其在执行任何代码前可用。
alert(hcd(1,2));//3
function hcd(a,b){
return a+b;
}
只是因为在解析时将函数声明放在了代码的顶部
函数表达式:
是按代码的顺序执行的。
alert(hcd(1,2));
var hcd = function(a,b){
return a+b;
}
这是会报错误hcd is not a function;
除了这点差别外,其他是一样的。
3.作为值的函数
var hcd = function (a) {
return a+5 ;
}
function h (b,c) {
return b(c);
}
alert(h(hcd,10 ));
h函数接受两个参数,第一个参数为函数,第二个参数为传递给改函数的一个值。
一个数组,含有多个对象,根据对象的某一属性值的大小,对对象进行排序
var data =[
{
name:"a" ,
age:10
},
{
name:"b" ,
age:11
},
{
name:"v" ,
age:20
},
{
name:"g" ,
age:8
}
]
function compare (s) {
return function hcd (obj1,obj2) {
var value1 = obj1[s];
var value2 = obj2[s];
if (value1<value2){
return -1 ;
}else if (value1>value2){
return 1 ;
}else {
return 0 ;
}
}
}
data.sort(compare("age" ))
console.log(data[0 ].age)
console.log(data[1 ].age)
console.log(data[2 ].age)
console.log(data[3 ].age)
4.函数内部的属性
在函数的内部有两个特殊的对象:arguments和this
arguments
arguments:类数组对象,包含所有传入函数中的参数。argument的callee属性指向该函数。
例如:
常见的递归函数
function hcd (a) {
if (a<=1 ){
return 1 ;
}else {
return a*hcd(a-1 )
}
}
console.log(hcd(4 ))
这样的话就会将执行函数紧紧的与hcd耦合在一起。
下面的方法会更好。
function hcd (a) {
if (a<=1 ){
return 1 ;
}else {
return a*arguments .callee(a-1 )
}
}
console.log(hcd(4 ))
这样一来:
var text = hcd;
hcd = function () {
return 0 ;
}
console.log(text(4 ))
console.log(hcd(24 ))
变量text获取了hcd的值,其实是在两外一个位置上保存了函数的指针。
this:
this引用的是当前函数执行的环境对象。
wndow.color = "red" ;
var o = {
color:"blue"
};
function say (){
console.log (this.color)
}
say (); // "red"
o.say = say ;
o.say (); // "blue"
say函数为在全局定义的函数,在调用函数前,并不能确定this是那个对象。
当在全局中调用时,this引用的是全局对象window,换句话说this.color会转换为window.color。
而当将say函数赋值给o.say()时,this引用的是对象o。
5.函数的属性和方法
函数就是对象,所以有属性和方法。
每个函数都有两个属性:length,prototype
length
length:接受参数的个数
function hcd (a,b) {
return a+b;
}
function h (a) {
return a;
}
alert(hcd. length );
alert(h. length );
prototype
对于prototype属性,它是保存所有实例方法的真正所在,也就是原型。这个属性,我们将在面向对象一章详细介绍。而prototype下有两个方法:apply()和call()每个函数都有2个非继承而来的方法call和apply,这两个方法可以让函数在特定的作用域中调用函数,其实等于设置this。
apply
apply:参数1.运行函数的作用域,参数2.数组,也可以是数组实例,也可以是arguments
function hcd (a,b) {
return a+b;
}
function h1 (c,d) {
return hcd.apply(this ,arguments );
}
function h2 (c,d) {
return hcd.apply(this ,[c,d]);
}
alert(h1(10 ,10 ));
alert(h2(10 ,10 ));
在全局环境下调用h2和h1,所以传入hcd的this 即为window对象
call:
和apply的用法类似,只不过参数2不为数组
function h2 (c,d) {
return hcd.call(this ,c,d);
}
apply和call的真正强大在于扩充函数赖以运行的作用域。
window.color = "red" ;
var o = {
color:"blue"
};
function hcd () {
alert(this .color)
}
hcd();
hcd.call(this );
hcd.call(window);
hcd.call(o);
bind()
创建一个函数的实例,this会绑定到传给bind()函数的值。
window.color = "red" ;
var o = {
color:"blue"
};
function hcd () {
alert(this .color)
}
var objectSay = hcd.bind(o);
objectSay()
hcd()调用bind()并传入对象o,意思就说在环境变量为o的情况下,创建一个函数实例objectSay。