Javascirpt學(xué)習(xí)筆記-This

Javascript中this.png

1. 關(guān)于This

相比在Java中this只指代當(dāng)前類的用法薛耻,Javascript中this的并不是一個(gè)固定的指代,其指代存在綁定規(guī)則

1.1 使用原因

其實(shí)大部分情況下,使用詞法作用域已經(jīng)可以解決所有需要使用this的場景苦丁,代碼中完全可以不使用this。但是使用this可以帶來函數(shù)調(diào)用過程中對于函數(shù)參數(shù)傳遞的優(yōu)化物臂。舉個(gè)例子:

function foo(a, b){
  console.log(a + b);
}
f(1, 2); // 3

在函數(shù)調(diào)用過程中需要傳遞兩個(gè)參數(shù)進(jìn)行調(diào)用旺拉,但是如果參數(shù)是某個(gè)對象中的某些屬性,那么使用這種調(diào)用方式就變成如下情況:

 var obj = {a: 1 ,b: 2};
 foo(obj.a, obj.b); // 3

在傳遞參數(shù)的時(shí)候明顯比較麻煩棵磷,尤其在于如果要利用對象中的復(fù)數(shù)屬性的時(shí)候蛾狗,參數(shù)傳遞就變得特別多,這個(gè)時(shí)候使用this就可以進(jìn)行簡化:

function foo() {
  console.log(this.a + this.b);
}
var obj = {a: 1, b: 2};
foo.apply(obj); // 3

當(dāng)然我們完全可以將obj作為參數(shù)進(jìn)行傳遞仪媒,所以說使用this并非開發(fā)中必須沉桌。

1.2 綁定要點(diǎn)

this到底代表什么關(guān)鍵在于this的綁定規(guī)則,但無論什么規(guī)則算吩,首先要明確兩點(diǎn):第一留凭,this不代表自身,第二偎巢,this和詞法作用域沒有必然聯(lián)系例:

function foo() {
  console.log(this.a); // 這里的this并不指代foo函數(shù)本身蔼夜,也和詞法作用域無關(guān)
}

以上兩點(diǎn)從根本上來說指在函數(shù)進(jìn)行聲明的時(shí)候,并不能確定this代表什么压昼,只有在函數(shù)調(diào)用的時(shí)候求冷,this的綁定關(guān)系才可以確定(就像動(dòng)態(tài)作用域一樣)
this只有在函數(shù)調(diào)用的時(shí)候確定綁定關(guān)系
this只有在函數(shù)調(diào)用的時(shí)候確定綁定關(guān)系
this只有在函數(shù)調(diào)用的時(shí)候確定綁定關(guān)系

重要的事情說三遍,不過再補(bǔ)充一下:

箭頭函數(shù)是特別的
箭頭函數(shù)是特別的
箭頭函數(shù)是特別的

2. 綁定規(guī)則

根據(jù)Javascript中的用法窍霞,this有以下的幾種綁定規(guī)則:

2.1 全局綁定

直接在Javascript全局中使用this匠题,這個(gè)時(shí)候this指向一個(gè)全局對象,在瀏覽器中代表window對象但金,node中代表global對象:

 console.log(this);

2.2 Function Invoke(默認(rèn)綁定)

直接調(diào)用方法的時(shí)候韭山,如果在嚴(yán)格模式下,this的綁定為undefined(之所以返回undefined,是因?yàn)楹瘮?shù)調(diào)用的時(shí)候執(zhí)行的上下文并不確定钱磅,嚴(yán)格模式于是將執(zhí)行中的this綁定為undefined)巩踏,非嚴(yán)格模式下,this綁定為全局對象续搀,同 2.1 全局綁定

// 1. 非嚴(yán)格模式下
function foo(){
  console.log(this);
}
foo(); // window
// 2. 嚴(yán)格模式下
(function(){
  'use strict'
  function foo() {
    console.log(this);
  }
  foo(); // undefined
})()
// 3. 嚴(yán)格模式下調(diào)用
(function(){
  'use strict'
  foo(); // window
})()

請注意例子中的第三個(gè)調(diào)用塞琼,在調(diào)用的時(shí)候使用了嚴(yán)格模式,但是打印結(jié)果并不是undefined禁舷,因此說明嚴(yán)格模式是在函數(shù)聲明的時(shí)候?qū)?code>this的綁定產(chǎn)生影響彪杉,在調(diào)用的時(shí)候?qū)Υ瞬]有影響。
如果之后所有的規(guī)則都不符合的時(shí)候牵咙,將使用這一條規(guī)則派近,來進(jìn)行this的綁定。

2.3 Method Invoke(隱式綁定)

當(dāng)使用對象調(diào)用的時(shí)候洁桌,this將綁定為該對象:

var obj = {
  a: 1,
  foo: function() {
    console.log(this.a);
  }
}
obj.foo(); // 1

但是隱式綁定過程存在兩種隱式丟失的情況(可以使用箭頭函數(shù)解決渴丸,詳見 2.6 箭頭函數(shù)),使用函數(shù)別名和回調(diào)函數(shù):

var obj = {
  a: 1,
  foo: function() {
    console.log(this.a);
  }
}
// 1. 使用函數(shù)別名
var baz = obj.foo;
baz(); // undefined
// 2. 使用回調(diào)函數(shù)
setTimeout(obj.foo , 0); // undefined

原因在于在使用函數(shù)別名和回調(diào)函數(shù)的時(shí)候另凌,可以理解為進(jìn)行了以下的操作

var baz = obj.foo = function() {
  console.log(this.a);
}      

于是谱轨,在調(diào)用baz的時(shí)候,將根據(jù) 2.2 默認(rèn)綁定this進(jìn)行處理

2.4 apply, call, bind (顯示綁定)

使用顯示綁定的時(shí)候吠谢,this綁定為傳入的對象:

function foo(a, b, c){
  console.log(this.a + a + b + c);
}
foo.apply({a: 1}, [1, 2, 3]) // 1 + 1 + 2 + 3 = 7
foo.call({a: 1}, 1, 2, 3) // 7
foo.bind({a: 1})(1, 2, 3) // 7

applycall的使用本質(zhì)上沒有區(qū)別土童,只是傳遞參數(shù)不同
bind是ES5之后Function原型鏈上增加的方法,先將this綁定到傳遞的對象工坊,但是使用bind進(jìn)行綁定的時(shí)候是硬綁定献汗,硬綁定是指這個(gè)綁定只能綁定一次,只有第一次綁定可以生效:

function foo() {
  console.log(this.a);
}
foo.bind({a: 1}).bind({a: 2})(); // 1

因?yàn)楝F(xiàn)在很多內(nèi)置對象提供了原型鏈上的方法王污,于是通過這種原型鏈上API的調(diào)用是顯示調(diào)用的過程

function foo(i) {
  console.log(this.a + i);
}
[1,2,3].forEach(foo, {a: 1}); // 2 3 4 

2.5 new(構(gòu)造調(diào)用)

使用new進(jìn)行綁定的時(shí)候罢吃,this綁定為構(gòu)建函數(shù)構(gòu)造的新對象,例:

function F() {
  this.a = 1;
}
var f = new F(); // 等同于 var f = new F;
console.log(f.a); // 1

這里使用new進(jìn)行構(gòu)造的時(shí)候昭齐,Javascript會(huì)按照以下的過程執(zhí)行
創(chuàng)建一個(gè)新對象 --> 將對象和構(gòu)造方法的prototype做關(guān)聯(lián) --> 將this綁定到該對象 --> 如果構(gòu)造函數(shù)無返回值則返回該對象尿招,否則返回新對象
所以糟袁,轉(zhuǎn)換為代碼理解也就是:

// 1. 無返回的構(gòu)造函數(shù)
function F() {
  this.a = 1;
}
/* 使用new以后异赫,函數(shù)內(nèi)部變化為
 function F() {
   var _o_ = {} ; // 創(chuàng)建一個(gè)新對象
   _o_.prototpye = F.prototype; // 原型鏈關(guān)聯(lián)
   _o_.a = 1; // this綁定到對象
   return _o_; // 返回該對象 
 }
*/
var f = new F();
console.log(f.a); // 1
// 2. 有返回的構(gòu)造函數(shù)
function F() {
  this.a = 1;
  return {b: 2};
}
/* 使用new以后,函數(shù)內(nèi)部變化為
 function F() {
   var _o_ = {} ; // 創(chuàng)建一個(gè)新對象
   _o_.prototpye = F.prototype; // 原型鏈關(guān)聯(lián)
   _o_.a = 1; // this綁定到對象
   return {b: 2}; // 返回原返回值 
 }
*/
var f = new F();
console.log(f.a) // undefined
console.log(f.b) // 2

于是,如果原構(gòu)造函數(shù)存在返回值的情況下啊易,this綁定為了函數(shù)內(nèi)部創(chuàng)建的新對象,但是這個(gè)新創(chuàng)建的對象并沒有返回饮睬,所以沒辦法再對這個(gè)綁定的內(nèi)容進(jìn)行引用了租谈。

2.6 箭頭函數(shù)

使用箭頭函數(shù)進(jìn)行綁定的時(shí)候,this綁定外層(函數(shù)或者全局)作用域環(huán)境:

var obj = {
  a: 1,
  foo: () => {
    console.log(this);
  }
}
obj.foo(); // window

這個(gè)例子看上去和 2.3 隱式綁定 是差不多的,但是區(qū)別這里的this指向發(fā)生變化割去,this不再指向調(diào)用對象本身窟却,而是直接使用外層函數(shù)全局對象。
但是下面的例子是需要注意的:

function f(){
    return function() {
        console.log(this.a);
    }
}
f.apply({a:1}).apply({a:2}) // 2;
// 箭頭函數(shù)
function f(){
    return() => {
        console.log(this.a);
    }
}
f.apply({a:1}).apply({a:2}) // 1 

在第一次調(diào)用 apply的時(shí)候呻逆,生成了一個(gè)外層的作用域夸赫,此時(shí)this指向了該外層全局對象的{a: 1},之后不會(huì)再發(fā)生變化

2.7 事件綁定

使用事件函數(shù)進(jìn)行綁定的時(shí)候咖城,根據(jù)使用情況不同茬腿,this通常指向當(dāng)前事件節(jié)點(diǎn),例:

// 1. 使用addEventListener等事件綁定宜雀,this指向綁定的節(jié)點(diǎn)
<html>
  <body>
    <button id="d">click</button>
    <script>
    document.getElementById('d').addEventListener('click', function(e){
      console.log(this === e.currentTarget); // true
      console.log(this === e.target); // true
    })
    </script>
  </body>
</html>
// 2. 使用html事件綁定切平,this指向當(dāng)前dom節(jié)點(diǎn)
<html>
  <body>
    <button id="d" onclick="console.log(this);">click</button>
  </body>
</html>
// 3. 使用html事件綁定,增加IIFE辐董,this指向window
<html>
  <body>
    <button id="d" onclick="(function(){console.log(this)})()">click</button>
  </body>
</html>

3. 優(yōu)先級

當(dāng)我們?nèi)绻褂玫那闆r比較復(fù)雜悴品,同時(shí)存在以上幾種綁定關(guān)系的時(shí)候,我們該如何處理简烘?實(shí)際上綁定的關(guān)系存在一個(gè)優(yōu)先級苔严,按照優(yōu)先級來進(jìn)行處理(這里除開 2.7 事件綁定,因?yàn)橥ǔJ录壎ㄏ虏粫?huì)存在那么復(fù)雜的關(guān)系)
2.5 構(gòu)造綁定 > 2.4 顯示綁定 > 2.3 隱式綁定 > 2.2 默認(rèn)綁定
2.6 箭頭函數(shù) 有特殊表現(xiàn)
例:

// 1. 隱式綁定 > 默認(rèn)綁定
function foo() {
  console.log(this.a);
}
var obj = {a: 1, foo: foo};
obj.foo(); // 1
// 2. 顯示綁定 > 隱式綁定
obj.foo.apply({a: 2}) // 2
// 3. 構(gòu)造綁定 > 顯示綁定
function F(a) {
  this.a = a;
}
var obj = {};
var bar = F.bind(obj);
bar(1);
console.log(obj.a); // 1
var obj2 = new bar(2);// 使用new修改了this的綁定
console.log(obj.a); // 1 原來的值沒有發(fā)生變化
console.log(obj2.a); // 2 新的值發(fā)生了變化
// 4. 箭頭函數(shù)的特殊表現(xiàn)
var a = 2;
var f = () => {
  console.log(this.a);
}
f.apply({a: 3}); // 2

4. 其他

4.1 顯示綁定中null的使用

在進(jìn)行顯示綁定的時(shí)候孤澎,如果傳遞綁定對象為null時(shí)邦蜜,因?yàn)椴环衔覀冋f的其他規(guī)則,所以將使用默認(rèn)規(guī)則:

function foo() {
  console.log(this);
}
foo.apply(null) // window

但是如果直接傳遞null進(jìn)行方法調(diào)用存在變量泄漏的風(fēng)險(xiǎn):

function foo() {
  this.a = 1;
}
foo.apply(null);
console.log(a); // 1

4.2 Object.create(null)

為了解決 4.1 顯示綁定中null的使用 存在的變量泄漏風(fēng)險(xiǎn)亥至,于是可以使用Object.create(null)創(chuàng)建一個(gè)空對象悼沈,使用這種方式創(chuàng)建的空對象,比{}更純粹姐扮,因?yàn)樗焕^承Object.prototype

function foo() {
  this.a = 1;
}
foo.apply(Object.create(null));
console.log(a); // ReferenceError

5. 參考

《你不知道的Javascript(上卷)》
MDN this
Gentle explanation of 'this' keyword in JavaScript
Function.prototype.call
Function.prototype.apply
Function.prototype.bind

6. 練習(xí)

慣例最后來個(gè)練習(xí)吧

function foo() {
  console.log(this.a);
}
var obj = {
  a: 1,
  foo: foo
}
var obj2 = {
  a: 1,
  foo: () => {
    console.log(this.a);
  }
}
function F() {
  this.a = 2;
}
var f = new F();
foo(); // undefined
obj.foo(); // 1
obj.foo.bind({a: 2})(); // 2
obj2.foo.bind({a: 2})(); // undefined
console.log(f.a); // 2
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末絮供,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子茶敏,更是在濱河造成了極大的恐慌壤靶,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惊搏,死亡現(xiàn)場離奇詭異贮乳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恬惯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評論 3 385
  • 文/潘曉璐 我一進(jìn)店門向拆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酪耳,你說我怎么就攤上這事浓恳。” “怎么了?”我有些...
    開封第一講書人閱讀 158,160評論 0 348
  • 文/不壞的土叔 我叫張陵颈将,是天一觀的道長梢夯。 經(jīng)常有香客問我,道長晴圾,這世上最難降的妖魔是什么颂砸? 我笑而不...
    開封第一講書人閱讀 56,736評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮死姚,結(jié)果婚禮上沾凄,老公的妹妹穿的比我還像新娘。我一直安慰自己知允,他們只是感情好撒蟀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著温鸽,像睡著了一般保屯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涤垫,一...
    開封第一講書人閱讀 50,043評論 1 291
  • 那天姑尺,我揣著相機(jī)與錄音,去河邊找鬼蝠猬。 笑死切蟋,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的榆芦。 我是一名探鬼主播柄粹,決...
    沈念sama閱讀 39,129評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匆绣!你這毒婦竟也來了驻右?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評論 0 268
  • 序言:老撾萬榮一對情侶失蹤崎淳,失蹤者是張志新(化名)和其女友劉穎堪夭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拣凹,經(jīng)...
    沈念sama閱讀 44,318評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡森爽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嚣镜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爬迟。...
    茶點(diǎn)故事閱讀 38,777評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖祈惶,靈堂內(nèi)的尸體忽然破棺而出雕旨,到底是詐尸還是另有隱情扮匠,我是刑警寧澤捧请,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布凡涩,位于F島的核電站,受9級特大地震影響疹蛉,放射性物質(zhì)發(fā)生泄漏活箕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一可款、第九天 我趴在偏房一處隱蔽的房頂上張望育韩。 院中可真熱鬧,春花似錦闺鲸、人聲如沸筋讨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悉罕。三九已至,卻和暖如春立镶,著一層夾襖步出監(jiān)牢的瞬間壁袄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工媚媒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗜逻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評論 2 362
  • 正文 我出身青樓缭召,卻偏偏與公主長得像栈顷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子嵌巷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評論 2 351

推薦閱讀更多精彩內(nèi)容