美文网首页
JS委托——apply()和call()

JS委托——apply()和call()

作者: 发发呆哟 | 来源:发表于2020-05-28 19:48 被阅读0次

    因为最近接手了一个老项目,关于类的封装很有趣,正好借机复习一下apply()和call()两位好兄弟。
    apply和call是Function对象原型链上的函数,每个function都自带这两个属性。两者的功能相同,调用方式略有差异,下面的例子主要用call作为例子演示,例子都来自MDN

    语法

    func.apply(thisArg, [argsArray])
    thisArg
    可选参数, function 函数运行时使用的 this 值。非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,浏览器环境为window,node环境为global,在react项目中全局对象得打印看一下,有惊喜。
    argsArray
    可选参数,一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。

    function.call(thisArg, arg1, arg2, ...)
    thisArg
    可选参数, function 函数运行时使用的 this 值。非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,浏览器环境为window,node环境为global,在react项目中全局对象得打印看一下,有惊喜。
    arg1, arg2, ...
    可选参数,指定的参数列表。

    第一个例子,寻找一个数组中的最大值
    // 方式一
    const numbers = [5, 6, 2, 3, 7];
    const max = Math.max.apply(null, numbers);
    console.log(max); // 7
    // 方式二,感谢es6
    const numbers = [5, 6, 2, 3, 7];
    const max = Math.max(...numbers);
    console.log(max); // 7
    
    第二个例子,用 apply 将数组添加到另一个数组
    // 方式一
    var array = ['a', 'b'];
    var elements = [0, 1, 2];
    array.push.apply(array, elements);
    console.info(array); // ["a", "b", 0, 1, 2]
    // 方式二,再次感谢es6
    var array = ['a', 'b'];
    var elements = [0, 1, 2];
    array.push(...elements)
    console.info(array); // ["a", "b", 0, 1, 2]
    

    上述两个例子都是体现了apply可以将数组或者类数组对象传给func这一特性

    第三个例子,使用 call 方法调用函数并且指定上下文的 this
    function greet() {
      var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
      console.log(reply);
    }
    var obj = {
      animal: 'cats', sleepDuration: '12 and 16 hours'
    };
    greet.call(obj);  // cats typically sleep between 12 and 16 hours
    

    理解:greet运行时,this指向了obj,obj的属性animal为cats,sleepDuration为12 and 16 hours,greet的数组reply中的this.animal这是指向obj.animal,this.sleepDuration指向obj.sleepDuration
    这里我用的是指向,其实也不正确,call只是在两个function建立的一层关联,一个对象就可以通过委托访问另一个对象的属性和函数,就如我的标题所说,委托,不过指向好像更便于理解。

    第四个例子,使用 call 方法调用函数并且不指定第一个参数
    var sData = 'Wisen';
    function display() {
      console.log('sData value is %s ', this.sData);
    }
    display.call();  // sData value is Wisen
    

    这个例子,对应的是,非严格模式下,不指定第一个参数,这个参数会指向全局对象。

    第五个例子,使用 call 方法调用父构造函数,也就是继承
    function Product(name, price) {
      console.log(this)
      this.name = name;
      this.price = price;
    }
    
    function Food(name, price) {
      Product.call(this, name, price);
      this.category = 'food';
    }
    
    function Toy(name, price) {
      Product.apply(this, [name, price]);
      this.category = 'toy';
    }
    
    var pro = new Product('a', 7);
    var cheese = new Food('feta', 5);
    var fun = new Toy('robot', 40);
    
    console.info(cheese.name); // feat
    console.info(fun.name); // robot
    

    补充知识点,new一个实例的过程中,发生了什么事情
    (1) 创建一个新对象;
    (2) 将构造函数的作用域赋给新对象(将构造函数的 this 指向了这个新对象);
    (3) 执行构造函数中的代码(为这个新对象添加属性);
    (4) 返回新对象。

    let Parent = function (name, age) {
        this.name = name;
        this.age = age;
    };
    
    let newMethod = function (Parent, ...rest) {
        var obj  = {};
        obj.__proto__ = Parent.prototype;
        Parent.apply(obj, rest)
        return obj;
    };
    
    const child = newMethod(Parent,'团子', '25');
    console.log(child)
    

    我们以第一个pro为例,打印this,发现this是Product,所以可以作以下分析,其实和上面是一个东西,没差的
    (1)以构造器的prototype属性为原型,创建新对象c;var c = Object.create(Product.prototype)
    (2)将c和Product上的属性关联,执行,参数实例化;Product.apply(c, [name, price]);
    (3)返回c

    // ES5构造函数
    let Parent = function (name, age) {
        this.name = name;
        this.age = age;
    };
    
    let newMethod = function (Parent, ...rest) {
        // 1.以构造器的prototype属性为原型,创建新对象;
        let c = Object.create(Parent.prototype);
        // 2.将c和Product上的属性关联,执行
        Parent.apply(c, rest);
        // 3.返回第一步的对象
        return c;
    };
    
    let newMethod = function (Parent, ...rest) {
        var obj  = {};
        obj.__proto__ = Base.prototype;
        Parent.apply(obj, rest)
        return c;
    };
    
    const child = newMethod(Parent,'团子', 25);
    console.log(child)
    

    此时,我们再去理解Product.call(this, name, price);
    Product获取了name和price,之后this和Product相关联,this就可以通过委托访问product的属性和函数,迷惑性的行为也可以被称为继承

    相关文章

      网友评论

          本文标题:JS委托——apply()和call()

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