最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 全面解析JavaScript中的call、apply、bind、new

    正文概述 掘金(谷底飞龙)   2021-03-23   477

    对JavaScript中的this进行的系列解读:

    • 全面解析JavaScript中this绑定
    • 全面解析JavaScript中的call、apply、bind、new

    call()和apply()

    call()apply()放在一起,是因为这两个方法非常接近,区别在于调用的时候参数形式不同。

    call和apply的原理及区别

    直接看代码直观点,定义一个函数person,分别用call()apply() 调用,我们来看下传递的参数的区别:

    var func = function(name, age) {
         console.log(`my name is ${name}, my age is ${age}`)
    };
    
    func('谷底飞龙', 28); // 直接调用方法
    func.call(this, '谷底飞龙', 28); // call执行方法,参数是多个对象
    func.apply(this, ['谷底飞龙', 28]) // apply执行方法,参数是多个对象组成的数组
    

    使用上述三种方式执行方法,都能打印出my name is 谷底飞龙, my age is 28

    call和apply使用方法及场景

    从上面小结中,我们知道call和apply主要有两方面的特性,使用场景就是基于这两方面可以分为两类:

    1.改变this指向

    从上面小结中,我们知道call和apply的第一个特性通过第一个参数来改变函数内部的this指向,利用这一特性的使用场景比较多。

    1.1 解决隐式绑定丢失问题

    我们来把this绑定之箭头函数绑定中的案例回顾下:

    var name = "天下无敌";
    var Person = {
      name : "谷底飞龙",
      getName: function () {
        console.log(`my name is ${this.name}`)
      },
      consoleName: function () {
        // 回调函数不使用箭头函数,属于隐式绑定中的隐式丢失的情况,this绑定的是全局对象window
        setTimeout(function (){
            this.getName()
        },100);
      }
    };
    
    // 执行函数
    Person.consoleName()  
    

    这种情况下,执行方法Person.consoleName()会报错Uncaught TypeError: this.getName is not a function,除了把setTimeout()的回调函数改用箭头函数外,还可以使用call和apply来改变this指向的方式解决。如下

    var name = "天下无敌";
    var Person = {
      name : "谷底飞龙",
      getName: function () {
        console.log(`my name is ${this.name}`)
      },
      consoleName: function () {
        // 回调函数不使用箭头函数,使用call/apply硬绑定到Person对象
        setTimeout(function (){
            this.getName()
        }.call(Person),100);
      }
    };
    // 执行函数
    Person.consoleName()  
    

    通过setTimeout()调用call(),将回调函数中的this硬绑定到对象Person,执行函数后,就能打印出my name is 谷底飞龙。这里把call换成bind也可以,具体详见本文后面对bind的介绍。

    1.2 合并两个数组
    const arr1 = [1,2,3];
    const arr2 = [4,5,6];
    // 使用apply()合并数组,合并后的 arr1 为 [1、2、3、4、5、6]
    Array.prototype.push.apply(arr1,arr2)
    

    使用Array.prototype.push.apply(arr1,arr2)时,apply()将第一个参数arr1硬绑定到Array内部的this,因此这里等价于arr1.push(arr2)

    • 注:由于浏览器对函数的参数个数限制(JS核心限制在 65535),如果数组太长,不建议使用apply的方式,在不同浏览器可以会出现数据丢失或者报错。可以将参数数组分成多组,循环遍历执行push.apply()来解决问题(但是遍历会损耗性能,不建议使用)
    1.3 类型判断

    咱们先来看看不同类型打印出来的日志

    toString = Object.prototype.toString;
     
    console.log(toString.call(['谷底飞龙'])); //[object Array]
    console.log(toString.call('谷底飞龙')); //[object String]
    console.log(toString.call({name: '谷底飞龙'})); //[object Object]
    console.log(toString.call(/谷底飞龙/)); //[object RegExp]
    console.log(toString.call(123)); //[object Number]
    console.log(toString.call(undefined)); //[object Undefined]
    console.log(toString.call(null)); //[object Null]
    

    通过Object.prototype.toString.call(obj),输出的是[object xxx]字符串,可以利用这一特点进行类型的精确判断,这里的call的作用就是将toString()内部的this指向obj

    我们先来运行下代码:

    console.log(['谷底飞龙','天下无敌'].toString()); //谷底飞龙、天下无敌
    console.log('谷底飞龙'.toString()); //谷底飞龙
    

    直接调用obj.toString()返回的是obj对应的字符串,而不是对象类型。因为toString()是Object的实例方法,而obj(如Array、Function等类型)是Object的实例,实例都对toString()进行了重写(注:重写后,Functionl类型返回的是字符串,Array类型返回的是数组元素组成的字符串),根据原型链知识,调用obj.toString(),执行的是重写后的方法,只能获取到对应的字符串,不能获取到对象类型。因此,应该通过原型方法Object.prototype.toString去判断对象类型。

    • 判断是否是数组,可以这样写:
    function isArray(obj) {
       return Object.prototype.toString.call(obj) === '[object Array]';
    }
    

    2.解构参数数组

    利用第二个特性调用apply()时,会将剩余参数数组解构成一系列对象,我们可以利用这一特性对数组进行一些操作。

    2.1 获取数组中的最大值和最小值
    const arr = [88,78,128,89,45]
    // 使用apply解构数组参数
    Math.max.apply(null,arr)
    
    // ES6中解构方法
    Math.max(...arr)
    // ES6中使用call
    Math.max.call(null,...arr)
    
    • Math.maxMath.min本身是不能对数组直接计算的,可以把数组解构成一系列对象再操作。apply()对第二个参数arr的解构,类似ES6中解构方法...arr,可以把数组解构成一系列对象。
    • 由于这里的this的绑定没影响,所以apply()call()的第一个参数可以随便设置,这里设置为null

    call和apply的模拟实现

    bind()

    bind的原理及与call/apply的区别

    我们来直接看个案例会更直观点

    var name = '天下无敌'
    var Person = {
      name: '谷底飞龙',
    }
    var getName = function () {
      console.log(`my name is ${this.name}`)
    }
    // 使用bind硬绑定到Person对象
    const consoleName = getName.bind(Person)
    // 执行函数
    consoleName()
    

    通过bind将方法getName内部的this指向Person对象,并返回一个新函数consoleName,执行新函数consoleName()后,会打印出Person对象内部的name,也就是my name is 谷底飞龙

    • 1、如果不调用bind,直接执行getName(),打印的将是全局window的name,也就是my name is 天下无敌
    • 2、如果将这里的bind改成call/apply,由于call/apply只执行函数,不会返回新函数,执行函数就会报错Uncaught TypeError: consoleName is not a function

    bind使用方法及场景

    bind的模拟实现

    new()

    new()的原理与分析

    function Person(name){
      // 属性
      this.name = name;
    }
    // person的方法
    Person.prototype.getAge = function() {
      return 18;
    }
    // 通过new调用函数创建对象实例
    var P = new Person('谷底飞龙')
    console.log(`my name is ${P.name}, my age is ${P.getAge()}`)
    

    打印出my name is 谷底飞龙, my age is 18,我们可以得出new调用函数创建的实例对象有两个特性:

    new()的模拟实现


    参考文档:

    • 深度解析 call 和 apply 原理、使用场景及实现
    • 一次性搞懂 this、call、apply 、 bind
    • 用Object.prototype.toString.call(obj)检测对象类型原因分析

    结语

    如果你也是一个对投资理财感兴趣的程序员,欢迎关注我的公众号「谷底飞龙」,一起成为技术界的投资大佬吧。


    起源地下载网 » 全面解析JavaScript中的call、apply、bind、new

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元