TypeScript是一个结构类型系统。意思就是当我们比较两个类型的时候,只关注他们的架构是否一样,而不是在乎他们的原型是否一样。
但是,当我们谈到Class的时候,上面的结论并不总是正确。这个取决于我们的Class的属性是public, private,还是protected。
一:当Class的所有属性都是public
首先,当你不特别指定的时候,Class的属性默认是public的。
如果是2个基于某个class而new出来的对象,我们说这两个对象的类型是否兼容,只要看这个对象的结构是否一致,而不一定要对应的class是否是相同的。-- 但是,这点只针对于class的所有属性都是public的时候。看下面的例子:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
let animal: Animal = new Animal('kitty');
let person: Person = new Person('nana');
animal = person; //这样是不会报错的,以为animal和person有相同的结构
animal = 1; //报错,数字number型和animal结构不一样。
二:当Class拥有private或者protect属性时候
当Class拥有private或者protected的属性的时候,光是结构一样,并不能带来类型一样的结果:
class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
}
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
let animal: Animal = new Animal('kitty');
let person: Person = new Person('nana');
animal = person; //Error, 类型不一样
这段代码,我们仅仅是使得Animal的name变为private,并没有改变它的结构,但是animal和person已经是不同类型。当我们把person赋值给animal的时候,就会得到一个error。
这里我们是把Animal的属性改为了private,对于protected也是一样的结果。
三:Public, Protect, Private的区别
1: public的属性,可以在instance上访问
2: protect的属性,不可以在instance上访问,但是可以在子类里 面访问
3: private的属性,只能在本class内部访问
例如:
class Animal {
public category: string;
protected color: string;
private name: string;
constructor(category: string, name: string, color: string) {
this.category = category;
this.name = name;
this.color = color;
}
}
class Cat extends Animal {
constructor(category: string, name: string, color: string) {
super(category, name, color);
}
printInfo() {
console.log(`The category of the cat is ${this.category}`); // No Error
console.log(`The color of the cat is ${this.color}`);// No Error
console.log(`The name of the cat is ${this.name}`);//Error!
}
}
let animal = new Animal('animal', 'animal', 'white');
let cat = new Cat('cat', 'kitty', 'black');
animal.category = 'animal_1';//No Error. 可以在instance上访问public的属性
console.log(cat.color);//Error!不能在insatance上访问protect的属性
总结的话,我们的属性访问权限有3个level:
1:可以在instance上访问的:public
2:可以在子类里面访问的:public和protected
3:只可以在class内部访问的:private
四:当构造函数是protected
不仅是属性,构造器也可以是protected的。当一个Class的构造器是protected的时候,表示:
1:此class不可以被实例化
2:此class可以被继承,子类可以被实例化
看下面的一个例子:
class Person {
name: string;
protected constructor(name: string){
this.name = name;
}
}
class Woman extends Person{
sex: string;
constructor(name: string, sex: string){
super(name);
this.sex = sex;
}
}
let person = new Person('nana');//Error
let girl = new Woman('nana', 'Female');//No Error
我们来解读一下上面的代码:Person类的构造函数是protected的,所以当我们尝试去实例化它的时候,会得到语法错误。但是,Woman类继承自Person类,Women类可以被实例化。
五:readonly的属性
如果你希望一个实例一旦被初始化之后,某个属性就不能再被修改,那你可以把这个属性添加readyonly的标识符。但是,readonly的属性,必须在定义的时候就初始化,或者在构造函数里被初始化:
class Apple {
readonly englishName: string = 'apple';
readonly chineseName: string;
constructor(chineseName: string){
this.chineseName = chineseName;
}
}
let apple = new Apple('苹果');
console.log(apple.englishName); //'apple'
apple.englishName = '...'; //Error
apple.chineseName = '。。。'; //Error
我们的englishName和chineseName都是只读属性,首先他们必须要在定义时被初始化,例如englishName;或者在构造函数里被初始化,例如chineseName。
而只读属性有个特性就是一旦被初始化,就不能再修改。所以,当我们尝试去给apple.englishName和apple.chineseName重新赋值的时候,就会收到语法错误。
六:参数属性
我们先来直接看一个代码,就能明白什么是参数属性:
class Apple {
readonly englishName: string = 'apple';
constructor(public chineseName: string){
}
getChineseName(){
return this.chineseName;
}
}
let apple = new Apple('苹果');
console.log(apple.getChineseName());
apple.chineseName = '苹果2'; //Error
console.log(apple.getChineseName());
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。