0.關(guān)于this是指什么
- 其實(shí)可以這么概括翎猛,this屬于調(diào)用被調(diào)用的方法的主體胖翰,也就是,誰調(diào)用切厘,誰就是this萨咳。
- 雖然說起來這么簡單,但是上面的話里面的概念其實(shí)涉及到:作為方法的調(diào)用(function)的this疫稿;作為構(gòu)造函數(shù)里的this培他;作為call或者apply的this鹃两。
- 以上三個概念,又涉及到j(luò)s里的對象創(chuàng)建舀凛,和方法的繼承俊扳,所以,要弄清楚this猛遍,就要弄清楚js里的對象創(chuàng)建和繼承機(jī)制馋记。
1.作為方法調(diào)用(function)的this
這個是最為簡單的,但也可以分為幾種情況懊烤,我們寫一個文件梯醒,叫functionThis.js
1.1 有如下代碼:
function fnThis(){
console.log(this);
}
fnThis();
打印結(jié)果如下:
可以看到this當(dāng)前是指向window,這個很好理解腌紧,因?yàn)閠his是指向調(diào)用者茸习,而fnThis沒有調(diào)用者,沒有調(diào)用者就默認(rèn)為window壁肋。
1.2 我們在文件繼續(xù)添加代碼号胚,如下
var obj = {
fnThis: function(){
console.log(this);
}
}
obj.fnThis();
var objIns = obj.fnThis;
objIns();
打印結(jié)果如下:
可以看到,第1行的打印this是obj浸遗,第2行是window涕刚,會這樣的差異是因?yàn)椋簅bj.fnThis()的調(diào)用者是obj,所以this指向obj乙帮;而objIns()沒有調(diào)用者杜漠,默認(rèn)為window。
2.作為構(gòu)造函數(shù)里的this
這里我們的demo文件為constructorThis.js
2.1 說到這里又要提一下構(gòu)造函數(shù)察净,構(gòu)造函數(shù)驾茴,就是可以構(gòu)造一個對象的函數(shù)類,js里面最簡單的莫過于直接定義一個對象氢卡,這個對象有一些屬性的方法锈至,然后用的時候直接拿來用,像我們functionThis.js這個文件里的就是這種情況译秦,這是直接定義峡捡,另外還有一些其他的方式就先不展開講了,下面的例子以比較常見的構(gòu)造函數(shù)的形式為例筑悴,至于為什么要用構(gòu)造函數(shù)们拙,簡單地講就是可以用面向?qū)ο蟮男问饺ゾ幊蹋衫^承阁吝,等砚婆。
2.2 測試代碼如下:
var thisObj;
var ConstructorThis = function(params){
this.myParams = params;
console.log(this);
thisObj = this;
}
ConstructorThis.prototype.sayParams = function(){
console.log(this.myParams);
}
var obj = new ConstructorThis('hi');
console.log(obj);
console.log(obj === thisObj)
obj.sayParams();
打印結(jié)果如下:
我們可以看到第1行和第2行打印的對象從字面上看是一樣的(字面一樣不等于兩個對象是同一個東西,后續(xù)可以自行初始化幾個對象進(jìn)行類似的對比驗(yàn)證)突勇,我們?yōu)榱蓑?yàn)證這個this是否就是obj装盯,把this賦值給外部的thisObj并進(jìn)行比較坷虑,結(jié)果是true,說明構(gòu)造函數(shù)內(nèi)的this確實(shí)就是new 構(gòu)造函數(shù)() 后的對象埂奈。
2.3 如此一來迄损,因?yàn)闃?gòu)造函數(shù)需要用new關(guān)鍵字實(shí)例化一個對象,那么和1-2作為方法的調(diào)用的情況相比账磺,this似乎就不是指向被調(diào)用者了海蔽?
實(shí)際上:
var obj = new ConstructorThis('hi');
等價于:
var obj = {};
obj.__proto__ = ConstructorThis.prototype;
ConstructorThis.call(obj, 'hi');
將第一個代碼塊改為下面三行的代碼塊,我們再看一下打印結(jié)果:
與2-2的情況是一致的绑谣。
也就是說党窜,new 構(gòu)造函數(shù)() 干了這么一件事情拴袭,第一步巨朦,創(chuàng)建一個空的對象嬉荆;第二步全跨,把空對象的原型指向構(gòu)造函數(shù)的原型闷袒;第三步卸伞,再根據(jù)call傳入?yún)?shù)('hi')速缨,同時把構(gòu)造函數(shù)內(nèi)部的this指向這個空對象褒翰,這樣一來就完成了構(gòu)造函數(shù)原型鏈的繼承(第二步)和自身屬性的賦值(第三步)欲间;最后返回新創(chuàng)建的對象楚里。
構(gòu)造函數(shù)內(nèi)部this等于新創(chuàng)建的對象,關(guān)鍵就在第三步猎贴,用了call方法把創(chuàng)建的對象指向了內(nèi)部的this班缎,call和apply方法會在接下來講∷剩總之达址,構(gòu)造函數(shù)內(nèi)部的this指向用new創(chuàng)建的新對象。
3.作為call或者apply的this
這里我們?nèi)匀粍?chuàng)建一個demo文件callThis.js
3.1 call或者apply的作用就是改變某個方法的運(yùn)行環(huán)境趁耗,也就是改變內(nèi)部this關(guān)鍵字的指向沉唠,正因?yàn)橛羞@樣的作用,也在js的繼承機(jī)制中其中有著顯著的作用苛败。正如2-3我們講到的一樣满葛,構(gòu)造函數(shù)用call把創(chuàng)建的一個類指向了內(nèi)部的this關(guān)鍵字,因此構(gòu)造函數(shù)類可以作為一個類被繼承罢屈,每個不同的實(shí)例通過內(nèi)部的this被賦予了不同的屬性嘀韧。
3.2 call和apply的用法其實(shí)很簡單,我們看如下代碼:
var CallThis = function(params){
console.log(this)
this.myParams = params;
}
CallThis.call();
var obj = {otherParams: 'ok'};
CallThis.call(obj, 'hi');
console.log(obj);
打印結(jié)果如下:
第一次call沒傳任何參數(shù)儡遮,內(nèi)部的this打印出來是window乳蛾,這在第一部分的時候已經(jīng)說明了;第二次call的時候第一個參數(shù)是一個對象鄙币,第二個參數(shù)對應(yīng)構(gòu)造函數(shù)的一個參數(shù)肃叶,可以看到this指向了obj,然后obj被添加了myParams屬性十嘿,因此 構(gòu)造函數(shù).call(obj, args)這種形式就是通過call方法把this指向obj因惭,args是傳入構(gòu)造函數(shù)內(nèi)部的參數(shù),需要傳多個參數(shù)是這樣子的 構(gòu)造函數(shù).call(obj, args, args1, ..., argsN)绩衷,第一個參數(shù)就是把this指向的對象蹦魔,這和我們在2-3說到的,new的步驟分解咳燕,第三步是一致的勿决。
apply和call的用法類似,構(gòu)造函數(shù).call(obj, args, args1, ..., argsN) 等同于 構(gòu)造函數(shù).call(obj, [args, args1, ..., argsN])招盲,我們修改一下代碼低缩,將
CallThis.call(obj, 'hi');
替換為
CallThis.apply(obj, ['hi']);
可見,打印結(jié)果一致:
即曹货,call和apply都能把上下文運(yùn)行到具體的this環(huán)境里咆繁,不同的只是傳參的形式,call參數(shù)的傳遞是分開傳遞顶籽,而apply是作為數(shù)組傳遞玩般。
4.總結(jié)
到這里就基本把this涉及到的情況說完了,包括作為方法的調(diào)用(function)的this礼饱;作為構(gòu)造函數(shù)里的this坏为;作為call或者apply的this;第二和第三種情況本質(zhì)上是統(tǒng)一的镊绪。
這算是第一次寫比較完整的文章久脯,大部分是自己的理解,當(dāng)然前期學(xué)習(xí)階段也參考了一些資料镰吆,若有不當(dāng)之處帘撰,還請指正,若有寶貴意見万皿,也可以多多交流摧找。
具體的實(shí)例代碼和demo地址:
https://github.com/CHristopherkeith/front-end-summary-this-new