Typescript 凭什么可以和 JavaScript 并肩作战(5)—类成员变量可见性和静态成员变量

1,173 阅读3分钟

这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战

TypeScript 通过对类成员变量的添加不同修饰符来控制类成员变量在类内部和类外部访问。学过 Java 或者其他支持面向对象语言的应该对 publicprotectedprivate 应该不会陌生。

成员变量可见性

public

如果类的成员变量,之前也叫做类的属性是 public 这可以任何位置都可以访问到该成员变量。

namespace MemberVisibility{
    class Tut{
        public description(){
            console.log("description");
        }
    }

    const machine_learning_tut = new Tut();
    machine_learning_tut.description()
}

之前已经提及了如果一个类的成员变量默认是 public ,所以如果对于无需显式指定修饰符为public但是有时候出于风格或者可读性原因还是加上一个public 修饰符。

protected

protected 成员变量可见范围为声明该成员变量的类或者子类的声明范围内

	class Tut{
  	public description(){
    console.log("description");
        }
        protected getName(){
            return "tut";
        }
    }

    const machine_learning_tut = new Tut();
    machine_learning_tut.description()

    class VideoTut extends Tut{
        public upload(){
            console.log(`upload ${this.getName()}`)
        }
    }

    const videoTut = new VideoTut();
    videoTut.upload();

protected 的成员只对所声明的类的子类可见。无论是父类实例还是子类实例上都无法调用 protected,可见由 protected 所修饰的属性类和其子类内部可见。

    videoTut.upload();
    // Property 'getName' is protected and only accessible within class 'Tut' and its subclasses.
    videoTut.getName();

子类需要遵循的基类一些约定,但可以选择 public 来这包括将 protected 的成员设置为 public

    class Tut{
        // 
        protected title:string = "tutorial";

        public description(){
            console.log("description");
        }
        protected getName(){
            return "tut";
        }
    }

    const machine_learning_tut = new Tut();
    machine_learning_tut.description()

    class VideoTut extends Tut{
        title = "video tutorial"
        public upload(){
            console.log(`upload ${this.getName()}`)
        }
    }

    const videoTut = new VideoTut();
    console.log(videoTut.title) //video tutorial

像 TypeScript 类型系统的其他方面一样,private 和 protected 只在类型检查中被强制执行。其实在 JavaScript 的运行时结构,如 in 或简单的属性查询,仍然可以访问一个 privateprotected成员。

    class Tut{
        private title = "tutorial"
    }

    const tut = new Tut()
    //Property 'title' is private and only accessible within class 'Tut'.ts(2341)
    console.log(tut.title)
console.log(tut['title'])
    class Tut {
        constructor() {
            this.title = "tutorial";
        }
    }
    const tut = new Tut();
    console.log(tut.title)

private

private 有点类似 protected 但是不同地方是无法在子类中访问到该成员变量

class BaseComponent{
    private x = 0;
}

const component  = new BaseComponent()
component.x//Property 'x' is private and only accessible within class 'BaseComponent'.ts

BaseComponent 外部是访问不到私有成员变量的

class CustomBaseComp extends BaseComponent{
    showSomething(){
        console.log(this.x)//Property 'x' is private and only accessible within class 'BaseComponent'.
    }
}

在子类声明内部函数也是无法访问到该父类中声明私有成员变量的

这里还是需要说明一下,这里 TypeScript 提供的 privateprotected 修饰符都是编译过程类型检测会进行错误提示,在实际在 javascript 中并没有这些限制,可以直接访问到

类静态成员

我们都知道类的静态成员,包括变量和方法都属于类级别的成员变量和方法,而不属于该类的某一个实例,在类的具体实例是无法访问这些静态变量和静态方法的。

class Tut{
    static count = 0;
    constructor(){
        Tut.count++
    }
    static printCount(){
        console.log(`count: ${Tut.count}`)
        
    }
}

下面就来通过代码演示一下,这里分别实例化了 2 个 Tut 和一个 Tut 的子类 VideoTut ,要访问类的静态方法和静态属性,我们需要 Tut 类调用,在类具体实例上是无法访问到类的属性和方法

const machine_learning_tut = new Tut()
const deep_learning_tut = new Tut()
Tut.printCount()//2

class VideoTut extends Tut{}

const deep_learning_video_tut = new Tut()
Tut.printCount()//3

如果把类的静态变量或者方法调整为 private,则只有在类定义可以访问到该私有静态方法或者属性,在类定义以外也无法访问到 count这个属性。

因为自定义函数静态方法或者属性覆盖函数prototype 覆盖属性和名称是不安全的/不可能的。像namelengthcall这样的函数属性都是保留字段,所以在对静态属性和方法进行命名需要避免使用这些保留名称。

为什么 TypeScript 没有静态类

TypeScript中的类、方法和字段可以是抽象的。在抽象类至少需要一个没有实现的抽象方法或者抽象成员变量。因为有没有实现抽象方法或者抽象字段,所以抽象类不能实例化,抽象的作用是作为基类,要求继承这个抽象类的子类实现其中所有抽象方法。如果一个类中并不存在任何抽象成员,则这个类为实体类而非抽象类。

function doSomeTask(){
    console.log("do some task")
}

class Article{
    methodOfArticle(){
        doSomeTask()
    }
}

TypeScript(和JavaScript)没有像C#和Java那样有一个叫做静态类的结构。那么静态类通过只有一个实例的类,强制所有数据和方法都挂接在这个类上,但是在 TypeScript 可能并不需要,这是因为在 JavaScript/TypeScript 的普通对象就具有这样功能。所以在 JavaScript/TypeScript 并不需要静态类的结构。例如,不必提供static class语法来创建一个静态类,这是因为通常对于对象就可以实现静态类同等作用

类泛型

类,和接口一样,可以是泛型的。当一个泛型类在 new 实例化时指定类型,其类型参数的推断方式与函数调用的方式相同。

class Article<T>{
    content:T
    constructor(content:T){
        this.content = content
    }

    description(){
        console.log(`${this.content}`)
    }
    
    
    methodOfArticle(){
        doSomeTask()
    }
    
}

const article = new Article<String>("machine learning");
article.description();
static defaultContent:T

请记住,为什么类的静态变量无法指定泛型,下面例子一看也就是为什么我们让静态成员变量接受一个泛型作为类型,因为类型在创建实例时动态指定, Article.defaultValue属性的类型只能够有一个。这意味着在创建一个实例时指定设置Article<string>.defaultValue(如果有可能的话)随后创建实例又会改变Article<number>.defaultContent,我们希望静态成员的类型是不变的。

const article1 = new Article<String>("machine learning");
article1.description();

const article2 = new Article<number>(12);
article1.description();