一道JS面試題所引發(fā)的"血案",透過現(xiàn)象尋本質(zhì)弟孟,再從本質(zhì)看現(xiàn)象

覺得本人寫的不算很爛的話贝咙,可以登錄關(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聲明的全局變量namewindow.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 對象夹供。

全局對象描述

  1. 全局對象是預(yù)定義的對象,作為 JavaScript 的全局函數(shù)和全局屬性的占位符仁堪。通過使用全局對象,可以訪問所有其他所有預(yù)定義的對象填渠、函數(shù)和屬性弦聂。全局對象不是任何對象的屬性,所以它沒有名稱氛什。
  1. 在頂層 JavaScript 代碼中莺葫,可以用關(guān)鍵字 this 引用全局對象。但通常不必用這種方式引用全局對象枪眉,因?yàn)槿謱ο笫亲饔糜蜴湹念^捺檬,這意味著所有非限定性的變量和函數(shù)名都會作為該對象的屬性來查詢。例如贸铜,當(dāng)JavaScript 代碼引用 parseInt() 函數(shù)時(shí)堡纬,它引用的是全局對象的 parseInt 屬性。全局對象是作用域鏈的頭蒿秦,還意味著在頂層 JavaScript 代碼中聲明的所有變量都將成為全局對象的屬性烤镐。
  1. 全局對象只是一個(gè)對象,而不是類棍鳖。既沒有構(gòu)造函數(shù)炮叶,也無法實(shí)例化一個(gè)新的全局對象。
  1. 在 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代碼分三種類型:
  1. Global Code食听,即全局的调煎、不在任何函數(shù)里面的代碼赵讯,例如:一個(gè)js文件依溯、嵌入在HTML頁面中的js代碼等毯炮。
  2. Eval Code省咨,即使用eval()函數(shù)動態(tài)執(zhí)行的JS代碼肃弟。
  3. 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

  1. 全局執(zhí)行環(huán)境即window對象所創(chuàng)建的争便,局部執(zhí)行環(huán)境是函數(shù)執(zhí)行過程中創(chuàng)建的。
  2. 全局對象断医,可以訪問所有其他所有預(yù)定義的對象滞乙、函數(shù)和屬性。
  3. 當(dāng)javascript代碼被瀏覽器載入后鉴嗤,默認(rèn)最先進(jìn)入的是一個(gè)全局執(zhí)行環(huán)境斩启。
  4. 明白了執(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;

  1. 全局變量會掛載在window對象下到涂,會成為window下的一個(gè)屬性。
  2. 如果你沒有使用嚴(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恨诱,一起剝皮案震驚了整個(gè)濱河市媳瞪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌照宝,老刑警劉巖材失,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異硫豆,居然都是意外死亡龙巨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門熊响,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旨别,“玉大人,你說我怎么就攤上這事汗茄〗粘冢” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵洪碳,是天一觀的道長递览。 經(jīng)常有香客問我,道長瞳腌,這世上最難降的妖魔是什么绞铃? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮嫂侍,結(jié)果婚禮上儿捧,老公的妹妹穿的比我還像新娘。我一直安慰自己挑宠,他們只是感情好菲盾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著各淀,像睡著了一般懒鉴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碎浇,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天临谱,我揣著相機(jī)與錄音咆畏,去河邊找鬼。 笑死吴裤,一個(gè)胖子當(dāng)著我的面吹牛旧找,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播麦牺,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼钮蛛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了剖膳?” 一聲冷哼從身側(cè)響起魏颓,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吱晒,沒想到半個(gè)月后甸饱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仑濒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年叹话,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墩瞳。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驼壶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喉酌,到底是詐尸還是另有隱情热凹,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布泪电,位于F島的核電站般妙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏相速。R本人自食惡果不足惜碟渺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望和蚪。 院中可真熱鬧止状,春花似錦烹棉、人聲如沸攒霹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽催束。三九已至,卻和暖如春伏社,著一層夾襖步出監(jiān)牢的瞬間抠刺,已是汗流浹背塔淤。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留速妖,地道東北人高蜂。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像罕容,于是被迫代替她去往敵國和親备恤。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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

  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    程序員poetry閱讀 114,377評論 24 450
  • 1.幾種基本數(shù)據(jù)類型?復(fù)雜數(shù)據(jù)類型?值類型和引用數(shù)據(jù)類型?堆棧數(shù)據(jù)結(jié)構(gòu)? 基本數(shù)據(jù)類型:Undefined锦秒、Nul...
    極樂君閱讀 5,514評論 0 106
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法露泊,類相關(guān)的語法,內(nèi)部類的語法旅择,繼承相關(guān)的語法惭笑,異常的語法,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 秦嶺之巔生真, 揮襟扶手北瞻沉噩! 渭水西環(huán)如帶,直上九天柱蟀! 耳邊叮叮伐木屁擅,歌聲裊裊云間, 看盡八百里秦川产弹,多少英雄兒女躍...
    DreamSirius夢中天狼閱讀 203評論 0 0
  • 《定風(fēng)波》 常羨人間琢玉郎派歌, 天應(yīng)乞與點(diǎn)酥娘。 自作清歌傳皓齒痰哨, 風(fēng)起胶果,雪飛炎海變清涼。 萬里歸來年愈少斤斧, 微笑早抠,...
    楠寶_2018閱讀 212評論 1 2