Skip to content

一、认识函数的上下文

  • 什么是上下文
    text
      垃圾分类,`这`是非常好的习惯,值得表扬
      随手关灯,`这`是非常好的习惯,值得表扬
      遛狗栓绳,`这`是非常好的习惯,值得表扬
      课后复习,`这`是非常好的习惯,值得表扬 
      早睡早起,`这`是非常好的习惯,值得表扬
  • 函数的上下文
    • 函数中可以使用this关键字,它表示函数的上下文
    • 与中文中"这"类似,函数中的this具体指代什么必须通过调用函数时的"前言后语"来判断
  • 函数中的this
    js
      var xiaoming = {
          nickname: '小明',
          age: 12,
          sayHello: function () {
              console.log('我是' + this.nickname + ',我' + this.age + '岁了'); 
            }
         };
      xiaoming.sayHello(); // 我是小明,我12岁了
    js
       var xiaoming = {
        nickname: '小明',
        age: 12, 
        sayHello: function () {
           console.log('我是' + this.nickname + ',我' + this.age + '岁了'); 
         }
       };
      var sayHello = xiaoming.sayHello;  // 将函数"提"出来,单独存为变量
      // 直接圆括号调用这个函数,而不是对象打点调用了
      sayHello(); // 我是undefined,我undefined岁了
  • 函数的上下文由调用方式决定
    • 同一个函数,用不同的形式调用它,则函数的上下文不同
      • 情形1:对象打点调用函数,函数中的this指代这个打点的对象
        js
          xiaoming.sayHello();
      • 情形2:圆括号直接调用函数,函数中的this指代window对象
        js
          var sayHello = xiaoming.sayHello; 
          sayHello();
      js
        var obj = { a: 1,
         b: 2,
         fn: function () {
             console.log(this.a + this.b);
         }
        };
        var fn = obj.fn;
        fn();

1. 上下文规则1

  • 函数的上下文由调用函数的方式决定

    • 函数的上下文(this关键字)由调用函数的方式决定, function是"运行时上下文"策略。
    • 函数如果不调用,则不能确定函数的上下文。
  • 规则1:对象打点调用它的方法函数,则函数的上下文是这个打点的对象。 对象.方法()

    js
      function fn() {
        console.log(this.a + this.b);
      }
      var obj = { 
        a: 66, b: 33, fn: fn
      }; 
      obj.fn(); // 99
    • 构成对象.方法()的 形式,适用规则1
    js
      var obj1 = {
      a: 1,
      b: 2,
      fn: function () {
          console.log(this.a + this.b);
       }
      };
      var obj2 = {
        a: 3,
        b: 4,
        fn: obj1.fn 
      };
      obj2.fn(); // 7
    • 构成对象.方法()的形式,适用规则1
    js
       function outer() { 
        var a = 11;
        var b = 22;
        return {
            a: 33,
            b: 44,
            fn: function () {
                console.log(this.a + this.b);
            }
       }; 
      }
      outer().fn(); // 77
    • 构成对象.方法()的 形式,适用规则1
    js
      function fun() {
        console.log(this.a + this.b);
      }
      var obj = {
        a: 1, 
        b: 2, 
        c: [{
            a: 3, b: 4, c: fun
          }] 
      };
      var a = 5; 
      obj.c[0].c(); // 7
    • 构成对象.方法()的形式,适用规则1

2. 上下文规则2

  • 规则2:圆括号直接调用函数,则函数的上下文是window对象

    js
      函数()
    js
       var obj1 = {
        a: 1,
        b: 2,
        fn: function () {
          console.log(this.a + this.b);
         }
        };
        var a = 3;
        var b = 4;
        var fn = obj1.fn;
        fn(); // 7
    • 构成函数()的形式,适用规则2
    js
      function fun() {
        return this.a + this.b;
      }
      var a = 1; 
      var b = 2;
      var obj = {
        a: 3,
        b: fun(), // 适用规则2
        fun: fun
      };
      var result = obj.fun(); // 适用规则1
      console.log(result); // 6

3. 上下文规则3

  • 规则3:数组(类数组对象)枚举出函数进行调用,上下文 是这个数组(类数组对象)

    js
      数组[下标]()
    js
      var arr = ['A', 'B', 'C', function () { 
        console.log(this[0]);
      }];
      arr[3](); // "A"
    • 适用规则3
  • 什么是类数组对象:所有键名为自然数序列(从0开始),且有length属性的对象。

  • arguments对象是最常见的类数组对象,它是函数的实参列表。

    js
      function fun() {
        arguments[3](); 
      }
      fun('A', 'B', 'C', function () { 
        console.log(this[1]); // 'B'
      });
    • 适用规则3

4. 上下文规则4

  • 规则4:IIFE中的函数,上下文是window对象
    js
      (function() {})();
    js
      var a = 1;
      var obj = {
        a: 2,
        fun: (function () {
          var a = this.a;
          return function () {
            console.log(a + this.a); // 3
          } 
        })() // 适用规则4
      };
      obj.fun(); // 适用规则1

5. 上下文规则5

  • 规则5:定时器、延时器调用函数,上下文是window对象
    js
      setInterval(函数, 时间);
      setTimeout(函数, 时间);
    js
      var obj = {
      a: 1,
      b: 2,
      fun: function () {
        console.log(this.a + this.b); // 7
        }
      }
      var a = 3; 
      var b = 4;
      setTimeout(obj.fun, 2000); // 适用规则5
    js
      var obj = {
        a: 1,
        b: 2,
        fun: function () {
          console.log(this.a + this.b); // 3
        }
      }
      var a = 3; 
      var b = 4;
      setTimeout(function() { 
        obj.fun(); // 适用规则1
      }, 2000);

6. 上下文规则6

  • 规则6:事件处理函数的上下文是绑定事件的DOM元素。
    js
      DOM元素.onclick = function () {};
  • 请实现效果:点击哪个盒子,哪个盒子就变红,要求使用同一个事件处理函数实现。
    html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            div{
                width: 200px;
                height: 200px;
                float: left;
                border: 1px solid #000;
                margin-right: 10px;
            }
        </style>
    </head>
    <body>
        <div id="box1"></div>
        <div id="box2"></div>
        <div id="box3"></div>
    
        <script>
            function setColorToRed() {
                this.style.backgroundColor = 'red';
            }
    
            var box1 = document.getElementById('box1');
            var box2 = document.getElementById('box2');
            var box3 = document.getElementById('box3');
    
            box1.onclick = setColorToRed;
            box2.onclick = setColorToRed;
            box3.onclick = setColorToRed;
        </script>
    </body>
    </html>
  • 请实现效果:点击哪个盒子,哪个盒子在2000毫秒后就变红,要求使用同一个事件处理函数实现。
    html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            div{
                width: 200px;
                height: 200px;
                float: left;
                border: 1px solid #000;
                margin-right: 10px;
            }
        </style>
    </head>
    <body>
        <div id="box1"></div>
        <div id="box2"></div>
        <div id="box3"></div>
    
        <script>
            function setColorToRed() {
                // 备份上下文
                var self = this;
                setTimeout(function() {
                    self.style.backgroundColor = 'red';
                }, 2000);
            }
    
            var box1 = document.getElementById('box1');
            var box2 = document.getElementById('box2');
            var box3 = document.getElementById('box3');
    
            box1.onclick = setColorToRed;
            box2.onclick = setColorToRed;
            box3.onclick = setColorToRed;
        </script>
    </body>
    </html>

7. call 和 apply

  • callapply能指定函数的上下文。

    js
      function sum() {
        alert(this.chinese + this.math + this.english);
      }
      var xiaoming = { 
        chinese: 80,
        math: 95,
        english: 93
      };
      sum.call(xiaoming);
      sum.apply(xiaoming);
    • 函数.call(上下文);
    • 函数.apply(上下文);
  • callapply的区别

    js
      function sum(b1, b2) {
        alert(this.c + this.m + this.e + b1 + b2);
      }
      sum.call(xiaoming, 5, 3); // call要用逗号罗列参数x
      sum.apply(xiaoming, [5, 3]); // apply要把参数写到数组中
  • 到底使用call还是apply?

    js
      function fun1() {
       fun2.apply(this, arguments);
      }
      function fun2(a, b) {
        alert(a + b);
      }
      fun1(33, 44);

8. 总结

规则上下文
对象.函数()对象
函数()window
数组[下标]()数组
IIFEwindow
定时器window
DOM事件处理函数绑定DOM的元素
call和apply任意指定

Released under the MIT License.