工做中有不少用到Class类的地方,一般是暴露出class类,或者暴露Class类的实例,好比商品有价格、规格、名称等属性,就能够经过new一个Class类来初始化商品对象。程序员
对es6有了解的朋友应该知道es6是es5的语法糖,Class类其实是基于es5的构造函数实现的生成实例对象的方法,可是Class类的写法更优雅,更趋近于传统的面向对象编程。es6
虽然说平时经常使用Class类,但是也常会忽略掉细节而致使问题,出于好奇和为了完全理解Class类,我利用babel工具降级es6语法,看看Class类的“庐山真面目”,以及理解babel到底作了什么,写文记录以供本身复习。编程
定义一个Class类数组
class K { constructor(name) { this.name = name; } // 静态方法 static classMethod() { this.getname() return 'hello'; } // setter set prop(value) { console.log('setter: '+ value); } // getter get prop() { return 'getter'; } // 原型方法 getName() { return "celeste"; } } let k = new K("celeste")
上面的类利用babel降级语法后代码以下:babel
var K = function () { function K(name) { _classCallCheck(this, K); this.name = name; } _createClass(K, [{ key: 'getName', value: function getname() { return "celeste"; } }, { key: 'prop', set: function set(value) { console.log('setter: ' + value); }, get: function get() { return 'getter'; } }], [{ key: 'classMethod', value: function classMethod() { this.getName(); return 'hello'; } }]); return K; }(); var k = new K("celeste");
能够发现Class类本质上是个自执行函数。这个函数执行完毕返回一个构造函数K。
而且,这里定义函数不是用函数声明的形式,而是用变量声明赋值var K,这其实就是class类不存在变量提高的缘由,由于虽然js函数会先扫描整个函数体语句,将全部声明的变量提高到函数的顶部,可是不会提高赋值,在console前变量K还未赋值因此打印结果是undefined。函数
// 变量赋值 console.log(Bb); // undefined var Bb = function Bb () {}; // 函数声明 console.log(Aa); // ƒ Aa () {} function Aa () {};
看完外层,再看看里面的关键信息,主要看_classCallCheck、_createClass他们作了什么,源码以下:工具
"use strict"; // 为了向前兼容,es6语法其实是严格模式的 // 类or模块中只有严格模式可用 // 判断right是否为left的构造函数 function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } } // 判断Constructor是否instance的构造函数,若是不是则抛出错误 function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // 遍历props,设置props里每一项的属性并挂载到target上 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; // 定义是否可枚举(否) descriptor.enumerable = descriptor.enumerable || false; // 定义是否可删除(可) descriptor.configurable = true; // descriptor有value属性的话(即除了set/get外的原型方法),可赋值 if ("value" in descriptor) descriptor.writable = true; // 将变量descriptor.key定义到target上 Object.defineProperty(target, descriptor.key, descriptor); } } // 参数分别是:构造函数、原型方法、静态方法 function _createClass(Constructor, protoProps, staticProps) { // 原型方法挂载到构造函数的原型上 if (protoProps) _defineProperties(Constructor.prototype, protoProps); // 静态方法(用了static关键字定义的函数)会做为第三个参数数组里的项传进来,会直接成为构造函数下的一个属性 if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
因此constructor事实上就是初始化了一个构造函数:this
function K(name) { _classCallCheck(this, K); this.name = name; }
_classCallCheck(this, K)的做用就是判断K是否为this的构造函数,不是的话抛出错误,确保万无一失(依据是若是K是个构造函数那么this必定是指向K的实例对象的)。es5
而_createClass函数的做用是就是将定义在类里的方法挂载到函数的原型(针对原型方法)或者类自己(针对static静态方法)上:prototype
把_createClass函数与函数调用直观地放在一块儿看: // _createClass函数 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } // 语法降级后自执行函数里的函数执行 _createClass(K, [{ key: 'getName', value: function getname() { return "celeste"; } }, { key: 'prop', set: function set(value) { console.log('setter: ' + value); }, get: function get() { return 'getter'; } }], [{ key: 'classMethod', value: function classMethod() { this.getName(); return 'hello'; } }]);
能够看到,setter、getter也在第二个参数数组里,他们也是原型上的方法,传参时有些许不一样,value —— set/get,是为了在挂载到原型上的时候加以区分的,把他们区分开的代码就是_defineProperties函数里的这句话:
if ("value" in descriptor) descriptor.writable = true;
1.类的全部方法都定义在类的prototype
属性上面
因此类的新方法能够利用`Object.assign`添加在`prototype`对象上面 Object.assign(Person.prototype, { // add some functions ... });
2.类的内部全部定义的方法,都是不可枚举的(non-enumerable)
3.js引擎会自动为空的类添加一个空的constructor
方法(事实上就是会默认建立一个构造函数,将构造函数的this指向类的实例)
4.constructor
函数能够return Object.create(null)返回一个全新的对象,可致使实例对象不是类的实例。
下一节再看剩余的问题啦,这两天总结完毕或许会合并成一篇也可能新开一篇...——2020/06/06 01:20
小贴士:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并无影响,可是更方便程序员使用。一般来讲使用语法糖可以增长程序的可读性,从而减小程序代码出错的机会。
babel在线工具 https://babeljs.io/repl