美文网首页
看一遍就能掌握 js 中的 this 指向

看一遍就能掌握 js 中的 this 指向

作者: Super超_fee3 | 来源:发表于2020-06-01 10:03 被阅读0次
关于 this 指向,可能有一部分人都是模糊的,本文对常见情况下的 this 指向作出总结,让你不再皱眉。

先了解

一个基本概念:普通函数的 this 指向不是在定义的时候确定,而是在调用的时候确定。

两个注意事项:

  • 所有例子在浏览器环境(window 对象同时也是全局对象)运行,而不是 node 环境(global)。
  • 非严格模式指向 Window 的对应严格模式是 undefined。(Window.setTimeout 等除外,由 setTimeout 调用的代码运行在与所在函数完全分离的执行环境上,即使在严格模式下仍然指向 Window)

接下来从一般形式函数调用、方法调用、apply 和 call 调用、箭头函数、class 等理清指向问题。

1. 一般形式函数调用

所谓一般形式函数调用就是 函数名(),this 指向全局对象。

function test() {
  console.log(this.name) // fang
}
var name = 'fang'
// let、const 声明的变量不是 Window 属性
const age = 1
console.log(this) // Window
console.log(this.age) // undefined
test()

2. 方法调用

一个函数被设置为对象(非全局对象)的属性值时,就是方法调用,this 指向对象自身。

  • 当函数属于对象外层属性的属性值

    function test() {
      'use strict'
      console.log(this) // obj
      console.log(this.name) // fang
    }
    var name = 'wang'
    const obj = {
      name: 'fang',
      fun: test
    }
    obj.fun()
    
  • 当函数属于深层对象的属性值,要明确知道它的使用者是谁

    function test() {
      console.log(this) // obj2
      console.log(this.name) // zhang
    }
    var name = 'wang'
    const obj1 = {
      name: 'fang',
      obj2: {
        name: 'zhang',
        fun: test
      }
    }
    
    obj1.obj2.fun()
    
  • 当把对象的属性值函数赋值给一个新的变量,就会变成一般形式函数调用

    function test() {
      console.log(this) // Window
      console.log(this.name) // wang
    }
    var name = 'wang'
    const obj1 = {
      name: 'fang',
      obj2: {
        name: 'zhang',
        fun: test
      }
    }
    var t = obj1.obj2.fun
    t()
    
  • 当属性值函数内部还有函数

    function test() {
      console.log(this) // Window
      console.log(this.name) // wang
    }
    var name = 'wang'
    const obj = {
      name: 'fang',
      foo: function() {
        console.log(this) // obj
        test() // test 是一般形式函数调用
      }
    }
    
    obj.foo() // foo 是方法调用
    

3. call、apply 和 bind 用来改变 this 指向

  • call: fun.call(thisArg[, arg1[, arg2[, ...]]]),第一个参数是要绑定给 this 的值,后面传入参数列表。

    const obj = {
      name: 'fang'
    }
    function a() {
      console.log(this)
    }
    a.call(obj) // obj
    a.call(null) // 如果第一个参数是 null、undefined,指向 Window(下同)
    
  • apply: fun.apply(thisArg[, [arg1, arg2, ...]]),可接收两个参数,第一个是绑定给 this 的值,第二个是数组。

    const obj = {
      name: 'fang'
    }
    function a() {
      console.log(this)
    }
    a.apply(obj) // obj
    a.apply(undefined) // Window
    
  • bind:fun.bind(thisArg[, arg1[, arg2[, ...]]]),与 call 相似,但是返回的是一个函数,需要手动去执行。

    const obj1 = {
      name: 'fang'
    }
    const obj2 = {
      name: 'wang'
    }
    function a() {
      console.log(this)
    }
    const b = a.bind(obj1)
    const c = a.bind(obj2)
    // 需要手动执行
    b() // obj1
    a() // Window,函数a不会受影响
    c() // obj2
    b.call(null) // obj1(绑定关系一旦确认给新变量,新变量继续使用 call、apply、bind 不会再次改变指向)
    c.bind(obj2)() // obj2
    

4. 箭头函数

箭头函数没有自己的 this,看其定义时外层是否有函数,如果有,外层函数的 this 就是内部箭头函数的 this,如果没有,则 this 指向 Window。

  • 箭头函数外层没有函数

    const arrow = () => {
        console.log(this.name) // wang
    }
    var name = 'wang'
    const obj = {
      name: 'fang',
      foo: function() {
        console.log(this) // obj
        arrow() // 看定义时
      }
    }
    obj.foo()
    
      var name = 'wang'
      const obj1 = {
        name: 'fang',
        obj2: {
          name: 'zhang',
          fun: () => {
              console.log(this) // Window
          }
        }
      }
      obj1.obj2.fun()
    
  • 箭头函数外层有函数,注意外层函数的 this 指向按照之前规则判断

    var name = 'wang'
    const obj = {
      name: 'fang',
      foo: function() {
        console.log(this) // obj
        const arrow = () => {
            console.log(this) // 指向外层函数 this
        }
        arrow()
      }
    }
    
    obj.foo()
    
  • 箭头函数能否被改变指向?

    var name = 'wang'
    const obj1 = {name: 'zhang'}
    const obj2 = {
      name: 'fang',
      foo: function() {
        console.log(this) // obj2
        const arrow = () => {
            console.log(this.name) // fang
        }
        arrow.call(obj1) // 箭头函数不会改变 this 指向
      }
    }
    
    obj2.foo()
    

5. class 类(es6 严格模式)

  • 创建类实例后,再去调用类的方法,this 指向实例对象

      class A {
        constructor({
          age,
          name
        }) {
          this.name = name
          this.age = age
        }
    
        test() {
          console.log(this) // {name:'fang', age:1}
        }
      }
    
      const a = new A({
        age: 1,
        name: 'fang'
      })
      a.test()
    
  • 直接通过 prototype 对象调用 test,指向 prototype

      class A {
        constructor({
          age,
          name
        }) {
          this.name = name
          this.age = age
        }
    
        test() {
          console.log(this)
        }
      }
    
      const a = new A({
        age: 1,
        name: 'fang'
      })
      a.test() // {name:'fang', age:1}
      console.log(A.prototype) // {constructor:f, test:f}
      A.prototype.test() // prototype(想想看是不是可以理解为方法调用)
    
  • 子类创建一个实例后,指向子类实例对象,包括子类调用父类的方法

      class A {
        constructor() {
          this.name = 1
          this.age = 1
          this.sex = 0
        }
          test1(){
              console.log(this)
          }
      }
      class B extends A {
        constructor({name,age}) {
          super() // super 必须置于 this 前
          this.name = name // 如果不写,继承父类的属性 1
          this.age = age // 如果不写,继承父类的属性 1
        }
    
        test2() {
          console.log(this)
        }
      }
      const b = new B({
        name: 3,
        age: 3
      })
      b.test2() // {age:3,name:3,sex:0}
      // 父类的方法被子类调用
      b.test1() // {age:3,name:3,sex:0}
    
  • 子类通过 prototype 调用的指向与父类是有区别的

      class A {
        constructor() {
          this.name = 1
          this.age = 1
          this.sex = 0
        }
        test1(){
            console.log(this)
        }
      }
      class B extends A {
        constructor({name,age}) {
          super() // super 必须置于 this 前
          this.name = name // 如果不写,继承父类的属性 1
          this.age = age // 如果不写,继承父类的属性 1
        }
        test2() {
          console.log(this)
        }
      }
      console.log(B.prototype) // A {constructor: ƒ, test2: ƒ} 注意与父类prototype的区别
      B.prototype.test1() // A {constructor: ƒ, test2: ƒ}
      B.prototype.test2() // A {constructor: ƒ, test2: ƒ}
    

6. vue 中的 this

一般来说,在 vue 生命周期函数或自定义方法中 this 指向的是 vue 实例,但是要注意下面的3种情况。

  • 回调函数 then 链式写法用普通函数,this 指向 undefined,可使用 _this=this 获取 vue 实例

    let _this = this // vue实例
    /* eslint-disable */
    request().then( function (res){
      console.log(this) // undefined
      console.log(_this) // vue实例
    })
    
  • setTimeout 执行普通函数指向 Window ,可使用箭头函数获取 vue 实例

    setTimeout(() => {
      console.log(this) // vue 实例
    }, 2000)
    
  • 不应该使用箭头函数来定义 method 函数,箭头函数绑定了父级作用域的上下文,this 指向 undefined。

    methods: {
      todo: () => console.log(this) // undefined
    }
    

以上理解如果有不对之处请指出。

相关文章

  • 看一遍就能掌握 js 中的 this 指向

    先了解 一个基本概念:普通函数的 this 指向不是在定义的时候确定,而是在调用的时候确定。 两个注意事项: 所有...

  • JS进阶篇-this指向问题

    JS中this的指向问题不同于其他语言,JS中的this不是指向定义它的位置,而是在哪里调用它就指向哪里。 JS中...

  • JS中this指向

    一、全局作用域中的thises5与es6中严格模式与非严格模式全局函数的this都指向window 二、全局作用域...

  • JS中this指向

    函数有4种调用方式,对应就有4种绑定规则:默认绑定、隐式绑定、硬绑定和构造函数绑定。 1、默认绑定 当作为普通函数...

  • js中this指向

    1.this总是指向函数的直接调用者2.如果有new关键字,this指向new出来的那个对象3.DOM事件中thi...

  • JS中this指向

    (1)事件调用环境:谁触发事件,函数里面的this指向就是谁(某个DOM)。 (2)node全局环境:this指向...

  • js中this指向

    当我们需要把函数当做另外函数参数传入时,我们使用箭头函数。 关于this的指向。 问题:箭头函数中的this是如何...

  • JS中this的指向

    什么是this? 它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。 JS中this的指向,取...

  • JS中this的指向

    JS中this的指向 1.背景介绍 2.知识剖析 3.常见问题 4.解决方案 5.编码实战 6.扩展思考 7.参考...

  • JS中this的指向

    1、普通函数中this的指向( 普通函数中的this指针指向于调用者) 2、定时器中的this的指向 3、在对象...

网友评论

      本文标题:看一遍就能掌握 js 中的 this 指向

      本文链接:https://www.haomeiwen.com/subject/nmrjzhtx.html