this

原文出處

JavaScript深入之從ECMAScript規(guī)范解讀this
ECMA-262-3 in detail. Chapter 3. This
ECMAScript5.1中文版
ECMAScript5.1英文版

Types


首先是第 8 章 Types:

Types are further subclassified into ECMAScript language types and specification types.

An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.

A specification type corresponds to meta-values that are used within algorithms to describe the semantics of ECMAScript language constructs and ECMAScript language types. The specification types are Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, and Environment Record.

我們簡(jiǎn)單的翻譯一下:

ECMAScript 的類型分為語(yǔ)言類型和規(guī)范類型北苟。

ECMAScript 語(yǔ)言類型是開(kāi)發(fā)者直接使用 ECMAScript 可以操作的。其實(shí)就是我們常說(shuō)的Undefined, Null, Boolean, String, Number, 和 Object钻弄。

而規(guī)范類型相當(dāng)于 meta-values,是用來(lái)用算法描述 ECMAScript 語(yǔ)言結(jié)構(gòu)和 ECMAScript 語(yǔ)言類型的。規(guī)范類型包括:Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, 和 Environment Record。

沒(méi)懂姻僧?沒(méi)關(guān)系,我們只要知道在 ECMAScript 規(guī)范中還有一種只存在于規(guī)范中的類型蒲牧,它們的作用是用來(lái)描述語(yǔ)言底層行為邏輯撇贺。

Reference


那什么又是 Reference ?

讓我們看 8.7 章 The Reference Specification Type

The Reference type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators.

所以 Reference 類型就是用來(lái)解釋諸如 delete冰抢、typeof 以及賦值等操作行為的松嘶。

抄襲尤雨溪大大的話,就是:

這里的 Reference 是一個(gè) Specification Type挎扰,也就是 “只存在于規(guī)范里的抽象類型”翠订。它們是為了更好地描述語(yǔ)言的底層行為邏輯才存在的,但并不存在于實(shí)際的 js 代碼中遵倦。

再看接下來(lái)的這段具體介紹 Reference 的內(nèi)容:

A Reference is a resolved name binding.

A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag.

The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1).

A base value of undefined indicates that the reference could not be resolved to a binding. The referenced name is a String.

這段講述了 Reference 的構(gòu)成尽超,由三個(gè)組成部分,分別是:

  1. base value: 就是屬性所在的對(duì)象或者就是 EnvironmentRecord梧躺,它的值只可能是 undefined, an Object, a Boolean, a String似谁。

  2. referenced name: 就是屬性的名稱。

  3. strict reference: 是否處于嚴(yán)格模式燥狰。

舉個(gè)例子:

var foo = 1;

// 對(duì)應(yīng)的Reference是:
var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};

再舉個(gè)例子:

var foo = {
    bar: function () {
        return this;
    }
};
 
foo.bar(); // foo

// bar對(duì)應(yīng)的Reference是:
var BarReference = {
    base: foo,
    propertyName: 'bar',
    strict: false
};

而且規(guī)范中還提供了獲取 Reference 組成部分的方法棘脐,比如 GetBase 和 IsPropertyReference
這兩個(gè)方法很簡(jiǎn)單龙致,簡(jiǎn)單看一看:

1.GetBase

GetBase(V). Returns the base value component of the reference V.

返回 reference 的 base value蛀缝。

  1. IsPropertyReference

IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.

簡(jiǎn)單的理解:如果 base value 是一個(gè)對(duì)象,就返回true目代。

GetValue


除此之外屈梁,緊接著在8.7.1 章規(guī)范中就講了一個(gè)用于從 Reference 類型獲取對(duì)應(yīng)值的方法: GetValue嗤练。

簡(jiǎn)單模擬 GetValue 的使用:

var foo = 1;

var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};

GetValue(fooReference) // 1;

GetValue 返回對(duì)象屬性真正的值,但是要注意:
調(diào)用 GetValue在讶,返回的將是具體的值煞抬,而不再是一個(gè) Reference

如何確定this的值


規(guī)范 11.2.3 Function Calls

這里講了當(dāng)函數(shù)調(diào)用的時(shí)候,如何確定 this 的取值构哺。
只看第一步革答、第六步、第七步:

1.Let ref be the result of evaluating MemberExpression.

6.If Type(ref) is Reference, then

    a.If IsPropertyReference(ref) is true, then
            i.Let thisValue be GetBase(ref).
    b.Else, the base of ref is an Environment Record
            i.Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).

7.Else, Type(ref) is not Reference.

   a. Let thisValue be undefined.

讓我們描述一下:

  1. 計(jì)算 MemberExpression 的結(jié)果賦值給 ref

  2. 判斷 ref 是不是一個(gè) Reference 類型

     2.1 如果 ref 是 Reference曙强,并且 IsPropertyReference(ref) 是 true, 那么 this 的值為 GetBase(ref)
     2.2 如果 ref 是 Reference残拐,并且 base value 值是 Environment Record, 那么this的值為 ImplicitThisValue(ref)
     2.3 如果 ref 不是 Reference,那么 this 的值為 undefined
    

具體分析


讓我們一步一步看:

  1. 計(jì)算 MemberExpression 的結(jié)果賦值給 ref

什么是 MemberExpression碟嘴?看規(guī)范 11.2 Left-Hand-Side Expressions

MemberExpression :

  • PrimaryExpression // 原始表達(dá)式 可以參見(jiàn)《JavaScript權(quán)威指南第四章》
  • FunctionExpression // 函數(shù)定義表達(dá)式
  • MemberExpression [ Expression ] // 屬性訪問(wèn)表達(dá)式
  • MemberExpression . IdentifierName // 屬性訪問(wèn)表達(dá)式
  • new MemberExpression Arguments // 對(duì)象創(chuàng)建表達(dá)式

舉個(gè)例子:

function foo() {
    console.log(this)
}

foo(); // MemberExpression 是 foo

function foo() {
    return function() {
        console.log(this)
    }
}

foo()(); // MemberExpression 是 foo()

var foo = {
    bar: function () {
        return this;
    }
}

foo.bar(); // MemberExpression 是 foo.bar

所以簡(jiǎn)單理解 MemberExpression 其實(shí)就是()左邊的部分溪食。

2.判斷 ref 是不是一個(gè) Reference 類型。

關(guān)鍵就在于看規(guī)范是如何處理各種 MemberExpression娜扇,返回的結(jié)果是不是一個(gè)Reference類型错沃。

舉最后一個(gè)例子:

var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//示例1
console.log(foo.bar());
//示例2
console.log((foo.bar)());
//示例3
console.log((foo.bar = foo.bar)());
//示例4
console.log((false || foo.bar)());
//示例5
console.log((foo.bar, foo.bar)());

foo.bar()

在示例 1 中,MemberExpression 計(jì)算的結(jié)果是 foo.bar雀瓢,那么 foo.bar 是不是一個(gè) Reference 呢枢析?

查看規(guī)范 11.2.1 Property Accessors,這里展示了一個(gè)計(jì)算的過(guò)程刃麸,什么都不管了登疗,就看最后一步:

Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.

我們得知該表達(dá)式返回了一個(gè) Reference 類型!
根據(jù)之前的內(nèi)容嫌蚤,我們知道該值為:

var Reference = {
  base: foo,
  name: 'bar',
  strict: false
};

接下來(lái)按照 2.1 的判斷流程走:

2.1 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值為 GetBase(ref)

該值是 Reference 類型断傲,那么 IsPropertyReference(ref) 的結(jié)果是多少呢脱吱?

前面我們已經(jīng)鋪墊了 IsPropertyReference 方法,如果 base value 是一個(gè)對(duì)象认罩,結(jié)果返回 true箱蝠。

base value 為 foo,是一個(gè)對(duì)象垦垂,所以 IsPropertyReference(ref) 結(jié)果為 true宦搬。

這個(gè)時(shí)候我們就可以確定 this 的值了:

this = GetBase(ref)

GetBase 也已經(jīng)鋪墊了,獲得 base value 值劫拗,這個(gè)例子中就是foo间校,所以 this 的值就是 foo ,示例1的結(jié)果就是 2页慷!

(foo.bar)()

看示例2:

console.log((foo.bar)());

foo.bar 被 () 包住憔足,查看規(guī)范 11.1.6 The Grouping Operator
直接看結(jié)果部分:

Return the result of evaluating Expression. This may be of type Reference.

NOTE This algorithm does not apply GetValue to the result of evaluating Expression.

實(shí)際上 () 并沒(méi)有對(duì) MemberExpression 進(jìn)行計(jì)算胁附,所以其實(shí)跟示例 1 的結(jié)果是一樣的。

(foo.bar = foo.bar)()

看示例3滓彰,有賦值操作符控妻,查看規(guī)范11.13.1 Simple Assignment ( = ):

3.Let rval be GetValue(rref).

因?yàn)槭褂昧?GetValue,所以返回的值不是 Reference 類型

按照之前講的判斷邏輯:

2.3 如果 ref 不是Reference揭绑,那么 this 的值為 undefined

this 為 undefined弓候,非嚴(yán)格模式下,this 的值為 undefined 的時(shí)候他匪,其值會(huì)被隱式轉(zhuǎn)換為全局對(duì)象菇存。

(false || foo.bar)()

看示例4,邏輯與算法诚纸,查看規(guī)范 11.11 Binary Logical Operators
計(jì)算第二步:

2.Let lval be GetValue(lref).

因?yàn)槭褂昧?GetValue撰筷,所以返回的不是 Reference 類型,this 為 undefined

(foo.bar, foo.bar)()

看示例5畦徘,逗號(hào)操作符毕籽,查看規(guī)范11.14 Comma Operator ( , )

計(jì)算第二步:

2.Call GetValue(lref).

因?yàn)槭褂昧?GetValue,所以返回的不是 Reference 類型井辆,this 為 undefined

揭曉結(jié)果

所以最后一個(gè)例子的結(jié)果是:

var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//示例1
console.log(foo.bar()); // 2
//示例2
console.log((foo.bar)()); // 2
//示例3
console.log((foo.bar = foo.bar)()); // 1
//示例4
console.log((false || foo.bar)()); // 1
//示例5
console.log((foo.bar, foo.bar)()); // 1

注意:以上是在非嚴(yán)格模式下的結(jié)果关筒,嚴(yán)格模式下因?yàn)?this 返回 undefined,所以示例 3 會(huì)報(bào)錯(cuò)杯缺。

補(bǔ)充

最最后蒸播,忘記了一個(gè)最最普通的情況:

function foo() {
    console.log(this)
}

foo(); 

MemberExpression 是 foo,解析標(biāo)識(shí)符萍肆,查看規(guī)范 10.3.1 Identifier Resolution袍榆,會(huì)返回一個(gè) Reference 類型的值:

var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};

接下來(lái)進(jìn)行判斷:

2.1 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值為 GetBase(ref)

因?yàn)?base value 是 EnvironmentRecord塘揣,并不是一個(gè) Object 類型包雀,還記得前面講過(guò)的 base value 的取值可能嗎? 只可能是 undefined, an Object, a Boolean, a String, a Number, 和 an environment record 中的一種亲铡。

IsPropertyReference(ref) 的結(jié)果為 false才写,進(jìn)入下個(gè)判斷:

2.2 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值為 ImplicitThisValue(ref)

base value 正是 Environment Record奖蔓,所以會(huì)調(diào)用 ImplicitThisValue(ref)

查看規(guī)范 10.2.1.1.6赞草,ImplicitThisValue 方法的介紹:該函數(shù)始終返回 undefined

所以最后 this 的值就是 undefined吆鹤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厨疙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疑务,更是在濱河造成了極大的恐慌轰异,老刑警劉巖岖沛,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搭独,居然都是意外死亡婴削,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門牙肝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唉俗,“玉大人,你說(shuō)我怎么就攤上這事配椭〕媪铮” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵股缸,是天一觀的道長(zhǎng)衡楞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)敦姻,這世上最難降的妖魔是什么瘾境? 我笑而不...
    開(kāi)封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮镰惦,結(jié)果婚禮上迷守,老公的妹妹穿的比我還像新娘。我一直安慰自己旺入,他們只是感情好兑凿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著茵瘾,像睡著了一般礼华。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拗秘,一...
    開(kāi)封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天卓嫂,我揣著相機(jī)與錄音,去河邊找鬼聘殖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛行瑞,可吹牛的內(nèi)容都是我干的奸腺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼血久,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼突照!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起氧吐,我...
    開(kāi)封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤讹蘑,失蹤者是張志新(化名)和其女友劉穎末盔,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體座慰,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陨舱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了版仔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片游盲。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蛮粮,靈堂內(nèi)的尸體忽然破棺而出益缎,到底是詐尸還是另有隱情,我是刑警寧澤然想,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布莺奔,位于F島的核電站,受9級(jí)特大地震影響变泄,放射性物質(zhì)發(fā)生泄漏令哟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一杖刷、第九天 我趴在偏房一處隱蔽的房頂上張望励饵。 院中可真熱鬧,春花似錦滑燃、人聲如沸役听。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)典予。三九已至,卻和暖如春乐严,著一層夾襖步出監(jiān)牢的瞬間瘤袖,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工昂验, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捂敌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓既琴,卻偏偏與公主長(zhǎng)得像占婉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甫恩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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