JavaScript之this

一途蒋、this的綁定規(guī)則

1. 默認(rèn)綁定

什么情況下使用默認(rèn)綁定呢?獨(dú)立函數(shù)調(diào)用。
獨(dú)立的函數(shù)調(diào)用我們可以理解成函數(shù)沒有被綁定到某個對象上進(jìn)行調(diào)用剔应;

案例一:普通函數(shù)調(diào)用
該函數(shù)直接被調(diào)用,并沒有進(jìn)行任何的對象關(guān)聯(lián);這種獨(dú)立的函數(shù)調(diào)用會使用默認(rèn)綁定峻贮,通常默認(rèn)綁定時席怪,函數(shù)中的this指向全局對象(window);

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

foo();

案例二:函數(shù)調(diào)用鏈(一個函數(shù)又調(diào)用另外一個函數(shù))
所有的函數(shù)調(diào)用都沒有被綁定到某個對象上纤控;

// 案例二:
function test1() {
  console.log(this); // window
  test2();
}

function test2() {
  console.log(this); // window
  test3()
}

function test3() {
  console.log(this); // window
}
test1();

案例三:將函數(shù)作為參數(shù)挂捻,傳入到另一個函數(shù)中

function foo(func) {
  func()
}

function bar() {
  console.log(this); // window
}

foo(bar);

我們對案例進(jìn)行一些修改,考慮一下打印結(jié)果是否會發(fā)生變化:
這里的結(jié)果依然是window船万,為什么呢刻撒?原因非常簡單,在真正函數(shù)調(diào)用的位置耿导,并沒有進(jìn)行任何的對象綁定声怔,只是一個獨(dú)立函數(shù)的調(diào)用

function foo(func) {
  func()
}

var obj = {
  name: "why",
  bar: function() {
    console.log(this); // window
  }
}

foo(obj.bar);

2. 隱式綁定

另外一種比較常見的調(diào)用方式是通過某個對象進(jìn)行調(diào)用的:
也就是它的調(diào)用位置中,是通過某個對象發(fā)起的函數(shù)調(diào)用碎节。

案例一:通過對象調(diào)用函數(shù)
foo的調(diào)用位置是obj.foo()方式進(jìn)行調(diào)用的捧搞;
那么foo調(diào)用時this會隱式的被綁定到obj對象上

function foo() {
  console.log(this); // obj對象
}

var obj = {
  name: "why",
  foo: foo
}

obj.foo();

案例二:案例一的變化
我們通過obj2又引用了obj1對象,再通過obj1對象調(diào)用foo函數(shù)狮荔;
那么foo調(diào)用的位置上其實還是obj1被綁定了this胎撇;

function foo() {
  console.log(this); // obj對象
}

var obj1 = {
  name: "obj1",
  foo: foo
}

var obj2 = {
  name: "obj2",
  obj1: obj1
}

obj2.obj1.foo();

案例三:隱式丟失
結(jié)果最終是window,為什么是window呢殖氏?
因為foo最終被調(diào)用的位置是bar晚树,而bar在進(jìn)行調(diào)用時沒有綁定任何的對象,也就沒有形成隱式綁定雅采;
相當(dāng)于是一種默認(rèn)綁定爵憎;

function foo() {
  console.log(this);
}

var obj1 = {
  name: "obj1",
  foo: foo
}

// 講obj1的foo賦值給bar
var bar = obj1.foo;
bar();

3. 顯示綁定

隱式綁定有以下前提條件:
必須在調(diào)用的對象內(nèi)部有一個對函數(shù)的引用(比如一個屬性);如果沒有這樣的引用婚瓜,在進(jìn)行調(diào)用時宝鼓,會報找不到該函數(shù)的錯誤;正是通過這個引用巴刻,間接的將this綁定到了這個對象上愚铡;

如果我們不希望在 對象內(nèi)部 包含這個函數(shù)的引用,同時又希望在這個對象上進(jìn)行強(qiáng)制調(diào)用胡陪,該怎么做呢沥寥?

JavaScript所有的函數(shù)都可以使用call和apply方法(這個和Prototype有關(guān))。它們兩個的區(qū)別這里不再展開柠座;

其實非常簡單邑雅,第一個參數(shù)是相同的,后面的參數(shù)妈经,apply為數(shù)組淮野,call為參數(shù)列表捧书;這兩個函數(shù)的第一個參數(shù)都要求是一個對象,這個對象的作用是什么呢骤星?就是給this準(zhǔn)備的鳄厌。

在調(diào)用這個函數(shù)時,會將this綁定到這個傳入的對象上妈踊。因為上面的過程,我們明確的綁定了this指向的對象泪漂,所以稱之為 顯示綁定廊营。

call、apply

通過call或者apply綁定this對象萝勤,顯示綁定后露筒,this就會明確的指向綁定的對象

function foo() {
  console.log(this);
}

foo.call(window); // window
foo.call({name: "why"}); // {name: "why"}
foo.call(123); // Number對象,存放時123

bind函數(shù)

如果我們希望一個函數(shù)總是顯示的綁定到一個對象上,可以怎么做呢敌卓?
方案一:自己手寫一個輔助函數(shù)(了解)
我們手動寫了一個bind的輔助函數(shù),這個輔助函數(shù)的目的是在執(zhí)行foo時慎式,總是讓它的this綁定到obj對象上

function foo() {
  console.log(this);
}

var obj = {
  name: "why"
}

function bind(func, obj) {
  return function() {
    return func.apply(obj, arguments);
  }
}

var bar = bind(foo, obj);

bar(); // obj對象
bar(); // obj對象
bar(); // obj對象

方案二:使用Function.prototype.bind

function foo() {
  console.log(this);
}

var obj = {
  name: "why"
}

var bar = foo.bind(obj);

bar(); // obj對象
bar(); // obj對象
bar(); // obj對象

內(nèi)置函數(shù)

有些時候,我們會調(diào)用一些JavaScript的內(nèi)置函數(shù)趟径,或者一些第三方庫中的內(nèi)置函數(shù)瘪吏。
這些內(nèi)置函數(shù)會要求我們傳入另外一個函數(shù);我們自己并不會顯示的調(diào)用這些函數(shù)蜗巧,而且JavaScript內(nèi)部或者第三方庫內(nèi)部會幫助我們執(zhí)行掌眠;

案例一:setTimeout

setTimeout中會傳入一個函數(shù),這個函數(shù)中的this通常是window
setTimeout(function() {
  console.log(this); // window
}, 1000);

為什么這里是window呢幕屹?這個和setTimeout源碼的內(nèi)部調(diào)用有關(guān)蓝丙;setTimeout內(nèi)部是通過apply進(jìn)行綁定的this對象,并且綁定的是全局對象望拖;

案例二:數(shù)組的forEach
數(shù)組有一個高階函數(shù)forEach渺尘,用于函數(shù)的遍歷:
在forEach中傳入的函數(shù)打印的也是Window對象;
這是因為默認(rèn)情況下傳入的函數(shù)是自動調(diào)用函數(shù)(默認(rèn)綁定)说敏;

var names = ["abc", "cba", "nba"];
names.forEach(function(item) {
  console.log(this); // 三次window
});

new綁定

JavaScript中的函數(shù)可以當(dāng)做一個類的構(gòu)造函數(shù)來使用鸥跟,也就是使用new關(guān)鍵字。
使用new關(guān)鍵字來調(diào)用函數(shù)時像云,會執(zhí)行如下的操作:
1.創(chuàng)建一個全新的對象锌雀;
2.這個新對象會被執(zhí)行Prototype連接;
3.這個新對象會綁定到函數(shù)調(diào)用的this上(this的綁定在這個步驟完成)迅诬;
4.如果函數(shù)沒有返回其他對象腋逆,表達(dá)式會返回這個新對象;

// 創(chuàng)建Person
function Person(name) {
  console.log(this); // Person {}
  this.name = name; // Person {name: "why"}
}

var p = new Person("why");
console.log(p);

優(yōu)先級總結(jié):
new綁定 > 顯示綁定(bind)> 隱式綁定 > 默認(rèn)綁定

二侈贷、this規(guī)則之外

1. 忽略顯示綁定

如果在顯示綁定中惩歉,我們傳入一個null或者undefined等脂,那么這個顯示綁定會被忽略,使用默認(rèn)規(guī)則:

function foo() {
  console.log(this);
}

var obj = {
  name: "why"
}

foo.call(obj); // obj對象
foo.call(null); // window
foo.call(undefined); // window

var bar = foo.bind(null);
bar(); // window

2. 間接函數(shù)引用

另外一種情況撑蚌,創(chuàng)建一個函數(shù)的 間接引用上遥,這種情況使用默認(rèn)綁定規(guī)則。
我們先來看下面的案例結(jié)果是什么争涌?
(num2 = num1)的結(jié)果是num1的值粉楚;

var num1 = 100;
var num2 = 0;
var result = (num2 = num1);
console.log(result); // 100

我們來下面的函數(shù)賦值結(jié)果:
賦值(obj2.foo = obj1.foo)的結(jié)果是foo函數(shù);
foo函數(shù)被直接調(diào)用亮垫,那么是默認(rèn)綁定模软;

function foo() {
  console.log(this);
}

var obj1 = {
  name: "obj1",
  foo: foo
}; 

var obj2 = {
  name: "obj2"
}

obj1.foo(); // obj1對象
(obj2.foo = obj1.foo)();  // window

3. ES6箭頭函數(shù)

在ES6中新增一個非常好用的函數(shù)類型:箭頭函數(shù)
這里不再具體介紹箭頭函數(shù)的用法,可以自行學(xué)習(xí)饮潦。
箭頭函數(shù)不使用this的四種標(biāo)準(zhǔn)規(guī)則(也就是不綁定this)燃异,而是根據(jù)外層作用域來決定this。
我們來看一個模擬網(wǎng)絡(luò)請求的案例:
這里我使用setTimeout來模擬網(wǎng)絡(luò)請求继蜡,請求到數(shù)據(jù)后如何可以存放到data中呢回俐?
我們需要拿到obj對象,設(shè)置data稀并;
但是直接拿到的this是window仅颇,我們需要在外層定義:var _this = this
在setTimeout的回調(diào)函數(shù)中使用_this就代表了obj對象

var obj = {
  data: [],
  getData: function() {
    var _this = this;
    setTimeout(function() {
      // 模擬獲取到的數(shù)據(jù)
      var res = ["abc", "cba", "nba"];
      _this.data.push(...res);
    }, 1000);
  }
}

obj.getData();

上面的代碼在ES6之前是我們最常用的方式,從ES6開始碘举,我們會使用箭頭函數(shù):
為什么在setTimeout的回調(diào)函數(shù)中可以直接使用this呢灵莲?
因為箭頭函數(shù)并不綁定this對象,那么this引用就會從上層作用域中找到對應(yīng)的this

var obj = {
  data: [],
  getData: function() {
    setTimeout(() => {
      // 模擬獲取到的數(shù)據(jù)
      var res = ["abc", "cba", "nba"];
      this.data.push(...res);
    }, 1000);
  }
}

obj.getData();

思考:如果getData也是一個箭頭函數(shù)殴俱,那么setTimeout中的回調(diào)函數(shù)中的this指向誰呢政冻?
答案是window;
依然是不斷的從上層作用域找线欲,那么找到了全局作用域明场;
在全局作用域內(nèi),this代表的就是window

var obj = {
  data: [],
  getData: () => {
    setTimeout(() => {
      console.log(this); // window
    }, 1000);
  }
}

obj.getData();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末李丰,一起剝皮案震驚了整個濱河市苦锨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趴泌,老刑警劉巖舟舒,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嗜憔,居然都是意外死亡秃励,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門吉捶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夺鲜,“玉大人皆尔,你說我怎么就攤上這事”依” “怎么了慷蠕?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長食呻。 經(jīng)常有香客問我流炕,道長,這世上最難降的妖魔是什么仅胞? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任浪感,我火速辦了婚禮,結(jié)果婚禮上饼问,老公的妹妹穿的比我還像新娘。我一直安慰自己揭斧,他們只是感情好莱革,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著讹开,像睡著了一般盅视。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旦万,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天闹击,我揣著相機(jī)與錄音,去河邊找鬼成艘。 笑死赏半,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的淆两。 我是一名探鬼主播断箫,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼秋冰!你這毒婦竟也來了仲义?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤剑勾,失蹤者是張志新(化名)和其女友劉穎埃撵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虽另,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暂刘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捂刺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸳惯。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡商蕴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芝发,到底是詐尸還是另有隱情绪商,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布辅鲸,位于F島的核電站格郁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏独悴。R本人自食惡果不足惜例书,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刻炒。 院中可真熱鬧决采,春花似錦、人聲如沸坟奥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爱谁。三九已至晒喷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間访敌,已是汗流浹背凉敲。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寺旺,地道東北人爷抓。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像阻塑,于是被迫代替她去往敵國和親废赞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355