JavaScript中this的三种指向

216 阅读2分钟

一.this概述

Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象。

其中this就是实现面向对象的一个非常重要的特性,但是 this在Javascript中非常容易被理解错,尤其是对于接触静态语言比较久的同学来说。而且 this又是面试中和实际项目中的重中之重,所以今天写一篇文章,加强记忆,温故而知新。

二.this三种指向

this的指向是可以修改的,接下来一一介绍

    **环境对象 this : 谁'调用'我,我就指向谁 
        普通函数;  函数名()          this指向window
        对象方法:   对象名.方法名()   this指向对象
        构造函数;  new 函数名()      this指向new创建的实例对象
            * 小技巧: 没点没newwindow, 有new是实例,有点是点左边的对象**

注意:this在箭头函数中失效了,因为箭头函数没有单独的 this值。箭头函数的 this与声明所在的上下文相同。也就是说调用箭头函数的时候,不会隐式的调用 this参数,而是从定义的函数上来继承上下文。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        /* 
        环境对象 this : 谁'调用'我,我就指向谁 
            普通函数;  函数名()          this指向window
            对象方法:   对象名.方法名()   this指向对象
            构造函数;  new 函数名()      this指向new创建的实例对象
                * 小技巧: 没点没new是window, 有new是实例,有点是点左边的对象
        */

        /* function fn(){
            console.log( this )
        }

        //普通函数 : window
        fn()
        //构造函数 : new创建实例对象
        new fn()

        let obj = {
            name:'张三',
            eat:fn
        }
        //对象方法 : obj对象
        obj.eat() */


        /* 
        1.函数this指向:谁'调用'我,我就指向谁
         1.1普通函数:函数名()   this-->window
         1.2构造函数: new 函数名()  this-->new创建的实例
         1.3对象方法: 对象名.方法名()  this-->对象 */
         /* let fn = function(){
             console.log(this)
         }
        //  1.普通函数
         fn()
        //  2.构造函数
         new fn()
        //  3.对象方法
         let obj = {
             name:'张三',
             eat:fn
         }
         obj.eat() */

         let person = {
             name:'李四',
             eat:function(){
                 console.log(this)
             },
             learn:function(){
                 let eat = this.eat
                 eat()
             }
         }
         person.eat()
         person.learn()
    </script>
</body>
</html>

三.this指向测试题

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <script>
        /* 
        环境对象 this : 谁'调用'我,我就指向谁 
            普通函数;  函数名()          this指向window
            对象方法:   对象名.方法名()   this指向对象
            构造函数;  new 函数名()      this指向new创建的实例对象
                * 小技巧: 没点没new是window, 有new是实例,有点是点左边的对象
        */

      //作用域链
      let obj = {
        name: "张三",
        eat: function() {
          //1级链
          console.log(this) //1.obj
          function fn() {
            //2级链
            console.log(this) //2.window
          }
          fn()
        }
      }

      let eat = obj.eat
      obj.eat()
    </script>
  </body>
</html>

四.call方法修改this指向

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        /* 
        1.环境对象 this : 谁'调用'我,我就指向谁 
            普通函数;  函数名()          this指向window
            对象方法:   对象名.方法名()   this指向对象
            构造函数;  new 函数名()      this指向new创建的实例对象

        *** 默认情况下,函数内部的this不能主动修改. 如果需要修改,则需要使用上下文方式

        2.上下文调用 : 修改函数内部的this
            2.1 函数名.call(修改后的this,形参1,形参2…………)
            2.2 函数名.apply()
            2.3 函数名.bind()      
        */
        /* function fn(a,b){
            console.log( a + b )
            console.log( this )
        }

        // 函数名.call(修改后的this)
        fn.call({name:'张三'},10,20) */
        
        1.函数this指向:谁'调用'我,我就指向谁
        1.1 普通函数:函数名()  this-->window
        1.2 构造函数:new 函数名()  this-->new创建的实例
        1.3 对象方法:对象名.方法名()  this-->对象
        2.默认情况下,函数内部的this是固定的,无法动态修改,
        如果想要动态修改函数this指向,需要使用函数上下文调用
          函数上下文:函数作用域
        3.函数上下文调用:动态修改this指向
           函数名.call(修改的this,参数1,参数2,....)
           函数名.apply()
           函数名.bind()       

        function fn(a,b){
            console.log(this)
            console.log(a+b)
        }
        // 函数名.call(修改后的this)
        fn.call({name:'张三'},20,40)
    </script>
</body>
</html>

五.apply方法修改this指向

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        /* 
        1.环境对象 this : 谁'调用'我,我就指向谁 
            普通函数;  函数名()          this指向window
            对象方法:   对象名.方法名()   this指向对象
            构造函数;  new 函数名()      this指向new创建的实例对象

        2.上下文调用 : 修改函数内部的this
            2.1 函数名.call(修改后的this,形参1,形参2…………)
            2.2 函数名.apply(修改后的this, 数组或伪数组 )
            2.3 函数名.bind()

        
        */

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

        // 函数名.call(修改后的this,形参1,形参2…………)
        fn.call({name:'张三'},10,20)
        
        // 函数名.apply(修改后的this, 数组或伪数组 )
        // apply会自动帮你遍历数组,然后按照顺序逐一传参
        fn.apply({name:'李四'}, [30,40] ) */


        函数名,apply(修改后的this,数组或伪数组)
        apply会自动帮你遍历数组,然后按照顺序逐一传参 */
        function fn(a, b,c) {
            console.log(this)
            console.log(a + b)
        }
        fn.apply({ name: '李到' }, [44, 555])
    </script>
</body>

</html>

六.bind方法修改this指向

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        /* 
        1.环境对象 this : 谁'调用'我,我就指向谁 
            普通函数;  函数名()          this指向window
            对象方法:   对象名.方法名()   this指向对象
            构造函数;  new 函数名()      this指向new创建的实例对象

        2.上下文调用 : 修改函数内部的this
            2.1 函数名.call(修改后的this,形参1,形参2…………)
            2.2 函数名.apply(修改后的this, 数组或伪数组 )
            2.3 函数名.bind(修改后的this)
                * 不会立即执行函数,而是得到一个修改this之后的新函数。
                * bind一般用于修改: 定时器函数、事件处理函数
        
        */

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

        // 函数名.call(修改后的this,形参1,形参2…………)
        fn.call({name:'张三'},10,20)
        
        // 函数名.apply(修改后的this, 数组或伪数组 )
        // apply会自动帮你遍历数组,然后按照顺序逐一传参
        fn.apply({name:'李四'}, [30,40] )

        //函数名.bind(修改后的this)
        // bind不会立即执行函数,而是返回一个修改this之后的新函数
        let newFn = fn.bind({name:'王五'})
        newFn(100,200)
        newFn(10,20)


    </script>
</body>
</html>

七.经典面试题:call、apply、bind区别

        面试必问:  call 和 apply 和 bind 三者区别
            相同点 : 作用一致,修改函数this指向
            不同点 :  
             传参方式不同 : call是按照顺序传参, apply是数组/伪数组传参
             执行机制不同 : call和apply会立即执行函数,
             而bind不会立即执行而是得到修改this的新函数