深入理解call、apply、bind

Function原型上有四个方法,其中一个是toString方法,其他三个分别是call、apply和bind

我们知道toString方法是对象的方法,但是每个类型的数据,都重写了该方法。函数类型也重写了该方法。Function.prototype.toString() 可以得到函数的字符串形式。

1、先从call聊起:Function.prototype.call   改变this指向

func.call(thisArg, arg1, arg2, ...)

 call方法让this指向thisArg,func的arguments对应接收arg1、arg2...

thisArg

如果这个函数处于非严格模式下,不指定或指定为 null 或 undefined 时会自动替换为指向全局对象。原始值会被包装。

如果是严格模式,指定什么this就为什么,且原始值不被包装。如果没有指定,this为undefined。

实际使用:与数组方法的结合使用,比如slice方法;调用或是借用别的对象的方法;对象想执行一个功能,但又不想在自身添加这个方法,可以通过匿名函数修改this指向来实现;构造函数继承别的函数。

(1)单纯的改变this指向,借用别的对象的方法

var obj1 = {
    age: 24,
    sayAge: function(){
        console.log(this.age);
    }
}
var obj2 = {
    age: 18
}
obj1.sayAge.call(obj2);

 (2)构造函数继承别的函数

function Foo(name, age){
    this.name = name;
    this.age = age;
}
function Bar(name,age, job){
    Foo.call(this,name,age);
    this.job = job;
}
console.log(new Bar("zhu",24,"programmer"))

(3)匿名函数调用call。某个匿名函数实现了一个功能,可以指向某个对象或数组来调用它。而不会把这个方法添加到对象或数组上

(function(){
    console.log(this.name, this.age);
}).call({name:"zhu",age: 24})

2、聊聊apply:Function.prototype.apply

call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

其次由于apply 内部通过call实现,是对call的二次封装,性能不如call。

func.apply(thisArg, [argsArray])

thisArg的特性与call一样。

func的arguments是argsArray的每一项

 兼容性:与 call一样,兼容IE5.5。ES5开始,第二个参数可以是类数组,兼容IE9

var obj1 = {
    age: 24,
    sayAge: function(){
        console.log(this.age);
        console.log(arguments);
    }
}
var obj2 = {
    age: 18
}
obj1.sayAge.apply(obj2, {0:"a",1:"b",2:"c",length:3});

实例:合并数组并修改原数组

我们知道push方法会修改原数组,但接收的是多个单一元素,如果接收一个数组,会把该数组作为一个元素push到原数组中;

var array = ['a', 'b'];
array.push("c","d","e");
console.info(array);//["a", "b", "c", "d", "e"]

而concat方法,虽然可以接收数组或多个单一元素,进行合并,但不会修改元素组;

var array = ['a', 'b'];
var array1 = array.concat(["c","d"],"e");
console.info(array);//["a", "b"]
console.info(array1);//["a", "b", "c", "d", "e"]

push.apply,既可以修改原数组,也可以合并数组

var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

实例:找出数组中最大数或最小数

Math.max() 和 Math.min() 接收多个单一元素,返回最大或最小的值

var arr = [1,3,2,4,8,6];
console.log(Math.max(...arr)); //8
console.log(Math.min(...arr)); //1
console.log(Math.max.apply(arr,arr)) //8

 主要使用在接收一串参数的方法上,运行该方法接收一个数组;比如:push、Math.max和Math.min

通过Array.apply来转arguments类数组为数组

apply的展开功能

Array.apply(null, arguments)

3、聊聊bind  Function.prototype.bind 

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

func.bind(thisArg[, arg1[, arg2[, ...]]])

 thisArg

调用绑定函数时作为 this 参数传递给目标函数的值。

如果使用new运算符构造绑定函数,则忽略该值。——》new时忽略thisArg,因为new会重新创建隐式的this。

arg1, arg2, ...

当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

当绑定函数执行时,其arguments是预置入的参数+函数执行时的参数。

 (1)new忽略thisArg

function Foo(name, age){
    this.name = name;
    this.age = age;
}
var obj = {
    name: "zhu",
    age: 24
}
var Bar = Foo.bind(obj);
console.log(new Bar("z",18)); //Foo {name: "z", age: 18}

4、通过call、apply实现bind函数

(1)不兼容new 

if (!Function.prototype.bind) (function(){
    var slice = Array.prototype.slice;
    Function.prototype.bind = function() {
      var thatFunc = this, thatArg = arguments[0];
      var args = slice.call(arguments, 1);
      if (typeof thatFunc !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - ' +
               'what is trying to be bound is not callable');
      }
      return function(){
        var funcArgs = args.concat(slice.call(arguments))
        return thatFunc.apply(thatArg, funcArgs);
      };
    };
  })();

(2)兼容new

if (!Function.prototype.mybind) (function(){
    var ArrayPrototypeSlice = Array.prototype.slice;
    Function.prototype.mybind = function(otherThis) {
      if (typeof this !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
      }
  
      var baseArgs= ArrayPrototypeSlice.call(arguments, 1),
          baseArgsLength = baseArgs.length,
          fToBind = this,
          fNOP    = function() {},
          fBound  = function() {
            //改变函数this后的函数
            //如果是fBound函数执行,this是函数执行时的上下文,比如window
            //如果是new fBound(),this是构造函数的实例对象
            baseArgs.length = baseArgsLength; // reset to default base arguments
            baseArgs.push.apply(baseArgs, arguments);
            return fToBind.apply( //执行fBound时执行原函数
                   fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs //如果是实例对象,this
            );
          };
  
      if (this.prototype) {//此处的this是mybind的this,是个函数
        // Function.prototype doesn't have a prototype property
        fNOP.prototype = this.prototype; 
      }
      fBound.prototype = new fNOP();
  
      return fBound; //执行bind返回的函数
    };
  })();

call在构造函数中的使用:https://blog.csdn.net/zyz00000000/article/details/106904163

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值