小昱个人博客
欢迎来到小昱的世界

勤学如春起之苗,不见其增,日有所长;辍学如磨刀之石,不见其损,日有所亏
深入理解JS中的Call和Apply
  • 首页 > >
  • 作者:小昱
  • 2017年8月31日 16:37 星期四
  • 浏览:130
  • 字号:
  • 评论:1
  • 零、写在前头

    首先梳理一下思路。先明白存在call和apply的原因:

    call和apply是为了动态改变this而出现的,当一个object没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。

     

    一、调用形式

    obj.call(thisObj, arg1, arg2, ...);
    obj.apply(thisObj, [arg1, arg2, ...]);

    两者作用一致,都是把obj(即this)绑定到thisObj,这时候thisObj具备了obj的属性和方法。

    或者说thisObj『继承』了obj的属性和方法。绑定后会立即执行函数。

    唯一区别是apply接受的是数组参数,call接受的是连续参数。

     

    二、基本用法

    示例一

    // 作者:杨志
    // 链接:https://www.zhihu.com/question/20289071/answer/14644278
    // 来源:知乎
    
    function cat() {} 
    cat.prototype = { 
      food: "fish", 
      say: function() { 
        alert("I love " + this.food); 
      } 
    };
    var blackCat = new cat;
    blackCat.say();
    
    whiteDog = {food:"bone"};
    blackCat.say.call(whiteDog); 

    上例可以看出,blackCat具有say方法,但whiteDog并没有,要想使用say方法而不用在whiteDog中声明,可以使用call,以取到blackCat的say方法。

     

    示例二

    function add(j, k){
      return j+k;
    }
    
    function sub(j, k){
      return j-k;
    }
    
    add(5,3); //8
    add.call(sub, 5, 3); //8  ①
    add.apply(sub, [5, 3]); //8
    
    sub(5, 3); //2
    sub.call(add, 5, 3); //2
    sub.apply(add, [5, 3]); //2

    上例子语句①中,用 add 来替换 sub,add.call(sub,5,3) == add(5,3) ,所以运行结果为:8; 其他语句同理。

    注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

     

    三、调用原生对象的方法

    var a = {0:1, 1:"yjc", length: 2}; 
    
    a.slice(); //TypeError: a.slice is not a function
    
    Array.prototype.slice.call(a);//[1, "yjc"]

    对象a类似array,但不具备array的slice等方法。使用call绑定,这时候就可以调用slice方法。

    自己封装一个方法,将类数组对象转化为数组:

    function toArr(){
      var args
      if (arguments.length === 1) {
        args = Array.prototype.slice.call(arguments[0])
      } else {
        args = Array.prototype.slice.call(arguments)
      }
      console.log(args)
      return args
    }
    
    toArr("h","e","l","l","o")   //输出 ["h","e","l","l","o"];
    
    arg = {
      0:"h",
      1:"e",
      2:"l",
      3:"l",
      4:"o",
      length:5
    };
    
    toArr(arg)

     

    四、继承

    var Parent = function(){
      this.name = "yjc";
      this.age = 22;
    }
    
    var child = {};
    
    console.log(child);//Object {} ,空对象
    
    Parent.call(child);
    
    console.log(child); //Object {name: "yjc", age: 22}

    简单地实现了对象的继承。

    复杂一点的例子如下:

    function Animal(){    
      this.name = "Animal";    
      this.showName = function(){    
        alert(this.name);    
      }    
    }    
      
    function Cat(){    
      this.name = "Cat";    
    }    
       
    var animal = new Animal();    
    var cat = new Cat();    
        
    //通过call或apply方法,将原本属于Animal对象的showName()方法交给对象cat来使用了。    
    //输入结果为"Cat"    
    animal.showName.call(cat,"");    
    //animal.showName.apply(cat,[]);  

    call 的意思是把 animal 的方法放到cat上执行,原来cat是没有showName() 方法,现在是把animal 的showName()方法放到 cat上来执行,所以this.name 应该是 Cat

    function Animal(name){      
      this.name = name;      
      this.showName = function(){      
        alert(this.name);      
      }      
    }      
        
    function Cat(name){    
      Animal.call(this, name);    
    }      
        
    var cat = new Cat("Black Cat");     
    cat.showName(); 

    以上常规的写法,在构造函数内部调用call实现继承

    多重继承:

    function Class1() {
      this.showSub = function(a, b) {
        alert(a - b);
      }
    }
    
    function Class2() {
      this.showAdd = function(a, b) {
        alert(a + b);
      }
    }
    
    function Class3() {
      Class1.call(this);
      Class2.call(this);
    }

     

    五、caller和callee

    caller返回一个函数的引用,这个函数调用了当前的函数;

    callee放回正在执行的函数本身的引用,它是arguments的一个属性

    先看一个示例

    var a = function() {
      console.log(a.caller);
      console.log(arguments.callee);
    }
    var b = function() {
      a();
    }
    b();
    a();

    b()和a()控制台分别打印:

    function() {
      a();
    }
    
    function() {
      console.log(a.caller);
      console.log(arguments.callee);
    }
    null
    
    function() {
      console.log(a.caller);
      console.log(arguments.callee);
    }

    caller

    caller返回一个函数的引用,这个函数调用了当前的函数。

    使用这个属性要注意:

    1 这个属性只有当函数在执行时才有用

    2 如果在JavaScript程序中,函数是由顶层调用的,则返回null

    3.调用形式: functionName.caller, functionName是当前正在执行的函数。

    callee

    callee放回正在执行的函数本身的引用,它是arguments的一个属性

    使用callee时要注意:

    1 这个属性只有在函数执行时才有效

    2 它有一个length属性,可以用来获得形参的个数,因此可以用来比较形参和实参个数是否一致,即比较arguments.length是否等于arguments.callee.length

    3 它可以用来递归匿名函数。

    var a = function(arg) {
      console.log(arguments.length);
      console.log(arguments.callee.length);
    }
    a();
    a(1,2,3)

    分别为 ( 0 1 ) 和 ( 3 1 )

    可以看出,arguments.length为实参数,arguments.callee.length为形参数

     


    参考资料

    如何理解和熟练运用js中的call及apply?

    js笔记——call,apply,bind使用笔记

    JS中的call()和apply()方法

    caller和callee的区别

    类数组对象是如何调用原生数组方法的?

      您阅读这篇文章共花了:  
     本文无需标签!
    二维码加载中...
    本文作者:小昱      文章标题: 深入理解JS中的Call和Apply
    本文地址:http://www.xiaoyulive.top/?post=130
    版权声明:若无注明,本文皆为“小昱个人博客”原创,转载请保留文章出处。
    taferase2018-02-01 16:27
    хоошь!

    ---
    Интересно, а аналог есть? скачать fifa 14 fifa 15, скачать fifa 15 через или <a href=http://15fifa.ru/skachat-fifa-15/12-skachat-demo-versiyu-fifa-15-besplatno.html>скачать fifa 15 demo</a> fifa 15 кряк 3dm
    返回顶部| 首页| 碰碰手气| 捐赠支持| 手机版本|后花园

    Copyright © 2016-2017 小昱个人博客 滇ICP备16006294号