天行健, 君子以自强不息
Sunny's Blog
Title

ES6 Demos(九) Introducing JavaScript Classes(JS的类)

Demo1: ES5 中的仿类结构

类的封装和继承最佳方法和解析

Demo2: 类的声明

                // 类声明以 class 关键字开始,其后是类的名称;剩余部分的语法看起来就像对象字面量中的方法简写,并且在方法之间不需要使用逗号.

                class PersonClass {

                    //语法糖,相当于创建了一个构造函数用于对象属性,使得每个实例的属性都私有化
                    constructor(name) {
                        this.name = name;
                    }

                    //相当于PersonType.prototype.sayName
                    sayName(){
                        console.log(this.name);
                    }
                }

                let person = new PersonClass("Nicholas");
                person.sayName(); // 输出 "Nicholas"

                console.log(person instanceof PersonClass); // true
                console.log(person instanceof Object); // true

                console.log(typeof PersonClass); // "function", 语法糖说明class本质是一个函数
                console.log(typeof PersonClass.prototype.sayName); // "function"            
            

尽管类与自定义类型之间有相似性,但仍然要记住一些重要的区别:

1. 类声明不会被提升,这与函数定义不同。类声明的行为与 let 相似,因此在程序的执行到达声明处之前,类会存在于暂时性死区内。

2. 类声明中的所有代码会自动运行在严格模式下,并且也无法退出严格模式。

3. 类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用Object.defineProperty() 才能将方法改变为不可枚举。

4. 类的所有方法内部都没有 [[Construct]] ,因此使用 new 来调用它们会抛出错误。

5. 调用类构造器时不使用 new ,会抛出错误。

6. 试图在类的方法内部重写类名,会抛出错误。

                // 不变的类名
                // 只有在类的内部,类名才被视为是使用 const 声明的。这意味着你可以在外部重写类名,但不能在类的方法内部这么做。例如:

                class Foo {
                    constructor() {
                        Foo = "bar"; // 执行时抛出错误
                    }
                }

                // 但在类声明之后没问题
                Foo = "baz";

                // 在此代码中,类构造器内部的 Foo 与在类外部的 Foo 是不同的绑定。内部的 Foo 就
                // 像是用 const 定义的,不能被重写,当构造器尝试使用任何值重写 Foo 时,都会抛出
                // 错误。但由于外部的 Foo 就像是用 let 声明的,你可以随时重写类名。           
            

Demo3: 类的表达式

使用类声明还是类表达式,主要是代码风格问题。相对于函数声明与函数表达式之间的区别,类声明与类表达式都不会被提升,因此对代码运行时的行为影响甚微。

                let PersonClass2 = class {
                    // 等价于 PersonType 构造器
                    constructor(name) {
                        this.name = name;
                    }
                    // 等价于 PersonType.prototype.sayName
                    sayName() {
                        console.log(this.name);
                    }
                };

                let person2 = new PersonClass2("Nicholas");
                person.sayName(); // 输出 "Nicholas"

                console.log(person instanceof PersonClass2); // true
                console.log(person instanceof Object); // true

                console.log(typeof PersonClass2); // "function"
                console.log(typeof PersonClass2.prototype.sayName); // "function"           
            

Demo4: 具名类表达式

                let PersonClass3 = class PersonClass4 {
                    // 等价于 PersonType 构造器
                    constructor(name) {
                        this.name = name;
                    }
                    // 等价于 PersonType.prototype.sayName
                    sayName() {
                        console.log(this.name);
                    }
                };

                console.log(typeof PersonClass3); // "function"
                //PersonClass4只能在类内部使用
                console.log(typeof PersonClass4); // "undefined"            
            

Demo5: 类可以用来作为参数传入函数

                function createObject(classDef) {
                    return new classDef();
                }

                let obj = createObject(class {
                    sayHi() {
                        console.log("Hi!");
                    }
                });

                obj.sayHi(); // "Hi!"          
            

Demo6: 单类,创建了一个匿名类表达式,并立即执行了它

                let person5 = new class {

                    constructor(name) {
                        this.name = name;
                    }
                    sayName() {
                        console.log(this.name);
                    }

                }("Nicholas");

                person5.sayName(); // "Nicholas"            
            

Demo7: 访问器属性(暂略,我没看懂有什么用处)

Demo8: 类中可计算的成员名

                let methodName = "sayName";
                class PersonClass6 {
                    constructor(name) {
                        this.name = name;
                    }
                    [methodName]() {
                        console.log(this.name);
                    }
                }

                let me = new PersonClass6("Nicholas");
                me.sayName(); // "Nicholas"            
            

Demo9: 类的静态成员,只要在方法的名称前添加正式的 static 标注

                class PersonClass7 {
                    // 等价于 PersonType 构造器
                    constructor(name) {
                        this.name = name;
                    }
                    // 等价于 PersonType.prototype.sayName
                    sayName() {
                        console.log(this.name);
                    }

                    // 等价于 PersonType.create
                    static create(name) {
                        return new PersonClass7(name);
                    }
                }

                //静态成员不能用实例来访问,你始终需要直接用类自身来访问它们。
                let person7 = PersonClass7.create("Nicholas+create");
                person7.sayName(); //Nicholas+create            
            

Demo10: ES6来进行类继承

                class Rectangle {
                    constructor(length, width) {
                        this.length = length;
                        this.width = width;
                    }
                    getArea() {
                        return this.length * this.width;
                    }
                }
                class Square extends Rectangle {
                    constructor(length) {
                        //因为 Square 构造器只需要单个参数,因此最好手动定义构造器
                        super(length, length);
                    }
                }

                var square = new Square(4);

                console.log(square.getArea()); // 16            
            

使用 super() 时需牢记以下几点:

1. 你只能在派生类中使用 super() 。若尝试在非派生的类(即:没有使用 extends关键字的类)或函数中使用它,就会抛出错误。

2. 在构造器中,你必须在访问 this 之前调用 super() 。由于 super() 负责初始化this ,因此试图先访问 this 自然就会造成错误。

3. 唯一能避免调用 super() 的办法,是从类构造器中返回一个对象。

Demo11: 继承静态成员

                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);
                    }
                }

                var rect = Square.create(3, 4);

                // 在此代码中,一个新的静态方法 create() 被添加到 Rectangle 类中。通过继承,该方法会
                // 以 Square.create() 的形式存在,并且其行为方式与 Rectangle.create() 一样。
                console.log(rect instanceof Rectangle); // true
                console.log(rect.getArea()); // 12
                console.log(rect instanceof Square); // false            
            
地势坤,君子以厚德载物