js this用法

參考鏈接

基本含義

var showName = function() {
    console.log('My name is', this.name);
};
var zs = {
        name: 'Zhang San',
        describe: showName
    },
    ls = {
        name: 'Li Si',
        describe: showName
    };
zs.describe();      // My name is Zhang San
ls.describe();      // My name is Li Si
showName();         // My name is
name = 'window';        // 等價(jià)于 window.name = 'window'; 和this.name = 'window'; 因?yàn)榇藭r(shí)window===this
showName();         // My name is window

以上示例中實(shí)際都是執(zhí)行的showName方法迷守,但是由于環(huán)境不同杭隙,輸出的結(jié)果也不同,根本原因是不同情況下的this是不一樣的。
zs.describe(); // this === zs
ls.describe(); // this === ls
showName(); // this === window

綁定機(jī)制導(dǎo)致的this不同指代

<input type="button" name="按鈕1" onclick="showName()" value="按鈕1">
<input id="btn1" type="button" name="按鈕2"  value="按鈕2">
<input id="btn2" type="button" name="按鈕3" value="按鈕3">
<script>
window.name = 'window';
var showName = function() {
    console.log('My name is', this.name);
};
var btn1 = document.getElementById('btn1');
btn1.onclick = showName;
var btn2 = document.getElementById('btn2');
btn2.addEventListener('click', showName, false);
</script>

點(diǎn)擊三個(gè)按鈕埋哟,控制臺(tái)輸出結(jié)果分別是什么呢?
第一個(gè)為:My name is window 第二個(gè)為:My name is 按鈕2 ,第三個(gè)為My name is 按鈕3
這是為什么呢赤赊?這個(gè)和綁定事件的機(jī)制有關(guān)系闯狱。第一種形式是HTML事件,onclick="showName()"表示在點(diǎn)擊時(shí)執(zhí)行showName方法抛计,此時(shí)執(zhí)行環(huán)境為全局環(huán)境哄孤,this為window,所以輸出window吹截。
第二種和第三種分別為DOM0級(jí)事件和DOM2級(jí)事件瘦陈,其實(shí)質(zhì)是給點(diǎn)擊事件指定了一個(gè)回調(diào)函數(shù),其為showName波俄。在點(diǎn)擊事件的回調(diào)函數(shù)中晨逝,this是指當(dāng)前這個(gè)dom元素,因此輸出的值為這兩個(gè)按鈕的name屬性懦铺。

使用場(chǎng)合

  • this的使用是很廣泛的咏花,其作用也非常強(qiáng)大。我們可以將this的使用歸為一下幾類阀趴。
    1.構(gòu)造函數(shù)
function Person(name, gender) { this.name = name;
    this.gender = gender; }
Person.prototype.showSelf = function() {
    return this; }
zs = new Person('zs', 'male'); // {name: "zs", gender: "male"}
zs.showSelf(); // {name: "zs", gender: "male"}
zs === zs.showSelf(); // true

2.對(duì)象的方法

var box = {
    name: 'box',
    getName: function() {
        return this.name;
    }
};
var bag = {
    name: 'bag'
};
bag.getName = box.getName;
bag.getName(); // "bag"

雖然bag.getName實(shí)際是對(duì)box.getName的一個(gè)引用昏翰, 由于運(yùn)行時(shí)使用的是bag.getName(), 此時(shí)是在bag對(duì)象下運(yùn)行的刘急, this也就指的是bag了棚菊。
再看一點(diǎn)奇怪的:

// 注意 box.getName 沒有括號(hào)
(false || box.getName)(); // window
(false ? alert : box.getName)(); // window

上面這兩種情況下, 輸出的都不再是box對(duì)象的name屬性叔汁, 而是window( 之前設(shè)置了window.name = 'window')统求。
表示此時(shí)方法內(nèi)部的this指向的是瀏覽器頂層對(duì)象window。
可以這么理解:
box對(duì)象指向了一個(gè)地址M1据块, box.getName作為box的一個(gè)方法码邻, 但本身也是對(duì)象, 它自己也有一個(gè)地址M2另假, 只有通過box.getName() 調(diào)用時(shí)像屋, 是從M1中調(diào)用M2, 所以this指向的是box边篮。 上面兩種情況都是直接拿到M2來調(diào)用己莺, 此時(shí)和M1已經(jīng)沒有任何關(guān)系了, this的指向當(dāng)前代碼塊所在的對(duì)象戈轿。

ES6箭頭函數(shù)

ES6中新增的箭頭函數(shù)里面所使用的this和之前介紹的情況都不一樣了凌受, 在箭頭函數(shù)中this不隨其運(yùn)行環(huán)境的改變而改變, 而是在聲明箭頭函數(shù)時(shí)思杯, 就已經(jīng)固定下來了胜蛉。 箭頭函數(shù)中this的指向就是聲明箭頭函數(shù)是所在的對(duì)象。

先看一個(gè)常規(guī)的例子:

function foo() {
    setTimeout(function() {
        console.log('name:', this.name);
    }, 100);
}
foo(); // name: window
foo.call({ name: 'an obj' }); // name: window

定義一個(gè)函數(shù)foo內(nèi)部使用定時(shí)器調(diào)用一個(gè)匿名函數(shù), 此時(shí)函數(shù)有多層了誊册, this的指向應(yīng)該是全局對(duì)象window奈梳, 輸出結(jié)果證明了這一點(diǎn)。 使用foo.call結(jié)果也相同的原因是解虱, call替換的是foo函數(shù)內(nèi)的this指向攘须, 而輸出的this是在定時(shí)器的回調(diào)中的, 故結(jié)果依然是window殴泰。

我們?cè)倏匆幌录^函數(shù)中這一點(diǎn)的表現(xiàn):

function arrow_foo() {
  setTimeout(() => {
      console.log('name:', this.name);
  }, 100);
}
arrow_foo(); // name: window
arrow_foo.call({ name: 'an obj' }); // name: an obj

我們發(fā)現(xiàn)結(jié)果于宙, 居然和上面不一樣了。 為什么呢悍汛? 我們將其轉(zhuǎn)化成ES5的結(jié)果來看一下捞魁, 上面代碼轉(zhuǎn)化后的結(jié)果是這樣的:

function arrow_foo() {
    var $__1 = this;
    setTimeout(function() {
        console.log('name:', $__1.name);
    }, 100);
}
arrow_foo();
arrow_foo.call({ name: 'an obj' });

看一下轉(zhuǎn)換后的結(jié)果, 原因就一目了然了离咐, 箭頭函數(shù)中this直接固定成了其定義時(shí)所在的對(duì)象谱俭, 此處為foo。 實(shí)際在箭頭函數(shù)中的所有this都是一個(gè)對(duì)象宵蛀, 這個(gè)對(duì)象就是其定義時(shí)所在對(duì)象的this昆著, 上面轉(zhuǎn)換后的結(jié)果中在foo中首先使用一個(gè)變量記錄下this, 而在箭頭函數(shù)中的this被替換成了之前存儲(chǔ)this的那個(gè)變量术陶。
因此直接運(yùn)行時(shí)凑懂, this是指全局對(duì)象, 而使用call時(shí)梧宫, 將foo內(nèi)的this替換成了指定的對(duì)象 { name: 'an obj' }接谨,從而輸出的上面的結(jié)果。

使用注意點(diǎn)

1.避免多層this

由于this的指向是不確定的塘匣, 所以切勿在函數(shù)中包含多層的this脓豪。

var box = {
    name: 'box',
    size: {
        width: 300,
        height: 300
    },
    show: function() {
        console.log('name', this.name);
        (function() {
            console.log('size', this.size);
        })();
    }
};
box.show();
// name box
// size undefined

我們本意是想在show方法內(nèi)部輸出name, 并輸出size忌卤, 但是結(jié)果卻并不是想要的這樣扫夜, 這是因?yàn)樵诹⒓磮?zhí)行的函數(shù)內(nèi)部, this的執(zhí)行不再是box對(duì)象而變成了頂層對(duì)象window埠巨, 因此第二行輸出為undefined历谍。
解決方法為, 在外層用一個(gè)變量記錄下this辣垒, 在要使用的地方使用那個(gè)變量。

改寫上列子

var box = {
    name: 'box',
    size: {
        width: 300,
        height: 300
    },
    show: function() {
        console.log('name', this.name);
        var that = this;
        (function() {
            console.log('size', that.size);
        })();
    }
};
box.show();
// name box
// size Object {width: 300, height: 300}

還用一種做法是JavaScript提供的嚴(yán)格模式use strict印蔬, 如果函數(shù)內(nèi)部的this直接指向了頂層對(duì)象會(huì)直接報(bào)錯(cuò)勋桶。

var box = {
    name: 'box',
    size: {
        width: 300,
        height: 300
    },
    show: function() {
        'use strict'
        console.log('name', this.name);
        (function() {
            console.log('size', this.size);
        })();
    }
};
box.show();
// Uncaught TypeError: Cannot read property 'size' of undefined(…)

2.避免在回調(diào)函數(shù)中使用this

通常回調(diào)函數(shù)中的this都有其特定的, 如果在回調(diào)函數(shù)中使用this例驹, 應(yīng)該需要了解其含義捐韩, 否則可能出現(xiàn)意料之外的結(jié)果。
事件處理函數(shù)作為一種特殊的回調(diào)函數(shù)鹃锈, 其this是指當(dāng)前的DOM對(duì)象荤胁, 最開始的例子已經(jīng)說明了這個(gè)問題。
回調(diào)函數(shù)本身是一個(gè)函數(shù)屎债, 其作為另一個(gè)函數(shù)的參數(shù)傳遞進(jìn)去仅政, 然后在那個(gè)函數(shù)內(nèi)部執(zhí)行, 這本身已經(jīng)構(gòu)成了多層this盆驹, 此時(shí)this的指向是不確定的圆丹, 需要慎用。

3.綁定this的方法

  • function.prototype.call()

使用函數(shù)的call方法躯喇,可以指定函數(shù)內(nèi)部this的指向辫封,使其在指定的作用域中運(yùn)行。

var obj = {};
var f = function() {
   return this;
};
f() === this; // true this === window
f.call(obj) === obj; // true

上面代碼中廉丽, 在全局環(huán)境運(yùn)行函數(shù)f時(shí)倦微, this指向全局環(huán)境; call方法可以改變this的指向正压, 指定this指向?qū)ο髈bj璃诀, 然后在對(duì)象obj的作用域中運(yùn)行函數(shù)f。
call方法的第一個(gè)參數(shù)為一個(gè)對(duì)象蔑匣, 其表示要為函數(shù)所指定的運(yùn)行上下文環(huán)境的對(duì)象( 當(dāng)指定為undefined或null是默認(rèn)傳入window)劣欢, 之后的參數(shù)依次作為原函數(shù)的參數(shù)。

  • function.prototype.apply()

使用函數(shù)的apply方法同樣可以指定函數(shù)運(yùn)行的環(huán)境裁良, 作用和call相同凿将, 使用方法也類似, 都是第一個(gè)參數(shù)傳入要指定的上下文對(duì)象价脾。 不同點(diǎn)在于牧抵, apply方法最多接收兩個(gè)參數(shù), 第二個(gè)參數(shù)為一個(gè)數(shù)組( 無論原函數(shù)需要的參數(shù)是何種類型侨把, 此數(shù)組中的每個(gè)元素將依次傳遞給原函數(shù))犀变, 表示傳遞給原函數(shù)的參數(shù), 而call可以接收多個(gè)參數(shù)秋柄, 從第二個(gè)參數(shù)開始获枝, 之后的所有參數(shù)都傳遞給原函數(shù)。
由于apply第二個(gè)參數(shù)接收的是數(shù)組骇笔, 其有很多巧用省店。 由于此文重點(diǎn)是描述this關(guān)鍵字嚣崭, 就不再贅述了。

  • function.prototype.bind()

ES5中有bind這樣一個(gè)方法懦傍, 也可以指定函數(shù)的運(yùn)行環(huán)境雹舀, 但是和call、 apply有所不同粗俱, bind方法可接收一個(gè)參數(shù)说榆, 用于指定函數(shù)運(yùn)行的上下文環(huán)境, 返回一個(gè)函數(shù)作為綁定指定上下文環(huán)境后的新函數(shù)寸认。
這樣bind和call签财、 apply的區(qū)別就出來了: 前者是根據(jù)指定的上下文環(huán)境返回一個(gè)新函數(shù), 而后兩者是使用指定的上下文壞境運(yùn)行原函數(shù)废麻。
其實(shí)bind和jQuery.proxy() 類似荠卷, 雖然沒有后者處理多種情況, 但作為JavaScript原生方法烛愧, 更輕量油宜、 高效。
用本文最開始的例子來演示此方法的使用怜姿, 某對(duì)象下有某方法慎冤, 我們要將此對(duì)象這個(gè)方法作為作為一個(gè)事件處理函數(shù), 但不希望方法內(nèi)部的this被改變:

< input id = "btn1" type = "button" name = "按鈕1" value = "張三的名字是" > 
< input id = "btn2" type = "button" name = "按鈕2" value = "張三的名字是" > 
< script > 
window.name = 'window';
var showName = function() { 
    console.log(this.name); 
};
var zs = { name: '張三' };
var btn1 = document.getElementById('btn1');
btn1.addEventListener('click', showName, false);
var btn2 = document.getElementById('btn2');
btn2.addEventListener('click', showName.bind(zs), false); 
< /script>

這樣點(diǎn)擊第二個(gè)按鈕沧卢,將可以正確輸出張三的名字蚁堤。
bind
第一個(gè)參數(shù)為一個(gè)對(duì)象,為undefined或null是默認(rèn)傳入window
除此之外但狭,bind還可以接收額外參數(shù)披诗,用于在生成新函數(shù)時(shí),從原函數(shù)的第一個(gè)參數(shù)開始替換一部分參數(shù)(和jQuery.proxy()
類似)立磁。比如原函數(shù)要接收兩個(gè)參數(shù)呈队,使用bind產(chǎn)生新函數(shù)時(shí),除了第一個(gè)參數(shù)的外唱歧,可以再傳入一個(gè)參數(shù)宪摧,此參數(shù)將替換原函數(shù)的第一個(gè)參數(shù),這樣生成的新函數(shù)就只用接收一個(gè)參數(shù)了颅崩,詳情見jQuery 工具方法簡(jiǎn)析jQuery.proxy( function, context [, additionalArguments ] )几于。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沿后,隨后出現(xiàn)的幾起案子沿彭,更是在濱河造成了極大的恐慌,老刑警劉巖得运,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膝蜈,死亡現(xiàn)場(chǎng)離奇詭異锅移,居然都是意外死亡熔掺,警方通過查閱死者的電腦和手機(jī)饱搏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置逻,“玉大人推沸,你說我怎么就攤上這事∪耄” “怎么了鬓催?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恨锚。 經(jīng)常有香客問我宇驾,道長(zhǎng),這世上最難降的妖魔是什么猴伶? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任课舍,我火速辦了婚禮,結(jié)果婚禮上他挎,老公的妹妹穿的比我還像新娘筝尾。我一直安慰自己,他們只是感情好办桨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布筹淫。 她就那樣靜靜地躺著,像睡著了一般呢撞。 火紅的嫁衣襯著肌膚如雪损姜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天殊霞,我揣著相機(jī)與錄音摧阅,去河邊找鬼。 笑死脓鹃,一個(gè)胖子當(dāng)著我的面吹牛逸尖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘸右,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼娇跟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了太颤?” 一聲冷哼從身側(cè)響起苞俘,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎龄章,沒想到半個(gè)月后吃谣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乞封,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年岗憋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肃晚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仔戈,死狀恐怖关串,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情监徘,我是刑警寧澤晋修,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站凰盔,受9級(jí)特大地震影響墓卦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜户敬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一落剪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧山叮,春花似錦著榴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至锐借,卻和暖如春问麸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钞翔。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工严卖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人布轿。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓哮笆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親汰扭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稠肘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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

  • 請(qǐng)移步:https://blog.cdswyda.com/post/20161019
    依韻宵音閱讀 588評(píng)論 5 19
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)萝毛,斷路器项阴,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法笆包,內(nèi)部類的語(yǔ)法环揽,繼承相關(guān)的語(yǔ)法略荡,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,622評(píng)論 18 399
  • 前言 javascript中的this,constructor ,prototype歉胶,都是老生常談的問題汛兜,深入理解...
    Myselfyan閱讀 416評(píng)論 0 1
  • HTML HTML5標(biāo)簽 媒體查詢head部分寫法 Doctype作用? 嚴(yán)格模式與混雜模式如何區(qū)分?它們有何意義...
    Mayo_閱讀 641評(píng)論 0 8