影子的知识库

影子的知识库

  • 知识库
  • GitHub

›ES6

JVM系列

  • JVM内存区域
  • 对象创建-布局-访问
  • 内存溢出实战
  • 内存区域回收
  • 四大引用
  • 垃圾回收算法
  • HotSpot回收算法细节

Java系列

  • java注解
  • springboot请求参数绑定
  • springboot请求参数校验框架
  • YAML语法
  • 动态代理
  • classpath和java命令
  • springboot-aop编程
  • springboot统一异常处理
  • springboot数据库和事务
  • springboot拦截器
  • springboot中的web配置
  • docker的简单开发
  • springboot自动配置
  • 数据库的隔离级别
  • springboot监控
  • java类加载
  • java-agent的相关内容
  • 类加载器详解
  • java的SecurityManager
  • maven学习

Node

    JS 基础

    • 语法基础和数据类型
    • 数据类型转换
    • 语句 表达式 运算符
    • 变量与对象
    • 函数
    • 数据处理
    • 常用 API
    • 重点知识

    ES6

    • 块级作用域
    • 字符串和正则表达式
    • 函数
    • 对象
    • Symbol
    • Set和Map
    • 迭代器和生成器
    • 类
    • 数组
    • Promise

    Node 基础

    • 模块系统
    • package.json
    • 内置对象
    • npm脚本的使用
    • Buffer
    • Stream
    • 事件循环机制
    • 示例代码

    stream系列

    • 流的缓冲
    • 可读流
    • 可写流
    • 双工流和转换流
    • 自定义流

后期计划

  • 学习计划
  • 专题研究计划
Edit

本文内容

类的定义

class Person {
    // Person 的构造函数
    constructor(name) {
        this.name = name;
    }

    // 等价于 Person.prototype.sayName
    sayName() {
        console.log(this.name);
    }
}

let person = new Person('czp');
person.sayName(); // czp
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
console.log(typeof Person); // function
console.log(typeof Person.prototype.sayName); // function

类只是个语法糖

需要注意以下内容

  • 类声明类似于 let 声明,不能被提升,真正执行声明语句之前,一直存在于临时死区
  • 类中所有代码运行在严格模式下,无法变更
  • 类声明中,所有方法都是不可枚举的
  • 只能使用 new 来调用类的构造函数,上例中如果直接调用 Person() 将会报错
  • 在类中修改类名会报错(在声明语句完成后是可以的)

访问器属性

上面的定义中,我们在构造函数中定义类的数据属性,我们也可以定义类的访问器属性

class CustomHTMLElement {
    constructor(element) {
        this.element = element;
    }

    get html() {
        return this.element.innerHTML;
    }

    set html(value) {
        this.element.innerHTML = value;
    }
}

这个属性也是不可枚举的

可计算成员名称

let methodName = 'sayName';

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

    [methodName]() {
        console.log(this.name);
    }
}

let me = new Person('czp');
me.sayName(); // czp

可以看出来,几乎跟对象字面量一样的用法

静态成员

在 ES 5 之前,模拟一个类的静态成员很简单,就是在构造函数上添加该属性

function Person(name) {
    this.name = name;
}

Person.staticAttribute = 1;

使用 ES 6 的类语法则是如下,使用 static 关键字

let methodName = 'sayName';

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

    [methodName]() {
        console.log(this.name);
    }

    // 等价于 Person.hhh = 这个方法
    static hhh() {
        console.log('hhhhhhh');
    }
}

Person.hhh(); // hhhhhhh

其实这种挺蠢的,实例对象没法访问所谓的静态成员

方法和访问器属性可以添加 static 关键字

继承

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }

    getArea() {
        return this.length * this.width;
    }
}

class Square extends Rectangle {
    constructor(length) {
        // 等价于 Rectangle.call(this,length,length)
        super(length, length);
    }
}

let square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true

派生类中必须调用超类的构造方法

如果我们不写子类的构造方法,那么实际上子类会具有一个传入所有参数给超类的构造方法,例如

class Square extends Rectangle {
        // 没有构造方法
}

等价于

class Square extends Rectangle {
    constructor(...args) {
        super(...args);
    }
}

使用 super() 时有以下注意点

  • 只能在子类的构造方法中调用 super()
  • 在构造方法中访问 this 之前,一定要先调用 super(),否则会报错
  • 如果不想调用 super(),唯一的方法是让构造方法返回一个对象

类方法遮蔽

子类会覆盖超类的同名方法,这个很好理解

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }

    getArea() {
        return this.length * this.width;
    }
}

class Square extends Rectangle {
    constructor(length) {
        // 等价于 Rectangle.call(this,length,length)
        super(length, length);
    }

    getArea() {
        return super.getArea();
    }
}

这里子类就是去调用超类的同名方法了,我们也可以完全重写属于子类自己的逻辑

静态成员继承

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }

    getArea() {
        return this.length * this.width;
    }

    static create(length, width) {
        return new Rectangle(length, width);
    }
}

class Square extends Rectangle {
    constructor(length) {
        // 等价于 Rectangle.call(this,length,length)
        super(length, length);
    }

    getArea() {
        return super.getArea();
    }
}

let rect = Square.create(3, 4);
console.log(rect instanceof Rectangle); // true
console.log(rect.getArea()); // 12
console.log(rect instanceof Square); // false

子类这个函数本身也继承了静态成员

但是行为都是 Rectangle.create 的行为

new.target

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
        console.log(new.target);
    }

    getArea() {
        return this.length * this.width;
    }

    static create(length, width) {
        return new Rectangle(length, width);
    }
}

class Square extends Rectangle {
    constructor(length) {
        // 等价于 Rectangle.call(this,length,length)
        super(length, length);
    }

    getArea() {
        return super.getArea();
    }
}

new Rectangle(1, 2); // [Function: Rectangle]
new Square(1); // [Function: Square]
  • 当我们使用 new 调用超类的构造方法,new.target 就是超类的构造方法
  • 当我们使用 new 调用子类的构造方法,new.target 就是子类的构造方法
  • 当不适用 new 调用,new.target 就是 undefined
Last updated on 11/8/2020
← 迭代器和生成器数组 →
  • 类的定义
  • 访问器属性
  • 可计算成员名称
  • 静态成员
  • 继承
  • 类方法遮蔽
  • 静态成员继承
  • new.target
影子的知识库
Docs
Getting Started (or other categories)Guides (or other categories)API Reference (or other categories)
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHub
Copyright © 2020 Cen ZhiPeng