ES6 更易于继承的类语法的使用


Posted in Javascript onFebruary 11, 2019

和其它面向对象编程语言一样,ES6 正式定义了 class 类以及 extend 继承语法糖,并且支持静态、派生、抽象、迭代、单例等,而且根据 ES6 的新特性衍生出很多有趣的用法。

一、类的基本定义

基本所有面向对象的语言都支持类的封装与继承,那什么是类?

类是面向对象程序设计的基础,包含数据封装、数据操作以及传递消息的函数。类的实例称为对象。

ES5 之前通过函数来模拟类的实现如下:

// 构造函数
function Person(name) {
 this.name = name;
}
// 原型上的方法
Person.prototype.sayName = function(){
 console.log(this.name);
};
// new 一个实例
var friend = new Person("Jenny");

friend.sayName(); // Jenny
console.log(friend instanceof Person);  // true
console.log(friend instanceof Object);  // true

总结来说,定义一个类的思路如下:

1.需要构造函数封装数据
2.在原型上添加方法操作数据,
3.通过New创建实例

ES6 使用class关键字定义一个类,这个类有特殊的方法名[[Construct]]定义构造函数,在 new 创建实例时调用的就是[[Construct]],示例如下:

/*ES6*/
// 等价于 let Person = class {
class Person {
 // 构造函数
 constructor(name) {
  this.name = name;
 }
 // 等价于Person.prototype.sayName
 sayName() {
  console.log(this.name);
 }
}

console.log(typeof Person);  // function
console.log(typeof Person.prototype.sayName);  // function

let friend = new Person("Jenny");

friend.sayName(); // Jenny
console.log(friend instanceof Person);  // true
console.log(friend instanceof Object);  // true

上面的例子中class定义的类与自定义的函数模拟类功能上貌似没什么不同,但本质上还有很大差异的:

  • 函数声明可以被提升,但是class类声明与let类似,不能被提升;
  • 类声明自动运行在严格模式下,“use strict”;
  • 类中所有方法都是不可枚举的,enumerable 为 false。

二、更灵活的类

类和函数一样,是JavaScript的一等公民(可以传入函数、从函数返回、赋值),并且注意到类与对象字面量还有更多相似之处,这些特点可以扩展出类更灵活的定义与使用。

2.1 拥有访问器属性

对象的属性有数据属性和访问属性,类中也可以通过get、set关键字定义访问器属性:

class Person {
 constructor(name) {
  this.name = name;
 }

 get value () {
  return this.name + this.age
 }
 set value (num) {
  this.age = num
 }
}

let friend = new Person("Jenny");
// 调用的是 setter
friend.value = 18
// 调用的是 getter
console.log(friend.value) // Jenny18

2.2 可计算的成员名称

类似 ES6 对象字面量扩展的可计算属性名称,类也可以用[表达式]定义可计算成员名称,包括类中的方法和访问器属性:

let methodName = 'sayName'

class Person {
 constructor(name) {
  this.name = name;
 }

 [methodName + 'Default']() {
  console.log(this.name);
 }

 get [methodName]() {
  return this.name
 }

 set [methodName](str) {
  this.name = str
 }
}

let friend = new Person("Jenny");

// 方法
friend.sayNameDefault(); // Jenny
// 访问器属性
friend.sayName = 'lee'
console.log(friend.sayName) // lee

想进一步熟悉对象新特性可参考: 【ES6】对象的新功能与解构赋值

2.3 定义默认迭代器

ES6 中常用的集合对象(数组、Set/Map集合)和字符串都是可迭代对象,如果类是用来表示值这些可迭代对象的,那么定义一个默认迭代器会更有用。

ES6 通过给Symbol.iterator属性添加生成器的方式,定义默认迭代器:

class Person {
 constructor(name) {
  this.name = name;
 }

 *[Symbol.iterator]() {
  for (let item of this.name){
   yield item
  }
 }
}

var abbrName = new Person(new Set(['j', 'j', 'e', 'e', 'n', 'y', 'y', 'y',]))
for (let x of abbrName) {
 console.log(x); // j e n y
}
console.log(...abbrName) // j e n y

定义默认迭代器后类的实例就可以使用for-of循环和展开运算符(...)等迭代功能。

对以上迭代器内容感到困惑的可参考:【ES6】迭代器与可迭代对象

2.4 作为参数的类

类作为"一等公民”可以当参数使用传入函数中,当然也可以从函数中返回:

function createClass(className, val) {
 return new className(val)
}

let person = createClass(Person,'Jenny')
console.log(person) // Person { name: 'Jenny' }
console.log(typeof person) // object

2.5 创建单例

使用类语法创建单例的方式通过new立即调用类表达式:

let singleton = new class {
 constructor(name) {
  this.name = name;
 }
}('Jenny')
 
console.log(singleton.name) // Jenny

这里先创建匿名类表达式,然后 new 调用这个类表达式,并通过小括号立即执行,这种类语法创建的单例不会在作用域中暴露类的引用。

三、类的继承

回顾 ES6 之前如何实现继承?常用方式是通过原型链、构造函数以及组合继承等方式。

ES6 的类使用熟悉的extends关键字指定类继承的函数,并且可以通过surpe()方法访问父类的构造函数。

例如继承一个 Person 的类:

class Friend extends Person {
 constructor(name, phone){
  super(name)
  this.phone = phone
 }
}

let myfriend = new Friend('lee',2233)
console.log(myfriend) // Friend { name: 'lee', phone: 2233 }

Friend 继承了 Person,术语上称 Person 为基类,Friend 为派生类。

需要注意的是,surpe()只能在派生类中使用,它负责初始化 this,所以派生类使用 this 之前一定要用surpe()。

3.1 继承内建对象

ES6 的类继承可以继承内建对象(Array、Set、Map 等),继承后可以拥有基类的所有内建功能。例如:

class MyArray extends Array {
}

let arr = new MyArray(1, 2, 3, 4),
 subarr = arr.slice(1, 3)

console.log(arr.length) // 4
console.log(arr instanceof MyArray) // true
console.log(arr instanceof Array) // true
console.log(subarr instanceof MyArray) // true

注意到上例中,不仅 arr 是派生类 MyArray 的实例,subarr 也是派生类 MyArray 的实例,内建对象继承的实用之处是改变返回对象的类型。

浏览器引擎背后是通过[Symbol.species]属性实现这一行为,它被用于返回函数的静态访问器属性,内建对象定义了[Symbol.species]属性的有 Array、ArrayBuffer、Set、Map、Promise、RegExp、Typed arrays。

3.2 继承表达式的类

目前extends可以继承类和内建对象,但更强大的功能从表达式导出类!

这个表达式要求可以被解析为函数并具有[[Construct]]属性和原型,示例如下:

function Sup(val) {
 this.value = val
}

Sup.prototype.getVal = function () {
 return 'hello' + this.value
}

class Derived extends Sup {
 constructor(val) {
  super(val)
 }
}

let der = new Derived('world')
console.log(der) // Derived { value: 'world' }
console.log(der.getVal()) // helloworld

3.3 只能继承的抽象类

ES6 引入new.target元属性判断函数是否通过new关键字调用。类的构造函数也可以通过new.target确定类是如何被调用的。

可以通过new.target创建抽象类(不能实例化的类),例如:

class Abstract {
 constructor(){
  if(new.target === Abstract) {
   throw new Error('抽象类(不能直接实例化)')
  }
 }
}

class Instantiable extends Abstract {
 constructor() {
  super()
 }
}

// let abs = new Abstract() // Error: 抽象类(不能直接实例化)
 let abs = new Instantiable()
console.log(abs instanceof Abstract) // true

虽然不能直接使用 Abstract 抽象类创建实例,但是可以作为基类派生其它类。

四、类的静态成员

ES6 使用static关键字声明静态成员或方法。在类的方法或访问器属性前都可以使用static,唯一的限制是不能用于构造函数。

静态成员的作用是某些类成员的私有化,及不可在实例中访问,必须要直接在类上访问。

class Person {
 constructor(name) {
  this.name = name;
 }

 static create(name) {
  return new Person(name);
 }
}

let beauty = Person.create("Jenny");
// beauty.create('lee') // TypeError

如果基类有静态成员,那这些静态成员在派生类也可以使用。

例如将上例的 Person 作为基类,派生出 Friend 类并使用基类的静态方法create( ):

class Friend extends Person {
 constructor(name){
  super(name)
 }
}

var friend = Friend.create('lee')
console.log(friend instanceof Person) // true
console.log(friend instanceof Friend) // false

可以看出派生类依然可以使用基类的静态方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
prototype与jquery下Ajax实现的差别
Sep 13 Javascript
JS+css 图片自动缩放自适应大小
Aug 08 Javascript
Vue.js基础学习之class与样式绑定
Mar 20 Javascript
javascript 中的try catch应用总结
Apr 01 Javascript
基于Vue制作组织架构树组件
Dec 06 Javascript
Angular2整合其他插件的方法
Jan 20 Javascript
利用JS动态生成隔行换色HTML表格的两种方法
Oct 09 Javascript
JavaScript this绑定过程深入详解
Dec 07 Javascript
JavaScript中构造函数与原型链之间的关系详解
Feb 25 Javascript
基于vue实现图片验证码倒计时60s功能
Dec 10 Javascript
如何使用JS console.log()技巧提高工作效率
Oct 14 Javascript
关于vue中如何监听数组变化
Apr 28 Vue.js
总结4个方面优化Vue项目
Feb 11 #Javascript
JavaScript 九种跨域方式实现原理
Feb 11 #Javascript
ES6 迭代器与可迭代对象的实现
Feb 11 #Javascript
详解mpvue中小程序自定义导航组件开发指南
Feb 11 #Javascript
如何用JavaScript实现功能齐全的单链表详解
Feb 11 #Javascript
vue2.0实现的tab标签切换效果(内容可自定义)示例
Feb 11 #Javascript
vue实现动态显示与隐藏底部导航的方法分析
Feb 11 #Javascript
You might like
用PHP实现的四则运算表达式计算实现代码
2011/08/02 PHP
php使用mb_check_encoding检查字符串在指定的编码里是否有效
2013/11/07 PHP
PHP获取星期几的常用方法小结
2018/12/18 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
2020/04/04 PHP
php与阿里云短信接口接入操作案例分析
2020/05/27 PHP
javascript 隐藏/显示指定的区域附HTML元素【legend】用法
2010/03/05 Javascript
JQuery插件Style定制化方法的分析与比较
2012/05/03 Javascript
javascript实现跳转菜单的具体方法
2013/07/05 Javascript
Bootstrap树形控件使用方法详解
2016/01/27 Javascript
文件上传插件SWFUpload的使用指南
2016/11/29 Javascript
详解基于Vue/React项目的移动端适配方案
2019/08/23 Javascript
Element Badge标记的使用方法
2020/07/27 Javascript
纯js+css实现在线时钟
2020/08/18 Javascript
vue 数据遍历筛选 过滤 排序的应用操作
2020/11/17 Javascript
[01:37]TI4西雅图DOTA2前线报道 VG拿下首胜教练357给出获胜秘诀
2014/07/10 DOTA
python 正则式使用心得
2009/05/07 Python
python抓取豆瓣图片并自动保存示例学习
2014/01/10 Python
Python实现将绝对URL替换成相对URL的方法
2015/06/28 Python
python3获取两个日期之间所有日期,以及比较大小的实例
2018/04/08 Python
Python安装与基本数据类型教程详解
2019/05/29 Python
将tensorflow模型打包成PB文件及PB文件读取方式
2020/01/23 Python
Django 解决上传文件时,request.FILES为空的问题
2020/05/20 Python
python爬虫用mongodb的理由
2020/07/28 Python
英国著名的药妆网站:Escentual
2016/07/29 全球购物
加拿大廉价机票预订网站:CheapOair.ca
2018/03/04 全球购物
加拿大国民体育购物网站:National Sports
2018/11/04 全球购物
Sisley法国希思黎美国官方网站:享誉全球的奢华植物美容品牌
2020/06/27 全球购物
日本语毕业生自荐信
2014/02/01 职场文书
毕业生简历自我评价范文
2014/04/09 职场文书
中班上学期幼儿评语
2014/04/30 职场文书
2014年扫黄打非工作总结
2014/12/03 职场文书
成品仓库管理员岗位职责
2015/04/09 职场文书
战友聚会致辞
2015/07/28 职场文书
2016企业先进集体事迹材料
2016/02/25 职场文书
合理缓解职场压力,让你随时保持最佳状态!
2019/06/21 职场文书
多属性、多分类MySQL模式设计
2021/04/05 MySQL