day133:2RenMJ:TypeScript的变量&函数&类&接口
目录
1.变量
1.变量的声明
// 1.即指定数据类型 也指定值 var 变量名:类型 = 值; eg:var username:string = "libolun" // 2.不指定类型,默认类型为any var 变量名 = 值; eg:var username = "libolun" // 3.预设变量,指定类型 var 变量名:类型; eg:var username:string // 4.不指定类型和值,默认类型为any, 默认值为 undefined: var 变量名; eg:var username
在上面四种格式中,其实最常见的是第三种,格式代码如下所示
var username:string username = "libolun"
2.变量的作用域
变量作用域指代:根据变量定义的位置来决定变量的使用范围和生命周期。
TypeScript提供了三种不同的作用域:
-
全局变量定义在程序结构的外部,它可以在你代码的任何位置使用。
-
类作用域
这个变量也可以称为 属性或者字段。
类变量基本声明在类里的头部位置,不但可以在类的方法里面进行调用,也可以在类方法外面,该变量可以通过类名来访问。
类变量也可以是静态变量,静态变量可以通过类名直接访问。
-
局部作用域
var global_num = 12 // 1.全局变量 class Numbers { num_val = 13; // 2.实例变量 static sval = 10; // 3.静态变量 storeNum():void { var local_num = 14; // 4.局部变量 } } console.log("全局变量为: "+global_num) console.log(Numbers.sval) // 静态变量 var obj = new Numbers(); console.log("实例变量: "+obj.num_val)
3.联合类型
// 语法 var 变量 = 类型1|类型2|类型3|...; // 注意:联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。只能赋值指定的类型,如果赋值其它类型就会报错。
例子如下所示:
// 允许变量在使用过程中,值可以是给出的多个类型之一 var age:string|number; // 联合类型的定义 age = 20; // ok age = "20"; // ok var data:string|number|number[]|string[]; // 4个类型组合,联合类型 data = 12; console.log("数字为 "+ data); data = "xiaoming"; console.log("字符串为 " + data); data = [1,2,3]; console.log("数组为 " + data); data = ["a","b","c"]; console.log("数组为 " + data); data = true; // error TS2322: Type 'boolean' is not assignable to type 'string | number | number[]'. console.log("布尔值为 " + data);
2.函数
typescript和javascript在函数的声明以及使用中,除了类型注解以外,并没有其他区别。所以接下来的例子中,仅仅以普通函数作为例子进行讲解,至于匿名函数,箭头函数(lambda函数),闭包函数,则不会提到。
1.定义函数的语法
// 普通函数 function func_name( param1 [:type], param2 [?:type],param3[?]....) [:return_type]{ } // lambda函数 var func = (param1:type) => expression; /* type表示形参的数据类型,可以指定类型,也可以不指定类型 return_type 表示函数执行以后的返回值的数据类型,可以指定,也可以不指定 形参后面跟着?,表示当前参数是可选参数,可填可不填 */
例子如下所示:
/* 普通函数的定义,参数有三种: 1.必填参数[可以限制类型] 2.可选参数:?[可以限制类型] 3.默认参数:提供了默认值[可以限定类型,即便不限定类型,typescript也会通过默认值进行类型判定] */ function func1(arg1,arg2:number,arg3?,arg4?:string,arg5:string="xioaming"):void{ console.log(`arg1=${arg1},arg2=${arg2},arg3=${arg3},arg4=${arg4},arg5=${arg5}`); }
ps:关于JavaScript/TypeScript代码的执行流程
func1(100,200,300,"400",500); // 此处arg5导致报错,是因为typescript内置的类型系统包含了类型的判断, // 在函数声明时的参数列表中,已经对arg5进行默认值的分配,因为这个默认值的原因, // 所以typescript根据arg5的默认值进行了类型判断,识别到了是string, // 因此,在调用函数时传递的参数是number就肯定报错了
ps:关于TypeScript的类型判定
2.JS/TS中箭头函数的写法
var func = (num1:number,num2:number):number => {return num1+num2}; // 原生JS的箭头函数写法 var func = (num1:number,num2:number):number => num1+num2; // typescript的箭头函数写法[允许出现表达式] console.log( func(100,200) );
3.函数重载
错误写法:
function func1(str1:string):void{ console.log(str1); } function func1(num1:any,str1?:any):void { console.log(num1); console.log(str1); } func1("hello"); func1(16,"hello");
正确写法:
function func1(str1:string):void; function func1(num1:number,str1:string):void; function func1(num1:any,str1?:any):void { console.log(num1); console.log(str1); } func1("hello"); func1(16,"hello");
3.类
1.声明一个类
class Humen { age:number; desc():string { return `我今年${this.age}岁`; } constructor(age:number) { this.age = age } } var people = new Human(13); console.log( people.desc() );
2.类的继承
class Person extends Humen { desc():string { return "您好,"+super.desc(); } } var people = new Person(13); console.log( people.desc() );
3.静态属性和方法
class Static { // 静态属性 static num:number; // 静态方法 static desc():void { console.log("num 值为 "+ Static.num) } } Static.num = 12; // 初始化静态属性/变量 Static.desc(); // 调用静态方法
4.访问控制符
面向对象中的所谓封装,本质上就是把一系列相关的数据(属性/变量/字段)和操作数据的方法(方法/函数)集中到一个数据结构中(类),达到隐藏数据和操作数据的方法,对外暴露有限的操作数据方法。
TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限。\
-
public(默认) : 公有的,可以在任何地方被访问。
-
protected : 受保护,可以被其自身以及其子类和父类访问。
-
private : 私有,只能被其定义所在的类访问。
// 1.公有属性: 允许任何一个地方调用 // 2.私有属性: 仅允许当前类内部进行调用 // 3.保护属性: 仅允许当前类或直接间接继承了当前类的子类内部进行调用 class Proto{ public desc(){ // 公有方法 return `我住在树上`; } } class Humen extends Proto{ public address:string = "北京市"; // 公有属性 public desc(){ // 公有方法 return `我住在${this.address}`; } private money:number = 10000; // 私有属性 private calc_money(){ return this.money*0.1; // 类的内部才可以调用私有属性,私有方法 } // 如果允许私有属性提供给外界查看, 往往通过公有方法来进行暴露 public show_money(){ return this.calc_money(); } protected phone:string = "13300000000"; // 保护属性 protected get_phone(){ // 保护方法 return `我的手机号码:${this.phone}`; // 类的内部或者子类才可以调用保护属性/方法 } // 如果允许保护属性提供给外界查看,往往通过公有方法来进行暴露 public show_phone(key?){ if(key == "123456"){ return this.get_phone(); } } } class People extends Humen{ public show_father_data(){ // return this.phone; // 调用了父类的保护属性 // return this.get_phone(); // 调用了父类的保护方法 // return this.money; // 子类无法调用父类的私有属性或方法 // return this.desc(); // 调用继承到的父类方法或者属性,如果当前类重载了则出现覆盖 return super.desc(); } public desc(){ return `您好, 我住在${this.address}`; } } var xiaoming = new People(); // console.log(xiaoming.address); // console.log(xiaoming.desc()); // console.log(xiaoming.show_money()); // console.log(xiaoming.show_phone()); // console.log(xiaoming.show_phone(123456)); console.log(xiaoming.show_father_data());
4.接口
1.什么是接口?
接口(interface)是一系列抽象属性和方法的集合声明,这些方法都应该是抽象的,需要由具体的类去实现,然后外界就可以通过这组抽象方法调用,让具体的类执行具体的方法。
接口的作用在开发中针对的是数据对象和类的结构进行描述和规范化。说白了,就是你老大叫你声明一个类/对象,但是这个类/对象长什么样?他会以接口的格式先定义好,然后你照着这个接口定义好的格式进行编写一个类/对象出来,免得你弄乱结构,以后没法复用代码。
一般只有在中大型项目,或者框架/大型模块中为了更好的组织代码结构才会出现抽象类/接口
2.定义一个最简单的接口
interface interface_name { field:string, func: ()=>string // 限定了方法名,和返回值 }
3.接口的声明和实现
interface Person { username: string; // 接口属性,仅仅定了属性名,属性值和属性的初始化,都是在类中完成的 age: number; desc(user: string): string; // 接口方法,仅仅定义了方法名,参数以及返回值,具体的方法代码是在类中实现的 } class Humen implements Person { username: string; age:number; desc(user:string){ return `你好!${user},我叫${this.username},我今年${this.age}岁.`; } constructor(username: string, age: number) { this.username=username; this.age=age; } } var xiaohong = new Humen("小红",15); console.log(xiaohong.desc("小白"));
注意:凡是实现(implements)了接口/抽象类的类,都要和接口/抽象类保持一样的属性和方法
4.如何使用接口
typescript允许直通过对象来直接实现接口,跳过了类的实现过程
interface Person { username: string; age: number; desc():string } function main(person: Person) { return "Hello, 我叫" + person.username + ",我今年" + person.age+"岁."; } // typescript允许直通过对象来直接实现接口,跳过了类的实现过程 // var 对象名 = <接口名称>{ // 属性; // 方法; // } let user = <Person>{ username: "小白", age: 16, desc(){ return "hello" } }; console.log(main(user)); // js就是披着面向对象外壳的函数式编程语言
5.鸭子类型
// 鸭子类型: // 一个对象有效的语义,不是由继承自特定的类或实现特定的接口, // 而是由"当前方法和属性的集合"决定 interface Person { username: string; age: number; } function main(person: Person) { return "Hello, 我叫" + person.username + ",我今年" + person.age+"岁."; } var xm = {username:"小明",age:20}; // 问题来了,这里明明没有实现Person接口,为什么也能调用 console.log(main(xm));
ps:python中的鸭子类型
class Person(object): def __init__(self,username,age): self.username = username self.age = age class Humen(object): def __init__(self,username,age): self.username = username self.age = age def main(obj:Person): return "我叫%s,我今年%s岁了" % (obj.username,obj.age) if __name__ == '__main__': p1 = Person("小明", 15) p2 = Humen("小白", 15) print( main(p2) )
6.接口继承
1.单继承
interface Person { age:number } interface Humen extends Person { username:string desc(user:string):string } class People implements Humen{ age:number; username:string; constructor(){ } init(username,age){ this.age = age; this.username=username; } desc(user:string):string{ return `${user},您好!我叫${this.username},我今年${this.age}岁.` } } var xm = new People(); console.log( xm.desc("小红") );
2.多继承
interface Person { age:number } interface Humen { username:string } interface People extends Person,Humen { // 子接口也可以有自己的接口属性和方法 } class Student implements People{ age:number; username:string; desc(user:string){ return `你好!${user},我叫${this.username},我今年${this.age}岁.`; } constructor(username: string, age: number) { this.username=username; this.age=age; } } var xm = new Student("小白",15); console.log(xm.desc("小黑"));