es6 快速入门 系列 —— 类 (class)
其他章节请看:
类
类(class)是 javascript 新特性的一个重要组成部分,这一特性提供了一种更简洁的语法和更好的功能,可以让你通过一个安全、一致的方式来自定义对象类型。
试图解决的问题
es5 及早期版本中没有类的概念,通常会编写类似下面这样的代码来自定义类:
// 自定义类的思路是:首先创建一个构造函数,然后定义一个方法并赋值给构造函数的原型
function Rectangle(length, width){
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function(){
return this.length * this.width
}
let rect = new Rectangle(2, 3)
console.log(rect.getArea()) // 6
console.log(rect instanceof Rectangle) // true
如果还需要实现继承,可能会这么实现:
// 定义类:Rectangle
function Rectangle(length, width){
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function(){
return this.length * this.width
}
// 定义类:Square
function Square(length){
// 对象冒充
Rectangle.call(this, length, length)
}
// 指定 Square 的原型
Square.prototype = Object.create(Rectangle.prototype, {
constructor: {
value: Square,
enumerable: true,
writable: true,
configurable: true
}
});
let square = new Square(3)
console.log(square.getArea()) // 9 - getArea() 方法从父类继承
console.log(square instanceof Square) // true
console.log(square instanceof Rectangle) // true
通过自己模拟类、模拟继承,实现起来比较复杂,也非常容易出错。
解决方法
es6引入类(class),让类的创建和继承都更加容易。下面我们重写 Rectangle
的例子:
// 通过 class 定义一个类
class Rectangle{
constructor(length, width){
this.length = length;
this.width = width;
}
// 方法直接写在 class 中
getArea(){
return this.length * this.width
}
}
// 通过 extends 关键字指定类继承的函数,原型会自动调整
class Square extends Rectangle{
constructor(length){
// 通过调用 super() 方法即可访问基类的构造函数
super(length, length)
}
}
let square = new Square(3)
console.log(square.getArea()) // 9
console.log(square instanceof Square) // true
console.log(square instanceof Rectangle) // true
这个版本比我们自定义的要简单很多,如果你先前对其他语言中的继承语法有所了解,那么你应该会觉得很亲切。
有关类(class)的具体使用、其他特性,都可以在下面的补充章节中找到答案
补充
类的创建
类和函数都存在声明形式和表达式形式。
声明类,首先编写 class 关键字,接着是类的名字,其他部分的语法类似对象字面量的简写形式,但类的各元素之间不需要使用逗号。就像这样:
// class 类名
class People{
constructor(name){
this.name =name;
}
// 不需要逗号
sayName(){
console.log(this.name)
}
}
let people = new People('aaron')
people.sayName() // aaron
console.log(typeof People) // function {1} - People 其实还是一个函数
类的表达式语法:
// 直接将一个类赋值给变量 People
let People = class{
constructor(name){
this.name =name;
}
sayName(){
console.log(this.name)
}
}
let people = new People('aaron')
people.sayName() // aaron
类(class)只是自定义类的一个语法糖。用 class 声明的类是一个函数,那么将这个函数赋值给一个变量,当然没问题。
类是一等公民
在程序中,一等公民是指可以传入函数,可以从函数返回,也可以赋值给变量。javascript 函数是一等公民,这是 javascript 中的一个独特之处,es6 也把类(class)设计成了一等公民。例如,将类(class)作为参数传入函数:
function createObject(classDef){
return new classDef()
}
let obj = createObject(class People{
sayName(){
console.log('aaron')
}
})
obj.sayName() // aaron
继承
在”解决方法“章节中,我们已经学会用 class 创建类,通过 extends 关键字指定类继承的函数,以及使用 super() 调用基类的构造函数。
当使用 super() 时切记以下几个关键点:
- 只可以在派生类(使用 extends 声明的类)的构造函数中使用 super(),否则会导致程序抛出错误
- 在构造函数中访问 this 之前,一定要调用 super(),它负责初始化 this,如果在调用 super() 之前访问 this 会导致程序报错
- 如果不想调用 super(),唯一的方法是让构造函数返回一个对象,否则也会导致程序报错
class A{}
class B extends A{
constructor(){
}
}
let b = new B() // 报错 - 没有调用super()
内建对象的继承
现在可以通过继承创建自己的特殊数组。请看下面:
class MyArray extends Array{
}
let myArray = new MyArray()
myArray[0] = 11
console.log(myArray.length) // 1
myArray.length = 0;
console.log(myArray[0]) // undefined
MyArray 继承自 Array,其行为与 Array 也很相似。但在以前,用传统的继承方式是无法实现这样的功能。es6 类语法的一个目标是支持内建对象继承,因而 es6 中的类继承模型与 es5 中的稍有不同,主要体现是:
- es5 先由派生类(例如,MyArray)创建 this,然后调用基类的构造方法(例如 Array.apply()方法)
- es6 则相反,先由基类创建this,然后派生类的构造函数再修改,所以从一开始可以访问基类的所有内建功能
我们也可以继承自其他内建对象
静态成员
es5 及更早,通过直接将方法添加到构造函数中来模拟静态成员,例如:
function People(){}
People.create = function(){}
es6 简化了这个过程,类(class)通过 static 关键字定义静态方法:
class People{
constructor(name){
this.name = name
}
static create(name){
return new People(name)
}
}
let p1 = People.create('aaron')
console.log(p1.name) // aaron
静态成员也可以被继承,请看示例:
class A{
static say(){
console.log('A say')
}
}
class B extends A{
}
B.say() // A say
派生自表达式的类
es6 中最强大的一面或许是从表达式中导出类了。只要表达式可以被解析为一个函数并且具有[[Constructor]]属性和原型,就可以用 extends 派生。举个例子:
function Rectangle(length, width){
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function(){
return this.length * this.width
}
function getBase(){
return Rectangle;
}
// getBase()这个表达式返回 Reactangle, 具有[[Constructor]]属性和原型,因此 Square 类可以直接继承它
class Square extends getBase(){
constructor(length){
super(length, length)
}
}
let square = new Square(3)
console.log(square.getArea()) // 9
extends 强大的功能使得类可以继承自任意类型的表达式,从而创建更多的可能性。
类的构造函数中使用 new.target
class A{
constructor(){
if(new.target === A){
console.log(true)
}else{
console.log(false)
}
}
}
class B extends A{
constructor(){
super()
}
}
new A() // true {1}
new B() // false {2} - B 调用 A 的构造函数,所以当调用发生时 new.target 等于 B
简单情况下,new.target 等于类的构造函数,就像行{1},但有时却会不同(行{2})。理解它(行{2})非常重要,因为每个构造函数可以根据自身被调用的方式改变自己的行为。例如,可以用 new.target 创建一个抽象基类(不能被直接实例化的类),就像这样:
// 抽象基类 BaseClass
class BaseClass{
constructor(){
if(new.target === BaseClass){
throw new Error('这个类不能被实例化')
}
}
sayName(){
console.log(this.name)
}
}
class B extends BaseClass{
constructor(name){
super()
this.name = name
}
}
let b = new B('aaron')
b.sayName() // aaron
new BaseClass() // Error: 这个类不能被实例化
访问器属性和可计算成员名称
类支持直接在原型上定义访问器属性,例如我们用访问器属性封装一下数据 name:
// 数据 name 存储在变量 _name 中,只能通过访问器属性来操作,从而达到数据封装的目的
class People{
constructor(name){
this._name = name;
}
set name(v){
this._name = v;
}
get name(){
return this._name
}
}
let people = new People('aaron')
console.log(people.name) // aaron
people.name = 'lj'
console.log(people.name) // lj
类方法和访问器属性也支持可计算成员名称,请看下面示例:
let methodName = 'sayName'
let getName = 'age'
class People{
constructor(name){
this.name = name;
}
[methodName](){
console.log(this.name)
}
get [getName](){
console.log('get')
}
}
let people = new People('aaron')
people.sayName() // aaron
people.age // get
生成器
在类中,可以将任意方法定义成生成器。但如果你的类是用来表示集合,那么定义一个默认的迭代器会更有用:
class Collection{
constructor(){
this.items = []
}
push(v){
this.items.push(v)
}
*[Symbol.iterator](){
yield *this.items.values()
}
}
let collection = new Collection()
collection.push('11')
collection.push('22')
collection.push('33')
for(let v of collection){
console.log(v) // 11 22 33
}
其他章节请看:
es6 快速入门 系列 —— 类 (class)的更多相关文章
- es6 快速入门 系列 —— 变量声明:let和const
其他章节请看: es6 快速入门 系列 变量声明:let和const 试图解决的问题 经典的 var 声明让人迷惑 function demo1(v){ if(v){ var color='red' ...
- es6 快速入门 系列 —— promise
其他章节请看: es6 快速入门 系列 Promise Promise 是一种异步编程的选择 初步认识Promise 用 Promise 来实现这样一个功能:发送一个 ajax,返回后输出 json ...
- es6快速入门 系列 - async
其他章节请看: es6 快速入门 系列 async 前文我们已经知道 promise 是一种异步编程的选择.而 async 是一种用于执行异步任务更简单的语法. Tip:建议学完 Promise 在看 ...
- es6 快速入门 系列 —— 对象
其他章节请看: es6 快速入门 系列 对象 试图解决的问题 写法繁杂 属性初始值需要重复写 function createPeople(name, age){ // name 和 age 都写了 2 ...
- es6 快速入门 系列
es6 快速入门(未完结,持续更新中...) 前言 为什么要学习es6 es6对于所有javaScript开发者来说,非常重要 未来,es6将构成javaScript应用程序的基础 es6中很多特性, ...
- es6 快速入门 —— 函数
其他章节请看: es6 快速入门 系列 函数 函数是所有编程语言的重要组成部分,es6之前函数语法一直没什么变化,遗留了许多问题,javaScript开发者多年来不断抱怨,es6终于决定大力度更新函数 ...
- vue 快速入门 系列 —— 侦测数据的变化 - [基本实现]
其他章节请看: vue 快速入门 系列 侦测数据的变化 - [基本实现] 在 初步认识 vue 这篇文章的 hello-world 示例中,我们通过修改数据(app.seen = false),页面中 ...
- 快速入门系列--WebAPI--01基础
ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...
- 快速入门系列--WebAPI--03框架你值得拥有
接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...
随机推荐
- maven中pom文件中scope的作用
Dependency Scope <dependency>中还引入了<scope>,它主要管理依赖的部署.目前<scope>可以使用5个值: compile 默认 ...
- JAVA获取昨天、今天、明天等日期
/** * 获取明天的日期字符串 * @return */ public static String tomorrowDateStr(){ Date date=new Date();//取时间 Cal ...
- 平衡二叉树判定方法(c++)实现
!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist -- 欢迎指正-- 平衡二叉树特点: 任意一个结点的平衡因子(左子树高度 - 右子树高度)的 ...
- 【九度OJ】题目1023:EXCEL排序 解题报告
[九度OJ]题目1023:EXCEL排序 解题报告 标签(空格分隔): 九度OJ [LeetCode] http://ac.jobdu.com/problem.php?pid=1023 题目描述: E ...
- 【LeetCode】686. Repeated String Match 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 【LeetCode】852. Peak Index in a Mountain Array 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 二分查找 查找最大值位置 寻找第一个下降的位置 日期 ...
- codeforce-601A. The Two Routes(最短路)
题意: 给你N个点表示N个站,有汽车和火车,汽车只能走公路,火车只能走铁路. 然后给你M条双向路,代表这两个点之间有铁路连接. 然后告诉你如果两个点之间没有铁路,那么就是公路连接. 问你汽车和火车都到 ...
- 【C#】C#中使用GDAL3(三):Windows下编译插件驱动
转载请注明原文地址:https://www.cnblogs.com/litou/p/15720236.html 本文为<C#中使用GDAL3>的第三篇,总目录地址:https://www. ...
- C# 服务器发送邮件失败
邮件发送相关端口 首先说下邮件发送的端口:25/465/587 25端口 25端口是为SMTP协议服务开放的,是这三个端口中最老的一个.25端口也称为消息中继端口,因为这个端口经常被恶意利用,所以现在 ...
- Docker 安装并运行 Redis
说明 在Windows下运行Redis主要有以下几种方式: 使用微软官方构建的Windows版Redis,最新版本是3.0.504,发布于2016-07-01.https://github.com/m ...