美文网首页码农
call和apply的使用-扩展篇

call和apply的使用-扩展篇

作者: 潜水的旱鸭子 | 来源:发表于2019-01-17 17:02 被阅读0次

一、回顾

在上篇文章call和apply的使用-基础篇中,我们已经提到了call和apply的功能和语法,这里稍作回顾:

介绍:call和apply都是函数的方法,需要加在函数体后执行。

功能:都是用来修改函数的执行上下文(this)。

语法:

  • call(thisObj,arg1,arg2,arg3,……)
  • apply(thisObj,argArr)
    说明:call和apply的主要区别就是:call可以接受一个或以上的参数,当接受多个参数时,从第二个参数开始,后面所有的参数都会改变原函数的参数;apply只能接受一个或两个参数,当接受两个参数时,第二个参数必须是一个数组或类数组,数组中的数据,会改变原函数arguments中的参数。
    而call和apply的第一个参数,都是用来改变原函数的this指向。

所以,以下演示以call为主,如果只有一个参数,那么可以直接替换成apply,并无区别;如果存在两个以上参数,替换成apply时,需要把第二个及后面所有参数放在一个数组中。

二、使用

使用方式1:执行时一个对象可以使用另一个对象的方法

    function Doctor(){
        this.name = "Doctor";
        this.say = function(){
            console.log(this.name);
        }
    }
    function Stephen(){
        this.name = "Stephen Strange";
    }
    var doctor = new Doctor();
    var stephen = new Stephen();
    // 通过call将stephen对象传入doctor的say方法,此时say方法中的this被指向stephen对象
    doctor.say.call(stephen);       //Stephen Strange

使用方式2:实现继承

    function Doctor(name){
        this.name = name;
        this.say = function(){
            console.log(this.name)
        }
    }
    function Stephen(name){
        //当前函数内的this指向函数Son的实例化对象
        //在执行Stephen时执行Doctor,同时将Doctor内的this改变成Stephen的this
        Doctor.call(this,name)
    }

    var doctor = new Doctor("Doctor")
    doctor.say();       //Doctor
    var stephen = new Stephen("Stephen Strange")
    // 在Stephen中并没有say方法,但是因为在new Stephen时,执行了Doctor,
    // 并将Doctor中的this指向Stephen的this,
    // 那么在new Stephen后,得到的实例,也具有了Doctor内的属性和方法
    stephen.say();      //Stephen Strange

使用方式3:多继承

    function People(){
        this.say = function(){
            console.log(`My name is ${this.name}. I will have ${this.attr} ${this.skill}.`)
        }
    }
    function Doctor(){
        this.skill = "cure";
    }
    function Magic(){
        this.attr = "Amazing";
    }
    function Stephen(name){
        this.name = name;
        // 执行其他函数的同时将原函数的this指向都改成Stephen的this,此时所有属性和方法可以互相访问
        People.call(this);
        Doctor.call(this);
        Magic.call(this);
    }
    var stephen = new Stephen("Stephen Strange");
    stephen.say();      //My name is Stephen Strange. I will have Amazing cure.

使用方式4:改变系统函数的this指向,实现伪数组转真数组

我们知道js中有很多类(伪)数组,伪数组虽然也按照索引存储数据,有length属性,但是却不具有数组的方法,如push,pop等。
如果我们想使用数组的方法来操作伪数组,那么需要先将伪数组转成真数组,伪转真的方法有很多种,这里我们只说使用call方法转换:

    var ali = document.querySelectorAll("li");
    // instanceof:查看一个实例是否指向某个构造函数的原型(查看一个实例是否属于某个类)
    console.log(ali instanceof Array);      //false
    // ali.push("hello");         //报错:ali.push is not a function
    
    var arr = new Array(4,5,6);
    // instanceof:查看一个实例是否指向某个构造函数的原型(查看一个实例是否属于某个类)
    console.log(arr instanceof Array);      //true
    arr.push("hello")
    console.log(arr);         //[4,5,6,"hello"]

    //此处开始转换
    var aliZ = Array.prototype.slice.call(ali)
    console.log(aliZ)
    console.log(aliZ instanceof Array);     //true
    // 此时aliZ就是一个真数组,可以使用数组的众多方法来操作
    aliZ.push("world");
    console.log(aliZ);       //[li,li,li,...,"world"]

使用方式5:优化Math对象的方法

Math对象的min和max方法只能接受多个数据,而不能接受单个数组。但是我们知道,函数内的arguments保存所有传进来的实参,此处利用apply第二个参数是数组,并且会覆盖原函数arguments的特点,将数组由apply传进去,交给min或max处理,即可快速得到数组的最大或最小值

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

    console.log(Math.min.apply(null,arr))   //1
    console.log(Math.max.apply(null,arr))   //7

使用方式6:改造系统方法的调用方式

我们知道js中许多实例的方法都是定义在构造函数的原型对象上,如Array.prototype.push / String.prototype.match / Function.prototype.bind等,当我们通过实例调用这些方法时,调用方式为arr.push() / str.match() / fn.bind(),我们可以利用call或apply函数改变这些实例方法的调用方式。
改造之后的调用如:push(arr,"hello")

    let arr = [3,4,5];
    Array.prototype.push.call(arr,"hello")
    console.log(arr);                       //[3,4,5,"hello"]

    // 通过执行Function原型上的call方法的bind方法,改变call中原本应指向Function实例的this为Array.prototype.push,
    // 并保存bind的返回值--改造之后的新call函数,放在newPush
    const newPush = Function.prototype.call.bind(Array.prototype.push);
    // 此时,call方法中的this指向为Array原型上的push方法,
    
    // 执行newPush,相当于执行了Function.prototype.call.call(Array.prototype.push),
    // call的第一个参数用来改变原函数this的指向,后call将前call中的this改成Array.prototype.push
    // 此时后call执行,得到改变之后的前call
    // 也就相当于得到了Array.prototype.push.call()
    // 最终执行newPush相当于执行了Array.prototype.push.call()
    newPush(arr,"world");
    console.log(arr);                       //[3,4,5,"hello","world"]

    // 此类改造还有:
    const slice   = Function.prototype.call.bind(Array.prototype.slice);
    console.log(slice(arr, 0, 2));          //[3,4]
    const match = Function.prototype.call.bind(String.prototype.match);
    console.log(match("a1ab12abc123", /\d+/g));     //["1", "12", "123"]

    // 练习:使用相同方式尝试改造数组或字符的其他方法

三、总结

其实不管在任何地方,只要牢记call和apply方法的功能:修改原函数的this指向,并执行这个新函数。就可以轻松的驾驭函数的call和apply方法。

临近结尾顺便提一下函数的另一个方法bind,其实bing和call/apply的功能类似,只不过bind修改this指向之后,返回的新函数不会自动执行,如果有需要,需要手动执行;而call和apply改变this之后,返回的新函数会自动执行。


写在最后,文中总结,如有不全或错误,欢迎留言指出,谢谢支持……^ _ ^

相关文章

  • call和apply的使用-扩展篇

    一、回顾 在上篇文章call和apply的使用-基础篇中,我们已经提到了call和apply的功能和语法,这里稍作...

  • JS高级学习:apply和call 的使用/bind方法/高阶函

    apply和call 的使用 apply和call的使用方法 apply的使用语法:函数名字.apply(对象,[...

  • 模拟实现bind

    不使用call apply 使用apply上面使用了es6的扩展操作符去模拟es5语法有点牵强,使用apply做改...

  • web前端 -- Day25 js高级

    apply和call的使用 apply和call作用: 可以改变this的指向 apply的使用语法函数名字.ap...

  • ES5

    严格模式 Object扩展 call apply bind

  • this

    apply、call 有什么作用,什么区别 使用call和apply方法,可以改变对象方法的运行环境。 call ...

  • js中apply/call/bind

    apply/call/bind 使用apply/call/bind改变 this 指向的 实现过程 apply 使...

  • JS | 构造函数的继承

    &&& 1.使用call和apply方法 这种方法是最简单的方法,使用call和apply方法,.把父对象的构造函...

  • apply,call,bind方法

    apply和call的使用 作用:可以改变this的指向 /** apply的使用语法* 函数名字.apply(对...

  • JavaScript深入之call和apply的模拟实现

    JavaScript深入系列第十篇,通过call和apply的模拟实现,带你揭开call和apply改变this的...

网友评论

    本文标题:call和apply的使用-扩展篇

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