ES6中class类的详解

类和模块的内部,默认就是严格模式,类不存在变量提升

es6 class基础用法

//es5
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.sayInfo = function() {
  console.log(`${this.name}${this.age}岁`);
};
const liLei = new Person("LiLei", 20);
liLei.sayInfo();	//LiLei是20岁


//es6
class Person {  //定义了一个名字为Person的类
  constructor(name, age) {  //constructor是一个构造方法,用来接收参数
    this.name = name; //this代表的是实例对象
    this.age = age;
  }
  sayInfo() {
    console.log(`${this.name}${this.age}岁`);
  }
}
const liLei = new Person("LiLei", 21);
liLei.sayInfo();

//等同于
Person.prototype = {
  constructor() {},
  toString() {},
};

上面代码用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Person,对应 ES6 的Person类的constructor构造方法。类的所有方法都定义在类的prototype属性上面。

console.log(typeof Person);//function
console.log(Person === Person.prototype.constructor);//true

类的数据类型就是函数,类本身就指向构造函数。

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo

上面代码中,constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。

static关键字:静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Person {
  //没有constructor的类会默认生成一个constructor构造器
  static sayName() {
    console.log("我是static函数");
  }
}
class Student extends Person {}
const student = new Student();
Person.sayName();
Student.sayName();
student.sayName();
//输出:
//我是static函数
//我是static函数
//student.sayName is not a function

类的继承

JavaScript中的类同样可以像java一样,可以继承某个类,其中被继承的类称为父类,而继承父类的被称为子类。子类可以有自己的函数和构造器,当子类中存在父类相同的方法时,则该方法不会从父类继承,而使用子类的方法。

class Student {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(this.name);
  }
  testFn() {
    console.log("我是父类的函数!");
  }
}
class Worker extends Student {
  sayWork() {
    console.log(this.name);
  }
  testFn() {
    console.log("我是子类的函数!");
  }
}
const person = new Worker("liLei");

person.sayName();	//liLei
person.sayWork();	//liLei
person.testFn();	//我是子类的函数!

可以看到子类Worker 继承了Student类的sayName函数和name这个内部变量。但是同名函数testFn没有继承,是调用到了子类的testFn函数。这里也可以理解为子类的testFn函数覆盖了父类的testFn函数。

super关键字,指代父类的实例(即父类的this对象)。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。

这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。
如果不调用super方法,子类就得不到this对象。

class Student {
  constructor(name) {
    this.name = name;
  }
  testFn() {
    console.log("我是父类的函数!");
  }
}
class Worker extends Student {
  constructor(name, age, sex) {
    super(name); //这里必须先调用super,才有下文的this对象,这里扩展了一个变量age
    this.age = age;
    this.sex = sex;
  }
  testFn() {
    super.testFn();
    console.log("年龄" + this.age);
    console.log("性别" + this.sex);
    console.log("我是子类的函数!");
  }
}
const person = new Worker("liLei", "20");
person.testFn();

//输出:
//我是父类的函数!
//年龄20
//性别undefined
//我是子类的函数!
//我是子类的函数!

成员:公有public、私有private、受保护的protected、只读readonly修饰符

默认为 public
在上面的例子里,对象可以自由访问定义的成员,在TypeScript里,成员默认为public,你也可以明确的将一个成员标记成public。

class Animal {
  public name: string;
  public constructor(theName: string){
    this.name = theName;
  }
  public move(distanceInMeters: number){
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}

理解private
当成员被标记成private时,就不能在类的外部访问:

class Animal {
  private name: string;
  constructor(theName: string){
    this.name = theName;
  }
}

new Animal('Cat').name; // 错误:’name‘是私有的

理解protected
protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类(子类)中仍然可以访问:

class Person{
  protected name: string;
  protected constructor(name: string){
    this.name = name;
  }
}

class Employee extends Person{
  private department: string;
  
  constructor(name: string, department: string){
    super(name)
    this.department = department;
  }
  
  public getElevatorPitch() {
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
  }
}

let howard = new Employe("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 错误
let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.

readonly修饰符
你可以使用 readonly 关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。

class Octopus {
    readonly name: string;
    readonly numberOfLegs: number = 8;
    constructor (theName: string) {
        this.name = theName;
    }
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

设计模式: 装饰器

//AccessControlBiz.js

import decorators from "./decorators"

class AccessControlBiz {
    
    /** 
     类相当于实例的原型, 所有在类中定义的方法和属性,都会被实例继承,如果在方法或属性加上 static 关键字,就表示该方法和属性
     不会被继承,而是通过类直接来调用
     */

    // 静态属性
    static className = 'AccessControlBiz'
    // 构造方法
    constructor (appId, token) {
        this.appId = appId
        this.token = token
    }
    //存/取值函数: 通过get和set关键字,对appId属性设置存值函数和取值函数,拦截属性的存取行为
    get appId () {
        return this.appId
    }
    set appId (appId) {
        this.appId = appId
    }
    get token () {
        return this.token
    }
    set appId (token) {
        this.token = token
    }
}
decorators.getInstance(AccessControlBiz)
export default AccessControlBiz
//decorators.js

//装饰器是对类进行处理的函数,装饰器函数的第一个参数就是所要装饰的目标类
/**
 * 给类装饰一个getInstance静态方法
 * @param { Function } targetClass 目标类
 */
let instances = {}

function getInstance(targetClass) { // targetClass 指向函数名(类)
    if (targetClass && targetClass instanceof Function && targetClass.constructor && targetClass.className) {
        let className = targetClass.className
        if (typeof (targetClass.getInstance) === 'undefined') {
            // 设置类的getInstance方法,并返回实例对象
            // rest参数(...变量名(数组)),变量将多余的参数放入数组 args = ['appid', 'token']
            targetClass.getInstance = function (...args) { 
                let hashKey = hashMd5(JSON.stringify(args)).toLowerCase() //输出字符串
                if (!instances[className]) {
                    instances[className] = {}
                }
                if (!instances[className][hashKey]) {
                    instances[className][hashKey] = new targetClass(...args)
                }
                return instances[className][hashKey]
            }
        } else {
            throw 'Method getInstance is defined of class' + className
        }
    }
}
exports.getInstance = getInstance
//main.js
import AccessControlBiz from "./accessControlBiz"

const appId = localStorage.getItem('appId')
const token = localStorage.getItem('token')
let accessControlBiz = AccessControlBiz.getInstance (appId, token)

console.log(accessControlBiz) //输出AccessControlBiz的实例对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值