JavaScript中没有类,是通过使用构造函数和原型模式的组合来实现类似其它面向对象编程语言中“类”的功能。ES6引入的关键字class,形式上向其它面向对象编程语言靠拢,其实质只是一个语法糖,绝大部分功能ES5都可以实现。

一、class

  在ES6之前,创建自定义对象最常用的方式是组合使用构造函数和原型模式。而ES6通过class关键字在形式上加以规范。

// ES6之前的写法
function OldStudent (name,age) {
this.name = name
this.age = age
}
OldStudent.prototype.sayMessage = function () {
console.log(`我叫${this.name},今年${this.age}岁。`)
} // ES6写法
class NewStudent{
constructor (name, age) {
this.name = name
this.age = age
} sayMessage() {
console.log(`我叫${this.name},今年${this.age}岁。`)
}
} let LiLei = new OldStudent('LiLei',20)
LiLei.sayMessage() // 我叫LiLei,今年20岁。 let HanMeiMei = new NewStudent('HanMeiMei',18)
HanMeiMei.sayMessage() // 我叫HanMeiMei,今年18岁。 console.log(typeof NewStudent) // function

  由上代码可以看出,通过class关键字定义的代码块也是函数。比较特殊的是通过class定义的函数只能通过new关键字来调用,并且不存在变量提升,只能先定义后使用。另外,class定义的函数内部模式是严格模式。

  class中的constructor方法相当于ES5中的构造函数,在实例化对象时必须调用constructor方法,如果没有显式定义,引擎也会自动添加一个空的constructor方法。constructor方法和构造函数生成对象的规则一样:创建一个新对象,作用方法的执行上下文,执行方法中的代码;如果没有明确return一个对象,则返回新创建的对象。

  class中除了定义constructor方法之外的方法是直接定义在构造函数的原型对象上的。如果该方法前面加了static关键字,则该方法为静态方法,直接定义在构造函数上而不是原型对象上。ES6明确规定class中可以定义静态方法,无法在其中定义静态变量。想要添加静态变量,可以在class之外直接向函数上添加。如下代码所示:

// sayHello为静态方法
class NewStudent{
static sayHello() {
console.log('hello')
}
} // 添加静态属性
NewStudent.type = 'student' NewStudent.sayHello() // hello
console.log(NewStudent.type) // student

  注意:通过class关键字向原型对象上只能添加方法而不能添加属性。也许是为了防止在原型对象上添加对象的情况,这样会导致在继承的时候子对象可以修改原型链上对象的数据。但是这样有些矫枉过正了,JavaScript的很大优势在于灵活性,class关键字定义类,外形规范的同时降低了灵活性。

  为了实现数据共享,可以在通过class定义过类之后,再向原型对象中添加属性。或者在class中定义getter方法,这样的话就不能在子对象上直接添加同名属性,要通过Object.defineProperty()方法来定义子对象上的同名属性。如下代码所示:

class Person{
get age () {
return 18
}
} class Student extends Person{
constructor (){
super()
}
} let LiLei = new Student()
console.log(LiLei.age) // 18
LiLei.age = 20 // 非严格模式下赋值失败,严格模式下报错
console.log(LiLei.age) // 18 // 通过Object.defineProperty()方法修改
Object.defineProperty(LiLei, 'age', {
value: 20
})
console.log(LiLei.age) // 20

  由上代码所示,ES6的原型属性添加很麻烦,如果想要往原型对象上添加属性,直接往类的prototype属性赋值最为简单。

二、extends

  实现引用类型继承的最佳方式是寄生组合式继承,ES6通过extends关键字来使这种继承方式形式更加优雅,如下代码所示:

// ES6之前的继承
function PersonA (name) {
this.name = name
} PersonA.prototype.sayName = function () {
console.log(this.name)
} function StudentA (name,age) {
PersonA.call(this,name)
this.age = age
} StudentA.prototype = Object.create(PersonA.prototype) // ES6的继承
class PersonB{
constructor (name){
this.name = name
}
sayName () {
console.log(this.name)
}
} class StudentB extends PersonB{
constructor (name, age) {
super(name)
this.age = age
}
} let LiLei = new StudentA('LiLei',20)
LiLei.sayName() // LiLei
let HanMeiMei = new StudentB('HanMeiMei',18)
HanMeiMei.sayName() // HanMeiMei

  子类的constructor中必须调用super方法,在执行super()之前子类不能使用this,这是因为ES6之前的寄生组合式继承是先创建子类的实例,然后通过call或者apply方法将该实例作为执行上下文执行一遍父类构造函数。而ES6正好相反,先创建父构造函数的实例对象,然后将实例对象作为this执行子构造函数。这就导致如果不调用super方法子类中就不能使用this。

  ES6继承中先创建父类实例的特性还带来一个ES5无法实现的功能:继承原生构造函数。如下代码所示:

class MyArray extends Array{
constructor (...args) {
super(...args)
}
} let number = new MyArray(1,2,3)
console.log(number.length) // 3 number.push(4)
console.log(number.length) // 4

  通过extends子类同时能够继承父类的静态方法。如下代码所示:

class Person{
constructor (){}
static sayHello () {
console.log('hello')
}
} class Student extends Person{
constructor () {
super()
}
} Student.sayHello() // hello

  super可以作为方法在子类构造函数中调用,也可以作为对象使用。当super作为对象时,在普通方法中指向父类的原型对象,在静态方法中,指向父类。另外,super在普通方法中指向的是父类的原型对象,无法通过super来访问父类实例中的属性和方法。如下代码所示:

class Person{
constructor (name){
this.name = name
} static sayStatic () {
console.log('static')
}
} class Student extends Person{
constructor (name) {
super(name)
} static sayStatic () {
super.sayStatic()
} sayName () {
console.log(super.name)
}
} let LiLei = new Student('LiLei')
Student.sayStatic() // static
LiLei.sayName() // undefined

三、总结

  ES6新增的关键字class、extends在形式上规范了JavaScript对类的模拟,使代码看起来更加优雅。而且能够通过extends来扩展内建的对象类型,比如Array或者RegExp。

  class带来的也不全是好处,将.prototype隐藏起来,使人更加迷惑。实际上JavaScript是没有类的,通过原型链来实现继承更恰当的说法应该是委托。而且仅仅通过class关键定义构造函数和原型对象的混合体灵活性有所降低,比如:在原型对象上只能添加方法,无法添加属性。

如需转载,烦请注明出处:https://www.cnblogs.com/lidengfeng/p/9342084.html

JavaScript夯实基础系列(五):类的更多相关文章

  1. JavaScript夯实基础系列(四):原型

      在JavaScript中有六种数据类型:number.string.boolean.null.undefined以及对象,ES6加入了一种新的数据类型symbol.其中对象称为引用类型,其他数据类 ...

  2. JavaScript夯实基础系列(三):this

      在JavaScript中,函数的每次调用都会拥有一个执行上下文,通过this关键字指向该上下文.函数中的代码在函数定义时不会执行,只有在函数被调用时才执行.函数调用的方式有四种:作为函数调用.作为 ...

  3. JavaScript夯实基础系列(二):闭包

      在JavaScript中函数是一等公民.所谓一等公民是指函数跟其他对象一样,很普通,可以进行把函数存在数组中.作为参数传递.赋值给变量等操作.当函数作为另一个函数的返回值在外部调用时,跟该函数在函 ...

  4. JavaScript夯实基础系列(一):词法作用域

      作用域是一组规则,规定了引擎如何通过标识符名称来查询一个变量.作用域模型有两种:词法作用域和动态作用域.词法作用域是在编写时就已经确定的:通过阅读包含变量定义的数行源码就能知道变量的作用域.Jav ...

  5. 【C++自我精讲】基础系列五 隐式转换和显示转换

    [C++自我精讲]基础系列五 隐式转换和显示转换 0 前言 1)C++的类型转换分为两种,一种为隐式转换,另一种为显式转换. 2)C++中应该尽量不要使用转换,尽量使用显式转换来代替隐式转换. 1 隐 ...

  6. 夯实基础系列四:Linux 知识总结

    前言 前三节内容传送门: 夯实基础系列一:Java 基础总结 夯实基础系列二:网络知识总结 夯实基础系列三:数据库知识总结 现在很多公司项目部署都使用的是 Linux 服务器,互联网公司更是如此.对于 ...

  7. UML基础系列:类图

    类图描述系统中类的静态结构,它不仅定义系统中的类,描述类之间的联系,如关联.依赖.聚合等,还包括类的内部结构(类的属性和操作).类图描述的是静态关系,在系统的整个生命周期中都是有效的.对象图是类图的实 ...

  8. web基础系列(五)---https是如何实现安全通信的

    https是如何实现安全通信的 如果有不正确的地方,还望指出! web基础系列目录 总结几种常见web攻击手段极其防御方式 总结几种常见的安全算法 回顾 总结几个概念(具体描述可以看上一篇文章) 数字 ...

  9. How Javascript works (Javascript工作原理) (十五) 类和继承及 Babel 和 TypeScript 代码转换探秘

    个人总结:读完这篇文章需要15分钟,文章主要讲解了Babel和TypeScript的工作原理,(例如对es6 类的转换,是将原始es6代码转换为es5代码,这些代码中包含着类似于 _classCall ...

随机推荐

  1. go语言 nsq源码解读四 nsqlookupd源码options.go、context.go和wait_group_wrapper.go

    本节会解读nsqlookupd.go文件中涉及到的其中三个文件:options.go.context.go和wait_group_wrapper.go. options.go 123456789101 ...

  2. .Net Remoting 调用远程对象

    根据需求,我们的系统必须以C/S方式构建,而且是三层架构,这样一来,就出现了服务器端和客户端通信的问题. 为了解决双方的通信问题,还要考虑效率.性能等方面,经过分析.试验,我们根据效率.移植.开发难易 ...

  3. EffictiveC++笔记 第3章

    Chapter 3 资源管理 条款13: 以对象管理资源 有时即使你顺利地写了对应对象的delete语句,但是前面的区域可能会有一个过早的return语句或者抛出了异常.它们一旦执行,控制流绝不会触及 ...

  4. 本地和svn都删除文件导致版本不同的问题

    想要删除一个项目中的文件,同是要删除svn上的文件. 自己操作 1.直接右键删除了本地项目中的一个目录的模块 2.右键删除了库中svn中的这个目录 3.同步本地和svn上的代码 4.问题出现了,本地和 ...

  5. Drrols规则引擎

    1.什么是规则引擎? 规则引擎是一种嵌套在应用程序中的组件,它实现了将业务规则从应用程序代码中分离出来.规则引擎使用特定的语法编写业务规则,规则引擎可以接受数据输入.解释业务规则.并根据业务规则做出相 ...

  6. 【机器学习基础】熵、KL散度、交叉熵

    熵(entropy).KL 散度(Kullback-Leibler (KL) divergence)和交叉熵(cross-entropy)在机器学习的很多地方会用到.比如在决策树模型使用信息增益来选择 ...

  7. Shiro安全框架【快速入门】就这一篇!

    Shiro 简介 照例又去官网扒了扒介绍: Apache Shiro™ is a powerful and easy-to-use Java security framework that perfo ...

  8. Springboot 系列(六)Spring Boot web 开发之拦截器和三大组件

    1. 拦截器 Springboot 中的 Interceptor 拦截器也就是 mvc 中的拦截器,只是省去了 xml 配置部分.并没有本质的不同,都是通过实现 HandlerInterceptor ...

  9. arcgis api 3.x for js 地图加载多个 SHP 图层压缩以及 json 文件展示(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  10. ArcGIS JS 3.x使用webgl绘制热力图

        ArcGIS Js Api 3.x 热力图在数据量达到三万左右的时候,绘制速度不尽人意,数据量再大些,缩放时候就会很卡,非常影响客户体验.     参考了一下网上webgl热力图,能达到更流畅 ...