一文详解JavaScript中this指向的问题

前提:文章讨论的都是非严格模式下this指向

1. 默认绑定

例子1

  1.      var fn = function () {
  2.      console.log(this === window);
  3.      };
  4.      fn(); // true

例子2

  1.      let fn = function () {
  2.      console.log(this === window);
  3.      };
  4.      fn(); // true

例1使用var定义在全局作用域中,例2使用let定义在块作用域中,但内部的this都指向window

原因:函数直接调用,会做默认绑定,可类比为 fn.call(undefined),call 第一个参数是null或undefined,那么 this 将指向全局对象

常见面试题

示例1:

  1.      var a = 3;
  2.      function c() {
  3.      alert(a); //3
  4.      }
  5.      (function () {
  6.      var a = 4;
  7.      c();
  8.      })();

示例2:

  1.      var name = “123”;
  2.  
  3.      var obj = {
  4.      name: “456”,
  5.      print: function () {
  6.          function a() {
  7.          console.log(this.name); //123
  8.          }
  9.          a();
  10.      },
  11.      };
  12.  
  13.      obj.print();

无论面试题设计的多花里胡哨,只要记住 普通函数直接调用,默认绑定,this指向全局 便可知道函数内部this指向window

2. 隐式绑定

如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上

  1.      let obj = {
  2.      name: “123”,
  3.      fn: function () {
  4.          console.log(this.name); //123
  5.      },
  6.      };
  7.      obj.fn();

如果函数调用前存在多个对象,this指向距离调用自己最近的对象

  1.      let obj = {
  2.      name: “123”,
  3.      o: {
  4.          name: “456”,
  5.          func: function () {
  6.          console.log(this.name);
  7.          },
  8.      },
  9.      };
  10.      obj.o.func(); //456

隐式丢失:通过变量赋值将对象中的函数变成普通函数

  1.      var name = “1”;
  2.      let obj = {
  3.      name: “2”,
  4.      fn: function () {
  5.          console.log(this.name);
  6.      },
  7.      };
  8.      let fn1 = obj.fn;
  9.      fn1(); //1

fn1 直接调用,默认绑定,this指向全局

3. 显示绑定

显示绑定:通过call、apply以及bind方法改变this的行为

  1.      let obj1 = {
  2.      name: “1”,
  3.      };
  4.      let obj2 = {
  5.      name: “2”,
  6.      };
  7.      let obj3 = {
  8.      name: “4”,
  9.      };
  10.      var name = “3”;
  11.  
  12.      function fn() {
  13.      console.log(this.name);
  14.      }
  15.      fn(); //3 默认绑定,this指向全局
  16.      fn.call(obj1); //1 this指向obj1
  17.      fn.apply(obj2); //2 this指向obj2
  18.      fn.bind(obj3)(); //4 this指向obj3

拓展:call、apply、bind 相同点与不同点

相同点:改变this的指向

不同点:

call 第二个参数传入一个参数列表

apply 第二个参数传入一个参数数组

bind 第二个参数传入一个参数列表,返回一个函数,不会立即执行

4. new 绑定

this指向生成的新对象

  1.      function Person(name, age) {
  2.      this.name = name;
  3.      this.age = age;
  4.      }
  5.  
  6.      const p1 = new Person(“1”, 20);
  7.  
  8.      console.log(p1); // {name:’1′, age:20}

5. 箭头函数的this

默认绑定外层 this

例1

  1.      var name = “1”;
  2.      let obj = {
  3.      name: “2”,
  4.      fn: function () {
  5.          setTimeout(function () {
  6.          console.log(this.name); //1
  7.          });
  8.      },
  9.      };
  10.      obj.fn();

setTimeout实际是window.setTimeout,因此函数内部this指向window,打印结果为1

例2

  1.      var name = “1”;
  2.      let obj = {
  3.      name: “2”,
  4.      fn: function () {
  5.          setTimeout(() => {
  6.          console.log(this.name); //2
  7.          });
  8.      },
  9.      };
  10.      obj.fn();

由于使用了箭头函数,默认绑定外层this,this指向函数fn

fn由obj调用,this隐式绑定到obj,最后打印的结果为2

例3:

  1.      var name = “window”;
  2.      var student = {
  3.      name: “1”,
  4.      fn: function () {
  5.          var fn2 = () => {
  6.          console.log(this.name);
  7.          };
  8.          fn2();
  9.      },
  10.      fn3: () => {
  11.          console.log(this.name);
  12.      },
  13.      };
  14.      student.fn(); // ‘1’
  15.      student.fn3(); // ‘window’
  • student.fn() 内部执行的是函数fn2,由于fn2是直接调用,默认绑定到window上,但由于fn2是箭头函数,绑定外一层this,所以this指向函数fn,fn由student对象调用,因此最终this指向student
  • student.fn3() 隐式绑定this指向student,由于fn3是箭头函数,默认绑定外一层的this,最终this指向window

例4:防抖函数

  1.      function debounce(fn, delay) {
  2.      let timer = null;
  3.      return function () {
  4.          clearTimeout(timer);
  5.          timer = setTimeout(() => {
  6.          //谁调用,this指向谁
  7.          fn.apply(this, arguments);
  8.          }, delay || 1000);
  9.      };
  10.      }
  11.  
  12.      function fn() {
  13.      console.log(this); //document
  14.      }
  15.  
  16.      document.addEventListener(“click”, debounce(fn));

setTimeout函数中的this默认指向window,因为是箭头函数,默认绑定外层this,因此this指向匿名函数,匿名函数由document调用,所以this指向document。再通过apply将函数fn的this绑定到document上,因此打印出document

6. 优先级

显式绑定(bind>call/apply) > 隐式绑定 > 默认绑定

1. 隐式绑定 > 默认绑定

  1.      function bar() {
  2.      console.log(this); //info
  3.      }
  4.  
  5.      const info = {
  6.      bar: bar,
  7.      };
  8.  
  9.      info.bar();

2. 显示绑定 > 隐式绑定

  1.      var fullName = “global”;
  2.      const info = {
  3.      fullName: “1”,
  4.      getName: function () {
  5.          console.log(this.fullName);
  6.      },
  7.      };
  8.  
  9.      info.getName.call(null); //global

3. bind > apply/call

  1.      function bar() {
  2.      console.log(this); //{age: 1}
  3.      }
  4.  
  5.      bar.bind({ age: 1 }).call({ age: 2 });

函数中的this绑定在 { age: 1 } 上,即使后面又使用了call绑定

7. 总结

1. 普通函数,this的值取决于函数被调用的方式,分为默认绑定,隐式绑定和显示绑定

2. 箭头函数,默认绑定外层this

到此这篇关于一文详解JavaScript中this指向的问题的文章就介绍到这了,更多相关javascript this指向内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

标签

发表评论