[原创]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 里的类与继承就能一笑泯恩仇了吧。
希望能有帮助,下篇再见。

转载于:https://www.cnblogs.com/CoderMonkie/p/class-in-es5-es6.html

[ES6系列-01]Class:面向对象的“新仇旧恨”相关推荐

  1. ES6 系列之 Babel 是如何编译 Class 的(上)

    前言 在了解 Babel 是如何编译 class 前,我们先看看 ES6 的 class 和 ES5 的构造函数是如何对应的.毕竟,ES6 的 class 可以看作一个语法糖,它的绝大部分功能,ES5 ...

  2. 【Web前端学习系列01】—HTML

    [Web前端学习系列01]-HTML HTML 基本标签-head head title标签 meta标签 link标签 style标签 script标签 base标签 文本 标题标签 h 段落标签 ...

  3. 【目录】【视频课 李游Leo】ES6 系列篇

    ES6入门基础部分-ES6简介及字符串方法拓展 ES6入门基础部分-字符串模版.箭头函数.生成器函数 ES6入门基础部分-ES6选项卡做法及对象中的SET和GET js学习之ES6入门基础篇 js学习 ...

  4. ES6 系列之 let 和 const

    块级作用域的出现 通过 var 声明的变量存在变量提升的特性: if (condition) {var value = 1; } console.log(value); 复制代码 初学者可能会觉得只有 ...

  5. PHP扩展开发系列01 - 我要成为一名老司机

    PHP扩展开发系列01 - 我要成为一名老司机 1. 关于扩展的教程貌似挺全了,为啥还写? 记录下我写扩展的历程 自认为会写的更容易理解 我的宗旨就是 "先用再识" 代码写着写着就 ...

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

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

  7. 石川es6课程---9、面向对象-基础

    石川es6课程---9.面向对象-基础 一.总结 一句话总结: js老版本的面向对象和继承都不是很方便,新版的面向对象向其它语言靠拢,有了class,extends,constructor等关键字,用 ...

  8. 大白话,讲编程之《ES6系列连载》汇总,再也不用翻历史消息了

    想翻看以前的ES6系列章节,老是要查看公众号的历史消息,特别是在地铁里没wifi的时候,每次页面切换都要load很久,相当麻烦. 别怕,最懂你们的前端君又来了,来一篇完美的汇总,你要的ES6,都在这里 ...

  9. 重磅:为ES6系列设计的2套习题+答案解析

    ES6系列共20期的连载已经完满结束,但是我们对ES6的学习还不能停止. 学习ES6的重要性不言而喻,毕竟是JavaScript开发的趋势,所有开发者和运行平台都要向ES6规范靠拢.ES6知识已经成为 ...

最新文章

  1. Mysql学习笔记【原创】
  2. Java理论知识及面试题
  3. Linux文本处理命令:cut grep awk sed printf
  4. Silverlight 2 搜索照片
  5. energy in transition课文翻译_思迪软件科技 招聘 字幕翻译(远程兼职)
  6. 数学建模——人口增长模型的matlab实现以及对2010年人口预测
  7. 数字图像处理与应用——图像滤波技术
  8. 欧洲语言框架A1到C2,法语等级 A1、A2、B1、B2、C1、C2
  9. 中科院计算机和理论物理双硕士白,中科院研究生理论物理怎么不学相对论?
  10. 合作博弈网页小游戏-Js源码
  11. 单片机控制点阵字符显示及字模提取原理
  12. woff文件 服务器上找不到,字体文件未找到错误:IIS服务器上部署svg/woff/woff2字体 MIMe类型配置...
  13. html 实现音乐的波形,GitHub - wanlixi/html5-audio: 展示html5提供的强大的音频控制API...
  14. 关于核磁共振图像的命名原则及含义(总结自用)
  15. MarkdownPad2安装汉化与注册码
  16. 微信小程序-开放标签
  17. Mybatis 子查询
  18. 微信小程序开店怎么做?
  19. mysql之any,in,some,all的区别
  20. 双馈风机虚拟惯性控制+下垂控制参与系统一次调频的Matlab/Simulink模型,调频结束后转速回复,造成频率二次跌落

热门文章

  1. Leetcode题目:House Robber
  2. 阿里云上CentOS的图形访问
  3. Android 编程下将 Bitmap 转为 InputStream
  4. 性能优化 - 之一 (C/C++)
  5. C# Winform播放多媒体文件 [AudioVideoPlayback ]
  6. www.SQnote.cn
  7. C语言中怎么将变量名转换为字符串 -转
  8. Mysql在window下的表现_Mysql在windows系统下的配置
  9. 蓝桥杯 ALGO-159 算法训练 P0103
  10. [Java] 蓝桥杯BASIC-26 基础练习 报时助手