JS函数 (this介绍)及 改变this的指向( call apply bind 三者的区别)

204 阅读4分钟

1、环境对象this简述

环境对象 this : 谁 ‘调用’ this,this就指向谁 相当于中文中的 '我' , 谁说出来这个字,这个字就代表谁

2、this指向

  • (1)普通函数: 函数名() this->window
  • (2)构造函数: new 函数名() this->new创建的实例对象
  • (3)对象方法: 对象名.方法名() this->对象

3、call apply bind 三者的区别

(1) call

语法: 函数名.call(修改的this,参数1,参数2………)

        function fn( a , b ) {
            console.log(this)
            console.log(a + b)
        }

        // 函数名.call(修改的this,参数1,参数2………)
        fn.call( { name: '张三' } , 10 , 20 )
  • 应用场景1:万能数据类型检测
  • typeof 数据 : 检测数据类型,但是有两种数据类型无法检测typeof无法检测 数组 与 null , 得到的都是object
        let str = 'abc'
        let num = 123
        let bol = true
        let und = undefined
        let nul = null

        //引用类型
        let arr = [10,20,30]
        let fn = function(){}
        let obj = {name:'ikun'}

        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'

因此,call就派上用场了~~~~~~~~

  • 万能数据类型检测: Object.prototype.toString.call( 数据 )
  • (1)Object.prototype.toString() 内部会返回this的数据类型, 得到固定格式字符串[object 数据类型]
  • (2)使用Object原型中的toString()要想得到数据类型,只需要把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]'

image.png

  • 可以改变函数内部的this指向
  • 会调用函数, 并且改变函数内部this指向
  • call是单个数据传参

(2) apply

//声明函数
        function fn(a, b) {
            console.log(this)
            console.log(a + b)
        }

        //(1) 函数名.call(修改的this,参数1,参数2………)
        fn.call({ name: '张三' }, 10, 20)

        //(2) 函数名.apply(修改的this, 数组/伪数组 )
        // apply会自动遍历数组和伪数组,然后逐一传参
        fn.apply({ name: "李四" }, [50, 60] )
  • 可以改变函数内部的this指向
  • 会调用函数, 并且改变函数内部this指向
  • apply是以数组类型进行传参
  • 应用场景1:伪数组转真数组
//需求: 有时候伪数组 想要使用 真数组的方法 , 就需要把伪数组转成真数组

        //(1)把伪数组的元素取出来, push到真数组中
        let arr = []
        // arr.push(obj[0], obj[1], obj[2])
        // console.log(arr)

        //(2)手动写循环遍历添加
        // for (let i = 0; i < obj.length; i++) {
        //     arr.push(obj[i])
        // }

        //(3) arr.push.apply( arr,伪数组 )
        //这里使用apply不是为了修改this,而是借助传参特点:自动遍历伪数组/数组传参. 所以第一个参数应该写arr(保持this不变)
        arr.push.apply(arr,obj)
        console.log( arr )

        //ES6 : 伪数组转真数组,固定静态方法   Array.from( 伪数组 )
        let newArr = Array.from( obj )
        console.log( newArr )

image.png

  • 应用场景2:求数组最大值
        let arr = [20, 55, 60, 80, 100, 30, 40]
        //(1)js基础 : 擂台思想

        let max = arr[0]
        for (let i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i]
            }
        }
        console.log(max)

        //(2)js高级 : Math.max()

        // let max1 = Math.max(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6])
        let max1 = Math.max.apply(Math, arr)
        console.log(max1)

        //ES6 : Math.max(...arr)
        // ...功能类似于apply,也会自动的把数组给遍历
        let max2 =  Math.max(...arr)
        console.log(max2)

image.png

(3) bind

//声明函数
    function fn(a, b) {
      console.log(this)
      console.log(a + b)
    }

    //(1) 函数名.call(修改的this,参数1,参数2………)
    fn.call({ name: '张三' }, 10, 20)

    //(2) 函数名.apply(修改的this, 数组/伪数组 )
    // apply会自动遍历数组和伪数组,然后逐一传参
    fn.apply({ name: "李四" }, [50, 60])

    //(3) 函数名.bind(修改的this)
    // bind不会立即执行函数,而是得到一个修改this之后的新函数
    let newFn = fn.bind({name:'王五'})
    newFn(22,33)
  • 应用场景1:修改定时器的this
//定时器中的this默认指向window,如果修改定时器的this,就需要使用bind

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

    let newFn = fn.bind({name:'111'})

    setTimeout( newFn  , 2000)

    //上面步骤可以简写一行
    setTimeout( function(){
      console.log(this)
    }.bind({name:'李四'}) , 2000 )

image.png

  • 可以改变函数内部的this指向
  • 不调用函数, 并且改变函数内部this指向

三者的区别(面试题)

  • 相同点 : (1)都可以修改this指向
  • 不同点 : (1)传参方式不同 : call是单个传参, apply是数组/伪数组传参 (2)执行机制不同 : call和apply会立即执行函数, bind不会立即执行而是得到修改this的新函数

4.箭头函数this指向

1.箭头函数this :

  • 箭头函数没有this
  • 箭头函数中使用this, 本质是通过作用域链找上一级作用域的this

2.拓展:箭头函数没有this,对箭头函数影响

  • (1)箭头函数不能作为构造函数
  • (2)箭头函数不能修改this
  • (3)事件处理函数一般不用箭头函数
        let fn = ()=>{
            //1级
            console.log(this)
        }

        fn()//window

  • 箭头函数经典面试题奉上

    let obj = {
      name: 'ikun',
      eat() {
        //1级  this : function函数指向对象obj
        function fn1() {
          //function函数this取决于调用   函数名()  this->window
          console.log(this)//window  
        }
        fn1()

        let fn2 = () => {
          //2级
          //this:箭头函数,访问上级1级this
          console.log(this)//obj 
        }
        fn2()
      },
      learn: () => {
        //1级 this箭头函数,访问上级 window
        function fn1() {
          //function函数this取决于调用   函数名()  this->window
          console.log(this)//window
        }
        fn1()

        let fn2 = () => {
          //2级  this箭头,访问上级1级的this -> window
          console.log(this)//window  
        }
        fn2()
      }
    } 

    obj.eat()
    obj.learn()

后记

如果发现内有语句或逻辑混乱的地方,还请各路大神指正。

image.png