js中的this

前言

近期對this很感興趣,于是乎簡單整理了一些關(guān)于this的技術(shù)點肿嘲,加深一下對this的理解融击。

非箭頭函數(shù)

在非箭頭函數(shù)下,this指向調(diào)用其所在函數(shù)的對象睦刃,而且是離誰近就是指向誰(此對于常規(guī)對象砚嘴,原型鏈,getter & setter等都適用涩拙;構(gòu)造函數(shù)下际长,this與被創(chuàng)建的新對象綁定;DOM事件兴泥,this指向觸發(fā)事件的元素工育;內(nèi)聯(lián)事件分兩種情況,bind綁定搓彻,call & apply 方法等如绸,容以下一步一步討論嘱朽。

解析

  • 全局環(huán)境下this 始終指向全局對象(window), 無論是否嚴格模式。
  • 函數(shù)直接調(diào)用的情況下怔接。
  • 普通函數(shù)內(nèi)部的this分兩種情況搪泳,嚴格模式和非嚴格模式。
  • 非嚴格模式下扼脐,this默認指向全局對象window岸军。
function f1(){
  return this;
}
f1() === window; // true
  • 其它函數(shù)的情況
  • 情況1:如果一個函數(shù)中有this,但是它沒有被上一級的對象所調(diào)用瓦侮,那么this指向的就是window艰赞,這里需要說明的是在js的嚴格版中this指向的不是window,但是我們這里不探討嚴格版的問題肚吏,你想了解可以自行上網(wǎng)查找方妖。
  • 情況2:如果一個函數(shù)中有this,這個函數(shù)有被上一級的對象所調(diào)用罚攀,那么this指向的就是上一級的對象党觅。
  • 情況3:如果一個函數(shù)中有this,這個函數(shù)中包含多個對象斋泄,盡管這個函數(shù)是被最外層的對象所調(diào)用仔役,this指向的也只是它上一級的對象。
  • 而嚴格模式下是己, this為undefined
function f2(){
  "use strict"; // 這里是嚴格模式
  return this;
}
f2() === undefined; // true
  • 對象中的this
  • 函數(shù)的定義位置不影響其this指向,this指向只和調(diào)用函數(shù)的對象有關(guān)任柜。
  • 多層嵌套的對象卒废,內(nèi)部方法的this指向離被調(diào)用函數(shù)最近的對象(window也是對象,其內(nèi)部對象調(diào)用方法的this指向內(nèi)部對象宙地,而非window)摔认。
//1
const o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};
console.log(o.f());  //37
const a = o.f;
console.log(a()):  //undefined
const o = {prop: 37};
function independent() {
  return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37

//2
o.b = {
  g: independent,
  prop: 42
};
console.log(o.b.g()); // logs 42
  • 原型鏈中this
  • 原型鏈中的方法的this仍然指向調(diào)用它的對象
var o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
  • 可以看出,在p中沒有屬性f宅粥,當執(zhí)行p.f()時参袱,會查找p的原型鏈,找到f函數(shù)并執(zhí)行秽梅,但這與函數(shù)內(nèi)部this指向?qū)ο髉沒有任何關(guān)系抹蚀,只需記住誰調(diào)用指向誰。以上對于函數(shù)作為getter & setter調(diào)用時同樣適用企垦。
const o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
const p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
  • 構(gòu)造函數(shù)中this
  • 構(gòu)造函數(shù)中的this與被創(chuàng)建的新對象綁定环壤。
  • 注意:當構(gòu)造器返回的默認值是一個this引用的對象時,可以手動設置返回其他的對象钞诡,如果返回值不是一個對象郑现,返回this湃崩。
function C(){
  this.a = 37;
}
const o = new C();
console.log(o.a); // logs 37
function C2(){
  this.a = 37;
  return {a:38};
}
const b = new C2();
console.log(b.a); // logs 38
  • 以上兩個例子內(nèi)部的this都指向?qū)ο髈, 看到這里的你不妨在控制臺執(zhí)行下以上代碼,看看對象o和b 接箫,這些是屬于構(gòu)造函數(shù)的內(nèi)容了攒读,此處不多做介紹。(C2函數(shù)中的this.a = 37對整個過程完全沒有影響的辛友,可以被忽略的)薄扁。
  • call & apply
  • 當函數(shù)通過Function對象的原型中繼承的方法 call() 和 apply() 方法調(diào)用時, 其函數(shù)內(nèi)部的this值可綁定到 call() & apply() 方法指定的第一個對象上瞎领, 如果第一個參數(shù)不是對象泌辫,JavaScript內(nèi)部會嘗試將其轉(zhuǎn)換成對象然后指向它。
function add(c, d){
  return this.a + this.b + c + d;
}
const o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function tt() {
  console.log(this);
}
tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}
  • bind 方法
  • bind方法在ES5引入九默, 在Function的原型鏈上震放, Function.prototype.bind。通過bind方法綁定后驼修, 函數(shù)將被永遠綁定在其第一個參數(shù)對象上殿遂, 而無論其在什么情況下被調(diào)用。
function f(){
  return this.a;
}

const g = f.bind({a:"azerty"});
console.log(g()); // azerty

const o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
  • DOM 事件處理函數(shù)中的 this & 內(nèi)聯(lián)事件中的 this
  • 1乙各、當函數(shù)被當做監(jiān)聽事件處理函數(shù)時墨礁,其this指向觸發(fā)該事件的元素(針對于addEventListener事件)。
// 被調(diào)用時耳峦,將關(guān)聯(lián)的元素變成藍色
    function bluify(e){
      //在控制臺打印出所點擊元素
      console.log(this);
      //阻止時間冒泡
      e.stopPropagation();
      //阻止元素的默認事件
      e.preventDefault();      
      this.style.backgroundColor = '#A5D9F3';
    }

    // 獲取文檔中的所有元素的列表
    const elements = document.getElementsByTagName('*');

    // 將bluify作為元素的點擊監(jiān)聽函數(shù)恩静,當元素被點擊時,就會變成藍色
    for(let i=0 ; i<elements.length ; i++){
      elements[i].addEventListener('click', bluify, false);
    }
  • 內(nèi)聯(lián)事件
  • 內(nèi)聯(lián)事件中的this指向分兩種情況:
  • 1蹲坷、當代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時驶乾,它的this指向監(jiān)聽器所在的DOM元素。
  • 2循签、當代碼被包括在函數(shù)內(nèi)部執(zhí)行時级乐,其this指向等同于函數(shù)直接調(diào)用的情況,即在非嚴格模式指向全局對象window县匠, 在嚴格模式指向undefined
<button onclick="console.log(this);">show me</button> //<button onclick="console.log(this);">show me</button> 
<button onclick="(function(){console.log(this)})()"></button> //window
<button onclick="(function(){'use strict';console.log(this)}())">use strict</button> //undefined
  • setTimeout & setInterval
  • 對于延時函數(shù)內(nèi)部的回調(diào)函數(shù)的this指向全局對象window(當然我們可以通過bind方法改變其內(nèi)部函數(shù)的this指向)
    看下邊代碼.
//默認情況下代碼
function Person() {  
    this.age = 0;  
    setTimeout(function() {
        console.log(this);
    }, 3000);
}

const p = new Person();//3秒后返回 window 對象
==============================================
//通過bind綁定
function Person() {  
    this.age = 0;  
    setTimeout((function() {
        console.log(this);
    }).bind(this), 3000);
}
const p = new Person();//3秒后返回構(gòu)造函數(shù)新生成的對象 Person{...}

規(guī)則

  • 默認綁定全局變量
function fn() {
    console.log( this.a );
}
const a = 2;
fn(); // 2 -- fn單獨調(diào)用风科,this引用window
  • 隱式綁定
  • 1、隱式調(diào)用的意思是乞旦,函數(shù)調(diào)用時擁有一個上下文對象贼穆,就好像這個函數(shù)是屬于該對象的一樣。
function fn() {
    console.log( this.a );
}
var obj = {
    a: 2,
    fn: fn
};
obj.fn(); // 2 -- this引用obj杆查。
  • 2扮惦、需要說明的一點是,最后一個調(diào)用該函數(shù)的對象是傳到函數(shù)的上下文對象(繞懵了)亲桦。
function fn() {
     console.log( this.a );
 }
 var obj2 = {
     a: 42,
     fn: fn
 };
 var obj1 = {
     a: 2,
     obj2: obj2
 };
 obj1.obj2.fn(); // 42 -- this引用的是obj2.
  • 3崖蜜、還有一點要說明的是浊仆,失去隱式綁定的情況。
function fn() {
     console.log( this.a );
 }
 var obj = {
     a: 2,
     fn: fn
 };
 var bar = obj.fn; // 函數(shù)引用傳遞
 var a = "全局"; // 定義全局變量
 bar(); // "全局"
  • 顯示綁定
  • 1豫领、學過bind()\apply()\call()函數(shù)的都應該知道抡柿,它接收的第一個參數(shù)即是上下文對象并將其賦給this。
function fn() {
     console.log( this.a );
 }
 var obj = {
     a: 2
 };
 fn.call( obj ); // 2
  • 2等恐、如果我們傳遞第一個值為簡單值洲劣,那么后臺會自動轉(zhuǎn)換為對應的封裝對象。如果傳遞為null课蔬,那么結(jié)果就是在綁定默認全局變量
function fn() {
      console.log( this.a );
  }
  var obj = {
      a: 2
  };
 var a = 10;
 fn.call( null); // 10
  • new新對象綁定
  • 1囱稽、如果是一個構(gòu)造函數(shù),那么用new來調(diào)用二跋,那么綁定的將是新創(chuàng)建的對象战惊。
function fn(a) {
    this.a = a;
}
var bar = new fn( 2 );
console.log( bar.a );// 2

用法

  • 在一般函數(shù)方法中使用 this 指代全局對象
function test(){
    this.x = 1;
    alert(this.x);
  }
  test(); // 1
  • 作為對象方法調(diào)用,this 指代上級對象
function test(){
  alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
  • 作為構(gòu)造函數(shù)調(diào)用扎即,this 指代new 出的對象
function test(){
    this.x = 1;
  }
  var o = new test();
  alert(o.x); // 1
    //運行結(jié)果為1吞获。為了表明這時this不是全局對象,我對代碼做一些改變:
  var x = 2;
  function test(){
    this.x = 1;
  }
  var o = new test();
  alert(x); //2
  • apply 調(diào)用 谚鄙,apply方法作用是改變函數(shù)的調(diào)用對象各拷,此方法的第一個參數(shù)為改變后調(diào)用這個函數(shù)的對象,this指代第一個參數(shù)
var x = 0;
  function test(){
    alert(this.x);
  }
  var o={};
  o.x = 1;
  o.m = test;
  o.m.apply(); //0
//apply()的參數(shù)為空時闷营,默認調(diào)用全局對象烤黍。因此,這時的運行結(jié)果為0傻盟,證明this指的是全局對象蚊荣。如果把最后一行代碼修改為
  o.m.apply(o); //1

箭頭函數(shù)中的this

  • 1、箭頭函數(shù)中的this是在定義函數(shù)的時候綁定莫杈,而不是在執(zhí)行函數(shù)的時候綁定,所謂的定義時候綁定奢入,就是this是繼承自父執(zhí)行上下文筝闹!
  • 2、箭頭函數(shù)的this綁定看的是this所在的函數(shù)定義在哪個對象下腥光,綁定到哪個對象則this就指向哪個對象
  • 3关顷、如果有對象嵌套的情況,則this綁定到最近的一層對象上
  • 4武福、不可以當作構(gòu)造函數(shù)议双,也就是說,不可以使用new命令捉片,否則會拋出一個錯誤平痰。
  • 5汞舱、不可以使用arguments對象,該對象在函數(shù)體內(nèi)不存在宗雇。如果要用昂芜,可以用 rest 參數(shù)代替。
  • 6赔蒲、不可以使用yield命令泌神,因此箭頭函數(shù)不能用作 Generator 函數(shù)。

解析

  • 由于箭頭函數(shù)不綁定this舞虱, 它會捕獲其所在(即定義的位置)上下文的this值欢际, 作為自己的this值
  • 1、call()/apply()/bind() 方法對于箭頭函數(shù)來說只是傳入?yún)?shù)矾兜,對它的this毫無影響损趋。
function Person() {  
    this.age = 0;  
    setInterval(() => {
        // 回調(diào)里面的 `this` 變量就指向了期望的那個對象了
        this.age++;
    }, 3000);
}
const p = new Person();
  • 2、考慮到 this 是詞法層面上的焕刮,嚴格模式中與 this 相關(guān)的規(guī)則都將被忽略舶沿。
const adder = {
  base : 1,
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },
  addThruCall: function inFun(a) {
    const f = v => v + this.base;
    const b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 輸出 2
console.log(adder.addThruCall(1)); // 仍然輸出 2(而不是3,其內(nèi)部的this并沒有因為call() 而改變配并,其this值仍然為函數(shù)inFun的this值括荡,指向?qū)ο骯dder
  • 對于是否嚴格模式示例代碼
const f = () => {'use strict'; return this};
const p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true
  • 以上的箭頭函數(shù)都是在方法內(nèi)部,總之都是以非方法的方式使用溉旋,如果將箭頭函數(shù)當做一個方法使用會怎樣呢畸冲?
const obj = {
     i: 10,
     b: () => console.log(this.i, this),
     c: function() {
       console.log( this.i, this)
     }
   }
   obj.b();  // undefined window{...}
   obj.c();  // 10 Object {...}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市观腊,隨后出現(xiàn)的幾起案子邑闲,更是在濱河造成了極大的恐慌,老刑警劉巖梧油,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苫耸,死亡現(xiàn)場離奇詭異,居然都是意外死亡儡陨,警方通過查閱死者的電腦和手機褪子,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骗村,“玉大人嫌褪,你說我怎么就攤上這事∨吖桑” “怎么了笼痛?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我缨伊,道長摘刑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任倘核,我火速辦了婚禮泣侮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘紧唱。我一直安慰自己活尊,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布漏益。 她就那樣靜靜地躺著蛹锰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绰疤。 梳的紋絲不亂的頭發(fā)上铜犬,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音轻庆,去河邊找鬼癣猾。 笑死,一個胖子當著我的面吹牛余爆,可吹牛的內(nèi)容都是我干的纷宇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛾方,長吁一口氣:“原來是場噩夢啊……” “哼像捶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桩砰,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤拓春,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后亚隅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體硼莽,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年煮纵,在試婚紗的時候發(fā)現(xiàn)自己被綠了沉删。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡醉途,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出砖茸,到底是詐尸還是另有隱情隘擎,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布凉夯,位于F島的核電站货葬,受9級特大地震影響采幌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜震桶,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一休傍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蹲姐,春花似錦磨取、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至江咳,卻和暖如春逢净,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背歼指。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工爹土, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踩身。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓胀茵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惰赋。 傳聞我的和親對象是個殘疾皇子宰掉,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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