详解callee和caller, apply()和call()的用法 — 第5.5.4节

一:函数内部的对象:arguments和this
  我们都知道js中arguments对象主要是保存函数的参数(如果不知道arguments,在有参数的函数中输出一下就知道了),但是这个对象还有一个属性为callee,它是一个指针,指向拥有这个arguments对象的函数。例如阶乘函数:

    function factorial(num){
        if(num <= 1){
            return 1;
        }else{
            return num * factorial(num-1);
        }
    }
    console.log(factorial(5));//120

由于函数的执行和函数名factorial紧紧耦合在了一起,为了消除这种紧密耦合的现象,可以使用arguments.callee:

    function factorial2(num){
        if(num <= 1){
            return 1;
        }else{
            return num * arguments.callee(num-1);
        }
    }
    console.log(factorial2(5)); //120

两者的区别如下:

    var trueFactorial = factorial2;
    factorial = function () {
        return 0;
    }
    console.log(trueFactorial(6)); //720
    console.log(factorial(6));      //0

  上面的trueFactorial实际上是在另一个位置上保存了一个函数的指针。然后又将一个简单的函数赋值给factorial变量。如果不用arguments.callee,那么,调用trueFactorial(6)的时候就返回0.但是上面的例子解除了代码和函数的耦合状态,因此trueFactorial仍然可以正常的计算阶乘。
二:this的意义?
  this引用的是函数执行的环境对象(当我们在网页的全局作用域中调用函数的时候,this对象就是window对象)。也就是说,在全局作用域中的所有变量和函数都是window对象的属性和方法,例如:

    var color = 'green';  //相当于window.color = 'green'
    var obj1 = {color:"blue"};
    function saycolor(){
        console.log(this.color);
    }
    saycolor();         //green,这里的this代表window
    window.saycolor()   //green
    obj1.saycolor = saycolor;
    obj1.saycolor();    //blue

三:caller属性是什么?
  caller属性保存着调用当前函数的函数的引用。如果是在全局作用域中调用当前的函数,它的值就是null。例子如下:

    function a(){
        console.log(a.caller); //返回正在调用俺的函数
    }
    function b(){
        a();
    }
    a();    //null
    b();    //function b(){a();}

但是为了实现更松散的耦合,也可以通过arguments.callee.caller来达到相同的目的:

    function a(){
        console.log(arguments.callee.caller); //返回正在调用俺的函数b
    }
    function b(){
        a();
    }
    a();    //null
    b();    //function b(){a();}

不过arguments.callee在严格模式下会导致错误。
四:call()和apply()的应用
  每个函数都包含两个属性:length和prototype。其中length表示函数希望接受地参数的个数,例如:

    function a(){
        console.log(000);
    }
    function b(arg1){
        console.log(111);
    }
    function c(arg1,arg2){
        console.log(222)
    }
    console.log(a.length); //0
    console.log(b.length); //1
    console.log(c.length); //2

  prototype属性是非常重要的,它指向一个对象,包含了所有它生成实例下的所有属性和方法,再次不再赘述,我的prototype博文中另有详细讲解。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,其实就是所在函数体内this对象的值。
1.apply()接受两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中:第二个参数可以是array的实例,也可以是arguments对象。实例如下:

    function fun(arg1,arg2){
        return arg1 + arg2;
    }
    function callfun1(arg1,arg2){
        return fun.apply(this,arguments); //传入arguments对象
    }
    function callfun2(arg1,arg2){
        return fun.apply(this,[arg1,arg2])//传入参数数组
    }
    console.log(callfun1(3,7)); //10
    console.log(callfun2(3,2)); //5

2.call()方法和apply()方法的作用相同,曲别在于接收参数的方式不同。用call方法传递参数的时候必须直接传递,也就是传递给函数的参数必须一一列举出来,例子如下:

    function fun(arg1,arg2){
        return arg1 + arg2;
    }
    function callfun3(arg1,arg2){
        return fun.call(this,arg1,arg2);
    }
    console.log(callfun3(2,6)); //8

但是传递参数并非apply()和call()用的最多的地方,他们真正强大的地方时能够扩充函数赖以运行的作用域。例子如下:

    var age = 18;
    var person = {age:17};
    function say(){
        console.log(this.age);
    }
    say();
    say.call(this);  //18
    say.call(window) //18
    say.call(person) //17,此时运行的作用域为person,相当于把person的作用域传给了say函数里面

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

    var age = 18;
    var person = {age:17};
    function say(){
        console.log(this.age);
    }
    var psay = say.bind(person);
    console.log(psay) //function say(){console.log(this.age);}
    psay();     //17,相当于把person作用域传给了say()构造函数里面,它和apply和call的区别就是,bind必须还要创建一个函数实例,也就是bind不能直接执行传参后的函数。

上面代码中person对象的作用域传到了bind()新创建的psay()函数执行环境里面,所以执行完输出this.age=17.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值