为什么说ES6的class是语法糖?
0. 前言
我们带着问题去阅读本文:
- 为什么说ES6的class是语法糖?
- class是原型的语法糖吗?
- 那又是如何使用原型来实现class这一语法糖的呢?
1. 基于Prototype的OOP
先来看一个prototype的例子:
function Person (name, sex) {this.name = namethis.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 = namethis.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, undefinedObject.setPrototypeOf(obj, { name: 'super' })obj.speak() // Jy, superlet 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, 欢迎你到评论区分享你的感受。
6. 参考链接
www.ecma-international.org/ecma-262/8.…
周爱民谈:JS面向对象的根基:无类继承
《重学前端》- winter
转载于:https://juejin.im/post/5c820d0e6fb9a04a0c2f3e12
为什么说ES6的class是语法糖?相关推荐
- ES6新特性_ES6语法糖class介绍与初体验_就是个语法糖而已---JavaScript_ECMAScript_ES6-ES11新特性工作笔记033
然后我们看es6中的class,es6中引入这个class这个概念,为了让js的写法更加的 面向对象,写起来方便好理解一些. 其实就是个语法糖而已,他的这个功能,es5的语法都能实现. . 可以看看e ...
- ES6新特性_ES6语法糖-class的类继承---JavaScript_ECMAScript_ES6-ES11新特性工作笔记036
然后我们再来看一下, class类的继承, 上一讲,我说了一下es5中,类的继承,可以看到,es5的写法,真的是.. 四不像,感觉像c? 所以,现在es6,支持的写法,这个写法有点像java了,理解起 ...
- ES6新特性_ES6语法糖-class中的getter和setter设置---JavaScript_ECMAScript_ES6-ES11新特性工作笔记038
我们来看一下这个class中的getter和setter这个,在java中也用对吧, 很简单. 看一下我们给Phone类添加一个get price(){} 这个表示我给price属性,添加了一个get ...
- ES6新特性_ES6语法糖-子类对父类方法的重写---JavaScript_ECMAScript_ES6-ES11新特性工作笔记037
然后我们再看,子类可以对父类方法进行重写. 可以看到我们这个父类,有个call 我可以打电话 我们子类也可以写一个和父类同样的一个方法,只要名字一样就相当于重写了 可以看到: 子类写了一个我可以进行视 ...
- ES6新特性_ES6语法糖_ES5中构造函数的继承---JavaScript_ECMAScript_ES6-ES11新特性工作笔记035
我们先看用es5实现一个构造函数的继承. 我们去用es5的写法写一个Phone这个类 然后给他添加一个Phone.prototype.call = function(){} 添加call的方法. 然后 ...
- ES6新特性_ES6语法糖_class静态成员---JavaScript_ECMAScript_ES6-ES11新特性工作笔记034
再来看一下class的静态成员 可以看到上面是es5的写法,我们写了个Phone这个函数,相当于一个类 然后给这个Phone类添加了change这个方法, 然后我们创建了Phone这个实例对象. 然后 ...
- 语法糖是什么?(ES6的一些小笔记)
专业的解释 在计算机科学中,语法糖(syntactic sugar)是指编程语言中可以更容易的表达一个操作的语法,它可以使程序员更加容易去使用这门语言:操作可以变得更加清晰.方便,或者更加符合程序员的 ...
- JavaScript中的 语法糖 是什么?
专业的解释 在计算机科学中,语法糖(syntactic sugar)是指编程语言中可以更容易的表达一个操作的语法,它可以使程序员更加容易去使用这门语言:操作可以变得更加清晰.方便,或者更加符合程序员的 ...
- JavaScript 精选:哪些能提高开发效率的es6 新语法糖
文章目录 一 ECMAScript 相关介绍 1 什么是 ECMA 2 什么是 ECMAScript 3 什么是 ECMA-262 4 谁在维护 ECMA-262 5 为什么要学习 ES6 6 ES6 ...
最新文章
- Biopython-Chapter3.生物序列对象
- 他是世界上最杰出的程序员,一个月写了个操作系统,退休后去做飞行员!
- 读取.bin激光雷达点云文件格式并可视化
- 003《区块链开发指南》一一1.2 区块和区块链 转
- linux桌面系统下的国密SM4算法工具
- php导出excel 繁体,php导出excel并解决乱码问题的方法介绍
- jvm:RunTimeDataArea
- python基础——求两个数的最大公因数和最小公倍数
- htc328d屏幕排线怎么换_HTC T328D中文Recovery刷机教程
- 使用C++代码解密微信加密数据库信息!
- 怎么用class引入svg_让动效更酷炫!4 个常见且常用的 SVG 交互动画方法
- 乐高大颗粒作品之西游记
- Jexl表达式引擎(1)
- SEO单个网页的最优化操作
- interview--- 帽子戏法
- 从零开始学USB(二十四、最简单的USB驱动开始)
- 笔记本加装内存条,更换散热风扇(以联想y7000为例)
- led matlab仿真,大功率LED照明电源研究及基于Matlab的仿真
- VSCode安装离线下载插件
- 2018老男孩脱产班linux运维51期
热门文章
- uni 登录token方法_封装uni-app的须要token的要求
- excel loc() python_python pandas df.loc[]的典型用法
- 全员编程的时代下,软件测试如何转渗透测试?
- 经典html,经典 HTML
- 第一个go程序: HTTP 服务示例
- 武汉工程大学计算机网络真题,2017年武汉工程大学计算机科学与工程学院836计算机网络考研导师圈点必考题汇编...
- centeros mysql_center os 7 Mysql 安装
- how to use automake to build files
- matlab中箭头详细设置
- 图片缩放与目标值的规范