call() 和 apply(),bind() 的区别

115 阅读3分钟

1. call()

// 函数上下文调用 : 可以修改this指向
     // 函数名.call(修改的this指向,参数1,参数2…………)
function fn(a, b){
    console.log( a + b ) // 30 
    console.log( this ) // {name:'张三'}
}
    // 函数名.call(this指向,参数1,参数2,………………)
fn.call({name:'张三'}, 10, 20)

call() 的应用场景---万能数据类型检测

   /* 
    1. typeof  数据 : 有两种数据类型无法检测
            null和数组,得到的都是object
    2. 万能数据类型 :  Object.prototype.toString.call(数据)
   */
   
        // (1) 值类型
        const str = 'abc'
        const num = 10
        const bol = true
        const und = undefined
        const nul = null
        // (2) 引用类型
        const arr = [10, 20, 30]
        const fn = function() {}
        const obj = {name: '张三'}

        console.log( typeof str ) // 'string'
        console.log( typeof num ) // 'number'
        console.log( typeof bol ) // 'boolean'
        console.log( typeof und ) // 'undefined'
        console.log( typeof nul ) // 'object'
        console.log( typeof arr ) // 'object'
        console.log( typeof fn )  // 'function'
        console.log( typeof obj ) // 'object'

        /* 
        (1) Object.prototype有一个方法toString(), 会得到固定格式字符串 '[object 数据类型]'
        (2) arr.toString() 不会调用Object原型中的toString, 因为Array的原型也有toString
        (3) 结论:  Object.prototype.toString.call() 修改里面的this,让这个方法知道你要检测谁的类型
         */

       console.log(  Object.prototype.toString.call( str ) ) // '[object String]'
       console.log(  Object.prototype.toString.call( num ) ) // '[object Number]'
       console.log(  Object.prototype.toString.call( bol ) ) // '[object Boolean]'
       console.log(  Object.prototype.toString.call( und ) ) // '[object Undefined]'
       console.log(  Object.prototype.toString.call( nul ) ) // '[object Null]'
       console.log(  Object.prototype.toString.call( arr ) ) // '[object Array]'
       console.log(  Object.prototype.toString.call( fn ) )  // '[object Function]'
       console.log(  Object.prototype.toString.call( obj ) ) // '[object Object]'

2. apply()

//  函数上下文调用 : 可以修改this指向
    // 函数名.apply(修改的this, 数组/伪数组 )
function fn(a, b){
    console.log( a + b ) // 30
    console.log( this ) // {name: '李四')
}
    // 函数名.apply( 修改的this, 数组/伪数组 )
fn.apply({name: '李四'}, [10, 20]) 

apply() 的应用场景

(1) 伪数组转数组

  /* 
    1.伪数组 : 有数组三要素(下标、元素、长度), 没有数组的方法
       * 伪数组本质是对象
  */ 
  const obj = {
        0: 20,
        1: 55,
        2: 60,
        3: 80,
        length: 4
  }
    // ES5方式
    const arr = []
    // arr.push(obj[0], obj[1], obj[2], obj[3])
    // 这里只是借助 apply传参的特点,不需要修改this
    // 因为这行代码 arr.push()原本的this就是arr, 所以apply第一个参数也传arr
    arr.push.apply( arr, obj )
    console.log(arr) // [20, 55, 60, 80]
    
    // ES6方式
    // 静态方法  Array.from( 伪数组 )
    const newArr = Array.from( obj )
    console.log( newArr ) // [20, 55, 60, 80]
  

(2) 求数组最大值

 const arr = [20, 50, 66, 100, 88]
        // (1) 声明变量存储最大值
        let max = arr[0]
        // (2) 遍历数组
        for (let i = 1; i < arr.length; i++) {
            // (3) 依次比较大小
            if( arr[i] > max ){
                max = arr[i]
            }
        }
        console.log( max ) // 100

        // 2. Math.max.apply(Math,数组)

        const max1 = Math.max.apply(Math, arr)
        console.log(max1) // 100

        // 3. ES6
        // ... 展开运算符,相当于自动遍历数组每一个元素。 在这里类似于apply功能
        const max2 = Math.max(...arr)
        console.log(max2) // 100

3. bind()

// 函数上下文调用 : 可以修改this指向
       // 函数名.bind(修改的this)
           //  * bind()不会立即执行函数,而是返回一个修改this之后的新函数
        function fn(a,b){
            console.log( a + b ) // 110
            console.log( this ) // {name: '王五'}
        }
        // 函数名.bind(修改的this)
        // bind()不会立即执行函数,而是得到一个修改this之后的新函数
        // (1)定时器  (2)事件处理函数
        let newFn = fn.bind({name: '王五'})

        newFn(50, 60)

bind() 的应用场景---修改定时器的this

  /* 
   1. 定时器函数中的this 默认一定是window
   2. 要想修改定时器函数中的this,需要用bind
  */

   const fn = function () {
        console.log(this)
   }

   const newFn = fn.bind({name: '王五'})
   // newFn : 取值(函数地址)   newFn() : 调用函数得到返回值
   setTimeout(newFn , 2000) // {name: '王五'}

   setTimeout(function() {
        console.log(this)
   }.bind({name: '张三'}), 3000) // {name: '张三'}

总结

相同点 :

  1. 作用一致, 都是用来改变函数的this指向
  2. 第一个参数都是this要指向的对象
  3. 都可以利用后续参数传参 不同点 :
  4. 传参方式不同 : call是单个传参,apply是数组/伪数组传参
  5. 执行机制不同 : callapply会立即执行, bind不会立即执行而是得到修改this的新函数