[原创]CoderPower

大家好,这里是码路工人有力量,我是码路工人,你们是力量。


这是公众号(码路工人有力量)开通后的第二篇,写得还是有待改进吧。
这次准备写一个关于ES6基础的短文系列,努力尽快更完。
欢迎关注分享,一起学习提高吧。
QRCode/微信订阅号二维码


今天主要聊聊JS中的面向对象即类的使用,先来看看ES5中的传统实践,再对比ES6中的便利优雅,面向未来又不忘历史。

1. ES5中的类与继承

1.1 function 是函数,也是类

先来一段例子代码

/* eg.1
* class example in ES5
*/ // Class Definition
function Person(name, gender) {
this.name = name;
this.gender = gender; Person.PCount++;
}
// Method Definition
Person.prototype.greeting = function(){
console.log("Hi! I am " + this.name+ ".");
}
// JS also has static methods and properties.
Person.PCount = 0;
Person.GetPCount = function() {
console.log("Static method called. PCount = " + Person.PCount);
} // Instance
var p1 = new Person("Tom", "male");
p1.greeting(); // Hi! I am Tom. var p2 = new Person("Jerry", "male");
p1.greeting(); // Hi! I am Jerry. Person.GetPCount(); // Static method called. PCount = 2
  • Person 构造函数

    Person 在这里既是类定义(类名),又是构造器(构造函数)。对于CSharper或者Javaer,这多新鲜,类跟构造函数竟然是同一个东西。(所以到了ES6,写起来就舒服多了,从这里也能看出开发语言的相互学习与特性趋同趋势)

    构造函数:通过 new 类名 来创建一个函数的对象
    实例:接收到 new 构造函数 创建出来的对象就是实例。

  • prototype 原型

    关于 prototype 即原型,用与实现对象的属性继承。有很多优秀的文章介绍原型链的,这里不再展开,或以后再做单独总结。

    其中的关系如下:

    • 构造函数.prototype === 原型
    • 实例.__proto__ === 原型
    • 原型.constructor === 构造函数
  • JS类也可以有静态函数和静态属性

    上面也提到了,这里说的构造器就是类名。

    构造器.属性名 = xxx
    构造器.方法名 = foo(){}

    通过 构造器.属性名/方法名() 来调用。在 eg.1 中实现一个实例自动计数器。

1.2 实现好继承还需要抱团才行(组合继承)

ES5中对继承的支持比较弱,或者说面向对象支持比较弱,继承要绕弯子来实现。
继承的实现方式有:

  • 通过构造函数继承

    就是在 new 的时候,构造函数里用父类的构造函数调用 call/apply 来传递子类这个对象改变 this 指向。
    原理上讲,就是用 call/apply,更改了父类构造函数里 this 的指向,实现了冒充继承。

  • 通过原型链继承

    将子类的 prototype 属性 指向一个父类的新对象:
    Child.prototype = new Parent();

  • 混合继承(即用构造函数又用原型链)

    前两种分别有弊端所有混合才成了最佳实践。Talk is cheap, show you the code.

    /* eg.2
    * extension example in ES5
    */ // Child Class Definition
    function Student(name, gender, schoolName) {
    Person.apply(this, arguments);
    this.school = schoolName;
    }
    Student.prototype = Person.prototype;
    // or:
    // Student.prototype = Object.create(Person);
    Student.prototype.constructor = Student; var s1 = new Student("Emily", "female", "Non-Famouse University");
    s1.greeting(); // Hi! I am Emily Person.GetPCount.apply(s1) // Static method called. PCount = 3
    s1.GetPCount(); // Uncaught TypeError: s1.GetPCount is not a function

    在上面的 eg.2 中,注释里记录了运行结果,大家也可以把代码copy到chrome的Console里回车执行一下来查看。

    很明显可以看到,Person 的属性和方法被继承了,但是静态属性和静态方法却没有,不能直接调用,不过可以绕路调用也就是仍然用Person,再用apply改变this指向子类。可以发现计数器确实增加了,也就是实例化子类时,父类里面有正常调用。


2. ES6中的类与继承

既然是ES6系列,ES5自然不是本文重点。回顾完了接下来我们看看ES6。

ES6中的类与继承主要概括为四个关键字:

  • class
  • constructor
  • extends
  • super
  • 除以上之外的补充:static 关键字

2.1 终于有了属于JS自己的Class

/* eg.3
* class example in ES6
*/ // Class Definition
class Person {
constructor(name, gender) {
this.name = name;
this.gender = gender; Person.PCount++;
} // Methods Definition
greeting() {
console.log("Hello! I am " + this.name + ".");
}
foo() {
console.log("bar");
} // mock static property
static get PCount() {
return Person.Count;
}
static set PCount(value) {
Person.Count = value;
} // static method
static GetPCount() {
console.log("Static method called in ES6. PCount = " + Person.PCount);
}
}
// static property
Person.Count = 0 // Instance
const p1 = new Person("Petter", "male");
p1.greeting(); // Hello! I am Petter. const p2 = new Person("Ben", "male");
p2.greeting(); // Hello! I am Ben. Person.GetPCount(); // Static method called in ES6. PCount = 2
  • 世界大同:class 关键字

    与C#和java里采用了相同的定义方式:class关键字声明一个类。

    类成员方法的定义,有了简便写法,即 方法名(){方法体} ,后面继续定义方法的时候也不需要加逗号之类的分隔。

  • 构造函数:constructor 关键字

    这样的写法看起来就舒服多了,类是类,构造函数是构造函数。

    类里的构造函数是必须存在的,如果没有显式指定,那么就会生成一个默认的构造函数(如下所示)。怎么样,听起来多么熟悉的味道,对于CShaper/Javaer。

    ...
    constructor() {}
    ...
  • 静态方法:static 关键字

    增加了static关键字,可以在类内部用来声明静态方法。

    静态方法的调用与之前相同,直接 类名.静态方法()

    不能用static在类里定义一个静态属性,但可以用 static get/set 来模拟操作属性。

2.1 继承变得很简单

/* eg.4
* extension example in ES6
*/ // Child Class Definition
class Student extends Person {
constructor(name, gender, schoolName) {
super(name, gender);
// must call [super] before use [this]
this.school = schoolName;
} greeting() {
console.log("Hello! I am " + this.name + " and I am studying in " + this.school + ".");
}
} const s1 = new Student("Lily", "female", "Non-Famouse College");
s1.greeting(); // Hello! I am Lily and I am studying in Non-Famouse College. Person.GetPCount.apply(s1); // Static method called in ES6. PCount = 3
s1.GetPCount(); // Uncaught TypeError: s1.GetPCount is not a function
  • 继承:extends 关键字

    ES6中的类与继承其实内部还是用的prototype与constructor实现的,只是增加了语法糖,写起来就简单多了,看起来也是清晰多了。

    在 eg.4 中,我们重写了父类 Person 的 greeting 方法,所以在调用时打印内容不再是父类里的内容。

  • 调用/访问父类:super 关键字

    通过 super 关键字,可以访问父类的属性或方法。比如在上面的 eg.4 中,重写的 greeting 方法还可以写成:

    greeting() {
    super.greeting(); // Hello! I am Lily.
    console.log("Hello! I am " + this.name + " and I am studying in " + this.school + ".");
    }

    需要注意的一点是,在子类的构造函数里,若要给属性赋值(这就用到了 this),必须要先调用 super,也就是先走父类的构造函数初始化,创建一个空对象(也就是咱们用到的 this),否则报错。

  • 补充(要实例化的类):new.target 关键字

    用 new.target 能区分要实例化的是哪个类,比如在实例化子类时,new.target.name 就是子类名字。
    放开 class Person 的 constructor 的 console.log(new.target.name); 这句,可以确认到以下打印结果:

    new Person()      // Person
    new Student() // Student

读到这里,对于 JavaScript 里的类与继承就能一笑泯恩仇了吧。
希望能有帮助,下篇再见。

[ES6系列-01]Class:面向对象的“新仇旧恨”的更多相关文章

  1. php从入门到放弃系列-01.php环境的搭建

    php从入门到放弃系列-01.php环境的搭建 一.为什么要学习php 1.php语言适用于中小型网站的快速开发: 2.并且有非常成熟的开源框架,例如yii,thinkphp等: 3.几乎全部的CMS ...

  2. java io系列01之 "目录"

    java io 系列目录如下: 01. java io系列01之  "目录" 02. java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括 ...

  3. SAP接口编程 之 JCo3.0系列(01):JCoDestination

    SAP接口编程 之 JCo3.0系列(01):JCoDestination 字数2101 阅读103 评论0 喜欢0 JCo3.0是Java语言与ABAP语言双向通讯的中间件.与之前1.0/2.0相比 ...

  4. Java 集合系列 01 总体框架

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  5. Java 之 I/O 系列 01 ——基础

    Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 整理<疯狂j ...

  6. [js高手之路] es6系列教程 - 对象功能扩展详解

    第一:字面量对象的方法,支持缩写形式 //es6之前,这么写 var User = { name : 'ghostwu', showName : function(){ return this.nam ...

  7. [js高手之路] es6系列教程 - 迭代器,生成器,for...of,entries,values,keys等详解

    接着上文[js高手之路] es6系列教程 - 迭代器与生成器详解继续. 在es6中引入了一个新的循环结构for ....of, 主要是用来循环可迭代的对象,那么什么是可迭代的对象呢? 可迭代的对象一般 ...

  8. ES6 系列之 Babel 是如何编译 Class 的(下)

    前言 ES5 寄生组合式继承 function Parent (name) { this.name = name; } Parent.prototype.getName = function () { ...

  9. JavaScript进阶系列01,函数的声明,函数参数,函数闭包

    本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...

随机推荐

  1. 虚拟 IP 设为静态 IP

    一:虚拟机设置桥接模式 1.进入虚拟机设置中将网络适配器设置成桥接模式 2.编辑--虚拟网络编辑器--选择桥接 二:将虚拟IP设置成静态IP (1)方案一:进入虚拟机系统 System 设置 (2)方 ...

  2. C# 基础知识系列- 15 异常处理篇

    0. 前言 为什么我们需要异常处理?什么是异常? 在汉语中,异常指非正常的:不同于平常的.翻译到程序中,就是指会导致程序无法按照既定逻辑运行的意外,或者说是错误.可能会有小伙伴好奇了,我们的程序不是正 ...

  3. 常用设计模式的实现,以及Netty中的设计模式

    1.观察者模式 有两个角色,观察者和被观察者.当被观察者发出消息后,注册了的观察者会收到其消息,而没有注册的观察者就不会收到. //定义观察者接口 interface Observer{ //通知观察 ...

  4. zigbee通用IO口小作业

    独立新建工程并编写.编译代码,实现按键控制流水灯运行,完成以下任务要求: [1]程序开始运行时,D4.D3.D6.D5灯全亮一会,然后全灭一会,开始进入流水灯. [2]流水灯的运行过程为:D4灯亮,其 ...

  5. 可怕!CPU竟成了黑客的帮凶!

    本故事根据CPU真实漏洞改编 前情回顾 还记得我吗,我是阿Q,就是那个CPU一号车间的阿Q啊.如果你忘记了我,记得看看这里回忆一下哦:完了!CPU一味求快出事儿了! 自从我们车间用上了乱序执行和分支预 ...

  6. CF-234 F. Fence DP

    F. Fence 这个刷Fence的问题看到好几个了... 题意 有一个栅栏,由n块宽为1cm的木板组成,第i块木板高为hi,要给他们刷上油漆,有一桶红色的可以刷a平方厘米的油漆,一桶绿色的可以刷b平 ...

  7. (1)从通信中的MCS含义开始讲起

    通信中的MCS:Modulation and Coding Scheme,意思为调制编码方案/调制编码策略,其内涵可分为两个部分:Modulation  和  Coding. 在基带的信号处理流程中, ...

  8. 设计模式之GOF23解释器模式

    解释器模式Interpreter -是一种不常用的设计模式 -用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计 -当我们需要开发一种新的语言时,可以考虑使用解释器 ...

  9. [hdu5351]找规律,大整数模板

    题意:f(1)="a",f(2)="b",f(i)=f(i-1)+f(i-2),"+"表示连接符.给定n,m,求f(n)的前m个字符的“ne ...

  10. Kubernetes管理员手边必备的9个kubectl命令

    导语:将这9个关键的kubectl命令放在手边,它们可以帮您快速排除故障并管理Kubernetes集群. Kubernetes是当今基础架构的主导技术,这意味着系统管理员需要熟悉其管理.多年来,笔者一 ...