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

 更新时间:2023年04月04日 09:11:18   作者:敲代码的彭于晏  
JavaScript中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指向全局 便可知道函数内部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 绑定

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

例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

例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

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

相关文章

  • JS函数修改html的元素内容,及修改属性内容的方法

    JS函数修改html的元素内容,及修改属性内容的方法

    下面小编就为大家带来一篇JS函数修改html的元素内容,及修改属性内容的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • JS查看对象功能代码

    JS查看对象功能代码

    通过JS查看到一个页面有些什么对象的实现代码
    2008-04-04
  • 前端微信支付js代码

    前端微信支付js代码

    这篇文章主要为大家详细介绍了前端微信支付js代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • JS求Number类型数组中最大元素方法

    JS求Number类型数组中最大元素方法

    这篇文章主要介绍了如何用JS求Number类型数组中最大元素
    2018-04-04
  • JS简单验证上传文件类型的方法

    JS简单验证上传文件类型的方法

    这篇文章主要介绍了JS简单验证上传文件类型的方法,涉及javascript文件遍历及字符串截取、匹配等相关操作技巧,需要的朋友可以参考下
    2017-04-04
  • Uniapp实现地图获取定位功能(推荐)

    Uniapp实现地图获取定位功能(推荐)

    本文详细介绍了如何在Uniapp项目中集成地图功能,实现定位获取,并解决微信小程序、APP、H5三端的兼容性问题,涵盖了环境准备、配置地图基础功能、获取用户定位、多平台适配要点以及常见问题及解决方案,感兴趣的朋友一起看看吧
    2025-03-03
  • 详解ES6新增字符串扩张方法includes()、startsWith()、endsWith()

    详解ES6新增字符串扩张方法includes()、startsWith()、endsWith()

    这篇文章主要介绍了详解ES6新增字符串扩张方法includes()、startsWith()、endsWith(),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • javascript表单验证大全

    javascript表单验证大全

    JavaScript是用来在数据被传输到服务前对html表单中输入的数据进行验证,使用javascript对用户输入的信息进行验证是项目必须的,下面小编给大家整理一些比较常用的javascript表单验证,需要的朋友可以参考下
    2015-08-08
  • IScroll5 中文API参数说明和调用方法

    IScroll5 中文API参数说明和调用方法

    IScroll是移动页面上被使用的一款仿系统滚动插件。IScroll5相对于之前的IScroll4改进了许多,使得大家可以更方便的定制所需的功能了。
    2016-05-05
  • JS限制文本框只能输入数字和字母方法

    JS限制文本框只能输入数字和字母方法

    这篇文章主要介绍了JS限制文本框只能输入数字和字母方法,本文给出了限制只能输入数字、限制只能输入字母、限制只能输入数字和字母3种脚本,需要的朋友可以参考下
    2015-02-02

最新评论