近期在研究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
的用途只有一个生成实例。虽然他是函数。而且是构造函数,没办法,实力不容许它去直接调用。
想调用能够,控制台飘红。