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. BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树

    BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树 题意: 给定n个点,m条双向边的图.其中有k个点是重要的.每条边都有一定的长度. 现在要你选定一些边来构成一个图,要使得k个重要的 ...

  2. 支付宝使用流程和踩坑小记(附Demo)

    # 支付宝使用整理 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym, ...

  3. nodejs-5.1 ejs模板引擎

    ejs官方文档:https://ejs.bootcss.com/ 1.什么是 EJS? "E" 代表 "effective",即[高效]. EJS 是一套简单的 ...

  4. enumerate取下标

    for index,item in enumerate(product_list): # print(product_list.index(item),item) print(index,item) ...

  5. 学会python可以上天!20行代码获取斗鱼平台房间数据,就是这么牛逼!

    Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定.它包含了一组完善而且容易理 ...

  6. Windows上安装配置SSH教程(2)——在Windows XP和Windows 10上安装并配置OpenSSH for Windows

    知识点汇总:http://www.cnblogs.com/feipeng8848/p/8559803.html ------------------------ 安装方式有3种: (1)Windows ...

  7. 【原】js数组对象去重最简单的方法

    简单的数组去重是比较简单的,方法也特别多,如给下面的数组去重: let arr = [1,2,2,4,9,6,7,5,2,3,5,6,5] 最常用的可以用for循环套for循环,再用splice删除重 ...

  8. 如何提高使用Java反射的效率?

    前言 在我们平时的工作或者面试中,都会经常遇到“反射”这个知识点,通过“反射”我们可以动态的获取到对象的信息以及灵活的调用对象方法等,但是在使用的同时又伴随着另一种声音的出现,那就是“反射”很慢,要少 ...

  9. ASP.NET Core的实时库: SignalR -- 预备知识

    大纲 本系列会分为2-3篇文章. 第一篇介绍SignalR的预备知识和原理 然后会介绍SignalR和如何在ASP.NET Core里使用SignalR. 本文的目录如下: 实时Web简述 Long ...

  10. Vue 进阶之路(六)

    上篇文章我们分析了一下 vue 中的条件渲染,本篇我们说一下 vue 中的列表渲染和 set 方法. <!DOCTYPE html> <html lang="en" ...