apply、call和bind

call、apply和bind是JavaScript中function对象的三个方法,他们的共同点有如下几点:

  1. 改变函数运行时的this指向
  2. 第一个参数都是this要指向的对象
  3. 都可以利用后续参数传参

不同点在于:

  1. bind是返回对应函数便于稍后调用;apply、call则是立即调用
  2. apply和call可以实现继承;而bind不可以
  3. bind会产生新的函数;而apply和call不会产生新函数,只是在调用时绑定一下
  4. apply的第二个参数是一个数组(包含函数的所有参数),call的第二个参数是参数列表

一、具体使用

var person = {
    name: "CD",
    age:21,
    sex:"boy",
    can:function (score,price){
        alert(this.sport + score + "分," + this.food + price + "/kg");
    },
    write:function(){
        alert(this.age);
    }
};
var p = {
    sport:"football",
    food:"bread",
    age:22
};

person.can.apply(p,[90,20]);
person.can.call(p,90,20);
person.can.bind(p)(90,20);

二、应用场景

  1. 将类数组转换为数组
    var tArr = Array.prototype.slice.call(lArr);
    
    //举个栗子
    function fn(){
        var arr = Array.prototype.slice.call(arguments);
        console.log(arr.shift());
    }
    fn(0,1,2,3,4,5);//0
  2. 数组追加:以下这两种写法都可行。apply也可以被替换成call和bind。当然,使用不同的方法也存在着不同之处:apply的结果是,arr1变成一个arr2的各元素依次追加到arr1的末尾的新数组,arr2自身保持不变;call的结果是,arr2整体作为一个元素追加到arr1的末尾,arr2自身保持不变;bind与call的效果相同
    Array.prototype.push.apply(arr1,arr2);
    
    [].push.apply(arr1,arr2);
    
    
    var arr1 = [1,2,3];
    var arr2 = [4,5,6];
    
    //apply
    [].push.apply(arr1, arr2);
    console.log(arr1);// [1, 2, 3, 4, 5, 6]
    console.log(arr2);// [4,5,6]
    
    //call
    [].push.call(arr1, arr2);
    console.log(arr1);// [1, 2, 3,[ 4, 5, 6]]
    console.log(arr2);// [4,5,6]
    
    //bind
    [].push.bind(arr1, arr2)();
    console.log(arr1);// [1, 2, 3,[ 4, 5, 6]]
    console.log(arr2);// [4,5,6]
  3. 判断变量类型
    function isArray(obj){
        return Object.prototype.toString.call() == "[object Array]";
    }
    console.log(isArray([]));//true
    console.log(isArray('string'));//false

三、用js实现

  1. 原生js实现bind方法:
    Function.prototype.myBind = function(){
        var self = this; //保存原函数
        var context = Array.prototype.shift.call(arguments); //保存第一个参数:this上下文
        var args = Array.prototype.slice.call(arguments); //剩余的参数(除了this上下文)转为数组
        return function(){
            self.apply(context,Array.prototype.concat.call(args,Array.prototype.slice.call(arguments)));
        }
    }
    
    //应用
    var arr1 = [1,2,3];
    var arr2 = [4,5,6];
    [].push.myBind(arr1,arr2)();
    console.log(arr1,arr2);//[1,2,3,[4,5,6]] [4,5,6]

    myBind()与bind()的应用结果相同,由此可见,两者等价。

  2. 原生js实现apply方法:

    //实现一:ES5
    Function.prototype.myApply = function (context, arr) {
        if (typeof context === 'object') {
            context = context || window
        } 
        else {
            context = Object.create(null);
        }
        context.fn = this;
    
        var result;
        if (!arr) {  // 判断函数参数是否为空
            result = context.fn();
        }
        else {
            var args = [];
            for (var i = 0; i < arr.length; i++) {
                args.push('arr[' + i + ']');
            }
            result = eval('context.fn(' + args + ')');
        }
    
        delete context.fn;
        return result;
    }
    //实现二:ES6
    Function.prototype.myApply = function(context, parameter) {
      if (typeof context === 'object') {
        context = context || window;
      }
      else {
        context = Object.create(null);
      }
      let fn = Symbol()
      context[fn] = this;
      var result = context[fn](...parameter);
      delete context[fn];
      return result;
    }
  3. 原生js实现call方法(与apply的实现类似):

    //实现一:ES5
    Function.prototype.myCall = function (context) {
        if (typeof context === 'object') {
            context = context || window
        } else {
            context = Object.create(null);
        }
        
        context.fn = this;
        
        let args = [];
        
        for(var i=1; i< arguments.length; i++) {
            args.push('arguments[' + i + ']');
        }
    
        // args => [arguments[1], arguments[2], arguments[3], ...]
        
        var result = eval('context.fn(' + args + ')');
        delete context.fn;
        return result;
    }
    //实现二:ES6
    Function.prototype.myCall = function (context,...params) {
        if (typeof context === 'object') {
            context = context || window
        } else {
            context = Object.create(null);
        }
        let fn = Symbol();
        context[fn] = this
        var result = context[fn](...params);
        
        delete context.fn;
        return result;
    }

原生js实现这三种方法的写法多种多样,欢迎交流分享与指正。侵删。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值