函数中的this问题

在函数体的语句中,会出现this这个词,this就是函数的上下文

函数中this是谁,就说明函数的上下文是谁

函数中的this是谁,要看是如何调用的,因为this不是一成不变的

比如看下面的案例

<script>
      var obj = {
        a: 100,
        fun: function () {
          console.log(this.a);
        },
      };
    </script>

我们此时在obj对象中定义了一个fun函数,就是obj的属性

现在如果直接对象打点调用

obj.fun()

此时会输出100,说明上下文就是对象本身

如果此时把整个方法进行了一次赋值

 <script>
      var obj = {
        a: 100,
        fun: function () {
          console.log(this.a);
        },
      };
      var f = obj.fun;
      f();
    </script>

页面中会输出undefined,因为此时this的上下文不是obj了而是window了

如果在外部定义一个a,那么此时就睡输出外部a的值200

<script>
      var a = 200;
      var obj = {
        a: 100,
        fun: function () {
          console.log(this.a);
        },
      };
      var f = obj.fun;
      f();
    </script>

 

1、直接圆括号执行,上下文是window对象

直接圆括号调用就是没有对象打点执行,不是方括号枚举执行,通常是从数组,对象中提取出来后单独执行的

 <script>
      var a = 200;
      var obj = {
        a: 100,
        fun: function () {
          console.log(this.a);
        },
      };
      var f = obj.fun;
      f();
</script>

f()就是直接圆括号执行,因为这个f是从obj提取出来的

注意:

  • 直接圆括号执行的this指向的是window对象
  • 需要注意的是js中全局变量都是window对象的属性
  • 还需要注意的是IIFE也属于直接圆括号调用的范畴,里面的this都是window对象

我们看IIFE

<script>
      var a = 200;
      var obj = {
        a: 100,
        fun: (function () {
          console.log(this.a);
        })()
      };
    </script>

此时输出的就是200,IIFE的this指向的是window

 

小题目1:

   <script>
      var xiaoming = {
        name: "小明",
        age: 25,
        sayHello: (function () {
          return this.age >= 18 ? "男生" : "未成年";
        })(),
      };
      console.log("大家好,我叫" + xiaoming.name + ",我是一个" + xiaoming.sayHello);
    </script>

答案是男生,这道题的重点是IIFE里面的this,上面我们说过了IIFE里面的this指向的是window,所以此时IIFE里面的this.age是undefined,由于undefined >= 18结果是false,三元表达式走后面的”未成年”

 此时输出的说明this.age是小于18的

我们输出this.age看一下

<script>
      var xiaoming = {
        name: "小明",
        age: 25,
        sayHello: (function () {
          console.log(this.age)
        })(),
      };
      xiaoming.sayHello()
    </script>

 

小题目2:

    <script>
      var obj = {
        a: 100,
        fun: function () {
          var a = 300;
          console.log(this.a);
        },
      };
      var a = 200;
      var f = obj.fun;
      f();
    </script>

 

输出的是200

切记this指向谁一定要看调用,此时我们发现,调用是圆括号直接执行的。所以我们就知道了,内部的this就是window,所以obj里面的所有的a都是障眼法。由于全局变量都是window对象的属性,

所以var a=200就是window.a = 200,此时弹出的结果就是200

 

2、从对象中调用或者数组中枚举执行的函数,上下文是这个对象或者数组

函数的实参是一个arguments对象

 <script>
      function fun(a, b, c, d, e, f) {
        console.log(arguments);
      }
      fun(1, 2, 3, 4, 5);
    </script>

每一个函数都有一个属性是arguments,值是一个类数组对象

 

什么是类数组对象?

类数组对象和数组很像,本质是对象,拥有数组的length属性,有对应的下标索引值。函数的arguments或者document.get**获取DOM的时候返回对象类型都是类数组对象,因为这些对象虽然看似数组,但

是没有数组的能力,不能进行push等等操作

函数中this是上下文,需要看如何调用,如果想表达函数自己,用arguments.callee

 <script>
      function fun() {
        console.log(arguments.callee == fun);
      }
      fun();
    </script>

 

小题目3:

<script>
      function fun1(a, b, c) {
        arguments[0]();
      }

      function fun2(a, b, c, d, e) {
        consloe.log(this.length);
      }
      fun1(fun2, 1, 2, 3, 4, 5, 6);
    </script>

此时this是谁要看调用。fun1在调用的,fun1调用的时候函数的执行arguments[0]的执行,因为arguments是fun1的实参列表,所以第0项就是fun2函数,所以符合规则2;fun2函数中的this指的就是fun1函数的

arguments类数组对象,所以length就是7

 

小题目4:

此时我们把上面的题目升级

 <script>
      function fun1(a, b, c) {
        arguments[0](1, 2, 3, 4, 5, 6);
      }
      function fun2(a, b, c, d, e) {
        console.log(this.length); 
        console.log(arguments.length); 
        console.log(arguments.callee.length); 
        console.log(this.callee.length);
      }
      fun1(fun2, 1, 2, 3, 4, 5, 6);
    </script>

 

解析:通过分析知道了fun2中的this指的是fun1函数,所以此时this.length指的就是fun1的arguments类数组对象(因为是类数组枚举执行的符合规则2);

arguments本身是fun2函数自己的实参列表,所以长度是6(调用的时候传了1~6的参数);

arguments.callee是fun2函数自己,所以length就是形参列表为5;

this.callee.length指的就是fun1的形参列表为3

 

小题目5:

      var m = 2;
      var obj = {
        fun1: function () {
          return this.fun2();
        },
        fun2: fun2,
        m: 4,
      };

      function fun2() {
        return this.m;
      }
      console.log(obj.fun1());

 

小题目6:

      var num = 1;
      var obj = {
        num: 2, 
        fun: (function () {
          var num = 3;
          this.num += 4;
          return function () {
            this.num *= 5;
            num *= 6; 
            console.log(num);
          };
        })(),
      };
      obj.fun(); 
      obj.fun(); 
      console.log(num); 
      console.log(obj.num); 
      var f1 = obj.fun;
      f1(); 
      console.log(num); 
      console.log(obj.num); 
      var f2 = obj.fun;
      f2(); 
      console.log(num);

 

小题目7:

    var length = 1;
      var obj = {
        length: 10,
        b: [
          {
            length: 20,
            fun: function () {
              console.log(this.length);
            },
          },
        ],
      };
      var arr = [obj, obj.b, obj.b[0], obj.b[0].fun];
      arr[0].b[0].fun(); 
      arr[1][0].fun(); 
      arr[2].fun(); 
      arr[3](); 

 

 

3、定时器直接调用,上下文是window对象

 var a = 100;

      function fun() {
        console.log(this.a);
      }
      setInterval(fun, 1000);

需要注意的是定时器调用和定时器函数内部调用是有区别的

下面代码是定时器在调用obj.fun函数,所以调用者是定时器

  var obj = {
        a: 300,
        fun: function () {
          console.log(this.a++);
        },
      };
      var a = 100;
      setInterval(obj.fun, 1000);

 

 

 

下面的代码本质是obj在调用函数,所以上下文是obj

   var obj = {
        a: 300,
        fun: function () {
          console.log(this.a++);
        },
      };
      var a = 100;
      setInterval(function () {
        obj.fun();
      }, 1000);

 

 

4、DOM事件中的this,指的是触发事件的这个DOM元素

var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
var box4 = document.getElementById('box4');

function changeColor() {
    this.style.backgroundColor = 'purple'
}
box1.onclick = changeColor;
box2.onclick = changeColor;
box3.onclick = changeColor;
box4.onclick = changeColor;

此时点击的元素变为紫色

 

 

5、call()和apply()可以设置函数的上下文

函数的上下文主要是看谁在调用,但是我们可以通过call()和apply()去设置函数的上下文。

call()和apply()本质就是调用函数的同时,指定上下文

比如我们有一个changeSex的函数,它的作用是修改sex的属性。

此时有一个xiaohong对象,sex为女

此时我们调用这个changeSex函数,强行将函数的上下文绑定为xiaohong

  function changeSex() {
        if (this.sex == "") {
          this.sex = "";
        } else {
          this.sex = "";
        }
        console.log(this);
      }

      var xiaohong = {
        name: "小红",
        sex: "",
      };
      changeSex.call(xiaohong);
      console.log(xiaohong.sex);

 

 

apply函数也有同样的功能

changeSex.apply(xiaohong)

 

需要注意的是call()和apply()的本质核心是有区别的:主要是语法上的区别,call是接受参数,apply是接受数组

 

call方法要求,所有的参数在上下文对象后面一一罗列

   function person(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        console.log(this)
      }
      var xiaoming = {
        name: "小明",
        age: 3,
        sex: "",
      };
      person.call(xiaoming, "小红",5,"");
         

 

 

此时我们换成apply,apply要求所有的参数必须规整到一个数组中

  function person(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        console.log(this)
      }
      var xiaoming = {
        name: "小明",
        age: 3,
        sex: "",
      };
      person.apply(xiaoming, ["小红",15,""])

 

 

apply本质上只要求两个参数,第二个参数是一个数组集合

在使用结果上两种方式都是一样的

小题目8:

      function fun1() {
        fun2.apply(obj, arguments);
      }

      function fun2(a, b, c) {
        console.log(obj);
        console.log(a);
        console.log(b);
        console.log(c);
      }
      var obj = {
        name: "小明",
        sex: "",
      };
      fun1("小红", "小刚", "小兰");

 

 

此时你会发现apply有一个功能是将第二个参数将数组进行结构,变成一个个的罗列参数,比如我们传进arguments是一个类数组对象,但是我们在fun2函数接受的a,b,c形参中给进行了解构,也就是分别变成

了小红、小刚、小兰

小题目9:

利用Math方法查找数组的最大值

Math.max.apply(null, [5432,123,5342,43,4,6,48,43,43,43,48,94,43,51,123,2])

 

posted @ 2022-01-02 22:02  keyeking  阅读(137)  评论(0编辑  收藏  举报