0
点赞
收藏
分享

微信扫一扫

JavaScript高阶之this的指向问题

香小蕉 2022-04-29 阅读 91
javascript

this的理解

我们先来看一下MDN文档中对this的解释
与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
看完MDN的解释,很多人还是不懂this的具体作用,下面我用自己的话阐述一下对this的理解,相比MDN的解释,更容易理解一点
根据函数的调用方式不同,this会指向不同的对象
1. 以函数的形式调用时,this永远都是window
2. 以方法的形式调用时,this就是调用方法的那个对象
3.以构造函数的形式调用时,this就是新建的对象
4.使用call和apply调用时,this就是指那个对象
5.在全局作用域中this代表window
this在全局作用下指向什么?
在浏览器中测试就是指向window

    // 全局下的this
    console.log(this);//window
    console.log(window);//window

但是,开发中很少直接在全局作用域下去使用this,通常都是在函数中使用this
所有的函数在被调用时,都会创建一个执行上下文
这个上下文中记录这函数的调用栈、AO对象等
this也是其中一条记录
总结
this指向什么,跟函数所在位置没有关系;
跟函数调用的方式有关系,就是谁调用的这个函数,this就会指向谁

this的绑定规则有四种

  1. 默认绑定
  2. 隐式绑定
  3. 显示绑定
  4. new绑定

第一种:默认绑定

默认绑定案例一

 // 默认绑定,独立函数调用
    function foo() {
      console.log(this);
    }
    foo()//window

默认绑定案例二

   // 也是独立调用
    function foo1() {
      console.log(this);//Window
    }
    function foo2() {
      console.log(this);//Window
      foo1()
    }
    function foo3() {
      console.log(this);//Window
      foo2()
    }
    foo3()

默认绑定案例三

      var obj = {
      name: "why",
      foo: function () {
        console.log(this);
      }
    }
    var bar = obj.foo
    // 为什么这里this指向window,this指向取决于调用者,这里是独立调用所以this就是window
    bar()//window

以上这三个案例就是默认绑定的情况,属于独立调用,没有调用者去调用函数,最终this都指向了window

第二种:隐式绑定

隐式调用就是通过某个对象进行调用;
也就是它的调用位置中,是通过某个对象发起的函数调用
隐式绑定案例一

   function foo() {
      console.log(this);
    }
    let obj = {
      name: 'why',
      // 把外面的foo函数传递给foo
      foo: foo
    }
    //这里this指向obj对象
    obj.foo()

隐式绑定案例二

    let obj = {
      name: 'why',
      eating: function () {
        console.log(this.name + '在吃东西');
      },
      running: function () {
        console.log(this.name + '在跑步中');
      }
    }
    //这里函数是obj在调用,this指向obj
    obj.eating() //why在吃东西
    obj.running()//why在跑步中

隐式绑定案例三

    let obj1 = {
      name: "obj1",
      foo: function () {
        console.log(this);//obj2
      }
    }
    let obj2 = {
      name: 'obj2',
      // 这里把obj1.foo传递给bar
      bar: obj1.foo
    }
    //这里打印的结果是obj2对象,obj2调用的函数,所以this指向obj2
    obj2.bar()

第三种:显示绑定

隐式绑定有一个前提条件:

  1. 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  2. 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
  3. 正是通过这个引用,间接的将this绑定到了这个对象上
    显示绑定案例一
  function foo() {
      console.log('函数被调用了', this);
    }
    // foo直接调用
    // foo直接调用和call/apply调用的不同在于this绑定的不同
    // foo直接调用指向的是全局对象(window)
    // foo()//window
    let obj = { name: 'obj' }
    // call和apply是可以指定this的绑定对象
    foo.call(obj)    //指向obj对象
    foo.apply(obj)   //指向obj对象
    foo.apply('abcd')//指向字符串对象

补充:call()和apply()的区别?
call/apply用来改变函数的执行上下文(this),它们的第一个参数thisArg是个对象,即作为函数内的this。
不同点:传参的类别不同
call 参数依次排列在后面
apply 接收参数形式是数组,把参数放在数组里面

 // call和apply有什么区别?
    function sum(num1, num2) {
      console.log(num1 + num2, this);
    }
    sum.call('call', 20, 30)
    sum.apply('apply', [50, 100])

在这里插入图片描述
call和apply在执行函数时,是可以明确的绑定this,这个绑定规则称之为显示绑定
显示绑定案例二

 // 显示绑定bind
    function foo() {
      console.log(this);
    }
    let newFoo = foo.bind('aaa')
    newFoo()//String对象

第四种:new绑定

JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
使用new关键字来调用函数是会执行如下操作:

  1. 创建一个全新的对象
  2. 这个新对象会被执行prototype连接
  3. 这个新对象会绑定到哈数调用的this上(this的绑定在这个步骤完成)
  4. 如果函数没有返回其他对象,表达式会返回这个新对象
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    let p1 = new Person("why", 18)
    console.log(p1.name, p1.age);   //why 18
    let p2 = new Person('lidi', 25) //lidi 25
    console.log(p2.name, p2.age);

我们通过一个new关键字调用一个函数时(构造器),这个时候this是在调用这个构造器时创建出来的对象
this=创建出来的对象
这个绑定过程就是new绑定

举报

相关推荐

0 条评论