在前几节里面,介绍了在es6之前实现继承的几种方法,这些继承的方法都是基于原型和原型链的,写起来比较繁琐和麻烦,于是,在es6之后,推出了用class来定义类,实现继承,让我们来一起看看es6中关于类的相关知识吧!
在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类,但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已,下面简单定义一个类并且添加一些属性和方法。
class Person{
var name;
var age;
running() {
console.log(this.name + " running~")
}
}
console.log(Person.prototype.constructor)//class Person
console.log(typeof Person) // function
var p = new Person()
console.log(p.__proto__ === Person.prototype) // true
通过以上代码,你会发现类和我们的构造函数的特性其实基本上是一致的;
es6之前我们使用function来作为构造函数,参数可以通过括号传递进来,那在类中,如何实现传递参数呢?其实在类中我们想传递进来参数是通过构造函数constructor,具体如何实现,见下面代码:
// 类的声明
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
var p1 = new Person("a", 18)
var p2 = new Person("b", 30)
console.log(p1, p2)
注意: 一个类只能有一个构造函数
1.在内存中创建一个对象 moni = {}
2.将类的原型prototype赋值给创建出来的对象 moni.proto = Person.prototype
3.将对象赋值给函数的this: new绑定 this = moni
4.执行函数体中的代码
5.自动返回创建出来的对象
普通的方法直接在类里面定义即可,通过创建的实例对象可以直接访问,静态方法在方法前面加static,可以通过类名来访问
var names = ["abc", "cba", "nba"]
class Person {
constructor(name, age) {
this.name = name
this.age = age
this._address = "广州市"
}
// 普通的实例方法
// 创建出来的对象进行访问
// var p = new Person()
// p.eating()
eating() {
console.log(this.name + " eating~")
}
running() {
console.log(this.name + " running~")
}
// 类的访问器方法
get address() {
console.log("拦截访问操作")
return this._address
}
set address(newAddress) {
console.log("拦截设置操作")
this._address = newAddress
}
// 类的静态方法(类方法)
// Person.createPerson()
static randomPerson() {
var nameIndex = Math.floor(Math.random() * names.length)
var name = names[nameIndex]
var age = Math.floor(Math.random() * 100)
return new Person(name, age)
}
}
var p = new Person("why", 18)
p.eating()
p.running()
console.log(p.address)
p.address = "北京市"
console.log(p.address)
// console.log(Object.getOwnPropertyDescriptors(Person.prototype))
for (var i = 0; i < 50; i++) {
console.log(Person.randomPerson())
}
通过extends关键字可以实现继承,
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
personMethod() {
console.log("处理逻辑1")
console.log("处理逻辑2")
console.log("处理逻辑3")
}
static staticMethod() {
console.log("PersonStaticMethod")
}
}
// Student称之为子类(派生类)
class Student extends Person {
// JS引擎在解析子类的时候就有要求, 如果我们有实现继承
// 那么子类的构造方法中, 在使用this之前
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
studying() {
console.log(this.name + " studying~")
}
// 类对父类的方法的重写
running() {
console.log("student " + this.name + " running")
}
// 重写personMethod方法
personMethod() {
// 复用父类中的处理逻辑
super.personMethod()
console.log("处理逻辑4")
console.log("处理逻辑5")
console.log("处理逻辑6")
}
// 重写静态方法
static staticMethod() {
super.staticMethod()
console.log("StudentStaticMethod")
}
}
var stu = new Student("why", 18, 111)
console.log(stu)
// console.log(Object.getOwnPropertyDescriptors(stu.__proto__))
// console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__))
stu.eating()
stu.running()
stu.personMethod()
Student.staticMethod()
console.log(Object.getOwnPropertyDescriptors(Person))
继承后,子类中可获得父类的方法,通过super关键字可向父类构造函数传递参数,也可以通过super获得父类中的方法并调用它。
其实,es6中的类只是一种语法糖,最后还是要通过babel转换成es5的代码,想知道转换后的代码,我们可以访问官方网站:babeljs.com,可以看到转换之后的代码。
以下是源码:
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
eating() {
console.log(this.name + " eating~")
}
}
转换后的代码:
// babel转换
"use strict";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
// /*#__PURE__*/ 纯函数
// webpack 压缩 tree-shaking
// 这个函数没副作用
var Person = /*#__PURE__*/ (function () {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [
{
key: "eating",
value: function eating() {
console.log(this.name + " eating~");
}
}
]);
return Person;
})();
_classCallCheck函数首先检测创建出来的是否是Person的实例,如果不是的话, throw new TypeError(“Cannot call a class as a function”);
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
_createClass函数:如果传进来的不是静态方法,调用_defineProperties函数,穿进去第一个参数是Person.prototype,也就是给erson.prototype.eating=function(){},如果是,则传进去的是Person,那么eating这个函数直接加在了Person对象里面。
_createClass(Person, [
{
key: "eating",
value: function eating() {
console.log(this.name + " eating~");
}
}
]);
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
_defineProperties函数:遍历props里面的每个属性,并且给Person设置属性描述符,
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
es6对象利用class关键字创建,通过constructor构造函数传递参数,子类通过extends关键字继承,子类通过super关键字可向父类构造函数传递参数或者调用父类里面的方法。