[TOC]

了解函数中 this 在不同场景下的默认值,知道动态指定函数 this 值的方法。

  1. 事件处理函数中,this表示事件源(事件给谁添加的,this就表示谁)
  2. 构造函数中或者类中this、对象的方法中,this表示实例对象(静态方法中的this表示类或者构造函数)
  3. 普通函数中、全局中、回调函数中,this表示window对象
  4. 箭头函数中没有 this(如果出现this,则按照作用域链去查找) ```javascript

    <a name="U8idG"></a>
    ## 默认值
    `this` 是 JavaScript 最具“魅惑”的知识点,不同的应用场合 `this` 的取值可能会有意想不到的结果,在此我们对以往学习过的关于【 `this` 默认的取值】情况进行归纳和总结。
    <a name="ll061"></a>
    ### 普通函数
    **普通函数**的调用方式决定了 `this` 的值,即【谁调用 `this` 的值指向谁】,如下代码所示:
    ```javascript
    <script>
      // 普通函数
      function sayHi() {
        console.log(this);
      }
      // 函数表达式
      let sayHello = function () {
        console.log(this);
      }
    
      // 函数的调用方式决定了 this 的值
      sayHi(); // window
      window.sayHi();
    
         // 普通对象
      let user = {
        name: '小明',
        walk: function () {
          console.log(this);
        }
      };
      // 动态为 user 添加方法
      user.sayHi = sayHi;
      uesr.sayHello = sayHello;
    
      // 函数调用方式,决定了 this 的值
      user.sayHi();
      user.sayHello();
    </script>
    

    注: 普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined

    箭头函数

    箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !箭头函数中访问的 this 不过是箭头函数所在作用域的 this 变量。

    <script>
      console.log(this); // 此处为 window
      // 箭头函数
      let sayHi = function() {
        console.log(this); // 该箭头函数中的 this 为函数声明环境中 this 一致
      }
    
      // 普通对象
      let user = {
        name: '小明',
        // 该箭头函数中的 this 为函数声明环境中 this 一致
        walk: () => {
          console.log(this);
        },
    
        sleep: function () {
          let str = 'hello';
          console.log(this);
          let fn = () => {
            console.log(str);
            console.log(this); // 该箭头函数中的 this 与 sleep 中的 this 一致
          }
          // 调用箭头函数
          fn();
        }
      }
    
      // 动态添加方法
      user.sayHi = sayHi;
    
      // 函数调用
      user.sayHi();
      user.sleep();
      user.walk();
    </script>
    

    在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此DOM事件回调函数不推荐使用箭头函数,如下代码所示:

    <script>
      // DOM 节点
      let btn = document.querySelector('.btn');
    
      // 箭头函数 此时 this 指向了 window
      btn.addEventListener('click', () => {
        console.log(this);
      })
    
      // 普通函数 此时 this 指向了 DOM 对象
      btn.addEventListener('click', function () {
        console.log(this);
      })
    </script>
    

    同样由于箭头函数 this 的原因,基于原型的面向对象也不推荐采用箭头函数,如下代码所示:

    <script>
      function Person() {
    
      }
    
      // 原型对像上添加了箭头函数
      Person.prototype.walk = () => {
        console.log('人都要走路...');
        console.log(this); // widow
      }
    
      let p1 = new Person();
      p1.walk();
    </script>
    

    定义值

    call、apply、bind只能由函数调用
    以上归纳了普通函数和箭头函数中关于 this 默认值的情形,不仅如此 JavaScript 中还允许指定函数中 this 的指向,有 3 个方法可以动态指定普通函数中 this 的指向:

    call

    使用 call 方法调用函数,同时指定函数中 this 的值,使用方法如下代码所示:

    // 修改 this 的指向,可以使用 函数的call、apply、bind三方方法
    // 语法:
    // 'adfasdfadsf'.toUpperCase();
    // [].push();
    // /sss/g.test()
    // 函数.call()  函数.apply()  函数.bind()
    
    // 1. -------------------------- call --------------------------
    // function sayHi(x, y) {
    //   console.log(x + y + this.age)
    //   console.log(this)
    // }
    // // sayHi(3, 5)
    
    // let user = { name: 'user', age: 20 }
    // let student = { name: 'zhangsan', age: 30 }
    
    // // call方法的作用是:1. 调用函数(函数会执行)  2. 修改函数中this的指向
    // sayHi.call(user, 3, 5)
    // sayHi.call(student, 1, 2)
    
    let obj = {
      say: function () {
        console.log(this)
      }
    }
    let student = { name: 'zhangsan', age: 30 }
    // obj.say.call(student)
    obj.say.call(Math)
    

    总结:

    1. call 方法能够在调用函数的同时指定 this 的值
    2. 使用 call 方法调用函数时,第1个参数为 this 指定的值
    3. call 方法的其余参数会依次自动传入函数做为函数的参数

    基于call的使用:

    <input type="checkbox">
    <input type="checkbox">
    <input type="checkbox">
    <button>判断</button>
    
    <script>
      // 扩展一个小知识(记一下规律)
      let btn = document.querySelector('button')
      let ck = document.querySelectorAll('input')
      btn.addEventListener('click', function () {
        // console.log(ck) // 伪数组
        // 复选框.checked
        // console.log(ck[0].checked) // 如果复选框选中,则返回true;否则返回false
        // console.log(ck[1].checked)
        // console.log(ck[2].checked)
        // 判断三个复选框是否都选中(checked属性值都是true)了?
        // let result = [6, 9, 7, 8].every(item => item > 5)
        // console.log(result)
    
        let result = [].every.call(ck, item => item.checked === true)
    
        // 规律是:如果一个伪数组,希望使用数组方法的话,可以使用下面的语法
        // [].方法.call(伪数组, 方法的参数)
    
        // let result = ck.every(item => {
        //   return item.checked === true
        // })
        console.log(result)
      })
    </script>
    

    apply

    使用 apply方法调用函数,同时指定函数中 this 的值,使用方法如下代码所示:

    function sayHi(x, y) {
      console.log(x + y + this.age)
      console.log(this)
    }
    // sayHi(3, 5)
    
    let user = { name: 'user', age: 20 }
    let student = { name: 'zhangsan', age: 30 }
    
    // 语法:
    // 函数.apply(对象, [3, 5])
    // sayHi.apply(user, [3, 5])
    
    // 一个牵强的案例
    // 计算数组的最大值
    // 关于展开语法(...) https://www.yuque.com/lao-tang/gaoji/oa7f6u#irzql
    // console.log(Math.max(...[4, 3, 9, 5, 0]))
    
    // let arr = [4, 3, 9, 5, 0]
    console.log(Math.max.apply(null, [4, 10, 3, 9, 5, 0]))
    

    总结:

    1. apply 方法能够在调用函数的同时指定 this 的值
    2. 使用 apply 方法调用函数时,第1个参数为 this 指定的值
    3. apply 方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数

      bind

      bind 方法并不会调用函数,而是创建一个指定了 this 值的新函数,使用方法如下代码所示: ```javascript function sayHi(x, y) { console.log(x + y + this.age) console.log(this) } // sayHi(3, 5)

    let user = { name: ‘user’, age: 20 } let student = { name: ‘zhangsan’, age: 30 }

    // sayHi.call(user, 3, 5) —— 1. 调用函数; 2. 修改this指向

    // bind语法和意义 // sayHi.bind(user, 3, 5) ——- 1. 修改this的指向; 2. 返回一个新函数 // let fn = sayHi.bind(user, 3, 5) // fn()

    // sayHi.bind(user, 3, 6)()

    // let fn = sayHi.bind(user) // fn(3, 9)

    sayHi.bind(user)(4, 10)

    注:`bind` 方法创建新的函数,与原函数的唯一的变化是改变了 `this` 的值。<br />**改变this三个方法总结:**<br />面试会问<br />底层代码会用,正常开发基本不会用上
    ```javascript
    call:         fun.call(this,arg1, arg2,......)
    
    apply:        fun.apply(this, [arg1, arg2,......])
    
    bind:         fun.bind(this, arg1, arg2,......)
    
    相同点:
        都可以用来改变this指向,第一个参数都是this指向的对象
    区别:
        call和apply:都会使函数执行,但是参数不同
        bind:不会使函数执行,参数同call