覺得本人寫的不算很爛的話贝咙,可以登錄關(guān)注一下我的GitHub博客,新手寫東西寫的不好之處拂募,還望見諒庭猩,畢竟水平有限窟她,寫東西只為交流提高,一起學(xué)習(xí)蔼水,還望大神多加指點(diǎn)震糖,指出紕漏,和提出寶貴的意見徙缴,博客會堅(jiān)持寫下去试伙。
今天同學(xué)去面試,做了兩道面試題,全部做錯(cuò)了于样,發(fā)過來給我看,我一眼就看出來了疏叨,因?yàn)檫@種題我做過,至于為什么結(jié)果是那樣穿剖,我也之前沒有深究過蚤蔓,他問我為什么,我也是一臉的懵逼糊余,不能從根源上解釋問題的原因秀又,所以并不能完全讓他信服。今天就借著這個(gè)機(jī)會深扒一下贬芥,如果沒有耐心可以點(diǎn)擊右上角吐辙,以看小說的心態(tài)看技術(shù)文章,走馬觀花蘸劈,不加思考昏苏,這樣的量變并不能帶來質(zhì)的改變⊥花上10+分鐘認(rèn)真閱讀我相信你會受益匪淺贤惯,沒收獲你買把武昌火車站同款菜刀砍我??。因?yàn)槲沂菍懲赀@篇博客再回頭寫這段話的棒掠,在寫的過程中也學(xué)到了很多孵构,所以在此分享一下共同學(xué)習(xí)。
登高自卑,與君共勉烟很。
下面一起看看這道題颈墅,同學(xué)微信發(fā)給我截圖:
如果看的不太清楚,我把代碼敲一遍,給大家看看:
var name = "jay"; //一看這二逼就是周杰倫的死忠粉
var person = {
name: "kang",
pro: {
name: "Michael",
getName: function() {
return this.name;
}
}
};
console.log(person.pro.getName());
var pepole = person.pro.getName;
console.log(pepole());
這里我就不賣關(guān)子了,不少童鞋也應(yīng)該遇到過做過類似的題目,就是考察this,我們先看看答案:
console.log(person.pro.getName());//Michael
console.log(pepole());//jay
第一個(gè)很簡單雾袱,this就是指向person.pro的引用恤筛,那么this.name就是person.pro.name,于是第一個(gè)就是輸出Michael谜酒,再來看看第二個(gè)就蹊蹺了叹俏,和第一個(gè)明明是一樣的方法妻枕,為什么輸出的結(jié)果是jay呢僻族?
既然我們知道結(jié)果是jay了粘驰,反著推理一步步來,不難推出調(diào)用people()這個(gè)方法時(shí)候的this.name就相當(dāng)于和var name = "jay"述么,var聲明的全局變量和全局環(huán)境下的this的變量有什么聯(lián)系呢蝌数?;那么這個(gè)this到底是什么度秘,總得是一個(gè)具體東西吧顶伞?
我們一步步分析,this.name這個(gè)this有一個(gè)name屬性剑梳,很明顯就是一個(gè)對象唆貌,那具體是什么對象呢?this的指向是在函數(shù)被調(diào)用的時(shí)候確定的垢乙,于是有人說就是Window對象锨咙,沒錯(cuò)是沒錯(cuò),確實(shí)是Window對象追逮,然后var name聲明的全局變量name和window.name是相同的作用酪刀;但是你只只知其然,而不知其所以然钮孵,學(xué)深一門語言就是要有刨根問底的精神骂倘,打破砂鍋問到底,知其然還要知其所以然巴席。
我們就先驗(yàn)證一下历涝,那個(gè)this到底是不是window對象吧。我們把代碼稍微調(diào)整一下情妖,輸出this睬关。
var name = "jay"; //一看這二逼就是周杰倫的死忠粉
var person = {
name: "kang",
pro: {
name: "Michael",
getName: function() {
console.log(this);
return this.name;
}
}
};
console.log(person.pro.getName());
var pepole = person.pro.getName;
console.log(pepole());
看看控制臺輸出,確實(shí)沒錯(cuò)就是window對象毡证。
再來看看var name聲明的name和window.name是否相等呢电爹?
var name;
console.log(name===window.name)
確實(shí)是一樣的,類型和值沒有任何的不同料睛。
好滴丐箩,那么你說this就是window對象,至于為什么是這樣你也不清楚恤煞,是否永遠(yuǎn)是這樣呢屎勘?我們看看這段代碼輸出又會是咋樣呢?
'use strict';
var name = "jay"; //一看這二逼就是周杰倫的死忠粉
var person = {
name: "kang",
pro: {
name: "Michael",
getName: function() {
console.log(this);
return this.name;
}
}
};
console.log(person.pro.getName());
var pepole = person.pro.getName;
console.log(pepole());
還會是跟上面一樣的結(jié)果嗎?我們拭目以待.
看到結(jié)果沒:Cannot read property 'name' of undefined,這是什么意思想必大家已經(jīng)很清楚了居扒,此時(shí)的this成了undefined了概漱,undefined當(dāng)然也就沒有name這個(gè)屬性,所以瀏覽器報(bào)錯(cuò)了喜喂。那么為什么會這樣呢瓤摧?
同樣換種寫法再來看看這段代碼輸出什么呢?
var name = "jay";
var person = {
name : "kang",
getName : function(){
return function(){
return this.name;
};
}
};
console.log(person.getName()());
控制臺自己輸出一下看看竿裂,我想此時(shí)你的心情一定是這樣的:
在弄明白這些問題之前,我們先弄清楚全局環(huán)境下的this照弥,var聲明的全局變量和window對象之間的聯(lián)系與區(qū)別:
先看四個(gè)簡單的例子對比腻异,均在js非嚴(yán)格模式測試,也就是沒有聲明'use strict':
demo1:
var name="jawil";
console.log(name);
console.log(window.name)
console.log(this.name)
demo2:
name="jawil";
console.log(name);
console.log(window.name)
console.log(this.name)
demo3:
window.name="jawil";
console.log(name);
console.log(window.name)
console.log(this.name)
demo4:
this.name="jawil";
console.log(name);
console.log(window.name)
console.log(this.name)
其實(shí)這四個(gè)demo是一個(gè)意思这揣,輸出的結(jié)果沒有任何差別悔常,為什么沒有差別呢?因?yàn)樗麄冊谕粋€(gè)環(huán)境,也就是全局環(huán)境下:
我們換一種在不同的環(huán)境下執(zhí)行這段代碼看一看結(jié)果:
demo5:
var name="jawil";
var test={
name:'jay',
getName:function(){
console.log(name);
console.log(window.name)
console.log(this.name)
}
}
test.getName();
最后結(jié)果一次輸出為:
console.log(name);//jawil
console.log(window.name)//jawil
console.log(this.name)//jay
因?yàn)榇颂幍?strong>this不再指向全局對象了给赞,所以結(jié)果肯定不同机打,我們先來看看全局對象和全局環(huán)境下的this,暫不考慮其他環(huán)境下的this片迅。
那么又有人會問什么是全局環(huán)境姐帚,什么又是全局對象,全局對象該怎么理解障涯?
題外話
其實(shí)我們看技術(shù)文章罐旗,總覺得似懂非懂,一知半解唯蝶,不是看不懂代碼九秀,而是因?yàn)楹芏鄷r(shí)候我們對一些概念沒有比較深入的了解,但是也沒有去認(rèn)真繼續(xù)下去考究粘我,這也不能怪我們鼓蜒,畢竟開發(fā)時(shí)候不太深入這些概念對我們業(yè)務(wù)也沒啥影響,但是我發(fā)現(xiàn)我自己寫東西時(shí)候征字,不把概念說清楚都弹,總不能讓人信服和徹底明白你講的是什么玩意,我想寫博客最大的好處可以讓自己進(jìn)一步提高匙姜,更深層次的理解你所學(xué)過的東西畅厢,你講的別人都看不懂,你確認(rèn)你真的懂了嗎氮昧?
說到全局環(huán)境框杜,我們就會牽扯到另一個(gè)概念那就是執(zhí)行環(huán)境和函數(shù)的作用域
既然扯到這么深,就順便扯扯執(zhí)行環(huán)境和作用域袖肥,這些都是js這門語言的重點(diǎn)和難點(diǎn)咪辱,沒有一定的沉淀很難去深入探討這些東西的.
函數(shù)的每次調(diào)用都有與之緊密相關(guān)的作用域和執(zhí)行環(huán)境。從根本上來說椎组,作用域是基于函數(shù)的油狂,而執(zhí)行環(huán)境是基于對象的(例如:全局執(zhí)行環(huán)境即全局對象window)。
我們還是先說一說全局對象吧,因?yàn)槿謭?zhí)行環(huán)境是基于全局對象的专筷。
JavaScript 全局對象
全局屬性和函數(shù)可用于所有內(nèi)建的 JavaScript 對象夹供。
全局對象描述
- 全局對象是預(yù)定義的對象,作為 JavaScript 的全局函數(shù)和全局屬性的占位符仁堪。通過使用全局對象,可以訪問所有其他所有預(yù)定義的對象填渠、函數(shù)和屬性弦聂。全局對象不是任何對象的屬性,所以它沒有名稱氛什。
- 在頂層 JavaScript 代碼中莺葫,可以用關(guān)鍵字 this 引用全局對象。但通常不必用這種方式引用全局對象枪眉,因?yàn)槿謱ο笫亲饔糜蜴湹念^捺檬,這意味著所有非限定性的變量和函數(shù)名都會作為該對象的屬性來查詢。例如贸铜,當(dāng)JavaScript 代碼引用 parseInt() 函數(shù)時(shí)堡纬,它引用的是全局對象的 parseInt 屬性。全局對象是作用域鏈的頭蒿秦,還意味著在頂層 JavaScript 代碼中聲明的所有變量都將成為全局對象的屬性烤镐。
- 全局對象只是一個(gè)對象,而不是類棍鳖。既沒有構(gòu)造函數(shù)炮叶,也無法實(shí)例化一個(gè)新的全局對象。
- 在 JavaScript 代碼嵌入一個(gè)特殊環(huán)境中時(shí)渡处,全局對象通常具有環(huán)境特定的屬性镜悉。實(shí)際上,ECMAScript 標(biāo)準(zhǔn)沒有規(guī)定全局對象的類型医瘫,JavaScript 的實(shí)現(xiàn)或嵌入的 JavaScript 都可以把任意類型的對象作為全局對象侣肄,只要該對象定義了這里列出的基本屬性和函數(shù)。例如醇份,在允許通過 LiveConnect 或相關(guān)的技術(shù)來腳本化 Java 的 JavaScript 實(shí)現(xiàn)中茫孔,全局對象被賦予了這里列出的 java 和 Package 屬性以及 getClass() 方法。而在客戶端 JavaScript 中被芳,全局對象就是 Window 對象缰贝,表示允許 JavaScript 代碼的 Web 瀏覽器窗口。
例子
在 JavaScript 核心語言中畔濒,全局對象的預(yù)定義屬性都是不可枚舉的剩晴,所有可以用 for/in 循環(huán)列出所有隱式或顯式聲明的全局變量,如下所示:
上一篇博客我就講到遍歷對象屬性的三種方法:
for-in
循環(huán)、Object.keys()
以及Object.getOwnPropertyNames()
不同的區(qū)別赞弥,想要了解可以細(xì)看我這篇博客:傳送門
var variables = "";
for (var name in this)
{
variables += name + "<br />";
}
document.write(variables);
再回過頭來談?wù)剤?zhí)行環(huán)境和函數(shù)的作用域
一開始要明白的
- 首先毅整,我們要知道執(zhí)行環(huán)境和作用域是兩個(gè)完全不同的概念。
- 函數(shù)的每次調(diào)用都有與之緊密相關(guān)的作用域和執(zhí)行環(huán)境绽左。從根本上來說悼嫉,作用域是基于函數(shù)類型的(當(dāng)然函數(shù)也是對象,這里我們細(xì)分一下)拼窥,而執(zhí)行環(huán)境是基于對象類型的(例如:全局執(zhí)行環(huán)境即window對象)戏蔑。
- 換句話說,作用域涉及到所被調(diào)用函數(shù)中的變量訪問鲁纠,并且不同的調(diào)用場景是不一樣的总棵。執(zhí)行環(huán)境始終是this關(guān)鍵字的值,它是擁有當(dāng)前所執(zhí)行代碼的對象的引用改含。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對象情龄,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對象中。雖然我們編寫的代碼無法訪問這個(gè)對象捍壤,但解析器在處理數(shù)據(jù)時(shí)會在后臺使用它骤视。
一些概念
1. 執(zhí)行環(huán)境(也稱執(zhí)行上下文–execution context)
首先來說說js中的執(zhí)行環(huán)境,所謂執(zhí)行環(huán)境(有時(shí)也稱環(huán)境)它是JavaScript中最為重要的一個(gè)概念鹃觉。執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù) 尚胞,決定了它們各自的行為。而每個(gè)執(zhí)行環(huán)境都有一個(gè)與之相關(guān)的變量對象帜慢,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對象中笼裳。
當(dāng)JavaScript解釋器初始化執(zhí)行代碼時(shí),它首先默認(rèn)進(jìn)入全局執(zhí)行環(huán)境粱玲,從此刻開始躬柬,函數(shù)的每次調(diào)用都會創(chuàng)建一個(gè)新的執(zhí)行環(huán)境。
每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境抽减。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí)允青,函數(shù)的環(huán)境就會被推入一個(gè)環(huán)境棧中(execution stack)。在函數(shù)執(zhí)行完后卵沉,棧將其環(huán)境彈出颠锉,把控制權(quán)返回給之前的執(zhí)行環(huán)境。ECMAScript程序中的執(zhí)行流正是由這個(gè)便利的機(jī)制控制著史汗。執(zhí)行環(huán)境可以分為創(chuàng)建和執(zhí)行兩個(gè)階段琼掠。在創(chuàng)建階段,解析器首先會創(chuàng)建一個(gè)變量對象(variable object停撞,也稱為活動對象activation object)瓷蛙,它由定義在執(zhí)行環(huán)境中的變量悼瓮、函數(shù)聲明、和參數(shù)組成艰猬。在這個(gè)階段横堡,作用域鏈會被初始化,this的值也會被最終確定冠桃。在執(zhí)行階段命贴,代碼被解釋執(zhí)行。
1.1可執(zhí)行的JavaScript代碼分三種類型:
- Global Code食听,即全局的调煎、不在任何函數(shù)里面的代碼赵讯,例如:一個(gè)js文件依溯、嵌入在HTML頁面中的js代碼等毯炮。
- Eval Code省咨,即使用eval()函數(shù)動態(tài)執(zhí)行的JS代碼肃弟。
- Function Code,即用戶自定義函數(shù)中的函數(shù)體JS代碼零蓉。
不同類型的JavaScript代碼具有不同的Execution Context
Demo:
<script type="text/javascript">
function Fn1(){
function Fn2(){
alert(document.body.tagName);//BODY
//other code...
}
Fn2();
}
Fn1();
//code here
</script>
特別說明:圖片來自于笨蛋的座右銘博客
1.2執(zhí)行環(huán)境小結(jié)
當(dāng)javascript代碼被瀏覽器載入后笤受,默認(rèn)最先進(jìn)入的是一個(gè)全局執(zhí)行環(huán)境。當(dāng)在全局執(zhí)行環(huán)境中調(diào)用執(zhí)行一個(gè)函數(shù)時(shí)敌蜂,程序流就進(jìn)入該被調(diào)用函數(shù)內(nèi)箩兽,此時(shí)JS引擎就會為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境,并且將其壓入到執(zhí)行環(huán)境堆棧的頂部章喉。瀏覽器總是執(zhí)行當(dāng)前在堆棧頂部的執(zhí)行環(huán)境汗贫,一旦執(zhí)行完畢,該執(zhí)行環(huán)境就會從堆棧頂部被彈出秸脱,然后落包,進(jìn)入其下的執(zhí)行環(huán)境執(zhí)行代碼。這樣摊唇,堆棧中的執(zhí)行環(huán)境就會被依次執(zhí)行并且彈出堆棧咐蝇,直到回到全局執(zhí)行環(huán)境。
此外還要注意一下幾點(diǎn):
- 單線程
- 同步執(zhí)行
- 唯一的全局執(zhí)行環(huán)境
- 局部執(zhí)行環(huán)境的個(gè)數(shù)沒有限制
- 每次某個(gè)函數(shù)被調(diào)用巷查,就會有個(gè)新的局部執(zhí)行環(huán)境為其創(chuàng)建有序,即使是多次調(diào)用的自身函數(shù)(即一個(gè)函數(shù)被調(diào)用多次,也會創(chuàng)建多個(gè)不同的局部執(zhí)行環(huán)境)岛请。
2. 作用域(scope)
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí)旭寿,會創(chuàng)建變量對象的一個(gè)作用域鏈(scope chain。作用域鏈的用途是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問崇败。
作用域鏈包含了執(zhí)行環(huán)境棧中的每個(gè)執(zhí)行環(huán)境對應(yīng)的變量對象.
通過作用域鏈许师,可以決定變量的訪問和標(biāo)識符的解析。
注意:全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈的最后一個(gè)對象。
在訪問變量時(shí)微渠,就必須存在一個(gè)可見性的問題(內(nèi)層環(huán)境可以訪問外層中的變量和函數(shù)搭幻,而外層環(huán)境不能訪問內(nèi)層的變量和函數(shù))。更深入的說逞盆,當(dāng)訪問一個(gè)變量或調(diào)用一個(gè)函數(shù)時(shí)檀蹋,JavaScript引擎將不同執(zhí)行環(huán)境中的變量對象按照規(guī)則構(gòu)建一個(gè)鏈表,在訪問一個(gè)變量時(shí)云芦,先在鏈表的第一個(gè)變量對象上查找俯逾,如果沒有找到則繼續(xù)在第二個(gè)變量對象上查找,直到搜索到全局執(zhí)行環(huán)境的變量對象即window對象舅逸。這也就形成了Scope Chain的概念桌肴。
特別說明:圖片來自于笨蛋的座右銘博客
作用域鏈圖,清楚的表達(dá)了執(zhí)行環(huán)境與作用域的關(guān)系(一一對應(yīng)的關(guān)系)琉历,作用域與作用域之間的關(guān)系(鏈表結(jié)構(gòu)坠七,由上至下的關(guān)系)。
Demo:
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 這里可以訪問color, anotherColor, 和 tempColor
}
// 這里可以訪問color 和 anotherColor旗笔,但是不能訪問 tempColor
swapColors();
}
changeColor();
// 這里只能訪問color
console.log("Color is now " + color);
上述代碼一共包括三個(gè)執(zhí)行環(huán)境:全局執(zhí)行環(huán)境彪置、changeColor()的局部執(zhí)行環(huán)境、swapColors()的局部執(zhí)行環(huán)境蝇恶。
- 全局環(huán)境有一個(gè)變量color和一個(gè)函數(shù)changecolor();
- changecolor()函數(shù)的局部環(huán)境中具有一個(gè)anothercolor屬性和一個(gè)swapcolors函數(shù)拳魁,當(dāng)然,changecolor函數(shù)中可以訪問自身以及它外圍(即全局環(huán)境)中的變量;
- swapcolor()函數(shù)的局部環(huán)境中具有一個(gè)變量tempcolor撮弧。在該函數(shù)內(nèi)部可以訪問上面的兩個(gè)環(huán)境(changecolor和window)中的所有變量潘懊,因?yàn)槟莾蓚€(gè)環(huán)境都是它的父執(zhí)行環(huán)境。
上述代碼的作用域鏈如下圖所示:
從上圖發(fā)現(xiàn)贿衍。內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境卦尊,但是外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。
標(biāo)識符解析(變量名或函數(shù)名搜索)是沿著作用域鏈一級一級地搜索標(biāo)識符的過程舌厨。搜索過程始終從作用域鏈的前端開始岂却,然后逐級地向后(全局執(zhí)行環(huán)境)回溯,直到找到標(biāo)識符為止裙椭。
3.執(zhí)行環(huán)境與作用域的區(qū)別與聯(lián)系
執(zhí)行環(huán)境為全局執(zhí)行環(huán)境和局部執(zhí)行環(huán)境躏哩,局部執(zhí)行環(huán)境是函數(shù)執(zhí)行過程中創(chuàng)建的。
作用域鏈?zhǔn)腔趫?zhí)行環(huán)境的變量對象的揉燃,由所有執(zhí)行環(huán)境的變量對象(對于函數(shù)而言是活動對象扫尺,因?yàn)樵诤瘮?shù)執(zhí)行環(huán)境中,變量對象是不能直接訪問的炊汤,此時(shí)由活動對象(activation object,縮寫為AO)扮演VO(變量對象)的角色正驻。)共同組成弊攘。
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會創(chuàng)建變量對象的一個(gè)作用域鏈姑曙。作用域鏈的用途:是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問襟交。作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象伤靠。
4.小練習(xí)
<script type="text/javascript">
(function(){
a= 5;
console.log(window.a);//undefined
var a = 1;//這里會發(fā)生變量聲明提升
console.log(a);//1
})();
</script>
window.a之所以是undefined捣域,是因?yàn)関ar a = 1;發(fā)生了變量聲明提升。相當(dāng)于如下代碼:
<script type="text/javascript">
(function(){
var a;//a是局部變量
a = 5;//這里局部環(huán)境中有a宴合,就不會找全局中的
console.log(window.a);//undefined
a = 1;//這里會發(fā)生變量聲明提升
console.log(a);//1
})();
</script>
更多關(guān)于變量提升和執(zhí)行上下文詳細(xì)解說這里就不多少了焕梅,不然越扯越深,有興趣可以看看這篇圖解卦洽,淺顯易懂:
前端基礎(chǔ)進(jìn)階(二):執(zhí)行上下文詳細(xì)圖解
相信大家看到這里贞言,也很累了,但是也有收獲阀蒂,大概有了一些深刻印象该窗,對概念也有一些比較深入的理解了。
這里我就稍微總結(jié)一下脂新,上面講了一些什么挪捕,對接下來的解析應(yīng)該有很大的幫助粗梭。
**1. 瀏覽器的全局對象是window
- 全局執(zhí)行環(huán)境即window對象所創(chuàng)建的争便,局部執(zhí)行環(huán)境是函數(shù)執(zhí)行過程中創(chuàng)建的。
- 全局對象断医,可以訪問所有其他所有預(yù)定義的對象滞乙、函數(shù)和屬性。
- 當(dāng)javascript代碼被瀏覽器載入后鉴嗤,默認(rèn)最先進(jìn)入的是一個(gè)全局執(zhí)行環(huán)境斩启。
- 明白了執(zhí)行上下文和作用域的一些概念,知道其中的運(yùn)行機(jī)制和原理醉锅。**
我們再回頭看看這兩個(gè)demo比較,我們解釋清楚這個(gè)demo執(zhí)行的結(jié)果兔簇。
demo1:
var name="jawil";
console.log(name);//jawil
console.log(window.name)//jawil
console.log(this.name)//jawill
demo2:
name="jawil";
console.log(name);//jawil
console.log(window.name)//jawil
console.log(this.name)//jawil
好,從例子看以看出硬耍,這兩個(gè)name都是全局屬性垄琐,未通過var聲明的變量a和通過var聲明的變量b,都可以通過this和window訪問到.
我們可以在控制臺打印出windowd對象经柴,發(fā)現(xiàn)name成了window對象的一個(gè)屬性:
var name="jawil";
console.log(window);
name2="test";
console.log(window);
這是其實(shí)一個(gè)作用域和上下文的問題狸窘。在JavaScript中,this指向當(dāng)前的上下文坯认,而var定義的變量值在當(dāng)前作用域中有效翻擒。JavaScript有兩種作用域氓涣,全局作用域和局部作用域。局部作用域就是在一個(gè)函數(shù)里陋气。var關(guān)鍵字使用來在當(dāng)前作用于中創(chuàng)建局部變量的劳吠,而在瀏覽器中的JavaScript全局作用域中使用var語句時(shí),會把申明的變量掛在window上恩伺,而全局作用域中的this上下文恰好指向的又是window赴背,因此在全局作用域中var申明的變量和window上掛的變量,即this可訪問的變量有間接的聯(lián)系晶渠,但沒有直接聯(lián)系凰荚,更不是一樣的。
上面的分析我們知道了褒脯,全局變量便瑟,全局環(huán)境下的this,還有全局對象之間的關(guān)系了,具體總結(jié)一下就是:
**1. 全局環(huán)境的this會指向全局對象window番川,此時(shí)this===window;
- 全局變量會掛載在window對象下到涂,會成為window下的一個(gè)屬性。
- 如果你沒有使用嚴(yán)格模式并給一個(gè)未聲明的變量賦值的話颁督,JS會自動創(chuàng)建一個(gè)全局變量践啄。**
那么用var聲明的全局變量賦值和未聲明的全局變量賦值到底有什么不同呢?這里不再是理解理解這道面試題的重點(diǎn)沉御,想深入探究可以看看這篇文章:javascript中加var和不加var的區(qū)別 你真的懂嗎.
該回頭了屿讽,好累??,再來看看這道面試題:
var name = "jay"; //一看這二逼就是周杰倫的死忠粉
var person = {
name: "kang",
pro: {
name: "Michael",
getName: function() {
return this.name;
}
}
};
console.log(person.pro.getName());
var pepole = person.pro.getName;
console.log(pepole());
最后就成了為什么person.pro.getName()的this是person.pro而pepole()的this成了window對象吠裆。這里我們就要了解this的運(yùn)行機(jī)制和原理伐谈。
在這里,我們需要得出一個(gè)非常重要一定要牢記于心的結(jié)論试疙,this的指向诵棵,是在函數(shù)被調(diào)用的時(shí)候確定的。也就是執(zhí)行上下文被創(chuàng)建時(shí)確定的祝旷。因此我們可以很容易就能理解到履澳,一個(gè)函數(shù)中的this指向,可以是非常靈活的怀跛。
在一個(gè)函數(shù)上下文中距贷,this由調(diào)用者提供,由調(diào)用函數(shù)的方式來決定敌完。
如果調(diào)用者函數(shù)储耐,被某一個(gè)對象所擁有,那么該函數(shù)在調(diào)用時(shí)滨溉,內(nèi)部的this指向該對象什湘。如果函數(shù)獨(dú)立調(diào)用长赞,那么該函數(shù)內(nèi)部的this,則指向undefined闽撤。但是在非嚴(yán)格模式中得哆,當(dāng)this指向undefined時(shí),它會被自動指向全局對象哟旗。
person.pro.getName()中贩据,getName是調(diào)用者,他不是獨(dú)立調(diào)用闸餐,被對象person.pro所擁有饱亮,因此它的this指向了person.pro。而pepole()作為調(diào)用者舍沙,盡管他與person.pro.getName的引用相同近上,但是它是獨(dú)立調(diào)用的,因此this指向undefined拂铡,在非嚴(yán)格模式壹无,自動轉(zhuǎn)向全局window。
再來看一個(gè)例子感帅,來加深理解這段話:
var a = 20;
function getA() {
return this.a;
}
var foo = {
a: 10,
getA: getA
}
console.log(foo.getA()); // 10
靈機(jī)一動斗锭,再來一個(gè)。如下例子失球。
function foo() {
console.log(this.a)
}
function active(fn) {
fn(); // 真實(shí)調(diào)用者岖是,為獨(dú)立調(diào)用
}
var a = 20;
var obj = {
a: 10,
getA: foo
}
active(obj.getA);
這個(gè)例子提示一下,關(guān)于函數(shù)參數(shù)的傳遞賦值問題她倘。
JS是按值傳遞還是按引用傳遞?
這里我就不多做解答了璧微,大家自行揣摩作箍。
以上關(guān)于this解答來自波同學(xué)的引用硬梁,我這里就偷了個(gè)懶在,直接拿來引用胞得。
原文地址:前端基礎(chǔ)進(jìn)階(五):全方位解讀this
最后把知道面試題梳理一下:
console.log(person.pro.getName());//Michael
var pepole = person.pro.getName;
console.log(pepole());//jay
person.pro.getName()中荧止,getName是調(diào)用者,他不是獨(dú)立調(diào)用阶剑,被對象person.pro所擁有跃巡,因此它的this指向了person.pro,所以this.name=person.pro.name="Michael";
而pepole()作為調(diào)用者,盡管他與person.pro.getName的引用相同牧愁,但是它是獨(dú)立調(diào)用的素邪,因此this指向undefined,在非嚴(yán)格模式猪半,自動轉(zhuǎn)向全局window兔朦。
這道題實(shí)在非嚴(yán)格模式下偷线,所以this指向了window,又因?yàn)槿肿兞繏燧d在window對象下沽甥,所以this.name=window.name=“jay”
完畢~寫的有點(diǎn)啰嗦声邦,只是盡量想說明白,講清一些概念的東西摆舟,反正我是收獲很多亥曹,你呢?
參考文章:
JavaScript 全局對象
原生JS執(zhí)行環(huán)境與作用域深入理解
理解Javascript_12_執(zhí)行模型淺析
前端基礎(chǔ)進(jìn)階(二):執(zhí)行上下文詳細(xì)圖解
前端基礎(chǔ)進(jìn)階(五):全方位解讀this