zoukankan      html  css  js  c++  java
  • 转:全面理解javascript的caller,callee,call,apply概念

    javascript中函数的隐含参数:arguments

    Arguments

    该对象代表正在执行的函数和调用它的函数的参数。

    [function.]arguments[n]
    参数
    function:选项。当前正在执行的 Function对象的名字。
    n:选项。要传递给 Function对象的从0开始的参数值索引。

    说明

    Arguments是进行函数调用时,除了指定的参数外,还另外创建的一个隐藏对象Arguments是一个类似数组但不是数组的对象,说它类似数组是因为其具有数组一样的访问性质及方式,可以由arguments[n]来访问对应的单个参数的值,并拥有数组长度属性length。还有就是arguments对象存储的是实际传递给函数的参数,而不局限于函数声明所定义的参数列表,而且不能显式创建 arguments 对象。arguments 对象只有函数开始时才可用。下边例子详细说明了这些性质:
     
    //arguments 对象的用法。
    function ArgTest(a, b){
       var i, s = "The ArgTest function expected ";
       var numargs = arguments.length;     // 获取被传递参数的数值。
       var expargs = ArgTest.length;       // 获取期望参数的数值。
       if (expargs < 2)
          s += expargs + " argument. ";
       else
          s += expargs + " arguments. ";
       if (numargs < 2)
          s += numargs + " was passed.";
       else
          s += numargs + " were passed.";
       s += "\n\n"
    
    for(i=0;i<numargs;i++)
    {
         s+="Arg"+i+arguments[i]+'\n';
    }
    }

    ArgTest(3,2,1);

    numargs:3
    expargs:2
     
    arguments不是Array的证明
     
    Array.prototype.selfvalue=1;
    document.write('<br/>'+new Array().selfvalue);//输出1
    function testArguments()
    {
              document.writeln(arguments.selfvalue); //显示undefined
    }
    testArguments();
    另一个简单的验证方法:
    alert(arguments instanceof Array);
    alert(arguments 
    instanceof Object);
     
    caller
      返回一个对函数的引用,该函数调用了当前函数。(即返回调用此函数的函数)
      functionName.caller 
      functionName 对象是所执行函数的名称。
    说明
    对于函数来说,caller 属性只有在函数执行时才有定义。如果函数是由顶层调用的,那么 caller 包含的就是 null 。如果在字符串上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本
    下面的例子说明了 caller 属性的用法:
    下面的例子说明了 caller 属性的用法:
    function callerDemo()
    {
         if(callerDemo.caller)
         {
              var a=callerDemo.caller;
              document.write('<br/>'+a);
         }
         else {
              document.write('<br/>'+"this is a top function");
         }
    }
    callerDemo();
    function handleCaller(){
         callerDemo();//返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文
    }
    handleCaller();
    output:
    this is a top function
    function handleCaller(){ callerDemo(); }
    callee

     返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。

    [function.]arguments.callee

    可选项 function 参数是当前正在执行的 Function 对象的名称。

    说明

    callee 属性的初始值就是正被执行的 Function 对象。

    callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,这有利于匿名
    函数的递归或者保证函数的封装性
    ,例如下边示例的递归计算1n的自然数之和。而该属性
    仅当相关函数正在执行时才可用。还有需要注意的是callee拥有length属性,这个属性有时候
    用于验证还是比较好的。arguments.length是实参长度,arguments.callee.length
    形参长度,由此可以判断调用时形参长度是否和实参长度一致。

    //打印其自身
    function calleeDemo()
    {
         document.write('<br/>'+arguments.callee);
    }
    calleeDemo();
    //用于验证参数
    function calleeLengthDemo(arg1,arg2)
    {
         if(arguments.length==arguments.callee.length)
         {
              document.write('<br/>'+'验证形参和实参长度正确!');
         }
         else
         {
              document.write("<br/>实参长度:"+arguments.length);
              document.write('<br/>形参长度'+arguments.callee.length);
         }
    }
    calleeLengthDemo(1,2,3,4);
    结果:
    function calleeDemo() { document.write('
    '+arguments.callee); }
    实参长度:4
    形参长度2
    用于递归计算
    var sum=function(n){
         if(n==1) return 1;
         else
           return n+sum(n-1);
    }
    document.write(sum(100));
     
    其中函数内部包含了对sum自身的引用,函数名仅仅是一个变量名,在函数内部调用sum即相当于调用
    一个全局变量,不能很好的体现出是调用自身,这时使用callee会是一个比较好的方法。
    var sum=function(n){
         if(n==1) return 1;
         else
           return n+arguments.callee(n-1);
    }
    document.write(sum(100));
     
    apply and call
    它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数方式有所区别:

        apply(thisArg,argArray);

        call(thisArg[,arg1,arg2…] ]);

    即所有函数内部的this指针都会被赋值为thisArg,这可实现将函数作为另外一个对象的方法运行的目的

    apply的说明

    如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError
    如果没有提供 argArray 和 
    thisArg任何一个参数,那么 Global 对象将被用作 thisArg 
    并且无法被传递任何参数。

    call的说明

    call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisArg指定的新对象
    如果没有提供 
    thisArg参数,那么 Global 对象被用作 thisArg

    相关技巧

    应用callapply还有一个技巧在里面,就是用callapply应用另一个函数(类)以后,当前的
    函数(类)就具备了另一个函数(类)的方法或者是属性,这也可以称之为“继承”。看下面示例:
    function base()
    {
         this.member='memmber';
         this.method=function(){
              document.write(this.member);
         }
    }
    function extend(){
         base.call(this);
         document.write('<br/><br/>'+member);
         document.write('<br/>'+method);
    }
    extend();
    会输出:
    memmber
    function (){ document.write(this.member); }
     
    上面的例子可以看出,通过call之后,extend可以继承到base的方法和属性。

    顺便提一下,在javascript框架prototype里就使用apply来创建一个定义类的模式,

    其实现代码如下:

    var Class = {
      create: 
    function() {
        
    return function() {
          
    this.initialize.apply(this, arguments);
        }
      }
    }

    解析:从代码看,该对象仅包含一个方法:Create,其返回一个函数,即类。但这也同时是类的
    构造函数,其中调用initialize,而这个方法是在类创建时定义的初始化函数。通过如此途径,
    就可以实现prototype中的类创建模式

    示例

    var vehicle=Class.create();
    vehicle.prototype
    ={
        initialize:
    function(type){
            
    this.type=type;
        }
        showSelf:
    function(){
            alert(
    "this vehicle is "+ this.type);
        }
    }
     
     
    var moto=new vehicle(“moto");
    moto.showSelf();
     
     
    另外的说明:
     

    The main difference is that apply lets you invoke the function with arguments as an array; callrequires the parameters be listed explicitly.

    apply和call的主要区别在于call必须显示列出参数,而apply可以调用数组。

    See here and here.

    Pseudo syntax:

    theFunction.apply(valueForThis, arrayOfArgs)

    theFunction.call(valueForThis, arg1, arg2, ...)

    mozilla解释:

    Function.prototype.apply method

    Calls a function with a given this value and arguments provided as an array.

    thisArg
    The value of this provided for the call to fun.  Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
    argsArray
    An array like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function.
    当第一个参数 为null 或undefined时,将是JS执行环境的全局变量。浏览器中是window,其它环境(如node)则是global。

    function func()
    {
    document.write('<br/>'+this);
    }
    func.call(1);

    func(null); //global 或window
    func(undefined); //global 或window

    严格模式下情况又有所不同,ES3比较宽容尽量去揣测代码意图。ES5严格模式(ie6/7/8/9除外)则不再揣测,给

    call/apply传入的任何参数不再转换。如下

    'use strict'

    function fun() {
        alert(this);
    }
    fun.call(null)      // null
    fun.call(undefined) // undefined

    call/apply用来改变函数的执行上下文(this),它们的第一个参数thisArg是个对象,即作为函数内的this。

    多数时候你传啥函数内就是啥。仅以call示例

    function fun() {
        alert(this);
    }
    fun.call(1);
    fun.call('a');
    fun.call(true);
    fun.call({name:'jack'}); 

    分别弹出“1”、“a”、“true”、“[object Object]”。

     

    John resig在pro javascript techniques 谈到了上下文对象:
     
     在js中,你的代码总有一个上下文对象(代码处在该对象内)。这是oo的常见特点,但其他语言没有js发挥的那么极致。
     上下文对象是通过this变量体现的。这个变量永远指向当前代码所处的对象中。全局对象其实就是window对象的属性。这意味着即使是在全局上下文中,this变量也能指向一个对象。上下文对象可以称为一个强大的工具。
    var obj = {
        yes: function(){
            // this == obj
            this.val = true;
        },
        no: function(){
            this.val = false;
        }
    };
    
    // We see that there is no val property in the 'obj' object
    alert( obj.val == null );
    
    // We run the yes function and it changes the val property
    // associated with the 'obj' object
    obj.yes();
    alert( obj.val == true );
    
    // However, we now point window.no to the obj.no method and run it 把window.no指向obj.no并执行之
    window.no = obj.no;
    window.no();
    
    // This results in the obj object staying the same (as the context was
    // switched to the window object)
    alert( obj.val == true );
    
    // and window val property getting updated.
    alert( window.val == false );
    我们把obj.no变量的上下文对象切换为window变量时,代码变得不好理解了,幸运的是,js提供了一套方法更好的让我们实现和理解:
    下面展示了call和apply2个方法,可以用于实现这一功能:
    // A simple that sets the color style of its context
    function changeColor( color ) {
        this.style.color = color;
    }

    // Calling it on the window object, which fails, since it doesn't
    // have a style object
    changeColor( "white" ); 在window对象中调用此函数会失败,因为widnow没有style属性
      //chrome 报错 
    Uncaught TypeError: Cannot set property 'color' of undefined

    // Find the element with an ID of main
    var main = document.getElementById("main");

    // Set its color to black, using the call method
    // The call method sets the context with the first argument
    // and passes all the other arguments as arguments to the function
    changeColor.call( main, "black" ); 、、main颜色会是black

    // A function that sets the color on  the body element
    function setBodyColor() {
        // The apply method sets the context to the body element
        // with the first argument, the second argument is an array
        // of arguments that gets passed to the function
        changeColor.apply( document.body, arguments );
    }

    // Set the background color of the body to black
    setBodyColor( "black" );
    上下文对象在面向对象就更明显了。
    call和apply区别在于调用的方式不同,上面的apply调用如下:
    changeColor.apply( document.body, arguments ); 后面一个参数为
    changeColor.call(document.body,arguments); 错误
    function setBodyColor()
    {
         changeColor.call(document.body,arguments[0]);
    }
    setBodyColor('lime');
    正确
    John Resig举的这个例子倒是蛮简单易懂的。
     

     在js中,你的代码总有一个上下文对象(代码处在该对象内)。这是oo的常见特点,但其他语言没有js发挥的那么极致。
     上下文对象是通过this变量体现的。这个变量永远指向当前代码所处的对象中。全局对象其实就是window对象的属性。这意味着即使是在全局上下文中,this变量也能指向一个对象。上下文对象可以称为一个强大的工具。
     
    var obj = {
        yes: function(){
            // this == obj
            this.val = true;
        },
        no: function(){
            this.val = false;
        }
    };

    // We see that there is no val property in the 'obj' object
    alert( obj.val == null );

    // We run the yes function and it changes the val property
    // associated with the 'obj' object

    obj.yes();
    alert( obj.val == true );

    // However, we now point window.no to the obj.no method and run it 把window.no指向obj.no并执行之
    window.no = obj.no;
    window.no();

    // This results in the obj object staying the same (as the context was
    // switched to the window object)
    alert( obj.val == true );

    // and window val property getting updated.
    alert( window.val == false );
     
    我们把obj.no变量的上下文对象切换为window变量时,代码变得不好理解了,幸运的是,js提供了一套方法更好的让我们实现和理解:
    下面展示了call和apply2个方法,可以用于实现这一功能:
    // A simple that sets the color style of its context
    function changeColor( color ) {
        this.style.color = color;
    }

    // Calling it on the window object, which fails, since it doesn't
    // have a style object
    changeColor( "white" ); 在window对象中调用此函数会失败,因为widnow没有style属性
      //chrome 报错 
    Uncaught TypeError: Cannot set property 'color' of undefined

    // Find the element with an ID of main
    var main = document.getElementById("main");

    // Set its color to black, using the call method
    // The call method sets the context with the first argument
    // and passes all the other arguments as arguments to the function
    changeColor.call( main, "black" ); 、、main颜色会是black

    // A function that sets the color on  the body element
    function setBodyColor() {
        // The apply method sets the context to the body element
        // with the first argument, the second argument is an array
        // of arguments that gets passed to the function
        changeColor.apply( document.body, arguments );
    }

    // Set the background color of the body to black
    setBodyColor( "black" );
    上下文对象在面向对象就更明显了。
    call和apply区别在于调用的方式不同,上面的apply调用如下:
    changeColor.apply( document.body, arguments ); 后面一个参数为
    changeColor.call(document.body,arguments); 错误
    function setBodyColor()
    {
         changeColor.call(document.body,arguments[0]);
    }
    setBodyColor('lime');
    正确

     

  • 相关阅读:
    [golang]text/template模板
    [golang]Go内嵌静态资源go-bindata的安装及使用
    GoCN每日新闻(2019-09-23)
    spring mvc中获取请求URL
    HBuilder搭配逍遥Android模拟器进行开发
    python 数据结构
    JDK8+Tomcat8配置https【转】
    JFinal的使用
    RESTful API 设计指南【转】
    jQuery验证控件jquery.validate.js的使用介绍
  • 原文地址:https://www.cnblogs.com/youxin/p/2687784.html
Copyright © 2011-2022 走看看