1:环境安装
npm install typescript -g
tsc --version
注意:mac电脑是:sudo npm install typescript -g
前提条件是电脑安装好的npm
2:变量类型
TypeScript最大的一个特点就是变量是强类型的,也就是说,在声明变量的时候,我们必须给他一个类型。
比如:字符串、数字、布尔,枚举… ,了解一下typeScript的声明类型。
TypeScript中的数据类型有:
Undefined :
Number:数值类型;
string : 字符串类型;
Boolean: 布尔类型;
enum:枚举类型;
any : 任意类型,一个牛X的类型;
void:空类型;
Array : 数组类型;
Tuple : 元祖类型;
Null :空类型。
在js中当你定义了一个变量,但没有给他赋予任何值的时候,他就是Undefined类型。这可能和你以前学的语言稍有不同,其他语言会有个类型的默认值。
undefined:
我们现在来看一个例子,比如我们要声明一个年龄的变量age,我们要使用数值类型,也就是Number,但是我们不给他任何的值,我们只是在控制台给它输出,然后我们来看结果。
//声明数值类型的变量age,但不予赋值
var age:number
console.log(age)
//控制台输出了undefined。
Number
在TypeScript中,所有的数字都是Number类型,这不分是整数还是小数。比如下面我们声明一个年龄是18岁,身高是178.5厘米。
var age:number = 18
var stature:number = 178.5
console.log(age)
console.log(stature)
在TypeScrip中有几种特殊的Number类型 我们需要额外注意一下:
- NaN:它是Not a Number 的简写,意思就是不是一个数字。如果一个计算结果或者函数的返回值本应该是数字,但是由于种种原因,他不是数字。出现这种状况不会报错,而是把它的结果看成了NaN。
- Infinity :正无穷大。
- -Infinity:负无穷大。
string
由单引号或者双引号括起来的一串字符就是字符串
var _li:string = "我是李二水"
console.log(_li)
Boolean
作任何业务逻辑判断都要有布尔类型的参与,通过对与错的判断是最直观的逻辑处理。boolean类型只有两种值,true和false。
var b:boolean = true
var c:boolean = false
enum
有很多固定的,不能修改的。比如说:
性别:男、女
一年的季节:春、夏、秋、冬 ,有四个结果。
enum PEOPLE{ nan , nv }
console.log(PEOPLE.nan) //返回了0,这是索引index,跟数组很像。
enum PEOPLE{
nan = '男',
nv = '女',
}
console.log(PEOPLE.nv) //返回了女 这个字
any
一个写惯了前端的人,有时候不自觉的就分不清类型了。这是个不好的习惯,也是前端的痛,就因为这个原因,JavaScript也多次被人诟病说大型项目不适合用JavaScript。但是习惯一旦养成,改是需要时间和磨练的。TypeScript友好的为我们提供了一种特殊的类型any,比如我们在程序中不断变化着类型,又不想让程序报错,这时候就可以使用any了。
Null
与 Undefined 类似,都代表空。Null 代表是引用类型为空。意义不大,但是有用。后续学习中会使用到。
注意:剩余的数组、void 会在下面的例子中体现出来。
Tuple
元祖类型,允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
let tupleArr:[number, string, boolean];
tupleArr = [12, '34', true]; //ok
typleArr = [12, '34'] // no ok
赋值的类型、位置、个数需要和定义(声明)的类型、位置、个数一致
3:TypeScript的函数
我们可以把功能相近的需求封装成一个独立的代码块,每次传入不同的变量或参数,就可以实现不同的结果。
定义函数
function searchPeople(age:number):string{
return '找到了'+age+'岁的人员'
}
var age:number = 18
var result:string = searchPeople(age)
console.log(result)
需要注意的是:
- 声明(定义)函数必须加 function 关键字;
- 函数名与变量名一样,命名规则按照标识符规则;
- 函数参数可有可无,多个参数之间用逗号隔开;
- 每个参数参数由名字与类型组成,之间用分号隔开;
- 函数的返回值可有可无,没有时,返回类型为 void;
- 大括号中是函数体。
形参和实参
形参的使用
函数定义的时候写的参数是形参。从字面意义上我们可以看出,形参就是形式上的参数。我们定义了形参也就规定了此函数的参数个数和参数类型,规范了函数。
function searchPeople(age:number):string{
return '找到了'+age+'岁的人员'
}
比如这个函数,就定义了一个形参,它的类型是数值类型。
实参的使用
var result:string = searchPeople(age)
注意:在函数调用的时候,我们需要按照形参的规则传递实参,有几个形参就要传递几个实参,并且每一个实参的类型要与对应的形参类型一致。
函数参数
可选参数,就是我们定义形参的时候,可以定义一个可传可不传的参数。这种参数,在定义函数的时候通过?标注。
1. 有可选参数的函数
function searchPeople(age:number,sex:string='男'):string{
let result:string = ''
_result = '找到了'+age+'岁'
if(sex!=undefined){
_result = _result + sex
}
return _result + '的人员'
}
var result:string = searchPeople()
console.log(result)
2. 有默认参数的函数
有默认参数就更好理解了,就是我们不传递的时候,他会给我们一个默认值,而不是undefined了。我们改造上边的函数,也是两个参数,但是我们把年龄和性别都设置默认值。
function searchPeople(age:number,sex?:string):string{
let result:string = ''
_result = '找到了'+age+'岁'
if(sex!=undefined){
_result = _result + sex
}
return _result + '的人员'
}
var result:string = searchPeople(23,'男')
console.log(result)
3. 有剩余参数的函数
function searchPeople(...xuqiu:string[]):string{
let _result:string = '找到了'
for (let i =0;i<xuqiu.length;i++){
_result = _result + xuqiu[i]
if(i<xuqiu.length){
_result=_result+'、'
}
}
_result = _result+'的人员'
return _result
}
var result:string = searchPeople('25岁','男','小眼睛','大长腿')
console.log(result)
4. 三种函数的定义方式
函数声明法
函数声明法创建函数是最常用的函数定义法。使用function关键字和函数名去定义一个函数。
function add(n1:number,n2:number):number{
return n1+n2
}
函数表达式法
函数表达式法是将一个函数赋值给一个变量,这个变量名就是函数名。通过变量名就可以调用函数了。这种方式定义的函数,必须在定义之后,调用函数。下面例子中等号右边的函数没有函数名,称为匿名函数。
var add = function(n1:number,n2:number):number{
return n1+n2
}
console.log(add(1,4))
箭头函数
箭头函数是 ES6 中新增的函数定义的新方式,我们的 TypeScript 语言是完全支持 ES6 语法的。箭头函数定义的函数一般都用于回调函数中。
var add = (n1:number,n2:number):number=>{
return n1+n2
}
console.log(add(1,4))
5:函数变量的作用域
每个变量都有一个起作用的范围,这个范围就是变量的作用域。在TypeScript语言中变量作用域划分是以函数为标准的。
函数作用域演示
function makeCake():void{
var type:string = '奶油蛋糕'
console.log(type)
}
makeCake()
console.log(type)
你会发现上述代码不会读取到。
认识全局变量和局部变量
- 局部变量:函数体内定义的变量就是局部变量。
- 全局变量:函数体外定义的变量就是全局变量。
var type:string = '奶油蛋糕'
function makeCake():void{
console.log('我做了一个'+type)
}
makeCake()
console.log(type)
这时候type变量是全局的,所以在函数内也可以调用,在函数外也可以调用。
局部变量和全局变量重名
当局部变量与全局变量重名的时候,在函数体内是局部变量起作用;如果重名,就有变量提升,这是一个坑,小伙伴们必须要注意。
var type:string = '奶油蛋糕'
function makeCake():void{
var type:string = '奶酪蛋糕'
console.log('我做了一个'+type)
}
makeCake()
console.log(type)
这回你会发现,没有变成奶酪蛋糕。我在函数没声明新变量前打印到控制台行不行?
var type:string = '奶油蛋糕'
function makeCake():void{
console.log('我做了一个'+type)
var type:string = '奶酪蛋糕'
console.log('我做了一个'+type)
}
makeCake()
console.log(type)
代码改造成了这样,但是你会发现,我们输出的结果如下:
我做了一个undefined
产生这个结果的原因就是变量提升,他的真实代码是这样的。
var type:string = '奶油蛋糕'
function makeCake():void{
var type:string
console.log('我做了一个'+type)
type = '奶酪蛋糕'
console.log('我做了一个'+type)
}
makeCake()
console.log(type)
也就是当内部声明了和全局的变量同名时,就会出现变量提升的效果,声明语句会提升到函数的第一句。这就是著名的变量提升效果。
let关键字变量的作用域
在早期javascript的变量作用域只有全局和局部,并且是以函数划分的,但在其他语言中,作用域的划分是以一对大括号作为界限的。
所以,JavaScript就遭到了无数开发者的吐槽,甚至说javascript不适合开发大型语言,容易内存溢出。JavaScript团队意识到了这一点,在ES6中推出了let关键字。
使用let关键字的变量就是一个块级作用域变量。希望大家在实际工作中多使用let来声明你的变量,让你的程序更有条理。
function makeCake():void{
var type_a:string = '奶油蛋糕'
{
let type_b:string = '慕斯蛋糕'
console.log('我做了一个'+type_b)
}
console.log('我做了一个'+type_a)
console.log('我做了一个'+type_b)
}
makeCake()
控制台正常打印。最后发现type_b找不到了。
6:引用类型-数组
Typescript分为值类型和引用类型
let people = {
name:'张三',
age:18,
saySometing:function(){
console.log('为了全栈技术')
}
}
console.log(people.name)
people.saySometing()
通过上面的案例,我们看到引用类型是一种复合的数据类型,引用类型中封装了很多对属性,每一对属性都有属性名和属性值。属性名是字符串,属性值是任意类型的数据。可以通过变量名和属性名获取属性的值或者调用属性的方法。
在TypeScript中也给我们提供了一些引用类型,例如:Array(数组)、String(字符串)、Date(日期对象)、RegExp(正则表达式)等。我们将用几节课的时间来学习这些用用类型,以及其中封装的常用方法和属性。
初始化数组的两种方法
声明数组的方法
声明数组跟声明一个普通变量是一样的,都是通过 var let 关键字实现的,只不过数组的类型说明符比较复杂而已。
let arr1:number[ ] //声明一个数值类型的数组
let arr2:Array<string> //声明一个字符串类型的数组
给数组赋值
字面量赋值法:
//定义一个空数组,数组容量为0
let arr1:number[] = []
//定义一个数组时,直接给数组赋值
let arr2:number[] = [1,2,3,4,5]
//定义数组的同时给数组赋值
let arr3:Array<string> = ['liershui','章三','张胖子']
let arr4:Array<boolean> = [ true,false,false]
需要注意的是,在TypeScript中指定数据类型的数组只能存储同一类型的数组元素。
//报错! 必须存储number类型的数据
let arr5:number[] = [1,2,true]
构造函数赋值法:
在 TypeScript 中使用 Array 这个引用类型来表示数组的,那么每一个数组都是 Array 类型的实例。
那么,我们在创建数组的时候也可以使用构造函数来进行赋值。
let arr1:number[] = new Array()
let ara2:number[] = new Array(1,2,3,4,5)
let arr3:Array<string> = new Array('zhangsan','章三','张胖子')
let arr4:Array<boolean> = new Array(true,false,false)
7:引用类型-字符串
引用类型初识:基本类型字符串和引用类型字符串。
字符串的两种类型:
- 基本类型字符串:由单引号或者双引号括起来的一串字符串。
- 引用类型字符串:用new 实例化的 String类型。
let cake:string = '蛋糕'
let cake_:String = new String("cake")
console.log(cake)
console.log(cake_)
字符串的长度
字符串的常用方法
str.indexOf(subStr)
str.substring(startIndex,[endIndex]) //endIndex是可选的
str.replace(subStr,newstr);
…
8:引用类型-日期对象
TypeScript中使用Date这个引用类型来存储日期对象,如果你要声明一个日期变量时,记得也要注明它的类型是Date。
let d:Date = new Date()
console.log(d)
9:引用类型-正则表达式
构造函数中可以传一个参数,也可以传递两个参数。一个是字符串描述,另一个是修饰符,比如g是全局修饰符,i是忽略大小写,m是多行模式
let reg1:RegExp = new RegExp("zhang") //表示字符串规则里含有zhang
console.log(reg1)
let reg2:RegExp = new RegExp("zhang",'gi')
console.log(reg2)
let reg3:RegExp = /zhang/
let reg4:RegExp = /zhang/gi
RegExp对象包含两个方法:test( )和exec( ),功能基本相似,用于测试字符串匹配。
- test(string) :在字符串中查找是否存在指定的正则表达式并返回布尔值,如果存在则返回 true,不存在则返回 false。
- exec(string) : 用于在字符串中查找指定正则表达式,如果 exec() 方法执行成功,则返回包含该查找字符串的相关信息数组。如果执行失败,则返回 null。
学习地址:http://www.runoob.com/regexp/regexp-tutorial.html
10:面向对象编程-类的声明和使用
class People{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name
this.age = age
}
say(){
console.log('大家好')
}
}
let _zhangsan:People = new People('张三',18)
console.log(_zhangsan)
_zhangsan.say()
11:面向对象编程-修饰符
TypeScript语言和Java很像,类中属性的访问可以用访问修饰符来进行限制。访问修饰符分为:public、protected、private。
- public:公有修饰符,可以在类内或者类外使用public修饰的属性或者行为,默认修饰符。
- protected:受保护的修饰符,可以本类和子类中使用protected修饰的属性和行为。
- private : 私有修饰符,只可以在类内使用private修饰的属性和行为。
12:面向对象编程-继承和重写
类的继承
在使用TypeScript这门语言时,一个最重要基本功就是面向对象编程,那对类的扩展就变的格外重要,扩展经常使用的手段就是继承。
继承:允许我们创建一个类(子类),从已有的类(父类)上继承所有的属性和方法,子类可以新建父类中没有的属性和方法
class Animal{
//属性
name:string;
age:number;
//构造函数,对象创立之前调用。
constructor(name:string,age:number){
//this表示当前实例
this.name=name;
this.age=age;
}
sayHello(){
console.log("hello, I am animal!");
}
}
//dog类继承Animal类
class Dog extends Animal{
run(){
console.log(this.name+"跑");
}
//与父类中包含相同的函数,子类中可以重写!!!!
sayHello(): void {
console.log('I am a dog!');
}
}
//cat类继承Animal类
class Cat extends Animal{
//子类重写父类的函数
sayHello(): void {
//调用当前类的父类
super.sayHello()
}
}
const dog= new Dog("小狗",12);
const cat=new Cat('小猫',12);
dog.run();
dog.sayHello();
小狗跑
I am a dog!
类的重写
class Dog extends Animal{
public _name:string = '小狗'
public sayHello(){
super.sayHello()
console.log('建立会说话的技能')
}
public zhuangQian(){
console.log('一天赚了一个亿')
}
}
先是继承了父类的方法,然后通过super关键字调用了父类的方法,实现了技能的增加。
13:面向对象编程-接口
在面向对象的语言中,术语interface经常被用来定义一个不包含数据和逻辑代码但是用来签名定义了行为的抽象类型。(接口是对象的状态(属性)和行为(方法)的抽象(描述))
// 定义接口
interface Animal{
name:string;
eat(str:string):void;
}
// 实现接口使用 implements
class Dog implements Animal{
name:string; // 实现接口的 name属性
constructor(name:string) {
this.name = name;
}
// 实现接口的函数
eat(food:string):void{
console.log(this.name + "在吃" + food)
}
}