js中对call()函数和apply()函数的深入探索

时间:2021-01-24 18:35:22

call的探索

01 call借用

    //对象1
   var myclass={
       getAllStudentsNumbers:function(){
           return 130}
   };
    //对象2
    var student={
       getDetail:function(){
           return {name:'莉莉',aihao:'唱歌跳舞'}
       }
   };
    //借用 -- 供爷法则
    console.log(myclass.getAllStudentsNumbers.call(student))

02 call 传参1

 //对象1
    var myclass={
        getAllStudentsNumbers:function(sum,sum1){
            return sum+sum1}
    };
    //对象2
    var student={
        getDetail:function(){
            return {name:'莉莉',aihao:'唱歌跳舞'}
        }
    };
    //借用 -- 供爷法则
    console.log(myclass.getAllStudentsNumbers.call(student,10,200))
    console.log(myclass.getAllStudentsNumbers.apply(student,[10,200]))

02 call 传参2

    //函数其实也是对象

    function add(a, b) {
        alert(a + b);
    }

    function sub(a, b) {
        alert(a - b);
    }


    /*借用:
    就是把人家的方法放到自己中来*/
    add.call(sub, 3, 1);

03 call 作用 修改this

用户名:<input type="text" id="myText"   value="探索" />

    var value="全局变量";
    /*函数中默认this指向window*/
    function Fun1(){
        console.log(this.value);
    }

    /*答案:全局变量*/
    window.Fun1();
    /*答案:window*/
    Fun1.call(window);
    /*答案:window*/
    /*可以这么理解:先把fun1变成window对象的方法,同时改变Fun1中的this指向*/
    /*这个时候this表示window*/
    function Fun1(){
        console.log(this.value);
    }
    Fun1.call(document.getElementById('myText'));
    /*答案*/
    /*这个时候this指向input元素对象*/
    //探索

05 call作用 伪数组 - 将dom元素集合数组化

    /*什么叫伪数组*/
    /*伪数组:只有数组的部分功能:length,下标,无法访问数组对象中的方法*/

    /*Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组*/
    /*这是一种固定用法*/

    /*为什么需要学习伪数组*/
   /*js中常见的伪数组:通过document获取的dom集合*/
    /*最为常用的arguments*/

    /*这些伪数组无法使用Array对象中的方法,因为他们不是数组对象,就是普通的含有length属性的json对象而已*/
  /* 比如:var json = {1:'',2:'',length:2}*/



    var divs = document.getElementsByTagName("div")
    console.log(divs.length)
    /*说明他不是一个数组,无法访问里面的方法*/
    divs.pop().style.background='green'//报错

    /*我们通过如下方式将其转换成数组*/
    /* slice : 截取数组,返回的还是数组,这里我们截取全部 */
    var domNodes =  Array.prototype.slice.call(divs);
    /*这样domNodes就可以应用Array下的所有方法了。*/
    domNodes.pop().style.background='green'

06 将arguments数组化

function add(){
        var sum=0;
       /* arguments.push(10) //报错*/
        var arr = Array.prototype.slice.call(arguments)
        arr.push(10)  //报错
        for(var i=0;i<arr.length;i++){
            sum+=arr[i]
        }
        return sum;
    }

    var sum = add(1,2,3,4,5)

    console.log(sum)

07 将自定义伪数组数组化

    var fackArray1 = {0:'first',1:'second',length:2};
    Array.prototype.slice.call(fackArray1);// ["first", "second"]


    var fackArray2 = {length:2};
    Array.prototype.slice.call(fackArray2);// [undefined, undefined]

aplay的探索

02 妙用1 参数数组拆分法则 - 计算数组最大值

 /* 我们先从Math.max()函数说起, Math.max后面可以接任意个参数,最后返回所有参数中的最大值。 比如*/
   console.log(Math.max(5,8)) //8
   console.log(Math.max(5,7,9,3,1,6)) //9
   /*问题:如何获取一个数组的最大值*/
   /*遍历*/


   /* 但是在很多情况下,我们需要找出数组中最大的元素。*/
   /* var arr=[5,7,9,1] alert(Math.max(arr)) // 这样却是不行的。一定要这样写*/


    /*传统方式写法*/
    function getMax(arr){
        var arrLen=arr.length;
        for(var i=0,ret=arr[0];i<arrLen;i++){
            ret=Math.max(ret,arr[i]);
    }
    return ret;
    }
   console.log(getMax([1,2,3,4,5,6,7]))

    /*这样写麻烦而且低效。如果用 apply呢,看代码:*/


   /*参数数组拆分法则*/
   /*传递一个数组,其实会将其拆成很多个参数*/


   /*适用场景:函数可以接受不限个数的参数*/
   /*这样我们只能使用arguments来管理可变参数*/
   /*比如max min push join split replace*/
   /*在js中有很多这样支持可变参数的函数 大家还记得我们前面写的extend,也是支持可变参数*/

   /*这也是为什么apply这么流行,这么重要的原因*/

   /*广泛运用在框架,算法中*/

   /*巧用apply虽然传递的是数组,但是使用的时候是把数组拆开的。。 等价于 return Math.max.call(null,1,2,3,4,5);*/
   /*所以等价于:Math.max(5,7,9,3,1,6)*/
    function getMax2(arr){
        return Math.max.apply(null,arr);
       /* return Math.max.call(null,1,2,3,4,5);*/
    }
   console.log(getMax2([1,2,3,4,5,6,7]))

03 练习 计算最小值

 //计算最小值
    /*参数数组拆分法则*/
    /*传递一个数组,其实会将其拆成很多个参数,正好符合min的语法*/
    var min=Math.min.apply(null,[3335,333,34343,34343,5657767,34455,445466,45454,343434,46466,56556,464646,464646,466,4646464])
    alert('最小值:'+min)

04 练习 将一个数组的值合并到另一个数组

  /*练习*/
    /*将一个数组合并到另一个数组中*/
    var arr1=new Array("1","2","3");
    var arr2=new Array("4","5","6");
    /*最终arr1["1","2","3","4","5","6"]*/


    //传统写法
    function PushArray(arr1,arr2){
        var arrLen=arr2.length
        for(var i=0;i<arrLen;i++){
            arr1.push(arr2[i])
        }
        return arr1;
    }
    var result = PushArray(arr1,arr2);
    console.log(result)


    /*使用apply写法*/
     /*Array.prototype.push 可以实现两个数组合并 同样push方法没有提供push一个数组,但是它提供了push(param1,param,…paramN) push(arr,2,3,3,3,3) 所以同样也可以通过apply来装换一下这个数组,即:*/
    Array.prototype.push.apply(arr1,arr2);
   /* Array.prototype.push.call(arr1,"4","5","6");*/
   /* push(arr1,"4","5","6")*/
    console.log(arr1)
    console.log(arr2)

05 自定义的函数

 /*有的说 这样的函数也不多,apply应该也就使用部分函数而已,。。。。大打错了。 下面看下高手的编程 工作中70%用不好apply 50%都不理解apply 后台开发人员也做部分前端 80%都不知道这个用法*/
    function add(a, b) {
        return a + b;
    }

    function add(){
        var sum=0;
        for(var i=0;i<arguments.length;i++){
            sum+=arguments[i]
        }
        return sum;
    }

    var sum = add(1,2,3,4,5)

    console.log(sum)

    /*如何计算数组的和*/
    var sum2 =  add.apply(null,[1,2,3,4,5])

    console.log(sum2)

06 总结:调用函数的5种方式

 /****************************************************************************** 普通模式 *******************************************************************************/

    // 声明一个函数,并调用
    function func() {
        console.log("Hello World");
    }
    func();


    /****************************************************************************** 函数表达式 *******************************************************************************/
    // 使用函数的Lambda表达式定义函数,然后调用
    var func = function() {
        console.log("你好,传智播客");
    };
    func();


    //可以发现函数调用很简单,就是平时学习的一样.
    //这里的关键是,在函数调用模式中,函数里的 this 关键字指全局对象,
    //如果在浏览器中就是 window 对象. 例如:
    var func = function() {
        console.log(this);
    };
    func();
    // 此时,会弹出对话框,打印出 [object Window]


    /****************************************************************************** 方法调用模式 *******************************************************************************/
// 函数调用模式很简单,是最基本的调用方式.
// 但是同样的是函数,将其赋值给一个对象的成员以后,就不一样了.
// 将函数赋值给对象的成员后,那么这个就不在称为函数,而应该叫做方法.

    // 定义一个函数
    var func = function() {
        alert("我是一个函数么?");
    };

    // 将其赋值给一个对象
    var o = {};
    o.fn = func; // 注意这里不要加圆括号
    // 调用
    o.fn();

// 此时,o.fn 则是方法,不是函数了.
// 实际上 fn 的方法体与 func 是一模一样的,但是这里有个微妙的不同. 看下面的代码:
    // 接上面的代码
    alert(o.fn === func);
// 打印结果是 true ,这个表明两个函数是一样的东西. 但是修改一下函数的代码:

    // 修改函数体
    var func = function() {
        alert(this);
    };
    var o = {};
    o.fn = func;
    // 比较
    alert(o.fn === func);
    // 调用
    func();
    o.fn();
    // 这里的运行结果是,两个函数是相同的,因此打印结果是 true.
    // 但是由于两个函数的调用是不一样的,
    // func的调用,打印的是 [object Window],而o.fn 的打印结果是[object Object].
    // 这里便是函数调用与方法调用的区别.
    // 函数调用中,this专指全局对象window,
    // 而在方法中this专指当前对象. 即o.fn 中的this 指的就是对象o.


    /****************************************************************************** 构造函数调用模式 *******************************************************************************/

// 同样是函数,在单纯的函数模式下,this表示window;
// 在对象方法模式下,this指的是当前对象.
// 除了这两种情况,JavaScript中函数还可以是构造器.
// 将函数作为构造器来使用的语法就是在函数调用前面加上一个new关键字. 如代码:
    // 定义一个构造函数
    var Person = function() {
        this.name = "传智播客";
        this.sayHello = function() {
            alert("你好,这里是" + this.name);
        };
    };
    // 调用构造器,创建对象
    var p = new Person();
    // 使用对象
    p.sayHello();

// 上面的案例首先创建一个构造函数Person,然后使用构造函数创建对象p.
// 这里使用 new语法.然后使用对象调用sayHello()方法.
// 这个使用构造函数创建对象的案例比较简单. 从案例可以看到,此时 this指的是对象本身.


    /****************************************************************************** apply call调用模式 *******************************************************************************/
        //前面已经详细讲解了

        /*Function对象定义函数*/

07 apply实现继承 - 继承的简单介绍

// apply实现继承
// 学生类本来不具备任何方法,
// 但是在 Person.apply(this,arguments) 后,
// 他就具备了 Person类的sayhello方法和 所有属性。
// 在 Print.apply(this,arguments) 后就自动得到了 show() 方法。


    //人对象
    function Person(name,age){
        this.name=name     //名字
        this.age=age       //年龄
        this.sayhello=function(){
            console.log("人对象方法")
        }
    }
    Person.prototype={
        buy:function(){
            console.log('测试是否能够继承原型中的方法')
        }
    }

    //输出打印对象
    function Print(){            //显示类的属性
        this.funcName="我是打印对象"
        this.show=function(){
           console.log ('打印对象方法')
        }
    }

    //学生对象
    function Student(name,age,grade,school){    //学生类
        Person.apply(this,arguments)
        Print.apply(this,arguments)
        this.grade=grade                  //年级
        this.school=school                    //学校
    }



    /*子类继承两个父类*/
   /* 也就是通俗一点讲就是: 用student去执行Person这个类里面的内容, 在Person这个类里面存在this.name等之类的语句, 这样就将属性创建到了student对象里面*/
    var lisi=new Student("tom",13,6,"清华小学")
    //学生继承了人和打印对象,则拥有了人的属性和方法
    /*打印父类*/
    lisi.show()
    /*人父类*/
    lisi.sayhello()
    /*无法继承原型对象中的方法*/
    alert(lisi.buy())