ECMAScript6
实现了 class
,实际上它是一个语法糖,然而它的呈现能使 JS
编码更清晰,更靠近 面向对象编程
。
实现原理
首先咱们来看 ES6
中 class
的实现和 ES5
构造函数的实现,两者相比拟不难看出 constructor
其实就是构造方法,指向 ES5
的构造函数,那么 class
自身指向的是构造函数,换言之底层仍旧是构造函数。
ES6
class Person { constructor(name, age) { this.name = name; this.age = age; } static run() { console.log("run"); } say() { console.log("hello!"); }}
ES5
function Person(name, age) { this.name = name; this.age = age;}Person.prototype.say = function () { console.log("hello!");};Person.run = function () { console.log("run");};
babel 编译剖析
通过 babel
编译器将 ES6
代码 转换成 ES5
代码之后(代码转换能够试用 babel 官网在线工具),可失去这两个要害函数 _defineProperties
和 _createClass
,当初咱们来一一解析阐明。
...var Person = /*#__PURE__*/ (function () { "use strict"; function Person(name, age) { _classCallCheck(this, Person); this.name = name; this.age = age; } _createClass( Person, [ { key: "say", value: function say() { console.log("hello!"); }, }, ], [ { key: "run", value: function run() { console.log("run"); }, }, ] ); return Person;})();
\_createClass
_createClass
函数次要用于配置构造函数或构造函数原型上的私有函数和静态方法,并返回构造函数自身。
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor;}
\_defineProperties
_defineProperties
函数次要用于申明私有函数和静态方法的描述符,并将其挂载到以后的构造函数或构造函数原型。它接管两个参数 target
() 和 props
。
target
指向以后的构造函数或构造函数原型props
数组类型,指向私有函数和静态方法
在遍历数组时,咱们能够看到 enumerable
默认是 false
,也就是说 class
类上的外部属性默认是不可枚举的,不能应用 Object.keys
遍历,具体如下:
Object.keys(Person.prototype); // []Object.keys(Person); // []
同时在遍历的时候还会判断以后描述符是否存在 value
值,如果存在就设置可写属性 writable
为 true
,反之就应用 get
和 set
属性。在遍历的开端,通过 Object.defineProperty
将描述符配置到以后的构造函数或构造函数原型上,至此就是 class
的根本实现了。
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); }}
构造函数的区别
暂时性死区
class
不会申明晋升,存在 暂时性死区
,构造函数的实质是函数,函数申明会有晋升作用。
// { const foo = new Foo(); function Foo() {}}// error{ const foo = new Foo(); // Cannot access 'Foo' before initialization class Foo {}}
严格模式
class
申明外部默认会启用 严格模式
,构造函数默认 非严格模式
。
// { const foo = new Foo(); function Foo() { size = 20; }}// error{ class Foo { constructor() { size = 20; // size is not defined } } const foo = new Foo();}
外部办法不可枚举
class
的所有办法(包含 静态方法
、实例办法
)都 不可枚举
,上文的 _defineProperties
函数办法实现中有提到,具体可参照上文,构造函数可枚举所有办法。
{ function Foo() {} Foo.print = function () {}; Foo.prototype.format = function () {}; console.log(Object.keys(Foo)); // [ "print" ] console.log(Object.keys(Foo.prototype)); // [ "format" ]}{ class Foo { constructor() {} static print() {} format() {} } console.log(Object.keys(Foo)); // [] console.log(Object.keys(Foo.prototype)); // []}
原型对象 prototype
class
的所有办法(包含 静态方法
、实例办法
)都没有原型对象 prototype
,因而也没有 [[construct]]
,不能通过 new
来调用,构造函数则反对 new
调用。
// { function Foo() {} Foo.prototype.format = function () {}; const foo = new Foo(); const fooFormat = new foo.format();}// error{ class Foo { static print() {} format() {} } const foo = new Foo(); const fooFormat = new foo.format(); // foo.format is not a constructor const fooPrint = new foo.print(); // foo.print is not a constructor}
new 调用
class
必须应用 new
调用,构造函数的实质是函数,反对间接调用。
// { function Foo() {} const foo = Foo();}// error{ class Foo {} const foo = Foo(); // Class constructor Foo cannot be invoked without 'new'}
类名重写
class
外部无奈重写类名,构造函数可任意更改。
// { function Foo() { Foo = "yo"; } const foo = new Foo();}// error{ class Foo { constructor() { // Foo = 'yo' // TypeError: Assignment to constant variable } } const foo = new Foo(); Foo = "yo"; // }