美文网首页
JavaScript中的this

JavaScript中的this

作者: w候人兮猗 | 来源:发表于2019-09-16 16:39 被阅读0次

    一、什么是this

    this的值:当前执行代码的环境对象,this的指向不取决于它在什么位置创建,完全取决于函数在什么地方被调用this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。

    二、this的值

    全局环境(任何函数调用的外部)中,this的值都是全局对象(浏览器中是window对象,node中是global对象)

    函数内部环境,this的值取决于函数被调用的方式

    三、this指向规则

    简单总结一个原理:this 永远指向最后调用它的那个对象

    • 函数绑定

    直接调用函数的时候this指向的是全局对象

    var name = 'Window.name'
    function foo() {
      console.log('this',this.name)
    }
    foo() // Window.name
    

    注意:如果使用的是严格模式的话,全局对象是undefined

    var name = 'Window.name'
    function foo() {
      'use strict'
      console.log('this',this.name)
    }
    foo() // Uncaught TypeError: Cannot read property 'name' of undefined
    
    • 隐式绑定(方法调用)

    当函数作为对象的属性被调用的时候就属于隐式绑定,这个时候,this指向的是这个函数的对象

    var obj = {
        num:0,
        add:function() {
          console.log('this',this) // this {num: 0, add: ƒ}
          this.num +=1
        }
    }
    obj.add()
    console.log('obj',obj) // obj {num: 1, add: ƒ}
    

    在看下面一个栗子:

    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo:function() {
          console.log('name',this.name)
        }
    }
    var res = obj.foo
    res() // ahwgs
    

    申明一个obj对象,虽然将obj.foo的引用赋值给res,但实际上此时是res()是不带修饰的函数调用(属于第一种函数绑定的情况),所以此时打印的值是ahwgs

    下面我们做一点改动:

    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo:function() {
          console.log('name',this.name)
        }
    }
    obj.foo() // name aaa
    

    这就是正常的隐式调用,这时候的thisobj本身

    在看一个栗子:

    var name = 'ahwgs'
    function doFoo(fn) {
        console.log('fn',fn)
        fn() //f(){console.log('name',this.name)}  fn=obj.foo
    }
    var obj = {
        name:'aaa',
        foo:function() {
          console.log('name',this.name)
        }
    }
    doFoo(obj.foo) // name ahwgs
    

    这时候,obj.foo作为参数传递给了doFoo,实际上调用还是doFoo,这时候this指向的是全局对象,所以打印的是ahwgs

    最后一个栗子:

    var name = 'ahwgs'
    function foo() {
        var name = 'aaa'
        fn()
        function fn() {
          console.log('name',this.name)
        }
    }
    foo() // ahwgs
    

    借用开头说的一句话,this指向的是这个函数所属的对象所以,fn指向的是全局对象

    • 显式绑定

    使用call/apply/bind方法进行显式绑定

    var name = 'ahwgs'
    function foo() {
        console.log('this',this)
        console.log('name',this.name)
    }
    
    var obj = {
        name:'obj'
    }
    
    foo.apply(obj) // name obj
    foo.call(obj)// name obj
    foo.bind(obj)()// name obj
    

    这时候的this指向都被显式绑定至obj,此后,无论如何调用函数,总会将obj绑定到foo中的this上。

    • new绑定

    通过new关键字调用的函数,属于new绑定模式。这时this关键字指向这个新创建的对象。

    function User(name,age) {
        this.name = name
        this.age = age
        this.getInfo = function() {
          console.log('info',this.name + '--->'+this.age)
        }
    }
    var user = new User('ahwgs',20)
    console.log(user) // User {name: "ahwgs", age: 20, getInfo: ƒ}
    console.log(user.getInfo()) // info ahwgs--->20
    
    • 总结:
    1. 函数是否是new绑定?如果是,则this指向新创建的对象
    2. 函数是否通过call/apply/bind显式绑定或硬绑定?如果是,则this指向指定的对象;
    3. 函数是否在某个上下文对象中隐式调用?如果是,this绑定的是那个上下文对象;
    4. 上述全不是,则使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局window对象。
    5. 记住:this 永远指向最后调用它的那个对象

    四、如何改变this指向

    • 使用ES6箭头函数

    箭头函数的 this 始终指向函数定义时的 this,而非执行时,箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined

    看一个栗子:

    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo1:function() {
          console.log('this.name',this.name)
        },
        foo2:function() {
          setTimeout(()=>{
            this.foo1()
          },100)
        }
    }
    obj.foo2() // aaa
    

    由于定时器中使用的是箭头函数的形式,上一级没有使用箭头函数,所以this绑定的是最近一层非箭头函数的this,在这里,即obj

    • 在函数内部const self = this
    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo1:function() {
          console.log('this.name',this.name)
        },
        foo2:function() {
         var self = this
         setTimeout(function() {
           self.foo1()
         })
        }
    }
    obj.foo2() // aaa
    

    定义一个变量,将当前的this指向改变至self中,这样调用foo1的时候this指向就是obj这个对象

    • 使用apply、call、bind

    先看一下这三个函数的使用方法:

    1. apply
    function.apply(obj, [param1,params2,...])
    // obj:要绑定的this
    // 第二个参数:类数组或数组,作为function的参数传入
    // 立即执行
    
    1. call
    function.call(obj, param1, param2, ...)
    // obj:要绑定的this
    // 第二个参数:函数运行的参数,用逗号隔开
    // 立即执行
    
    1. bind
    function.bind(obj, param1, param2, ...)
    // obj:要绑定的this
    // 第二个参数:函数运行的参数,用逗号隔开
    // 返回一个函数
    

    下面使用这三种方法修改this指向

    1. 使用apply
    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo1:function() {
          console.log('this.name',this.name)
        },
        foo2:function() {
          setTimeout(function() {
            this.foo1()
          }.apply(obj),100)
        }
    }
    obj.foo2() // aaa
    
    1. 使用call
    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo1:function() {
          console.log('this.name',this.name)
        },
        foo2:function() {
          setTimeout(function() {
            this.foo1()
          }.call(obj),100)
        }
    }
    obj.foo2() // aaa
    
    1. 使用bind
    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo1:function() {
          console.log('this.name',this.name)
        },
        foo2:function() {
          setTimeout(function() {
            this.foo1()
          }.bind(obj)(),100)
        }
    }
    obj.foo2() // aaa
    

    注意:bind与其他两种方法不同,因为他返回的是一个函数,所以需要我们()去调用它。

    1. 使用null/undefined作为bind/call/apply的参数
    var name = 'ahwgs'
    var obj = {
        name:'aaa',
        foo1:function() {
          console.log('this.name',this.name)
        },
        foo2:function() {
          setTimeout(function() {
            this.foo1()
          }.call(null),100)
        }
        }
    obj.foo2() //Uncaught TypeError: this.foo1 is not a function
    

    如果使用null/undefined作为参数的话,被调用的时候会被忽略,所以调用obj.foo2()的时候this指向的是全局对象,而全局对象中却没有foo2这个函数,所以报错。

    • new实例化新对象

    可看上述new绑定实例

    总结

    • js中的this指的是允许的上下文环境,与后端语言不同
    • this不是一成一变的,会随着环境而变化
    • 严格模式与非严格模式下的this也不一样
    • 可以使用多种方式修改this的指向
    • 本文首发于:你不知道的this关键字

    相关文章

      网友评论

          本文标题:JavaScript中的this

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