關(guān)于this

this是什么

在函數(shù)運(yùn)行時,基于調(diào)用位置的條件自動生成的內(nèi)部對象苛败,可以理解為動態(tài)綁定對象到this上满葛。

需要強(qiáng)調(diào)的是:

  • 只針對函數(shù),在函數(shù)內(nèi)部使用
  • this由調(diào)用位置的上下文自動的決定罢屈,而不是函數(shù)聲明的位置(代碼書寫的位置)
  • 必須是運(yùn)行時才確定嘀韧,而不是編寫時
  • this綁定之后可理解為自動生成的內(nèi)部對象

示例:

var foo = "golbal foo";  
var myObj = {foo : 'myObj foo'};  
var say = function(){  
    console.log(this.foo);  
}  
  
myObj.say = say;  
myObj.say(); //結(jié)果:myObj foo  
say(); //結(jié)果:golbal foo  ,相當(dāng)于window.say()缠捌,內(nèi)部的this->window對象

this的四個綁定規(guī)則及優(yōu)先級

下面四個為this的綁定優(yōu)先級規(guī)則锄贷,第一個優(yōu)先級最高译蒂。判斷執(zhí)行流程需要做的就是找到函數(shù)的調(diào)用位置并判斷使用哪條規(guī)則。

1. 函數(shù)是否通過new Base()方式綁定谊却?如果是柔昼,this綁定新創(chuàng)建的對象

new的調(diào)用會自動執(zhí)行下面代碼(示例代碼),Base函數(shù)中的this指向新創(chuàng)建的對象(一般):

var obj = new Base()

// 1. 創(chuàng)建(或者說構(gòu)造)一個全新的對象炎辨;
var _obj = {}

// 2. 我們將這個空對象的__proto__成員指向了Base函數(shù)對象prototype成員對象
_obj.__proto__ = Base.prototype

// 3. 我們將Base函數(shù)對象的this指針替換成_obj捕透,然后再調(diào)用Base函數(shù)
var _return = Base.call(_obj)

// 4 如果函數(shù)沒有返回其他對象,那么new表達(dá)式中的函數(shù)調(diào)用會自動返回這個新對象
if (typeof(_return) === 'object') {
    obj = _return
} else {
    obj = _obj
}

2. 函數(shù)是否通過call碴萧、apply乙嘀、bind顯式綁定?如果是破喻,this綁定所指定的對象

強(qiáng)制將foo方法中的this綁定到obj對象上虎谢,即使后面再次更新綁定也不生效。

function foo(){  
    console.log(this.a);  
}  
var obj = { a:2 };  
var bar = function(){  
    foo.call(obj);  
};  
bar(); //2  
setTimeout( bar, 1000); //2  
bar.call( window ); //2  

注意:

bind綁定會返回新函數(shù)曹质,對新函數(shù)無法更改內(nèi)部的this婴噩,原因同上。但是對原函數(shù)可以隨意切換綁定羽德。

function base () {
  console.log(this.hello)
}
var a = {
  hello:'aaa'
}
var b = {
  hello:'bbb'
}

base.call(a) // aaa
base.call(b) // bbb
var bb = base.bind(b) // 強(qiáng)綁定讳推,返回的bb函數(shù)已無法更改this
bb.call(a) // bbb
bb.call(b) // bbb
base.call(a) // aaa
base.call(b) // bbb

3. 函數(shù)是否在某個上下文對象中隱式調(diào)用?如果是玩般,this綁定到那個上下文對象

function foo(){  
    console.log(this.a);  
}  
var obj1 = {  
    a : 2,  
    foo : foo  
}  
var obj2 = {  
    a : 1,  
    obj1 : obj1  
}  
obj2.obj1.foo();    //結(jié)果:2  

foo()執(zhí)行時的上下文是obj1,因此函數(shù)內(nèi)的this->obj1

注意:

隱式綁定會出現(xiàn)綁定丟失的問題礼饱,不過這個很好推理坏为。

var a = "foo";  
function foo(){  
    console.log(this.a);  
}  
function doFoo(fn){     //var fn = obj.foo  
    fn();  
}  
var obj = {  
    a : 2,  
    foo : foo  
}  
doFoo(obj.foo); //"foo"  this->window

var bar = obj.foo
bar(); // "foo" 相當(dāng)于:window.bar(), this->window
bar.call(obj); // "2" this->obj

setTimeout(obj.foo, 100);   //"foo"  

4. 上述全部不是镊绪,則this->window上匀伏,如果是嚴(yán)格模式,this->undefined

// 嚴(yán)格模式是蝴韭?
var a = function ( ) {
    "use strict"
    //...
}

事件中的this

1. 作為DOM事件處理

在事件處理函數(shù)中的this指向綁定事件的對象event.currentTarget

document.getElementById('button').addEventListener('click',function (event) {
  console.log(this)
  console.log(event.target === event.currentTarget)
  console.log(event.target === this)
})

currentTarget:綁定事件的元素够颠,恒等于this
target:觸發(fā)事件的元素,可能是綁定事件元素的子元素接收到了事件

2. 作為內(nèi)聯(lián)事件處理

當(dāng)代碼在元素上進(jìn)行調(diào)用處理榄鉴,this指向的是這個DOM元素履磨,this->$(button)

<button onclick="alert(this.tagName.toLowerCase());"> Show this</button>

function函數(shù)中返回,則this指向window庆尘,this->window

<button onclick="alert((function(){return this}()));">Show inner this</button>

IIFE中的this

不管IIFE寫在哪剃诅,里面的this都指向window。相當(dāng)于是在window下執(zhí)行IIFE函數(shù)驶忌。此外矛辕,自執(zhí)行函數(shù)返回值由內(nèi)部函數(shù)返回。

注意點

1. 忽略this

nullundefined作為this的綁定對象傳入callapply聊品、bind飞蹂,調(diào)用時會被忽略,實際應(yīng)用的是默認(rèn)綁定規(guī)則this->window翻屈!

function foo(){  
    console.log(this.a);  
}  
var a = 1;  
foo.call(null, 2);          // 1  this->window
foo.apply(undefined, [3]);  //1  this->window
foo.apply(window, [3]);  //1  this->window

2. 間接引用

function foo(){  
    console.log(this.a);  
}  
var a = 2;  
var o = { a : 3,foo : foo};  
var p = { a : 4};  
o.foo();            //3  
(p.foo = o.foo)();  //2 間接引用, 前面返回foo函數(shù)陈哑,相當(dāng)于:(foo)(), this->window
var pfoo = o.foo;  
pfoo();         //2 隱式丟失  

3. 箭頭函數(shù)

箭頭函數(shù)中的this無法被修改,this指向由外層函數(shù)決定妖胀,常用于事件處理器或定時器等異步場景芥颈。

function foo(){  
    setTimeout(()=>{  
        console.log(this.a);  
    },100);  
}  
var obj = { a : 2};  
foo.call(obj);  

等價于:

function foo(){  
    var self = this;  
    setTimeout(function(){  
        console.log(self.a);  
    },100);  
}  
var obj = { a : 2};  
foo.call(obj);  

補(bǔ)充

1. bind()實現(xiàn)?

function bind(f, o){  
    if(f.bind){  
        return f.bind(o);  
    }else{  
        return function(){  
            return f.apply(o, arguments);  
        }  
    }  
}  

2. 什么是嚴(yán)格模式赚抡?

為了向新版JS語法過度的模式爬坑。

放置位置

嚴(yán)格模式編譯指示: "use strict"

  • 放置在腳本第一行
  • 聲明在函數(shù)體中第一行
var a = function ( ) {
    "use strict"
    //...
}

與非嚴(yán)格模式的區(qū)別

  • 無法創(chuàng)建全局變量,變量必須使用var聲明
"use strict"
var a = 123
console.log(a)
console.log(a === window.a)
  • 靜默失敗都會拋出錯誤
  • 禁止使用with
var obj= { };obj.a=1;obj.b=2;
with(obj){
  alert(a+b)
}

with的作用是將obj對象中的變量在{}中展開可直接訪問涂臣,注意這沒有影響window對象盾计。類似于作用域拼接。

  • 嚴(yán)格模式下赁遗,eval內(nèi)部有自己的作用域
  • 默認(rèn)的this指向undefined署辉,而不是window,避免window變量被污染
  • 禁止在函數(shù)內(nèi)部使用callee岩四、caller哭尝、arguments這些屬性
  • 對象屬性名唯一,函數(shù)傳參名唯一
  • 禁止八進(jìn)制表示法
  • 不允許對arguments賦值
  • arguments不在追蹤參數(shù)的變化
  • 不能再非函數(shù)的代碼塊中聲明函數(shù)
var a= 6;

if(a>2){
    function fn ( ) {
        alert('hi');
    }
    fn( );
} //報錯
  • 禁止使用保留關(guān)鍵字

測試題

1. 為什么要使用this剖煌,而不是傳參解決問題

  • 復(fù)用

在不同的對象環(huán)境下執(zhí)行了它們材鹦,達(dá)到了復(fù)用的效果,而不用為了在不同的對象環(huán)境下執(zhí)行而必須針對不同的對象環(huán)境寫對應(yīng)的函數(shù)了耕姊。

  • 模擬類

2. 基礎(chǔ)call

function identify() {
    return this.name.toUpperCase();
}
function sayHello() {
    var greeting = "Hello, I'm " + identify.call( this );
    console.log( greeting );
}
var person1= {
    name: "Kyle"
};
var person2= {
    name: "Reader"
};
identify.call( person1); // KYLE
identify.call( person2); // READER
sayHello.call( person1); // Hello, I'm KYLE
sayHello.call( person2); // Hello, I'm READER

函數(shù)在哪里調(diào)用才決定了this到底引用的是啥

3. this不是指向函數(shù)本身

function fn(num) {
    console.log( "fn: " + num );
    // count用于記錄fn的被調(diào)用次數(shù)
    this.count++;
}
fn.count = 0;
var i;
for (i=0; i<10; i++) {
    if (i > 5) {
        fn( i );
    }
}
// fn: 6
// fn: 7
// fn: 8
// fn: 9
 
console.log( fn.count ); // 0 -- 耶桶唐?咋不是4捏?
  • fn.count是函數(shù)本身的屬性茉兰,因為函數(shù)也是對象
  • this.count是fn函數(shù)構(gòu)造器中的變量尤泽, 也是全局變量,this->window

4. 繼承+引用+this

function Parent() {
            this.a = 1;
            this.b = [1, 2, this.a]; // this.a只在函數(shù)體內(nèi)存在规脸,這里相當(dāng)于設(shè)置“默認(rèn)值”
            this.c = { demo: 5 };
            this.show = function () {
                console.log(this.a , this.b , this.c.demo );
            }
        }
 function Child() {
     this.a = 2;
     this.change = function () {
         // this中變量就近引用坯约,如果沒有就從原型鏈繼續(xù)找
         this.b.push(this.a);
         this.a = this.b.length;
         this.c.demo = this.a++;
     }
 }
 Child.prototype = new Parent(); 
 var parent = new Parent();
 var child1 = new Child();
 var child2 = new Child();
 child1.a = 11;
 child2.a = 12;
 parent.show(); // 1 [1,2,1] 5
 child1.show(); // 11 [1,2,1] 5
 child2.show(); // 12 [1,2,1] 5
 child1.change(); // a->5, b->[1,2,1,11], c.demo->4,this就近+繼承+引用
 child2.change(); // a->6, b->[1,2,1,11,12], c.demo->5莫鸭,this就近+繼承+引用
 parent.show(); // 1 [1,2,1] 5
 child1.show(); // 5 [1,2,1,11,12] 5
 child2.show(); // 6 [1,2,1,11,12] 5

需要注意的是:

  • 繼承+引用
 child2.__proto__ === child1.__proto__ // true
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鬼店,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子黔龟,更是在濱河造成了極大的恐慌妇智,老刑警劉巖滥玷,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巍棱,居然都是意外死亡惑畴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門航徙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來如贷,“玉大人,你說我怎么就攤上這事到踏「芨ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵窝稿,是天一觀的道長楣富。 經(jīng)常有香客問我,道長伴榔,這世上最難降的妖魔是什么纹蝴? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮踪少,結(jié)果婚禮上塘安,老公的妹妹穿的比我還像新娘。我一直安慰自己援奢,他們只是感情好兼犯,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著集漾,像睡著了一般免都。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帆竹,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音脓规,去河邊找鬼栽连。 笑死,一個胖子當(dāng)著我的面吹牛侨舆,可吹牛的內(nèi)容都是我干的秒紧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挨下,長吁一口氣:“原來是場噩夢啊……” “哼熔恢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起臭笆,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤叙淌,失蹤者是張志新(化名)和其女友劉穎秤掌,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹰霍,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闻鉴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茂洒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孟岛。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖督勺,靈堂內(nèi)的尸體忽然破棺而出渠羞,到底是詐尸還是另有隱情,我是刑警寧澤智哀,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布次询,位于F島的核電站,受9級特大地震影響盏触,放射性物質(zhì)發(fā)生泄漏渗蟹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一赞辩、第九天 我趴在偏房一處隱蔽的房頂上張望雌芽。 院中可真熱鬧,春花似錦辨嗽、人聲如沸世落。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屉佳。三九已至,卻和暖如春洲押,著一層夾襖步出監(jiān)牢的瞬間武花,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工杈帐, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留体箕,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓挑童,卻偏偏與公主長得像累铅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子站叼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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

  • 從學(xué)習(xí)前端開始已經(jīng)大概要兩個月了娃兽,這幾天突然發(fā)現(xiàn)this并沒有想想中這么簡單,之前很長一段時間對于this的認(rèn)識停...
    悲歡自飲丶閱讀 277評論 0 0
  • this作為全局對象window的幾種情況 作為函數(shù)調(diào)用的this 作為內(nèi)部嵌套函數(shù)的this 在setTimeo...
    羞澀的澀閱讀 739評論 0 0
  • 什么是this this是JS中一個非常重要的關(guān)鍵字尽楔。this 就是你 call 一個函數(shù)時投储,傳入的 contex...
    字母31閱讀 176評論 0 1
  • 〈光明〉 我始終相信 不遠(yuǎn)處 會有 我要的光明 讓它永永遠(yuǎn)遠(yuǎn) 照亮大地 〈木香〉 小河橋的檀木 縷縷幽香 被我拈下...
    冰影燈下閱讀 332評論 1 4
  • 念與酒 稀燭殘光 獨(dú)影微晃 燒酒入喉 我一人享 月落屋房 孤芳自賞 回...
    孤十四閱讀 145評論 0 2