近期在研究ES6Class
的時候,心血來潮,想看看國外是否有相似的文章來解釋Class
對Prototype
進行糖化的。而後有一篇文件就映入眼簾。javascript
裏面有這樣的一個細節剖析。大體的劇情以下:html
咱們爲Canvas
建立一個Cricle
類,這個類大體能作java
Circle
被實例化幾回Circle
實例的半徑和查詢對應的半徑數值Talk is cheap ,show you the code:es6
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}
Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},
set: function(val) {
this._count = val;
}
});
Circle.prototype = {
area: function area() {
return Math.pow(this.radius, 2) * Math.PI;
}
};
Object.defineProperty(Circle.prototype, "radius", {
get: function() {
return this._radius;
},
set: function(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
})
複製代碼
若是熟悉ES5語法的童鞋說,這雞毛代碼,有啥,不就是定義了一個Circle
類,而後實現了一些方法嗎。bash
可是我想說的是,這個題目的大體提綱就是這個。可是有一個點,很讓人費勁。可以計算Circle
被實例化幾回。其實在通常開發中,若是遇到這個問題,第一反應就是,要想計算某一個東西被操做了幾回,用一個全局flag
實現不就好了。(個人第一反應也是這個)。可是看到上面的代碼以後,發現本身仍是太年輕。有捷徑不走,非要從繞遠。(腦子瓦塔了)函數
其實上面的例子是用來說述:ES6的Class
是如何優雅的進行代碼書寫。 可是我在看徹底文的時候,其實並不關心優雅的結果。其實我關心的是,這玩意兒是如何實現的。若是你們想看美美噠的代碼實現,能夠先移步到原文進行對美的觀摩。可是不要忘記回來,聽我繼續嘮叨。工具
從上面的例子中,我有幾點比較感興趣(好奇害死貓,我頭髮上的Tony又風雨飄搖了,由於我又要熬夜了)oop
flag
如何實現計算Circle
被實例化多少次Object.defineProperty
裏面的this
是指向了whoClass
是如何對Prototype
進行優雅的糖化的Class
(它自己就是一個函數,而且仍是一個構造函數)不能直接調用,可是ES5卻能夠讓咱們就開始慘案的解密過程吧。 首先,咱們不按常規去思考上面的疑惑,咱們須要在破案以前,須要一些準備工具。 首先讓人很刺眼的一個是:Object.defineProperty
。既然遇到了,咱們就來會會他。post
該方法是用於對指定對象進行自定義屬性的賦值。具體公式Object.defineProperty(obj, prop, descriptor)
。也就是說,若是想爲一個對象定義一個屬性,用這個很好用(固然也能夠直接字面量),同時還能夠進行configurable
、enumerable
等屬性的配置。若是想了解更多,能夠直接參考MDN的相關介紹。 若是你查看的比較細緻的話,其實第三個參數descriptor
是一個針對須要設置屬性的描述性對象信息。其中有一段話,頗有意思。 ui
get()/set()
這兩個可選函數中的
this
的指向就是,
誰訪問了被descriptor
描述的屬性,這個this
就指向誰(可是若是涉及到繼承,那就狀況不同了)。這和函數的
this
指向的機制是同樣的。那很瓜熟蒂落,上面的第二個
謎團解開了。
this
===Circle
若是對函數中this
還不是很瞭解,能夠先移步理解JS函數調用和"this"。(明白了以後,記得繼續看破案過程哈,很赤雞的)
而後,咱們既然已經有了點眉目了,讓咱們繼續快馬加鞭的尋找下一個受害者。
flag
如何實現計算Circle
被實例化多少次不知道你們,對這個問題如何看待,反正我是第一次遇到這種代碼(不新增全局flag
來計算構造函數被實例化多少次)
我喜歡挑戰,那咱們就迎難(男)而上吧。
首先,須要明確的一點就是,咱們是須要實例化一個Circle
類。而用ES5去實現一個'類',其實很機械的就是以下的模板:
var C = function (x,y){
this.x = x;
this.y = y;
}
C.prototype = {
constructor:C
toStirng:function(){
return '北宸南蓁'
}
}
}
複製代碼
Note:上面有一個在進行prototype
賦值的時候,多寫了一行,這個在有些狀況下很重要。具體緣由
那咱們分析一下Circle
的實現
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}
複製代碼
看起來很平淡無奇,可是若是細心的童鞋就會發現。
咦。咦,咦。咋和上面的那個模板有一丟丟的區別。其實就是這麼一丟丟的區別,致使了質的飛越。
搬一個小板凳一塊兒研究一下。
首先,咱們須要回顧一下ES5或者是ES6實例化一個類時。是否是常常掛在嘴邊的話。 在進行new
的時候,會自動觸發構造函數。同時將this
指向哪裏....等等的樣板術語。
而後咱們來模擬一下如何實例化一個對象。(這裏咱們用僞代碼)
var instance1 = new Circle();
new 裏面發生了不少事情,
1. 建立一個空對象,做爲將要返回的對象實例。
2. 將這個空對象的原型,指向構造函數的prototype屬性。
3. 將這個空對象賦值給函數內部的this關鍵字。
4. 開始執行構造函數內部的代碼。
複製代碼
其實咱們很關心最後一步,開始執行構造函數內部代碼。在Circle
中有一個很扎眼的代碼Cricle.circlesMade++
,將它更加簡便一點就是Cricle.circlesMade=Cricle.circlesMade+1
。
也就是說每次在進行一次Circle
實例化的時候,Cricle.circlesMade
的數值好像都增長1。不是好像,確實就是每次加1。關鍵這個circlesMade
他的級別還很高,是個王者段位,它比永恆磚石的radius
的級別都高。由於他是掛載在Cricle
對象上的。(畢竟人家是人民幣玩家,V8)
而後咱們繼續來分析,上面的分析中瞭解到,每次實例化都加1,可是這個王者段位的circlesMade
的初始段位0是0啊,還有它是如何一步一步,從最低段位艱難的爬到最強王者的。
其實結合上面講到的Object.defineProperty
很容易瞭解到,原來circlesMade
這哥們,也是從白銀這個初始段位0一步一步漲上去的。
Cricle.circlesMade++
,而這個操做能夠前後分爲取值(get)/賦值(set)
,而這些操做的使用說明書就在以下代碼中。在第一次進行get
的時候,會有一個判斷!this._count ? 0 : this._count
而咱們在分析Object.defineProperty
的時候,就講到過裏面的this
指向問題。get()/set()
這兩個可選函數中的this
的指向就是:誰訪問了被descriptor
描述的屬性,這個this
就指向誰(爲了避免讓大家向上找,我CV過來了,有點貼心有木有)this
就是Cricle
,也就是說在進行取值(get)
的時候,會進行一次三元判斷,若是沒有,那就是新賽季剛開始,有一個初始值(0)。若是原來已經有值了,那就是在原有段位上,繼續上分。賦值(set)
的時候,其實就是直接將Cricle.circlesMade+1
做爲val
賦值給this._count
.Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},
set: function(val) {
this._count = val;
}
});
複製代碼
Note:若是有些童鞋,對new
是如何進行對象的構建和ES5是如何基於Prototype
進行繼承的。能夠參考理解JS中的原型(Prototypes)
Class
是如何對Prototype
進行優雅的糖化的在寫的時候,發現這是一個比較有趣的問題,我選擇再寫一篇相關文件,進行詳細的說明,這裏就不說了。
天不早了我們找個酒店聊會兒天吧。-----郭德綱
而後大家想要的番外篇-ES6-Class如何優雅的進行Prototype「糖化」他來了。
Class
(它自己就是一個函數)不能直接調用人狠話很少,直接進入正題。 定義一個最最最簡單的ES6的Class
class A {
}
複製代碼
守得雲開見日月
"use strict";
function _instanceof(left, right) {
if (
right != null &&
typeof Symbol !== "undefined" &&
right[Symbol.hasInstance]
) {
return !!right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var A = function A() {
_classCallCheck(this, A);
};
複製代碼
咱們來簡單的剖析一下啊。速度,用小本本記錄一下哈。
strict
千言萬語匯成一句話就是:ES6中的Class
的用途只有一個生成實例。雖然他是函數。而且是構造函數,沒辦法,實力不容許它去直接調用。
想調用能夠,控制檯飄紅。