js之this的簡單總結

1 什麼是this

1.1 this 的指向

this 是 js 中的關鍵字,看起來很混亂,但其實很好理解,用網上通用的一句話來講就是: this 的指向在函數定義時肯定不了,只有函數指向的時候才能肯定 this 到底指向誰,也便是說this的卒中指向的是那個調用他的對象javascript

總結起來就六個字 誰調用,指向誰java

例子1數組

function foo() {
    var name = "我愛js";
    console.log("this", this); // this window
    console.log("name", this.name)// this undefined
}
foo()
複製代碼

爲何這裏的 this 是window對象,由於咱們是在全局做用域中調用的,其實是省略了 window. 看以下代碼的調用方式,跟上一個徹底相同bash

例子2app

function foo() {
    var name = "我愛js";
    console.log("this", this); // this window
    console.log("name", this.name)// this undefined
}
window.foo()
複製代碼

效果跟上面是徹底相同的函數

例子3ui

var foo = {
    name: "JavaScript",
    child: function () {
        console.log("this", this) // this {name: "JavaScript", child: ƒ}
        console.log("name", this.name) //name JavaScript
    }
}

foo.child()
複製代碼

在這裏調用者是 foo, 因此this指向的是 foo 然而事實是這樣嗎,再看看上面的例子this

例子4spa

var foo = {
    name: "JavaScript",
    child: function () {
        console.log("this", this) // this {name: "JavaScript", child: ƒ}
        console.log("name", this.name) //name JavaScript
    }
}

window.foo.child()
複製代碼

這裏最終調用者是window,可是this並無指向window,這是怎麼回事?難道是六字真言有問題?要弄懂這個問題,咱們須要再看幾個例子prototype

例子5

var foo = {
    a: 1,
    b: {
        a: 2,
        fn: function() {
            console.log(this.a) // 2
        } 
    }
}

foo.b.fn()
複製代碼

並且這個例子中的this也一樣沒指向foo, 看到這裏,可能會有疑惑,會推翻六字箴言,可是,若是再補充幾句話,就會消除歧義

  1. 非嚴格模式下,若是一個函數中有this,而且這個this沒有被上一級調用,那麼這個this的指向就是window,具體參考例子1,例子2

  2. 若是一個函數中有this,這個函數被上一級對象調用,那麼this就指向上一級對象,具體參考例子3

  3. 若是一個函數有this,假如調用這個函數的有多級對象,那麼this指向的是調用它的上一級對象,具體參考例子5

咱們再來驗證一下狀況3 例子 6

var foo = {
    a: 1,
    b: {
        //a: 2,
        fn: function() {
            console.log(this.a) // undefined
        } 
    }
}

foo.b.fn()
複製代碼

此時,咱們看到,輸入的值是undefined,也就是說,這個時候this指向的是b的做用域,而b中沒有定義a,因此輸出的是undefined 然而一切並非依靠想象,總有狀況例外,咱們看下一個例子

例子7

var foo = {
    a: 1,
    b: {
        a: 2,
        fn: function() {
            console.log(this) // window
            console.log(this.a) // undefined
        } 
    }
}

var tm = foo.b.fn
tm()
複製代碼

從這個例子中咱們看到,this指向的是window,這是怎麼回事?其實很好理解,還記得六字箴言嗎?誰調用,指向誰,這一句var tm = foo.b.fn只是進行了賦值,把 fn賦值給了全局變量 tm ,然而最終的調用時機是在 window做用域調用的函數 tm(), 因此 this的指向是window對象。至此, this 的指向已經完畢

1.2 this指向總結

就六個字 誰調用,指向誰,另加幾個條件

  1. 非嚴格模式下,若是一個函數中有this,而且這個this沒有被上一級調用,那麼這個this的指向就是window

  2. 若是一個函數中有this,這個函數被上一級對象調用,那麼this就指向上一級對象

  3. 若是一個函數有this,假如調用這個函數的有多級對象,那麼this指向的是調用它的上一級對象

2 改變 this 的指向

一般狀況下,咱們想使用其它環境變相下的狀態,這就須要藉助this來實現,經常使用改變this指向的有如下幾種方式能夠實現

  1. 構造函數
  2. call
  3. apply
  4. bind
2.1 構造函數改變this的指向

先看這個例子 例子8

function Foo () {
    this.name = 'Justin'
}

var f = new Foo()
console.log(f.name) // Justin
複製代碼

在使用 new 關鍵字調用構造函數時,會依次執行

  1. 首先建立一個新對象 f,並把構造函數的 prototype賦值給這個新對象的 __proto__
  2. 將這個新對象 f 賦值給this,並執行構造函數
  3. 若是函數返回了其它對象,那麼 new 表達式中的函數調用會自動返回這個新對象, 不然忽略,這句話的意思是,若是函數沒有 return一個對象, this表明的就是 new 出來的實例,不然,this指向的是返回的新對象

假如函數中有 return, 來看看this的指向? 看下面這個例子 例子9

function Foo () {
    this.name = 'Justin'
    return {}
}

var f = new Foo()
console.log(f.name) // undefined
複製代碼

在這裏, 輸出的是undefined,也就是說,this根本沒有指向 f, 那麼this到底指向哪裏了呢 其實,若是一個返回值是對象,那麼this指向的就是返回的對象, 若是反回的不是對象,那麼指向的仍是指向函數的實例, null除外

例子10 利用下面幾個例子驗證

function Foo () {
    this.name = 'Justin'
    return function(){}
}

var f = new Foo()
console.log(f.name) // undefined
複製代碼

例子11

function Foo () {
    this.name = 'Justin'
    return 1
}

var f = new Foo()
console.log(f.name) // Justin
複製代碼

例子12

function Foo () {
    this.name = 'Justin'
    return undefined
}

var f = new Foo()
console.log(f.name) // Justin
複製代碼

例子13

function Foo () {
    this.name = 'Justin'
    return null
}

var f = new Foo()
console.log(f.name) // Justin
複製代碼

以上跟第三點總結相符合,至此,構造函數改變this指向解析完畢

2.2 經過 call, apply, bind 改變 this 的指向

例子13

var w = "流星火雨"
var obj = {
    name: "黃忠",
    w: this.w,
    state: function(name, ow){
        console.log(`${this.name} 的 w 技能是 ${this.w}`)
    }
}

var skills = {
    name: '馬岱',
    w: "紫電箭雨"
}
obj.state() // 忠 的 w 技能是 undefined
obj.state.call(skills) //馬岱 的 w 技能是 紫電箭雨
obj.state.apply(skills) //馬岱 的 w 技能是 紫電箭雨
obj.state.bind(skills) //馬岱 的 w 技能是 紫電箭雨
複製代碼

從上面看出來, call, apply, bind把this的指向改變爲call, apply, bind所傳入的第一個參數, 除此以外,他們是沒有什麼區別的

2.3 call, apply, bind的區別

先看一個例子

var w = "流星火雨"
var obj = {
    name: "黃忠",
    w: this.w,
    state: function(HeroType, HP){
        console.log(`${this.name} 的 w 技能是 ${this.w}, 他是一個 ${HeroType}, 他的血量是${HP}`)
    }
}

var skills = {
    name: '馬岱',
    w: "紫電箭雨"
}
obj.state() // 忠 的 w 技能是 undefined
obj.state.call(skills, "法師", 100) //馬岱 的 w 技能是 紫電箭雨
obj.state.apply(skills, ["法師", 100]) //馬岱 的 w 技能是 紫電箭雨
obj.state.bind(skills, "法師", 100)() //馬岱 的 w 技能是 紫電箭雨
複製代碼

不一樣的是, apply第二個參數接受的是一個數組, call 直接按參數排列就能夠了,而 bind 跟call類似,只不事後面多了個 ()

最後,再加一個複雜的案例

function foo() {
    let args = Array.prototype.slice.apply(arguments)
    console.log("args", args) //[ '國家隊', '黃忠', '典韋', '張瑩瑩', '黃蓋', '許褚' ]
}

foo("國家隊", "黃忠", "典韋", "張瑩瑩", "黃蓋", "許褚")
複製代碼

更多請參考: www.iquanku.com/admin/28.ht…

相關文章
相關標籤/搜索