3

Function 类型

每个函数都是Function 类型的实例。函数名实际上就是一个指向函数对象的指针,不会与某个函数绑定。

函数声明方式创建Function,语法如下:

function sum(x,y){
    return x + y;
}

函数表达式定义Function,用var 如下:

var sum = function(x,y){
    return x + y;
};

第二种方法不要忘记在结尾加上分号

另外还可以使用Function 构造函数。如:

var sum = new function("x","y","return x + y");

非常不推荐使用这种形式。

如何证明函数名仅仅是指向函数的指针呢:

var sum = function(x,y){
    return x + y;
};
console.log(sum(1,2)); //3

var anotherSum = sum;
console.log(anotherSum(1,1)); //2

sum = null;
console.log(anotherSum(10,10)); //20

没有重载

同名的后面的函数将会覆盖前面的函数。如:


function add(num){
    return num + 100;
}
function add(num){
    return num + 200;
}
console.log(add(100)); //300

用另外一种写法就可以很清楚的看出原因。如:

var add = function(num){
    return num + 100;
}
add = function(num){
    return num + 200;
}
console.log(add(100)); //300

函数的声明与函数表达式(以及函数声明提升)

解析器会率先读取函数声明,并使其在执行任何代码之前可用(可访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。如:

console.log(sum(1,1)); //2
function sum(x,y){
    return x + y;
}

以上代码可行,是因为在代码开始值钱,解析器就已经通过一个名为函数声明提升(function declaration hoisting)的过程,读取并将函数声明添加到执行环境中去。即使声明函数的代码在调用它的代码后面,JavaScript 引擎也能把函数声明提升到顶部。

但是,如果把函数声明改成函数表达式,则会导致错误。如:

console.log(sum(1,1)); 
var sum = function(x,y){
    return x + y;
}; //TypeError: undefined is not a function (evaluating 'sum(1,1)')

失败的原因就在于函数位于初始化语句中,而不是一个函数声明。

也可同时使用函数声明和函数表达式,但在safari 浏览器中会出错。

作为值的函数

函数也可以作为值来使用,也就是可以将一个函数作为另一个函数的结果返回。如

function callFunction(someFunction,someArgument){
    console.log(someFunction(someArgument));
}
function addNum(x){
    return x + 100;
}
callFunction(addNum,100); //200

要访问函数的指针而不执行函数的话,必须去掉函数名后面的括号。

function compare(func,x,y){
    var num1 = func(x);
    var num2 = func(y);
    var array = [num1,num2];
    
    var result = array.sort(sortFunc);
    console.log(result.toString());
    document.write(result.join(" then "));
    
    
    function sortFunc(value1,value2){
        if (value1 > value2){
            return 1;
        }else if(value2 > value1){
            return -1;
        }else{
            return 0;
        }
    }
}

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

compare(add10,10000,999100); //10010 then 999110

函数内部属性

这里主要说一下callee属性、this属性以及caller属性。

callee属性的功能就是消除函数名的耦合问题。如:

function min(num){
    if(num == 1){
        return 1;
    }else if(num < 1){
        return 0;
    }else{
        return min(num - 1);
    }
}

var result = min(100);
document.write(result);

上面这个min()函数如果改了函数名,函数里面也相应的需要修改函数名。但添加了callee属性,就不需要修改。如:

function mins(num){
    if(num == 1){
        return 1;
    }else if(num < 1){
        return 0;
    }else{
        return arguments.callee(num - 1);
    }
}

var result = mins(100);
document.write(result);

着就解决了耦合现象所带来的问题。

this这个特殊对象的作用与Java 和C# 中的this 类似。this引用的是函数据以执行的环境对象——或者this值。如:

window.color = "red";
var obj = {color: "yellow"};

function show(){
    document.write(this.color);
}

show(); //red

obj.show = show;
obj.show(); //yellow

caller用来调用当前函数的函数的引用(源代码)。如:

function sum(x,y){
    plus(x,y);
}

function plus(x,y){
    console.log(x + y);
    document.write("<pre>" + plus.caller + "</pre>");
}

sum(10,20);

如:


(
    function a(){
        document.write("function a");
        b();
        function b(){
            document.write("function b");
            alert(arguments.callee.caller);
        }
    }
)()

用arguments.callee 可能在某些浏览器中会导致错误:

function sum(x,y){
    plus(x,y);
}

function plus(x,y){
    console.log(x + y);
    document.write("<pre>" + arguments.callee.caller + "</pre>");
}

sum(10,20);

函数属性和方法

每个函数都包含两个属性lengthprototype

length属性中,将返回参数的个数。如:

function colors(color1,color2){
    var array = [color1,color2];
    document.write(array.join("/"))
}
colors("red","blue");
document.write(" " + colors.length + "个参数"); //red/blue 2个参数

又如:

function toStr(){
        var result = "";
        for (var i = 0; i < arguments.length; i ++){
            result += arguments[i].toString() + " ";
        }
        document.write(result);
        document.write(" " + arguments.length + "个参数" + "<br/>");
        document.write(" " + arguments.callee.length + "个函数期望参数");
}
toStr(321,32,43243,432,3213);
    
/*
321 32 43243 432 3213 5个参数
0个函数期望参数    
*/

prototype属性。这个属性是保存它们所有实例方法的真正所在。在创建自定义引用类型以及实现继承时,这个属性是非常重要的(以后再单独讨论)。另外,在ECMAScript 5 中,这个属性不可枚举(不能用for-in)。主要的两个非继承方法有apply()call();这两个方法的用途都是在特定的作用域中调用函数。

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

function sum(x,y){
    document.write(x.toString() + y.toString());
}

function plus(){
    return sum.apply(this,arguments);
}

plus(321,"fff");

下面这个则是Array 的实例:

function sum(x,y){
    document.write(x.toString() + y.toString());
}

function plus(a,b){
    return sum.apply(this,[a,b]);
}

plus(321,"fff"); 

call()方法与前者的作用相同,区别在于接收参数的方式不同。第二个参数必须一个个列举出来。如下:

function sum(x,y){
    document.write(x.toString() + y.toString());
}

function plus(a,b){
    return sum.call(this,a,b);
}

plus(321,"fff");

上面例子中plus(a,b) 不能写成plus();call(this,a,b)不能写成call(this,arguments)call(this,[a,b])

高能!!!

高能!!!

事实上,apply()call()真正有价值的用途是扩充函数的作用域。如:

var child = {
    name: "Oliver",
    age: 18
}
var adult = {
    name: "Troy",
    age: 24
}

function showName(){
    document.write(this.name);
}

showName.call(adult); //Troy
showName.call(child); //Oliver

使用上面这两个方法来扩充作用域的最大好处就是对象不需要与方法又任何耦合关系。

ECMAScript 5 还定义了一个方法:bind()。this 的值会被绑定到传给bind()函数的值。如:

var child = {
    name: "Oliver",
    age: 18
}
var adult = {
    name: "Troy",
    age: 24
}

function showName(){
    document.write(this.name);
}

var newFunc = showName.bind(child);
newFunc(); //Oliver

JS菌
6.4k 声望2k 粉丝