Javascript中的this是什麼樣的

1、什麼是this

this的值:當前執行代碼的環境對象,this的指向不取決於它在什麼位置建立,徹底取決於函數在什麼地方被調用this不能在執行期間被賦值,而且在每次函數被調用時this的值也可能會不一樣。javascript

2、this的值

全局環境(任何函數調用的外部)中,this的值都是全局對象(瀏覽器中是window對象,node中是global對象)html

函數內部環境,this的值取決於函數被調用的方式java

3、this指向規則

簡單總結一個原理:this 永遠指向最後調用它的那個對象node

  • 函數綁定

直接調用函數的時候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: ƒ}
複製代碼

在看下面一個栗子:app

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函數

下面咱們作一點改動:ui

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 永遠指向最後調用它的那個對象

4、如何改變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關鍵字
相關文章
相關標籤/搜索