美文网首页
call、apply、bind 的用法

call、apply、bind 的用法

作者: Marshall3572 | 来源:发表于2021-03-17 14:49 被阅读0次

    bind、call、apply都是用来指定一个函数内部的this的值, 先看看bind、call、apply的用法

    var year = 2021
    function getDate(month, day) {
      return this.year + '-' + month + '-' + day
    }
    
    let obj = {year: 2022}
    getDate.call(null, 3, 8)    //2021-3-8
    getDate.call(obj, 3, 8)     //2022-3-8
    getDate.apply(obj, [6, 8])  //2022-6-8
    getDate.bind(obj)(3, 8)     //2022-3-8
    

    call

    call()方法接受的语法和作用与apply()方法类似,只有一个区别就是call()接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。
    二者都是函数对象Function的方法,且第一个参数都是要绑定对象的上下文
    例如:

    let obj = {
        a: 1,
        get: function(){
            return 2
        }
    }
    let g = obj.get
    g.call({},1,2,3)
    g.apply({},[1,2,3])
    
    1. call方法调用父构造函数
    function Product(name, price){
        this.name = name;
        this.food = food;
    }
    
    // 调用父构造函数的call方法来实现继承
    function Food(name, price){
        Product.call(this.name, toy);
        this.category = 'food';
    }
    
    function Toy(name, price){
        Product.call(this, name, price);
        this.category = 'toy';
    }
    
    var cheese = new Food('feta', 5);
    var fun = new Toy('robot', 40);
    
    1. call方法调用匿名函数
    var animals = [
        {species: 'Lion', name: 'King'},
        {species: 'Whale', name: 'Fail'}
    ];
    
    for(var i = 0; i < animals.length; i++){
        (function(i){
            this.print = function(){
                console.log('#' + i + ' ' + this.species + ': ' + this.name);
            }
            this.print();
        }).call(animals[i], i); //call调用匿名函数
    }
    
    1. call方法调用函数并且指定上下文的this
    var obj = {
        animal: 'cats', sleepDuration: '12 and 16 hours'
    };
    
    function greet(){
        var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
        console.log(reply);
    }
    
    greet.call(obj);  //"cats typically sleep between 12 and 16 hours"
    
    1. call方法调用函数并且不指定第一个参数(argument)
      在这个例子中,我们没有传递第一个参数,this的值将被绑定为全局对象。
    var sData = 'marshall';
    
    function display(){
        console.log("sData's value is %s",this.sData);
    }
    
    display.call();  // sData value is marshall
    

    但是在严格模式下,this 的值将会是undefined

    var sData = 'marshall';
    
    function display(){
        console.log("sData's value is %s",this.sData);
    }
    
    display.call();  // Cannot read the property of 'sData' of undefined
    

    手写一个call

    Function.prototype.call2 = function(context, ...args) {
      context = (context === undefined || context === null) ? window : context
      context.__fn = this
      let result = context.__fn(...args)
      delete context.__fn
      return result
    }
    

    apply

    使用 apply, 我们可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。
    apply 与 call() 非常相似,不同之处在于提供参数的方式。apply 使用参数数组而不是一组参数列表。apply 可以使用数组字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或数组对象, 如 fun.apply(this, new Array('eat', 'bananas'))。

    1. apply方法调用一个具有给定this值的函数,以及以一个数组的形式提供参数。
    var array = ['marshall','eminem'];
    var elements = [0,1,2];
    array.push.apply(array,elements);
    console.log(array);  //['marshall','eminem',0,1,2]
    
    1. 使用apply和内置函数
      对于一些需要写循环以遍历数组各项的需求,我们可以用apply完成以避免循环。
    //找出数组中最大值和最小值
    var numbers = [5, 6, 2, 3, 7];
    //使用Math.min和Math.max以及apply函数时的代码
    var max = Math.max.apply(null, numbers);
    var min = Math.min.apply(null, numbers);
    

    上边这种调用apply的方法,有超出JavaScript引擎参数长度上限的风险。
    如果我们的参数数组非常大,推荐使用下边这种混合策略:将数组切块后循环传入目标方法

    function minOfArray(arr) {
        var min = Infinity;
        var QUANTUM = 32768;
      
        for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
          var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
          min = Math.min(submin, min);
        }
      
        return min;
      }
      
      var min = minOfArray([5, 6, 2, 3, 7]);
    

    手写一个apply

    Function.prototype.apply2 = function(context, args) {
      context = (context === undefined || context === null) ? window : context
      context.__fn = this
      let result = context.__fn(...args)
      delete context.__fn
      return result
    }
    

    bind

    bind()函数会创建一个新的绑定函数,这个绑定函数包装了原函数的对象。调用绑定函数通常会执行包装函数。
    绑定函数内部属性:

    • 包装的函数对象
    • 在调用包装函数时始终作为this传递的值
    • 在对包装函数做任何调用时都会优先用列表元素填充参数列表。

    bind() 最简单的用法是创建一个绑定函数,不管怎么调用这个函数都有相同的this值。

    this.x = 9; //this指向全局的window对象
    var module = {
        x: 81,
        getX: function(){return this.x;}
    };
    
    console.log(module.getX()); //81
    
    var retrieveX = module.getX;
    console.log(retrieveX()); //9,因为函数是在全局作用域中调用的
    
    
    // 创建一个新函数,把this绑定到module对象
    // 不要将全局变量 x 与 module 的属性 x 混淆
    var boundGetX = retrieveX.bind(module);
    console.log(boundGetX()); //81
    

    手写一个bind

    Function.prototype.bind2 = function(context, ...args1) {
      context = (context === undefined || context === null) ? window : context
      let _this = this
      return function(...args2) {
        context.__fn = _this
        let result = context.__fn(...[...args1, ...args2])
        delete context.__fn
        return result
      }
    }
    

    相关文章

      网友评论

          本文标题:call、apply、bind 的用法

          本文链接:https://www.haomeiwen.com/subject/iwzscltx.html