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

作者:代码拾荒时间:2026-10-26 12:36:22浏览:508

本文详细解析非严格模式下JavaScript中this的四种绑定规则:默认绑定、隐式绑定、显示绑定及new绑定,并深入探讨箭头函数外层this的继承机制。通过对比call、apply、bind的行为差异,明确bind>call/apply>隐式绑定>默认绑定的优先级顺序,帮助开发者准确掌握this指向逻辑。

说明:本文所讨论的内容均基于非严格模式下的this指向规则。

1. 默认绑定

示例1

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

示例2

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

在示例1中,使用var声明变量位于全局作用域;在示例2中,使用let声明变量位于块级作用域。尽管作用域不同,但两个例子内部的this均指向window对象。

原因分析:当函数被直接调用时,会触发默认绑定机制。这可以类比为执行fn.call(undefined),当call方法的第一个参数为null或undefined时,this会自动指向全局对象。

常见面试题解析

示例1代码展示:

  var a = 3;
  function c() {
    alert(a); //3
  }
  (function () {
    var a = 4;
    c();
  })();

示例2代码展示:

  var name = "123";

  var obj = {
    name: "456",
    print: function () {
      function a() {
        console.log(this.name); //123
      }
      a();
    },
  };

  obj.print();

无论面试题形式如何复杂多变,核心原则不变:普通函数直接调用时遵循默认绑定规则,此时this指向全局对象window。

2. 隐式绑定

当函数调用时,如果调用位置前方存在一个引用该函数的对象,则this会隐式绑定到该对象上。

  let obj = {
    name: "123",
    fn: function () {
      console.log(this.name); //123
    },
  };
  obj.fn();

若函数调用链前方存在多个对象,this将指向距离调用位置最近的那个对象。

  let obj = {
    name: "123",
    o: {
      name: "456",
      func: function () {
        console.log(this.name);
      },
    },
  };
  obj.o.func(); //456

隐式丢失现象:当通过变量赋值将对象中的方法提取为普通函数时,会发生隐式丢失。

  var name = "1";
  let obj = {
    name: "2",
    fn: function () {
      console.log(this.name);
    },
  };
  let fn1 = obj.fn;
  fn1(); //1

此时fn1变为直接调用,触发默认绑定,this重新指向全局对象。

3. 显示绑定

显示绑定是指通过call、apply以及bind方法强制改变this的指向行为。

  let obj1 = {
    name: "1",
  };
  let obj2 = {
    name: "2",
  };
  let obj3 = {
    name: "4",
  };
  var name = "3";

  function fn() {
    console.log(this.name);
  }
  fn(); //3 默认绑定,this指向全局
  fn.call(obj1); //1 this指向obj1
  fn.apply(obj2); //2 this指向obj2
  fn.bind(obj3)(); //4 this指向obj3

扩展知识:call、apply与bind方法的异同点

相同点:三者均用于改变函数执行时的this指向。

不同点:

call方法接收第二个参数作为独立的参数列表传入。

apply方法接收第二个参数作为参数数组传入。

bind方法接收第二个参数作为独立的参数列表,但返回一个新函数,不会立即执行。

4. new绑定

使用new关键字调用构造函数时,this指向新生成的对象实例。

  function Person(name, age) {
    this.name = name;
    this.age = age;
  }

  const p1 = new Person("1", 20);

  console.log(p1); // {name:'1', age:20}

5. 箭头函数的this

箭头函数没有自己的this,默认继承外层作用域的this值。

示例1代码展示:

  var name = "1";
  let obj = {
    name: "2",
    fn: function () {
      setTimeout(function () {
        console.log(this.name); //1
      });
    },
  };
  obj.fn();

由于setTimeout本质上是window.setTimeout,因此其中的函数内部this指向window,打印结果为1。

示例2代码展示:

  var name = "1";
  let obj = {
    name: "2",
    fn: function () {
      setTimeout(() => {
        console.log(this.name); //2
      });
    },
  };
  obj.fn();

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

函数fn由obj对象调用,因此this隐式绑定到obj上,最终打印结果为2。

示例3代码展示:

  var name = "window";
  var student = {
    name: "1",
    fn: function () {
      var fn2 = () => {
        console.log(this.name);
      };
      fn2();
    },
    fn3: () => {
      console.log(this.name);
    },
  };
  student.fn(); // '1'
  student.fn3(); // 'window'
  • student.fn()内部执行的是函数fn2。由于fn2是直接调用,默认绑定到window上;但fn2是箭头函数,它绑定了外一层this,因此this指向函数fn。又因为fn由student对象调用,所以最终this指向student。
  • student.fn3()采用隐式绑定,this指向student;但由于fn3是箭头函数,它默认绑定外一层的this,而外层作用域中this指向window,因此最终this指向window。

示例4:防抖函数实现

  function debounce(fn, delay) {
    let timer = null;
    return function () {
      clearTimeout(timer);
      timer = setTimeout(() => {
        //谁调用,this指向谁
        fn.apply(this, arguments);
      }, delay || 1000);
    };
  }

  function fn() {
    console.log(this); //document
  }

  document.addEventListener("click", debounce(fn));

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

6. 优先级

绑定优先级顺序为:显式绑定(bind优先级高于call/apply) > 隐式绑定 > 默认绑定。

具体规则1:隐式绑定优先级高于默认绑定。

  function bar() {
    console.log(this); //info
  }

  const info = {
    bar: bar,
  };

  info.bar();

具体规则2:显示绑定优先级高于隐式绑定。

  var fullName = "global";
  const info = {
    fullName: "1",
    getName: function () {
      console.log(this.fullName);
    },
  };

  info.getName.call(null); //global

具体规则3:bind优先级高于apply和call。

  function bar() {
    console.log(this); //{age: 1}
  }

  bar.bind({ age: 1 }).call({ age: 2 });

函数中的this绑定在{ age: 1 }上,即使后续又使用了call进行绑定,优先级规则仍适用。

7. 总结

1. 普通函数的this值取决于函数被调用的方式,主要分为默认绑定、隐式绑定和显示绑定三种情况。

2. 箭头函数没有自己的this,默认绑定外层作用域的this。

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

作者最新文章