站长资讯网
最全最丰富的资讯网站

es6 class是语法糖吗

class是语法糖。原因:class是基于原型继承的实现,对语言的功能并没有什么影响,只是方便了语法的书写及阅读;class的本质是function,能够让对象原型的写法更加清晰,更像面向对象编程的语法。

es6 class是语法糖吗

前端(vue)入门到精通课程:进入学习
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API调试工具:点击使用

本教程操作环境:windows7系统、ECMAScript 6版、Dell G3电脑。

ES6 class类——语法糖

class (类)作为对象的模板被引入,可以通过 class 关键字定义类。它的本质是函数(function),可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

它的class和别的语言不一样,它依旧是基于原型继承的实现,对语言的功能并没有影响,只是方便了你的书写及阅读

为什么说ES6的class是语法糖

我们带着问题去阅读下文:

  • 为什么说ES6的class是语法糖?
  • class是原型的语法糖吗?
  • 那又是如何使用原型来实现class这一语法糖的呢?

1. 基于Prototype的OOP

先来看一个prototype的例子:

function Person (name, sex) { 	this.name = name 	this.sex = sex }   function Man (name) { 	this.name = name }   Man.prototype = new Person('', 'male')   let Jy = new Man('Jy')   console.log(Jy.name, Jy.sex) // Jy, male
登录后复制

这是我们使用原型的一个很简单的例子,Person具有名字和性别,Man是一个性别为男的Person,Jy是一个Man。我们先记住这一个例子,下面将使用class重写这个例子。

Tips: new, this等是Brendan Eich使之更像Java的OOP而加上的,有兴趣的读者可以自行查阅相关信息。

2. ES6 Class的OOP

class Person { 	constructor (name, sex) { 		this.name = name 		this.sex = sex 	} }   class Man extends Person { 	constructor (name) { 		super('', 'male') 		this.name = name 	} }   let Jy = new Man('Jy')   console.log(Jy.name, Jy.sex) // Jy, 'male'
登录后复制

我们通过重写这个例子,采用了class、constructor、extends、super 这些单词,接下来就具体来说说ES6规范中对它们做了什么。

3. 使用Prototype实现的Class OOP(ES6规范)

在ES6之前,JS对象其实就是属性的集合,而属性则是一组键值对(key, value),key可以是String or Symbol, value包括数据属性特征值和访问器特征值。

你说普通的属性还好,不还有对象下面的方法吗?怎么就变成了属性的集合呢?

其实在ES5规范中出现的method的定义是“function that is the value of a property”,是对象的函数属性而已,不能称之为方法,直到ES6出现,规范中才有Method Definitions。

我们能想到的在ES3有关OOP的东西: prototype、new、 this、 constructor、 instanceof, 甚至不是规范的 __proto__ 属性。

所幸的是在ES5中我们增加了很多方法来补全它,使之完备:

  • Object.defineProperty
  • Object.freeze
  • Object.create
  • Object.getPrototypeOf
  • Object.setPrototypeOf
  • isPrototypeOf
  • ……

再来看一段代码:

let obj = { 	name: 'Jy', 	speak () { // Note: it's not speak: function () {} 		console.log(this.name, super.name) 	} }   obj.speak() // Jy, undefined   Object.setPrototypeOf(obj,  { name: 'super' })   obj.speak() // Jy, super   let speak = obj.speak speak() // undefined, super
登录后复制

obj.speak在ES6中定义已经是Method了,它具有属性[[homeObject]],homeObject指向方法被调用的对象(代码中指的是obj), 它是绑定在对象中的Internal Slots,也就是你不能去修改,就相当于写死了。

那么homeObject有什么用呢?它跟super密切相关,当解析到super这一关键字的时候就会找homeObject的prototype。

简单来说,总结为下面两条公式:

  • let homeObj = Method[[HomeObject]] = obj
  • super = Object.getPrototypeOf(homeObj)

Note: homeObject是静态绑定在internal slots中的,而super是动态查找的。

讲完super,我们来讲讲extends和constructor

class A extends B { }   class A extends B { 	constructor (...args) { 		super(args) 	} }   class C extends null { }
登录后复制

extends主要做了以下两件事:

  • Object.setPrototypeOf(A, B)
  • Object.setPrototypeOf(A.prototype, B.prototype)

如果父类是null, 则执行Object.setPrototypeOf(C.prototype, null)

上述代码的第一和第二部分区别在于有没有显示声明constructor, 那么这两段代码是否等价呢?答案是等价的。

规范中就是这么定义的:

代码的第三部分是继承了null, 它不会报语法错误,但是我们无法new一个C出来,原因是new的时候会调用null的constructor,而null没有constructor。

看到这里,ES6的class oop, 规范声明都是使用原型来操作,所以我们是不是可以说class是原型的语法糖了?

4. babel编译后的class

我们实际项目中多采用babel来编译ES6、7的代码,所以这节我们就来分析以下babel编译后的代码,其中会省略一些报错、类型检测的一些相关代码来更好地呈现使用原型来实现OOP的主题。

编译前:

class A extends B {}   console.log(new A)
登录后复制

编译后:

"use strict";   function _getPrototypeOf(o) {   _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {     return o.__proto__ || Object.getPrototypeOf(o);   };   return _getPrototypeOf(o); }   function _inherits(subClass, superClass) {   if (typeof superClass !== "function" && superClass !== null) {     throw new TypeError("Super expression must either be null or a function");   }   subClass.prototype = Object.create(superClass && superClass.prototype, {     constructor: {       value: subClass,       writable: true,       configurable: true     }   });   if (superClass) _setPrototypeOf(subClass, superClass); }   function _setPrototypeOf(o, p) {   _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {     o.__proto__ = p;     return o;   };   return _setPrototypeOf(o, p); }   var A =   /*#__PURE__*/   function (_B) {     _inherits(A, _B);       function A() {         return _getPrototypeOf(A).apply(this, arguments);     }       return A;   }(B);   console.log(new A());
登录后复制

我们重点看_inherits 方法,跟我们上述说的extends做的两件事是一样的:

  • Object.setPrototypeOf(subClass, superClass)
  • Object.setPrototypeOf(subClass.prototype, superClass.prototype)

只不过它采用的是Object.create方法,这两个方法的区别可以去MDN上查看。

再看function A内部,其实就是执行了B的构造器函数来达到super(arguments)的效果, 这个与规范:如果没有显示声明constructor会自动加上constructor是一致的。

5. 总结

至此,我们终于理解了为什么class是原型的语法糖以及如何使用原型来实现class这一语法糖。

但切记我们使用原型的目的并不是来模拟class oop的,prototype based的oop应该用prototype去理解而不是class。

ES6的class oop 是不完备的 ,例如abstract class 、interface、private等都还没有,不过有些功能已经在提案中了,大家可以拥抱它,或者TypeScript是个不错的选择,如果你的项目中使用到了TS, 欢迎你到评论区分享你的感受。

赞(0)
分享到: 更多 (0)
网站地图   沪ICP备18035694号-2    沪公网安备31011702889846号