• call()、apply()和bind()用法、区别和场景使用


    先简单了解下:
    例一:

    var name = "小王";
    var age = 17;
    var obj = {
        name: "小张",
        objAge: this.age,
        myFun: function() {
            console.log(this.name + "年龄" + this.age);
        }
    }
    console.log(obj.objAge); // 17
    console.log(obj.myFun()); // 小张年龄 undefined
    

    例二:

    var fav = "盲僧"
    function shows(){
        console.log(this.fav);
    }
    console.log(shows()); // 盲僧
    

    比较一下这两者 this 的差别,第一个打印里面的 this 指向 obj,第二个全局声明的 shows() 函数 this 是 window ;

    1. call()、apply()、bind() 都是用来重定义 this 这个对象的!
    var name = "小王";
    var age = 17;
    var obj = {
        name: "小张",
        objAge: this.age,
        myFun: function() {
            console.log(this.name + "年龄" + this.age);
        }
    }
    var db = {
        name: "德玛",
        age: 99
    }
    console.log(obj.myFun.call(db)); // 德玛年龄 99
    console.log(obj.myFun.apply(db)); // 德玛年龄 99
    console.log(obj.myFun.bind(db)()); // 德玛年龄 99
    

    以上出了:

    1. 如果call()和apply()的第一个参数是null或者undefined,那么this的指向就是全局变量,在浏览器里就是window对象。
    2. bind 方法后面多了个 () 外 ,结果返回都一致!
      由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。

    2,对比call 、bind 、 apply 传参情况下

    var name = "小王";
    var age = 17;
    var obj = {
        name: "小张",
        objAge: this.age,
        myFun: function(fm,t) {
            console.log(this.name + "年龄" + this.age, "来自" + fm + "去往" + t);
        }
    }
    var db = {
        name: "德玛",
        age: 99
    }
    obj.myFun.call(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海
    obj.myFun.apply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
    obj.myFun.bind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海
    obj.myFun.bind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined
    

    微妙的差距!

    从上面四个结果不难看出:

    call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:

    call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' )。

    apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])。

    bind 除了返回是函数以外,它 的参数和 call 一样。

    当然,三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!

    • 总结:
    1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
    2. 相同点:这两个方法的作用是一样的。
      都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
      一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。
    3. 不同点:接收参数的方式不同。
      apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
      语法:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。

    说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个
    TypeError,如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。

    call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
    语法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);,应用某一对象的一个方法,用另一个对象替换当前对象。

    说明: call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为thisObj指定的新对象,如果没有提供thisObj参数,那么Global对象被用于thisObj。

    call():

    call方法既可以调用函数,又可以改变函数内的this指向。

    var obj = {
    	name: 'andy'
    }
    function fn(a, b) {
    console.log(this);
    console.log(a+b)
    };
    fn(1,2)// 此时的this指向的是window 运行结果为3
    fn.call(obj,1,2)//此时的this指向的是对象obj,参数使用逗号隔开,运行结果为3
    

    应用场景: 经常做继承,参考下面这篇讲原型链的末尾部分:https://blog.csdn.net/caipital/article/details/108396438

    function f1(a,b){
        console.log(this)  //输出f2
        console.log(a+b)	//输出3
    }
    function f2 (a,b) {
        console.log(this)
        console.log(a-b)
    }
    f1.call(f2,1,2)		//打印输出f2 和 3
    

    利用call()判断数据类型(在判断数据类形式使用typeof,一般不是太准确的,我们可以使用Object.prototype.toString.call()方法来判断一个数据的数据类型

    console.log(Object.prototype.toString.call("sun"))            // [Object String] 返回值都是字符串类型
    console.log(Object.prototype.toString.call(18))              // [object Number]
    console.log(Object.prototype.toString.call(false))           // [object Boolean]
    console.log(Object.prototype.toString.call(undefined))       // [object Undefined]
    console.log(Object.prototype.toString.call(null))            // [object Null]
    console.log(Object.prototype.toString.call(function(){}))    // [object Function]
    console.log(Object.prototype.toString.call([]))              // [object Array]
    console.log(Object.prototype.toString.call({}))              // [object Object]
    
    // 封装
    function getType(para){
       var obj = Object.prototype.toString.call(para); //区分对象类型  确定当前的数据的类型
       var sub = obj.substr(8); 
       // stringObject.substr(start,length)  start 要抽取的子符串的起始下标,
       // length 截取的长度,如果不写则表示从start开始截取到最后 ,stringObject表示某一字符串
      var len = sub.length;
      var sub = sub.substr(0,len-1)
      var change =  sub.toLowerCase(sub) //转换成小写
      return change ;
    }
     console.log(getType("sun")); //string
    

    翻转字符串

    //思路:将字符串转化为数组,借用数组中的reverse,将字符串翻转过来
     var str = "abcdefg";
     console.log(Array.prototype.reverse.call(str)); //此时会报错误,即引用类型错误,就是说只有数组才能使用reverse这个方法;(错误写法)
        //方法一:这种方法内有使用call()
     var arr =  Array.from(str).reverse().join("") //将字符串转化为数组,在进行翻转,然后在进行拼接
     console.log(arr) //gfedcba
     console.log(typeof arr) //string
         //方法二:
    var rs = Array.prototype.reverse.call(str.split("")).join(""); 
        //splice(start,length)方法用于把一个字符串分割成字符串数组,start 表示从指定的地方分割字符串    length表示分割的长度。
        //返回一个一个字符串数组 如果把空字符串 ("") 用为参数那么字符串中的每个字符之间都会被分割
    console.log(rs); //gfedcba
    console.log(typeof arr) //string
    

    apply()

    call方法既可以调用函数,又可以改变函数内的this指向。与call()的区别是函数调用时参数是数组。

    function fn(a,b){
        console.log(this)  
        console.log(a+b)
    }
    let obj = {
        name:'zy'
    }
    fn.apply(obj,[1,2]) //此时的this指向的是对象obj,参数传入了一个数组,运行结果为3
    

    应用场景:

    1. 数组运用到Math的API中,如:Math.max,Math.min等,
    2. Array.prototype.push 可以实现两个数组合并等
      Math的API:
    let a = Math.max(9,21,33);
    console.log(a)   //33
    
    let arr = [9,21,33];
    let res1 = Math.max(...arr)  
    let res2 = Math.max.apply(null,arr); 
    let res3 = Math.max.apply(Math,arr);   // 推荐  
    let res4 = Math.max.apply(Function,arr);  
    
    console.log(res1)    //33
    console.log(res2)    //33
    console.log(res3)    //33
    console.log(res4)    //33
    

    数组合并:

    vararr1=new Array("1","2","3");  
      
    vararr2=new Array("4","5","6");  
      
    Array.prototype.push.apply(arr1,arr2);  
    

    bind()

    bind()改变函数this指向,但不调用函数,会生成一个新的函数

    function fn(a,b){
         console.log(this)  
         console.log(a+b)
     }
     let obj = {
         name:'zy'
     }
     let new_fn1 = fn.bind(obj)
     new_fn1(2,3)    //this指向obj 打印输出5
     let new_fn2 = fn.bind(obj,1,2)
     new_fn2()   //this指向obj 打印输出3
    

    应用场景:当你想改变函数的this指向同时又不想立即执行函数的时候。
    例子:要求:给三个按钮绑定点击事件,点击之后按钮变成不可点的状态,1秒之后恢复,变成可点击的状态。
    方案一 :let 块级作用域

    for(let i =0;i<btns.length;i++) {
        btns[i].onclick = function() {
            //表达式函数this指向绑定事件的对象
            this.disabled = true;
            setTimeout(function(){
                btns[i].disabled = false;
            },1000)
        }
    }
    

    方案二:使用自调用函数(闭包思想)
    闭包(closure):指有权访问另一个函数作用域中变量的函数。

    for (var i = 0; i < btns.length; i++) {
        (function (i) {
            btns[i].onclick = function () {
                //表达式函数this指向绑定事件的对象
                this.disabled = true;
                setTimeout(function () {
                    console.log(this)   //Window
                    btns[i].disabled = false;
                }, 1000)
            }
        })(i)
    }
    

    方案三: var that = this,储存this(闭包)

    for(var i =0;i<btns.length;i++) {
        btns[i].onclick = function() {
            //表达式函数this指向绑定事件的对象
            this.disabled = true;
            var that = this;
            setTimeout(function(){
                that.disabled = false;
            },1000)
        }
    }
    

    方案四:使用 bind()改变this指向

    for(var i =0;i<btns.length;i++) {
        btns[i].onclick = function() {
            //表达式函数this指向绑定事件的对象
            this.disabled = true;
            setTimeout(function(){
                this.disabled = false;
            }.bind(this),1000)
        }
    }
    

    参考地址:
    https://www.runoob.com/w3cnote/js-call-apply-bind.html
    https://www.cnblogs.com/phoebeyue/p/9216514.html
    https://blog.csdn.net/caipital/article/details/108437024

    砥砺前行
  • 相关阅读:
    IDEA常用快捷指令整理
    Python dict 字典
    内联函数
    【MFC】编辑框 CEdit Ctrl控件自动换行设置
    mysql 多sql文件恢复方案
    Linux: 用64位的系统,能编译32位的程序吗?
    C++ 遍历数组
    工业现场传感器传感器为什么采用电流形式输出?
    【转】电磁阀、电磁铁的工作原理说明
    PCB板强弱电隔离距离不够导致损坏和问题检查记录
  • 原文地址:https://www.cnblogs.com/lhongsen/p/14849053.html
Copyright © 2020-2023  润新知