成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JavaScript中this綁定詳解

開(kāi)發(fā) 前端
this 可以說(shuō)是 javascript 中最耐人尋味的一個(gè)特性,就像高中英語(yǔ)里各種時(shí)態(tài),比如被動(dòng)時(shí)態(tài),過(guò)去時(shí),現(xiàn)在時(shí),過(guò)去進(jìn)行時(shí)一樣,無(wú)論弄錯(cuò)過(guò)多少次,下一次依然可能弄錯(cuò)。本文啟發(fā)于《你不知道的JavaScript上卷》,對(duì) javasript 中的 this 進(jìn)行一個(gè)總結(jié)。

[[173217]]

this 可以說(shuō)是 javascript 中最耐人尋味的一個(gè)特性,就像高中英語(yǔ)里各種時(shí)態(tài),比如被動(dòng)時(shí)態(tài),過(guò)去時(shí),現(xiàn)在時(shí),過(guò)去進(jìn)行時(shí)一樣,無(wú)論弄錯(cuò)過(guò)多少次,下一次依然可能弄錯(cuò)。本文啟發(fā)于《你不知道的JavaScript上卷》,對(duì) javasript 中的 this 進(jìn)行一個(gè)總結(jié)。

學(xué)習(xí) this 的第一步就是明白 this 既不是指向函數(shù)自身也不指向函數(shù)的作用域。this 實(shí)際上是在函數(shù)被調(diào)用時(shí)發(fā)生的綁定,它指向什么地方完全取決于函數(shù)在哪里被調(diào)用。

默認(rèn)綁定

在 javascript 中 ,最常用的函數(shù)調(diào)用類(lèi)型就是獨(dú)立函數(shù)調(diào)用,因此可以把這條規(guī)則看作是無(wú)法應(yīng)用其他規(guī)則時(shí)的默認(rèn)規(guī)則。如果在調(diào)用函數(shù)的時(shí)候,函數(shù)不帶任何修飾,也就是“光禿禿”的調(diào)用,那就會(huì)應(yīng)用默認(rèn)綁定規(guī)則, 默認(rèn)綁定的指向的是全局作用域。

  1. function sayLocation() { 
  2.     console.log(this.atWhere) 
  3.  
  4. var atWhere = "I am in global" 
  5.  
  6. sayLocation() // 默認(rèn)綁定,this綁定在全局對(duì)象,輸出 “I am in global”  

再看一個(gè)例子

  1. var name = "global" 
  2. function person() { 
  3.     console.log(this.name) //  (1) "global" 
  4.       person.name = 'inside' 
  5.     function sayName() { 
  6.         console.log(this.name) // (2) "global"  不是 "inside" 
  7.     } 
  8.     sayName() // 在person函數(shù)內(nèi)部執(zhí)行sayName函數(shù),this指向的同樣是全局的對(duì)象 
  9. person()  

在這個(gè)例子中,person 函數(shù)在全局作用域中被調(diào)用,因此第(1)句中的 this 就綁定在了全局對(duì)象上(在瀏覽器中是是window,在node中就是global),因此第(1)句自然輸出的是一個(gè)全局對(duì)象的 name 屬性,當(dāng)然就是"global"了。sayName函數(shù)在person函數(shù)內(nèi)調(diào)用,即使這樣第(2)句中的this指代的仍然是全局對(duì)象,即使 person 函數(shù)設(shè)置了 name 屬性。

這就是默認(rèn)綁定規(guī)則,它是 javascript 中最常見(jiàn)的一種函數(shù)調(diào)用模式,this 的綁定規(guī)則也是四種綁定規(guī)則中最簡(jiǎn)單的一種,就是綁定在全局作用域上。

默認(rèn)綁定里的嚴(yán)格模式

在 javascript 中,如果使用了嚴(yán)格模式,則 this 不能綁定到全局對(duì)象。還是以第一個(gè)例子,只不過(guò)這次加上了嚴(yán)格模式聲明

  1. 'use strict' 
  2. function sayLocation() { 
  3.     console.log(this.atWhere) 
  4. var atWhere = "I am in global" 
  5. sayLocation() 
  6. // Uncaught TypeError: Cannot read property 'atWhere' of undefined  

可以看出,在嚴(yán)格模式下,把 this 綁定到全局對(duì)象上時(shí),實(shí)際上綁定的是 undefined ,因此上面這段代碼會(huì)報(bào)錯(cuò)。

隱式綁定

當(dāng)函數(shù)在調(diào)用時(shí),如果函數(shù)有所謂的“落腳點(diǎn)”,即有上下文對(duì)象時(shí),隱式綁定規(guī)則會(huì)把函數(shù)中的 this 綁定到這個(gè)上下文對(duì)象。如果覺(jué)得上面這段話不夠直白的話,還是來(lái)看代碼。

  1. function say() { 
  2.     console.log(this.name
  3. var obj1 = { 
  4.     name"zxt"
  5.     say: say 
  6.  
  7. var obj2 = { 
  8.     name"zxt1"
  9.     say: say 
  10. obj1.say() // zxt 
  11. obj2.say() // zxt1  

很簡(jiǎn)單是不是。在上面這段代碼中,obj1 , obj2 就是所謂的 say 函數(shù)的落腳點(diǎn),專(zhuān)業(yè)一點(diǎn)的說(shuō)法就是上下文對(duì)象,當(dāng)給函數(shù)指定了這個(gè)上下文對(duì)象時(shí),函數(shù)內(nèi)部的this 自然指向了這個(gè)上下文對(duì)象。這也是很常見(jiàn)的一種函數(shù)調(diào)用模式。

隱式綁定時(shí)丟失上下文

  1. function say() { 
  2.     console.log(this.name
  3. var name = "global" 
  4. var obj = { 
  5.     name"inside"
  6.     say: say 
  7. var alias = obj.say // 設(shè)置一個(gè)簡(jiǎn)寫(xiě)   (1)  
  8. alias() // 函數(shù)調(diào)用 輸出"global"  (2)  

可以看到這里輸出的是 ”global“ ,為什么就和上例中不一樣,我們明明只是給 obj.say 換了個(gè)名字而已?

首先我們來(lái)看上面第(1)句代碼,由于在 javascript 中,函數(shù)是對(duì)象,對(duì)象之間是引用傳遞,而不是值傳遞。因此,第(1)句代碼只是alias = obj.say = say ,也就是 alias = say ,obj.say 只是起了一個(gè)橋梁的作用,alias 最終引用的是 say 函數(shù)的地址,而與 obj 這個(gè)對(duì)象無(wú)關(guān)了。這就是所謂的”丟失上下文“。最終執(zhí)行 alias 函數(shù),只不過(guò)簡(jiǎn)單的執(zhí)行了say函數(shù),輸出"global"。

顯式綁定

顯式綁定,顧名思義,顯示地將this綁定到一個(gè)上下文,javascript中,提供了三種顯式綁定的方法,apply,call,bind。apply和call的用法基本相似,它們之間的區(qū)別是:

apply(obj,[arg1,arg2,arg3,...] 被調(diào)用函數(shù)的參數(shù)以數(shù)組的形式給出

call(obj,arg1,arg2,arg3,...) 被調(diào)用函數(shù)的參數(shù)依次給出

而bind函數(shù)執(zhí)行后,返回的是一個(gè)新函數(shù)。下面以代碼說(shuō)明。

  1. // 不帶參數(shù) 
  2. function speak() { 
  3.     console.log(this.name
  4.  
  5. var name = "global" 
  6. var obj1 = { 
  7.     name'obj1' 
  8. var obj2 = { 
  9.     name'obj2' 
  10.  
  11. speak() // global 等價(jià)于speak.call(window) 
  12. speak.call(window) 
  13.  
  14. speak.call(obj1) // obj1 
  15. speak.call(obj2) // obj2  

因此可以看出,apply, call 的作用就是給函數(shù)綁定一個(gè)執(zhí)行上下文,且是顯式綁定的。因此,函數(shù)內(nèi)的this自然而然的綁定在了call 或者 apply 所調(diào)用的對(duì)象上面。

  1. // 帶參數(shù) 
  2. function count(num1, num2) { 
  3.     console.log(this.a * num1 + num2) 
  4.  
  5. var obj1 = { 
  6.     a: 2 
  7. var obj2 = { 
  8.     a: 3 
  9.  
  10. count.call(obj1, 1, 2) // 4 
  11. count.apply(obj1, [1, 2]) // 4 
  12.  
  13. count.call(obj2, 1, 2) // 5 
  14. count.apply(obj2, [1, 2]) // 5  

上面這個(gè)例子則說(shuō)明了 apply 和 call 用法上的差異。

而 bind 函數(shù),則返回一個(gè)綁定了指定的執(zhí)行上下文的新函數(shù)。還是以上面這段代碼為例

  1. // 帶參數(shù) 
  2. function count(num1, num2) { 
  3.     console.log(this.a * num1 + num2) 
  4.  
  5. var obj1 = { 
  6.     a: 2 
  7.  
  8. var bound1 = count.bind(obj1) // 未指定參數(shù) 
  9. bound1(1, 2) // 4 
  10.  
  11. var bound2 = count.bind(obj1, 1) // 指定了一個(gè)參數(shù) 
  12. bound2(2) // 4  
  13.  
  14. var bound3 = count.bind(obj1, 1, 2) // 指定了兩個(gè)參數(shù) 
  15. bound3() //4 
  16.  
  17. var bound4 = count.bind(obj1, 1, 2, 3) // 指定了多余的參數(shù),多余的參數(shù)會(huì)被忽略 
  18. bound4() // 4 

 所以,bind 方法只是返回了一個(gè)新的函數(shù),這個(gè)函數(shù)內(nèi)的this指定了執(zhí)行上下文,而返回這個(gè)新函數(shù)可以接受參數(shù)。

new 綁定

最后要講的一種 this 綁定規(guī)則,是指通過(guò) new 操作符調(diào)用構(gòu)造函數(shù)時(shí)發(fā)生的 this 綁定。首先要明確一點(diǎn)的是,在 javascript 中并沒(méi)有其他語(yǔ)言那樣的類(lèi)的概念。構(gòu)造函數(shù)也僅僅是普通的函數(shù)而已,只不過(guò)構(gòu)造函數(shù)的函數(shù)名以大寫(xiě)字母開(kāi)頭,也只不過(guò)它可以通過(guò)new 操作符調(diào)用而已.

  1. function Person(name,age) { 
  2.     this.name = name 
  3.     this.age = age 
  4.     console.log("我也只不過(guò)是個(gè)普通函數(shù)"
  5. Person("zxt",22) // "我也只不過(guò)是個(gè)普通函數(shù)" 
  6. console.log(name) // "zxt" 
  7. console.log(age) // 22 
  8.  
  9. var zxt = new Person("zxt",22) // "我也只不過(guò)是個(gè)普通函數(shù)" 
  10. console.log(zxt.name) // "zxt" 
  11. console.log(zxt.age) // 22  

上面這個(gè)例子中,首先定義了一個(gè) Person 函數(shù),既可以普通調(diào)用,也可以以構(gòu)造函數(shù)的形式的調(diào)用。當(dāng)普通調(diào)用時(shí),則按照正常的函數(shù)執(zhí)行,輸出一個(gè)字符串。 如果是通過(guò)一個(gè)new操作符,則構(gòu)造了一個(gè)新的對(duì)象。那么,接下來(lái)我們?cè)倏纯磧煞N調(diào)用方式, this 分別綁定在了何處首先普通調(diào)用時(shí),前面已經(jīng)介紹過(guò),此時(shí)應(yīng)用默認(rèn)綁定規(guī)則,this綁定在了全局對(duì)象上,此時(shí)全局對(duì)象上會(huì)分別增加name 和 age 兩個(gè)屬性。當(dāng)通過(guò)new操作符調(diào)用時(shí),函數(shù)會(huì)返回一個(gè)對(duì)象,從輸出結(jié)果上來(lái)看 this 對(duì)象綁定在了這個(gè)返回的對(duì)象上。

因此,所謂的new綁定是指通過(guò)new操作符來(lái)調(diào)用函數(shù)時(shí),會(huì)產(chǎn)生一個(gè)新對(duì)象,并且會(huì)把構(gòu)造函數(shù)內(nèi)的this綁定到這個(gè)對(duì)象上。

事實(shí)上,在javascript中,使用new來(lái)調(diào)用函數(shù),會(huì)自動(dòng)執(zhí)行下面的操作。

  1. 創(chuàng)建一個(gè)全新的對(duì)象
  2. 這個(gè)新對(duì)象會(huì)被執(zhí)行原型連接
  3. 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this
  4. 如果函數(shù)沒(méi)有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象

四種綁定的優(yōu)先級(jí)

上面講述了javascript中四種this綁定規(guī)則,這四種綁定規(guī)則基本上涵蓋了所有函數(shù)調(diào)用情況。但是如果同時(shí)應(yīng)用了這四種規(guī)則中的兩種甚至更多,又該是怎么樣的一個(gè)情況,或者說(shuō)這四種綁定的優(yōu)先級(jí)順序又是怎么樣的。

首先,很容易理解,默認(rèn)綁定的優(yōu)先級(jí)是最低的。這是因?yàn)橹挥性跓o(wú)法應(yīng)用其他this綁定規(guī)則的情況下,才會(huì)調(diào)用默認(rèn)綁定。那隱式綁定和顯式綁定呢?還是上代碼吧,代碼可從來(lái)不會(huì)說(shuō)謊。

  1. function speak() { 
  2.     console.log(this.name
  3.  
  4. var obj1 = { 
  5.     name'obj1'
  6.     speak: speak 
  7. var obj2 = { 
  8.     name'obj2' 
  9.  
  10. obj1.speak() // obj1 (1) 
  11. obj1.speak.call(obj2) // obj2 (2)  

所以在上面代碼中,執(zhí)行了obj1.speak(),speak函數(shù)內(nèi)部的this指向了obj1,因此(1)處代碼輸出的當(dāng)然就是obj1,但是當(dāng)顯式綁定了speak函數(shù)內(nèi)的this到obj2上,輸出結(jié)果就變成了obj2,所有從這個(gè)結(jié)果可以看出顯式綁定的優(yōu)先級(jí)是要高于隱式綁定的。事實(shí)上我們可以這么理解obj1.speak.call(obj2)這行代碼,obj1.speak只是間接獲得了speak函數(shù)的引用,這就有點(diǎn)像前面所說(shuō)的隱式綁定丟失了上下文。好,既然顯式綁定的優(yōu)先級(jí)要高于隱式綁定,那么接下來(lái)再來(lái)比較一下new 綁定和顯式綁定。

  1. function foo(something) { 
  2.     this.a = something 
  3.  
  4. var obj1 = {} 
  5. var bar = foo.bind(obj1)  // 返回一個(gè)新函數(shù)bar,這個(gè)新函數(shù)內(nèi)的this指向了obj1  (1) 
  6. bar(2) // this綁定在了Obj1上,所以obj1.a === 2 
  7. console.log(obj1.a) 
  8.  
  9. var baz = new bar(3)  // 調(diào)用new 操作符后,bar函數(shù)的this指向了返回的新實(shí)例baz  (2) 
  10.  
  11. console.log(obj1.a) 
  12. console.log(baz.a)   

我們可以看到,在(1)處,bar函數(shù)內(nèi)部的this原本指向的是obj1,但是在(2)處,由于經(jīng)過(guò)了new操作符調(diào)用,bar函數(shù)內(nèi)部的this卻重新指向了返回的實(shí)例,這就可以說(shuō)明new 綁定的優(yōu)先級(jí)是要高于顯式綁定的。

至此,四種綁定規(guī)則的優(yōu)先級(jí)排序就已經(jīng)得出了,分別是

new 綁定 > 顯式綁定 > 隱式綁定 > 默認(rèn)綁定

箭頭函數(shù)中的this綁定

箭頭函數(shù)是ES6里一個(gè)重要的特性。

箭頭函數(shù)的this是根據(jù)外層的(函數(shù)或者全局)作用域來(lái)決定的。函數(shù)體內(nèi)的this對(duì)象指的是定義時(shí)所在的對(duì)象,而不是之前介紹的調(diào)用時(shí)綁定的對(duì)象。舉一個(gè)例子

  1. var a = 1 
  2. var foo = () => { 
  3.     console.log(this.a) // 定義在全局對(duì)象中,因此this綁定在全局作用域 
  4.  
  5. var obj = { 
  6.     a: 2 
  7. foo() // 1 ,在全局對(duì)象中調(diào)用 
  8. foo.call(obj) // 1,顯示綁定,由obj對(duì)象來(lái)調(diào)用,但根本不影響結(jié)果  

從上面這個(gè)例子看出,箭頭函數(shù)的 this 強(qiáng)制性的綁定在了箭頭函數(shù)定義時(shí)所在的作用域,而且無(wú)法通過(guò)顯示綁定,如apply,call方法來(lái)修改。在來(lái)看下面這個(gè)例子

  1. // 定義一個(gè)構(gòu)造函數(shù) 
  2. function Person(name,age) { 
  3.     this.name = name 
  4.     this.age = age  
  5.     this.speak = function (){ 
  6.         console.log(this.name
  7.         // 普通函數(shù)(非箭頭函數(shù)),this綁定在調(diào)用時(shí)的作用域 
  8.     } 
  9.     this.bornYear = () => { 
  10.         // 本文寫(xiě)于2016年,因此new Date().getFullYear()得到的是2016 
  11.         // 箭頭函數(shù),this綁定在實(shí)例內(nèi)部 
  12.         console.log(new Date().getFullYear() - this.age) 
  13.         } 
  14.     } 
  15.  
  16. var zxt = new Person("zxt",22) 
  17.  
  18. zxt.speak() // "zxt" 
  19. zxt.bornYear() // 1994 
  20.  
  21. // 到這里應(yīng)該大家應(yīng)該都沒(méi)什么問(wèn)題 
  22.  
  23. var xiaoMing = { 
  24.     name"xiaoming"
  25.     age: 18  // 小明永遠(yuǎn)18歲 
  26.  
  27. zxt.speak.call(xiaoMing) 
  28. // "xiaoming" this綁定的是xiaoMing這個(gè)對(duì)象 
  29. zxt.bornYear.call(xiaoMing) 
  30. // 1994 而不是 1998,這是因?yàn)閠his永遠(yuǎn)綁定的是zxt這個(gè)實(shí)例  

因此 ES6 的箭頭函數(shù)并不會(huì)使用四條標(biāo)準(zhǔn)的綁定規(guī)則,而是根據(jù)當(dāng)前的詞法作用域來(lái)決定 this ,具體來(lái)說(shuō)就是,箭頭函數(shù)會(huì)繼承 外層函數(shù)調(diào)用的this綁定 ,而無(wú)論外層函數(shù)的this綁定到哪里。

小結(jié)

以上就是javascript中所有this綁定的情況,在es6之前,前面所說(shuō)的四種綁定規(guī)則可以涵蓋任何的函數(shù)調(diào)用情況,es6標(biāo)準(zhǔn)實(shí)施以后,對(duì)于函數(shù)的擴(kuò)展新增了箭頭函數(shù),與之前不同的是,箭頭函數(shù)的作用域位于箭頭函數(shù)定義時(shí)所在的作用域。

而對(duì)于之前的四種綁定規(guī)則來(lái)說(shuō),掌握每種規(guī)則的調(diào)用條件就能很好的理解this到底是綁定在了哪個(gè)作用域。

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2020-02-19 14:02:49

JavaScriptthis前端

2013-05-08 10:36:07

JavaScriptJS詳解JavaScrip

2010-04-23 13:23:42

Silverlight

2017-03-20 14:45:42

JavaScript詳解

2010-10-09 09:56:51

JavaScriptObject對(duì)象

2009-09-21 16:59:29

Array擴(kuò)展

2012-05-28 10:34:50

MVVM 數(shù)據(jù)綁定

2015-12-24 10:05:39

JavaScripttypeofinstanceof

2016-12-27 10:19:42

JavaScriptindexOf

2010-09-08 15:13:09

Node節(jié)點(diǎn)Node屬性

2009-10-26 15:07:12

checkbox樹(shù)

2012-02-14 09:45:02

JavaScript

2024-04-26 08:27:15

JavaScriptCSSHTML元素

2020-11-18 09:06:02

JavaScript開(kāi)發(fā)技術(shù)

2016-08-12 11:04:17

JavaScript物聯(lián)網(wǎng)應(yīng)用

2009-11-30 17:40:13

Suse雙網(wǎng)卡綁定

2009-11-06 13:28:19

Javascript框

2011-09-09 17:31:45

Android WebJavascript

2017-10-27 22:03:35

javascrip

2009-03-17 15:36:29

JavaScript循環(huán)事件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 99精品电影| 国产偷录视频叫床高潮对白 | 国产电影一区二区三区爱妃记 | 99久久日韩精品免费热麻豆美女 | 国产精品一区在线观看 | www四虎影视 | 成人免费在线播放视频 | 亚洲国产成人久久综合一区,久久久国产99 | 午夜网| 天天夜碰日日摸日日澡 | 国产精品视频500部 a久久 | 欧美爱爱视频网站 | 人人精品 | 天天搞夜夜操 | 99精品国产一区二区青青牛奶 | 婷婷精品| 亚洲第一av | 综合视频在线 | av影音资源 | 美女日批免费视频 | 97在线观视频免费观看 | 免费性视频 | 欧美日韩一区二区在线观看 | 国产精品久久久久久久久久久久久久 | 国产成人一区二区三区精 | 天天做日日做 | 国产激情免费视频 | 久久精品国产一区二区电影 | 国产精品久久免费观看 | 国产精品mv在线观看 | 久久久久久天堂 | 成人九区| 福利视频网站 | 亚洲美女一区 | 亚洲欧美综合 | 欧美成人h版在线观看 | 亚洲精品68久久久一区 | 亚洲精品日韩视频 | 国产一区二区在线免费观看 | 日本黄色大片免费 | 久久精品99久久 |