摘要:那么什么是基礎(chǔ)對(duì)象組件呢,舉兩個(gè)例子我們?cè)賮?lái)看看屬性訪問(wèn)器,就是括號(hào)操作符及點(diǎn)號(hào)操作符都做了什么屬性訪問(wèn)器也就是說(shuō)括號(hào)跟點(diǎn)號(hào)對(duì)解釋器而言是一樣的。
ES規(guī)范解讀之賦值操作符&屬性訪問(wèn)器
原文:https://github.com/kuitos/kuitos.github.io/issues/24
事情起源于某天某妹子同事在看angular文檔中關(guān)于Scope的說(shuō)明Understanding Scopes(原文) 理解angular作用域(譯文)時(shí),對(duì)于文章中的例子有一點(diǎn)不理解,那個(gè)例子抽離細(xì)節(jié)之后大致是這樣的:
// 一個(gè)標(biāo)準(zhǔn)的構(gòu)造函數(shù) function Scope(){} Scope.prototype.array = [1,2,3]; Scope.prototype.string = "Scope"; // 生成Scope實(shí)例 var scopeInstance = new Scope();
當(dāng)我們?cè)L問(wèn)scopeInstance上的屬性時(shí),假如scopeInstance上不存在該屬性,則js解釋器會(huì)從原型鏈上一層層往上找,直到找到有該屬性,否則返回undefined。
// get對(duì)象上某一屬性時(shí)會(huì)觸發(fā)原型鏈查找 console.log(scopeInstance.string); // "Scope" console.log(scopeInstance.name); // undefined
而當(dāng)我們往scopeInstance上某一屬性設(shè)值時(shí),它并不會(huì)觸發(fā)原型鏈查找,而是直接給對(duì)象自身設(shè)值,如果對(duì)象上沒(méi)有該屬性則創(chuàng)建一個(gè)該屬性。
scopeInstance.string = "scopeInstance"; scopeInstance.array = []; console.log(scopeInstance.string); // "scopeInstance" console.log(scopeInstance.array); // [] console.log(Scope.prototype.string); // "Scope" console.log(Scope.prototype.array); // [1,2,3]
總結(jié)起來(lái),關(guān)于對(duì)象的屬性的set和get操作看上去有這樣一些特性:
讀(get)操作會(huì)觸發(fā)原型鏈查找,解釋器會(huì)從原型鏈一層層往上查找,直到找不到返回undefined.
寫(xiě)(set)操作不會(huì)觸發(fā)原型鏈查找,寫(xiě)操作會(huì)直接在對(duì)象上進(jìn)行,沒(méi)有這個(gè)屬性會(huì)新建一個(gè)屬性。
沒(méi)錯(cuò),這是最基本的原型鏈機(jī)制,我以前一直是這么理解的,然后我也是這么跟妹子解釋的,然而文章后面的例子打了我臉。。。例子大致是這樣的:
var scope2 = new Scope(); scope2.array[1] = 1; console.log(scope2.array); // [1,1,3] console.log(Scope.prototype.array); // [1,1,3]
WTF!!!
按照我的理解,寫(xiě)操作跟原型鏈無(wú)關(guān),在對(duì)象自身操作。
順著這個(gè)思路,那么 scope2.array[1]=1這行代碼壓根就會(huì)報(bào)錯(cuò)啊,因?yàn)閟cope2在創(chuàng)建array屬性之前壓根就沒(méi)有自身的array屬性??!可是它竟然沒(méi)報(bào)錯(cuò)還把Scope.prototype給改了!
于是我又在想,是不是這種引用類(lèi)型(array,object)都會(huì)觸發(fā)原型鏈查找,所以會(huì)出現(xiàn)這個(gè)結(jié)果?
然而我又想起前面那段代碼:
scopeInstance.array = []; console.log(scopeInstance.array); // [] console.log(Scope.prototype.array); // [1,2,3]
這下徹底斯巴達(dá)了?
從表象來(lái)看,scopeInstance.array[1]的讀寫(xiě)操作都會(huì)觸發(fā)原型鏈查找,而為啥scopeInstance.array的寫(xiě)操作就不會(huì)觸發(fā)。如果說(shuō)引用類(lèi)型都會(huì)觸發(fā),那么scopeInstace.array=[]就等價(jià)于Scope.prototype.array = [],但是事實(shí)并不是這樣。。。
碰到這種時(shí)候我只有祭出神器了(ecmascript),google什么的絕對(duì)不好使相信我。
翻到ecmascript關(guān)于賦值操作符那一小節(jié),es是這樣描述的
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
Evaluate LeftHandSideExpression.
Evaluate AssignmentExpression.
Call GetValue(Result(2)).
Call PutValue(Result(1), Result(3)).
Return Result(3).
前面三步都知道,關(guān)鍵點(diǎn)在第四步, PutValue(Result(1), Result(3))
我們?cè)賮?lái)看看PutValue干了啥
If Type(V) is not Reference, throw a ReferenceError exception.
Call GetBase(V).
If Result(2) is null, go to step 6.
Call the [[Put]] method of Result(2), passing GetPropertyName(V) for the property name and W for the value.
...
第二步有一個(gè)GetBase(V)操作,然后第四步依賴第二步的計(jì)算結(jié)果做最終賦值。
那么GetBase(V)究竟做了什么呢(V即我們賦值操作時(shí)候的左值)
GetBase(V). Returns the base object component of the reference V.
翻譯下來(lái)就是:返回引用V的基礎(chǔ)對(duì)象組件。
那么什么是基礎(chǔ)對(duì)象組件呢,舉兩個(gè)例子:
GetBase(this.array) => this GetBase(this.info.name) => this.info GetBase(this.array[1]) => this.array
我們?cè)賮?lái)看看屬性訪問(wèn)器(Property Accessors),就是括號(hào)[]操作符及點(diǎn)號(hào).操作符都做了什么
屬性訪問(wèn)器(Property Accessors)MemberExpression . Identifier is identical in its behaviour to MemberExpression [
]
也就是說(shuō)括號(hào)跟點(diǎn)號(hào)對(duì)解釋器而言是一樣的。
The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:
Evaluate MemberExpression.
Call GetValue(Result(1)).
...
跟到GetValue
GetValue(V)If Type(V) is not Reference, return V.
Call GetBase(V).
If Result(2) is null, throw a ReferenceError exception.
Call the [[Get]] method of Result(2), passing GetPropertyName( V) for the property name.
第四步的私有方法[[Get]]是關(guān)鍵:
[[Get]]When the [[Get]] method of O is called with property name P, the following steps are taken:
If O doesn"t have a property with name P, go to step 4.
Get the value of the property.
Return Result(2).
If the [[Prototype]] of O is null, return undefined.
Call the [[Get]] method of [[Prototype]] with property name P.
Return Result(5).
意思很明顯,[[Get]]會(huì)觸發(fā)原型鏈查找.
我們?cè)倩氐劫x值操作符的PutValue操作,走到第四步
Call the [[Put]] method of Result(2), passing GetPropertyName(V) for the property name and W for the value.
這里的Result(2)就是GetBase(V)的結(jié)果,拿上面的例子也就是GetBase(this.array[2]) == this.array
再看看[[Put]]操作干了什么事情:
When the [[Put]] method of O is called with property P and value V, the following steps are taken:
Call the [[CanPut]] method of O with name P.
If Result(1) is false, return.
If O doesn"t have a property with name P, go to step 6.
Set the value of the property to V. The attributes of the property are not changed.
Return.
Create a property with name P, set its value to V and give it empty attributes.
Return.
很簡(jiǎn)單,就是給對(duì)象o的屬性P賦值時(shí),o存在屬性P就直接覆蓋,沒(méi)有就新建屬性。此時(shí)無(wú)關(guān)原型鏈。
此時(shí)再結(jié)合我們自己的案例來(lái)看,scopeInstance.array[1]=2跟scopeInstance.array=[]究竟都干了啥(忽略不相關(guān)細(xì)節(jié)):
scopeInstance.array[1]=2GetBase(scopeInstance.array[1]) == scopeInstance.array
GetValue(scopeInstance.array) => 觸發(fā)scopeInstace.array的[[Get]]方法,此時(shí)觸發(fā)原型鏈查找 => 找到 Scope.prototype.array
設(shè)值操作 Scope.prototype.array.[Put];
scopeInstance.array=[]GetBase(scopeInstance.array) == scopeInstance
GetValue(scopeInstance) => scopeInstance object
設(shè)值操作 scopeInstance.[Put];
完美解釋所有現(xiàn)象!
如果思考的比較深入的同學(xué)可能會(huì)問(wèn),scopeInstance又從哪兒取來(lái)的呢?也是類(lèi)似原型鏈這樣一層層往上查出來(lái)的么?這涉及到另一點(diǎn)知識(shí),js中的作用域,具體可以看我的另一篇文章一道js面試題引發(fā)的思考
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86004.html
摘要:可迭代對(duì)象就具有屬性,它是一種與迭代器密切相關(guān)的對(duì)象。它通過(guò)指定的函數(shù)可以返回一個(gè)作用于附屬對(duì)象的迭代器。迭代器特點(diǎn)每次調(diào)用方法時(shí),返回一個(gè)數(shù)組,數(shù)組中兩個(gè)元素,分別表示鍵和值。示例之輸出輸出輸出之迭代器特點(diǎn)返回集合中存在的每一個(gè)鍵。 Iterator由來(lái) 不推薦Iterator方法。 Iterator 函數(shù)是一個(gè) SpiderMonkey 專有特性,并且會(huì)在某一時(shí)刻被刪除。有一點(diǎn),需...
摘要:深入系列第六篇,本篇我們追根溯源,從規(guī)范解讀在函數(shù)調(diào)用時(shí)到底是如何確定的。因?yàn)槲覀円獜囊?guī)范開(kāi)始講起。規(guī)范類(lèi)型包括和。下一篇文章深入之執(zhí)行上下文深入系列深入系列目錄地址。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑?qǐng)務(wù)必給予指正,十分感謝。 JavaScript深入系列第六篇,本篇我們追根溯源,從 ECMAScript5 規(guī)范解讀 this 在函數(shù)調(diào)用時(shí)到底是如何確定的。 前言 在《JavaScript...
摘要:返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。參考語(yǔ)法返回一個(gè)布爾值與的全等操作符比較兼容環(huán)境把對(duì)象的值復(fù)制到另一個(gè)對(duì)象里淺拷貝定義方法用于將所有可枚舉的屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。語(yǔ)法要設(shè)置其原型的對(duì)象。 一步一步似爪牙。 前言 學(xué)習(xí)es6之前我們可能并不知道es6相比es5差距在哪, 但是這并不妨礙我們站在巨人的肩膀上; 程序員就是要樂(lè)于嘗鮮; 學(xué)習(xí)es6最終目的是...
摘要:對(duì)于這種疑問(wèn),我們只能求助給出官方解釋后自增操作符從上的算法描述,我們能夠清晰的得知,后自增操作符是先自增賦值,然后返回自增前的值,這樣的一個(gè)順序。 ES規(guī)范解讀之自增操作符 原文:https://github.com/kuitos/kuitos.github.io/issues/24幾個(gè)月前,不知道什么緣由跟同事討論了起js里自增操作符(i++)的問(wèn)題,現(xiàn)將前因后果整理出來(lái),傳于世人...
閱讀 1923·2021-11-24 09:39
閱讀 2145·2021-09-22 15:50
閱讀 2024·2021-09-22 14:57
閱讀 712·2021-07-28 00:13
閱讀 1077·2019-08-30 15:54
閱讀 2368·2019-08-30 15:52
閱讀 2695·2019-08-30 13:07
閱讀 3794·2019-08-30 11:27