js高级之函数的三种调用方式(this三种指向)/(函数的上下文调用)

120 阅读2分钟

this三种指向

this 环境对象: 谁调用我,我就指向谁

  • 普通函数:函数名() this指向window

  • 构造函数: new 函数名() this指向new创建的实例对象

  • 对象方法: 对象名.方法名() this指向对象

小技巧 * 调用的时候没有点没有new就是window,有new就是实例,有点就是左边的对象

调用时如果内部还有其他函数调用,看作用域.

示例如下:

  function fn() {
            console.log(this)
        }
        // 普通函数
        fn() //window
        // 构造函数 
        new fn() // new创建的实例对象

        let obj = {
            name: '张三',
            age: fn
        }

        //对象方法:obj对象

        obj.age() // obj


        let obj1 = {
            name1: '李四',
            eat: function () {
                //1级链
                console.log(this)

                function fn1() {
                    //2级链
                    console.log(this) //window
                }
                fn1()
            }
        }

        let eat = obj1.eat
        obj1.eat() // obj  window

        eat() //window window

        new eat() // eat{}  window
        

这个时候我们就会发现他们有共同的特点: this的指向无法动态修改

示例如下:

         function fn(){
            //三种执行模式this无法动态修改
           this = {age:18};
           console.log(this);
          }
        fn()//会报错

如果想要修改this我们也有三种方式

上下文调用

函数上下文模式 作用: 可以动态修改函数中的this

示例代码如下:

 /* 上下文模式 */
        function fn(a, b) {
            console.log(this);
            console.log(a + b);
        };

        //a. 函数名.call(修改的this,arg1,arg2…………) 
        //应用场景: 适用于函数原本形参 <= 1
        fn(10, 20); //this:window
        fn.call({
            age: 18
        }, 100, 200); //this:object

        //b. 函数名.apply(修改的this,[数组或伪数组])
        //应用场景: 适用于函数原本形参 >=2
        fn.apply({
            age: 88
        }, [20, 30]); //this:object

        //c. 函数名.bind(修改的this,arg1,arg2…………)
        //特点:这个函数不会立即执行,而是返回一个修改this之后的新函数
        //应用场景 : 事件处理函数,定时器
        let newFn = fn.bind({
            name: '小张'
        });
        newFn(50, 60); //this:object

函数名.call()

应用:万能检测数据类型

语法 : Object.prototype.toString.call(数据)

示例代码如下:

<script>
      /* 
        1. typeof检测数据类型:  typeof 数据
            * 有两种数据类型无法检测,得到'object' : 数组 和 null
        
        2.万能检测数据类型方法
            Object.prototype.toString.call(数据)
                * 得到一个固定格式字符串: [object 数据类型]
        */

      let num = 10
      let str = 'abc'
      let bol = true
      let und = undefined
      let nul = null
      let arr = [10, 20, 30]
      let fn = function () {}
      let obj = { name: '张三' }
      
    //   console.log(typeof  num)//'number'
    //   console.log(typeof  str)//'string'
    //   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'

      console.log( Object.prototype.toString.call(num) )//['object Number']
      console.log( Object.prototype.toString.call(str) )//['object String']
      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']

   
    </script>

函数名.apply()

应用 : 伪数组转成真数组

示例代码如下:

  <script>
        /* 
        伪数组 : 拥有数组三要素,但是没有数组的api
            伪数组本质是一个对象
         */

        let obj = {
            0: 'a',
            1: 'b',
            2: 'c',
            3: 'd',
            length: 4
        };
        console.log(obj)
        console.log(obj[0])

        //需求:伪数组转成真数组 (希望伪数组也可以调用数组的api)

        //以前方式:通过apply
        let arr = []
        arr.push.apply(arr, obj)
        console.log(arr)

    /* 1.slice 可以查询数组,默认情况下不传参这个方法会得到一个空数组
       2.但是伪数组由于原型不是Array,所以无法调用slice
       3.slice方法存储在哪里,Array.prototype */
        obj = Array.prototype.slice.call(obj)
        console.log(obj)

        //最新方式: Array.from(伪数组)  重点掌握
        let arr1 = Array.from(obj)
        console.log(arr1)
    </script>

应用 :求数组最大值

示例代码如下:

 <script>
        // 需求:求数组最大值
        let arr = [100, 20, 66, 88, 90]

        //1.擂台思想/js基础
        let max = arr[0]
        for (let i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i]
            }
        }
        console.log(max)

        //2.Math.max(数字1,数字2,数字3,…………)
        // let max = Math.max(arr[0],arr[1],arr[2],arr[3],arr[4]);
        //这里没有必要修改this,借助apply可以自动遍历数组传参
        let max1 = Math.max.apply(Math, arr)
        console.log(max1)

        //3.ES6最新方式 和apply类似,也会遍历数组,然后逐一传参
        let max2 = Math.max(...arr)
        console.log(max2)

        
    </script>

函数名.bind()

特点:这个函数不会立即执行,而是返回一个修改this之后的新函数

应用场景 : 事件处理函数,定时器

示例代码如下:

  // 定时器:一段代码间隔事件执行 setTimeout(一段代码,间隔时间)

        //1 具名函数
        let test = function () {
            console.log('我是具名函数');
            console.log(this);
        };

        let newTest = test.bind({
            name: '张三'
        })

        setTimeout(newTest, 3000);

        //2 匿名函数
        setTimeout(function () {
            console.log('我是定时器中的函数');
            console.log(this);
        }.bind({
            name: '李四'
        }), 2000);

call apply 和bind三者的区别

相同点: 作用一致,修改函数this的指向

不同点:

  • 传参方式不同:call是按照顺序传参,apply是数组/伪数组传参

  • 执行机制不同:call和apply会立即执行函数,bind得到修改this的新函数之后