TypeScript初试(四)

其实之前写js的时候,很少使用继承、原型这种东东。但是在开发游戏的时候,发现这个得到了大量的使用。特别是最开始不会的时候,后端童鞋不厌其烦的给我讲private、public、static等等,先感谢一下公司后端童鞋的技术支持,哈哈。
其实es6中,已经有了class,但是平常的项目中还是用的较早版本的js。而在ts的使用中,class作为原生支持的写法,使得我们能够更好的开发。

start

类 class

如果之前接触过java的童鞋肯定会更觉得,下面的代码跟java非常的相像。

class Person {
    private name: string;

    public constructor(name: string) {
        this.name = name;
    }

    public speakName() {
        return 'My name is' + this.name;
    }
}

var zhangshuang: Person = new Person('zhangshuang');

我们声明了一个类’Person’,这个类里面有三个成员分别是私有变量’name’,构造函数’constructor’,方法’speakName’;
先不用管private跟public,当我们发现this的时候,就可以说明这个变量或者方法是属于’Person’这个类的。
最后一行,通过’new’关键字,我们构造了一个’Person’类的实例。

修饰符 private public
private 私有

我们法相上面的例子中,声明变量’name’时,我们使用了private这个关键字。
当我们使用’private’声明变量时,该变量只能在类的内部使用,也就是这个变量是这个类的私有成员,外部或者实例化时不可以调用。

public 公有

在上面的例子中,我们使用’public’关键字写了一个方法。通过’public’修饰的成员是公有的,可以通过外部或者实例化来调用。如果我们不用’public’修饰的话,变量或者方法默认就是’public'(公有)的。

protected 受保护的

‘protected’作为一个修饰关键字,跟private很类似,但是有一点不同。通过’protected’修饰的成员在派生类中依然可以使用。扒一个例子:

class Person {
    protected name: string;
    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 Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error

我们不能在’Person’类外使用name,但是可以在继承自’Person’类的’Employee’类中使用。

静态属性 static

在使用类或者其内部的方法时,我们一般都会实例化一个,然后再调用方法。但是当我们把一个变量或者方法的属性设置为static时,就可省去实例化的步骤,直接调用。

class Person {
    public static name: string = 'person';
}
单例模式

在游戏开发中,有些类如场景等,我们是不希望出现多个的,所以这里使用单例模式开发会避免重复实例化同一个类出现的问题。

class MainView {

    private static instance: MainView;

    public static getInstance() : MainView {
        if(MainView.instance == null) {
            MainView.instance = new MainView();
        }
        ruturn MainView.instance;
    }
}

这里就是通过判断instance这个属性是否为null,如果是null则实例化一个类,如果不为null则直接ruturn instance

继承 extends
class Person {
    private name: string;

    public constructor(name: string) {
        this.name = name;
    }

    public speakName() {
        return 'My name is' + this.name;
    }
}

class Student extends Person {
    public constructor() {
        super();
    }

    public grade: number;
}

上面的例子中,我们先声明了一个类Person,然后又声明了一个类Student继承自Person类。
构造函数中的super()标示,这个类继承了原型中的所有的方法和属性。

TypeScript初试(三)

之前在js中,为了防止全局变量污染,我们经常会声明一个对象,然后将变量或者函数作为对象的一个属性存在。
但是在ts中,我们可以通过关键字namespace来解决这个问题。
这里的namespace相当于es6中的module。

start

下面通过一个例子来说明一下

namespace gameConfig = {

    export const TextColor = {
        valueGolden: 0xf7b44a, //净值数值颜色
        transparent: 0x00000000, //透明色
        btnGolden: 0xffe8a9 //按钮上的文字的颜色
    }

    export enum errorMsg = {
        "网络连接故障,请耐心等待" = 5000,
        "账号在其他位置登录,如不是本人请更改密码" = 5001
    }

    export let isProto: boolean = false;

    let a;

    export function curHeight(): number {
        //当前游戏宽度
        console.log(a);
        return egret.MainContext.instance.stage.stageHeight;
    }

    export class Card{
        console.log('this is a card');
    }

}

class Example {
    const gold = gameConfig.TextColor.valueGolden;
}

在namespace命名空间中,我们可以声明变量,编写函数,编写类等等。
当我们在变量声明或者函数、类的编写之前使用export关键字后,我们就可以在命名空间之外找到它。当然有一些内部使用的,我们没有必要使用export关键字。

TypeScript初试(二)

在js中,变量的声明一般用var,但是在上一次分享中大家可能也看到了,当时采用的变量声明的关键字为let。那么这个let是个什么东西呢?
let 是es6新增的一个变量声明关键字,用来声明局部变量。同时let也是ts中推荐的声明变量的关键字。let声明的变量是局部变量,它的作用于是块级作用域。

有一个比较好的例子可以让我们清楚的了解

for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}

上面代码的执行结果为:

10
10
10
10
10
10
10
10
10
10

改为let后:

for (let i = 0; i < 10 ; i++) {
    setTimeout(function() {console.log(i); }, 100 * i);
}

执行结果:

0
1
2
3
4
5
6
7
8
9

这个才是我们想要的结果,因此推荐在ts中或者es6中使用let来代替。

start

let 声明

通过上面的小例子可以看出var声明存在的问题,这也说明了用let声明变量的好处。除了名字不一样以外,let声明变量的写法与var是一样的。
let i: number = 0;
我们都知道,在js中,通过var声明的变量会有一个变量声明提前,即js解析引擎读取到var之后,会将var声明的变量提升至开始并赋值undefined,当用到的时候,才会再进行重新赋值。

变量声明不提前

而使用let声明的变量,不会出现变量声明提前,所以如果在let声明之前使用let声明的变量会报错。

a++; //变量a的作用域在let声明之后,所以此处找不到,报错。
let a: number = 0;
let的作用域

如果是在函数中或者其他块级作用域如for、if等,用let声明的话,函数外面也是找不到的。

function f(input: boolean) {
    let a = 100;

    if (input) {
        // if语句块在变量a的作用域中,可以找到。
        let b = a + 1;
        return b;
    }

    // 变量b的作用域为if语句块,所以此处找不到,报错。
    return b;
}

在上面的例子中,我们通过let分别声明了两个变量a、b。
a的作用域是f函数体内,所以,在if语句中可以找到。
b的作用域是if语句块内,所以外部找不到就会报错。

关于重复声明的问题

之前的js中,无论用var重复声明一个变量多少次,都不会报错,但是还是只有一个。如果重复声明并给同一个变量赋值的话,也不会报错,只是变量的值是最后一次赋值的值。
但是当我们通过let进行声明变量时,在同一个作用域内声明两个相同的变量就会报错。

var a: string = 'hi';
var a: string = 'hehe'; //不会报错,但是a的值为最后一次赋值的值,即为hehe;


let b: number = 0;
let b; //报错,相同作用域内不能使用let重复声明同一个变量。

const 声明

const是声明常量,这个大家在js中应该也有用过。const的作用域与let相同,但是const声明的变量的值不会发生改变。
const还有一个好处就是,采用const声明的变量如果赋值的是一个表达式或者计算,在声明之初,这个值就已经计算出来了。不会像var一样,每次用到这个变量都会在计算一遍。在项目中遇到了很多绘制环形图或者折线图这种的需求,有很多变量是通过传入的数据或者图形来计算大小的,这个时候使用const,在声明之初就将这个变量的值计算了出来,后续的调用会提高效率。

function drawLine(length: number): void {
    //声明完就会将num的值计算出来。
    const num: number = length * 10 + 1;

    //num已经计算好了,直接调用,不会像var声明的变量一样每次调用都会重新计算。
    console.log(num);
}

const变量的内部状态是可以改变的,如:

const zhangshuang = {
    name: zhangshuang,
    age: 26
}

zhangshuang.name = ZhangShuang; //不会报错,内部状态修改了。

end

let VS const

那么我们什么时候用let,什么时候使用const呢?一般来说,变量的声明最好遵循最小特权原则。也就是说,如果这个变量声明之后不会更改,那么我们就用const来声明;如果以后可能会改变,那我们就使用let来声明。

TypeScript初试(一)

跟egret引擎一样,学习typescript也是因为公司的需求。虽然typescript算是JavaScript的超集,按理说应该跟js很像,但是个人感觉ts真的跟java更像一下。遇到有搞不懂的问题都是像公司后端及android童鞋们求助解决的,在这里先谢啦~
当然有一些像interface接口这种的,我的项目中没有用到,而且我问了几次也不是很理解,感觉不用这个也可以操作。
废话不多说了,下面就说一下我通过这个项目总结的一些经验:

start

ts跟js相比,语法上严禁了一些。js动态语言的特性当然也就没了,书写的时候一般都要跟上类型,如:
let isProto: boolean = false;
后面就不会对这种东东再进行赘述。
这里的 : boolean 就是对变量 isProto 类型的说明,每一种语言都会有相应的基本类型,这些网上都有很多,
例如:TypeScript Handbook等,当时学习的时候就是从网上找的这种的一点一点看的。基本的语法在这里就简单一点带过。

布尔值 boolean

最基本的数据类型就是简单的true/false,这个就是布尔类型(boolean)
let isProto: boolean = false;

数字类型 number

ts中的number跟js一样也是浮点数,这个跟java什么的不太一样,没有int或者float整形或浮点型,只有一个number数字类型。这里有一点需要注意,如果想要通过二进制传输的话,js跟ts的number类型最多只支持15位数字,数字位数变多就会不精确。例如我的项目中,前后端数据传输就采用的protobuf这种二进制格式,在定义userID的时候用的long,并且是18位,结果在传输过来时发现末尾不是0的出现了些问题,变得跟原来不一样了。后来查阅各种资料才发现了这个问题,然后就将这种较长的数字也改为string类型替代了。

let i: number = 0;
let colorRed = 0xff0000;
字符串 string

在ts中,string类型跟js中的一样,就不在多说了
let name: string = 'zhangshuang';
TypeScript Handbook中还介绍了模板字符串的用法,如果有需要也可以参考一下。当然我的项目中没有用到这个。

数组

TypeScript中,数组有两种表示方法:
1.元素类型后跟上[ ]
let numList: number[] = [1,2,3];
2.数组泛型
let numList: Array<number> = [1,2,3];

枚举 enum

最初我理解枚举的时候是将enum分成e+num,本质上还是数字,官方的解释就是将一系列数字赋予友好的名字,它的用法也很强大:

enum fontColor = {
    gameRed = 0xff0099, //游戏中的红色
    gameBlue = 0x0099ff //游戏中的蓝色
}
let red: number = fontColor.gameRed;


enum errorMsg = {
    "网络连接故障,请耐心等待" = 5000,
    "账号在其他位置登录,如不是本人请更改密码" = 5001
}
let eMsg: string = errorMsg[5000];

可以说是结合了对象的.跟数组的[number]的用法于一身,具体怎么使用要看自己。比如第一种的fontColor,颜色数值0xff0099比较难记,如果使用fontColor.gameRed就可以解决这个问题;第二种errorMsg,错误提示较长,就可采用errorMsg[5000]来解决。

任意值 any

ts中的任意值就是为了解决在编程时不清楚变量的类型而定义的一种类型,这个就相当于js的隐式类型转换。

let notSure: any = 4;
notSure = '改变了类型';
空值 void

我理解这个void是通过函数来理解的。有的函数有返回值,有的没有,为了代码清晰严谨,有返回值的函数就写返回值的类型,没有返回值的函数直接写void

//有返回值,类型为string
function getName(name: string): string {
    return name;
}

//没有返回值
function alert(): void {
    alert(1);
}
其他的类型

还有一些类型如元组Tuple,Never等因为在项目中没有用到,所以在这里也没再多说,如果有兴趣可以去TypeScript Handbook中寻找一下。

下次分享一下变量声明var、let、const