小柳の编程大冒险typescriptjs

TypeScript入门

2017-02-22  本文已影响2877人  shangpudxd

慕课网@JoJozhai 老师 TypeScript入门课程分享

<a href="http://www.imooc.com/video/13515">TypeScript入门</a>

ES5,ES6,JS,TypeScript间的关系

ES5、ES6是两个版本的语言规则,他们规定了脚本语言如何去写,浏览器对应的去解析而JS脚本语言应用的就是ES5规则,TypeScript(以下简称ts)应用的是ES6规则,且由微软公司开发
 在我个人理解,TypeScript是用更丰富的语法简便或优化了JavaScript里一些比较复杂、非人性化的代码编写过程,就如less、sass之于css
值得一提的是:新版本的angular2是由谷歌团队开发,基础代码应用TypeScript编辑的前端框架,因此可认为是由两大科技巨头主导的技术

TypeScript环境搭建

目前的主流浏览器对TypeScript的支持还不够完善,因此需要编译工具将TypeScript代码转译成主流的JS代码才能得以使用

在NodeJs条件下命令行输入以下代码全局安装TypeScript环境
npm install -g typescript
 安装完成后输入
tsc -v
 查看TypeScriptCompiler版本

进入到TypeScript文件所在的目录,在此目录的命令行中运行tsc命令将.ts文件转译为.js文件
tsc index.ts
(转译index.ts文件)
 会在相同目录下生成一个index.js文件

上面的方法在实际开发过程中显得十分的笨拙,而主流的IDE都为ts文件准备了一套自动tsc的方法,例如在webstorm中,开启设置项languages中的TypeScript勾选Enable TypeScript compiler选项,即可自动在ts文件下生成对应的js文件

TypeScript语法

var str = '123
456
789';

转译后在js文件中代码会自动添加换行符,如下

var str = '123\n456\n789';
var myName = 'programmer';
var getName = function () {return 'programmer'};
console.log(`I am ${myName}`);  //此处小括号中不是引号
console.log(`I am ${getName()}`);

转译js代码如下

var myName = 'programmer';
var getName = function () {
    return 'programmer';
};
console.log("I am " + myName);
console.log("I am " + getName());
function test(template,name,age) {
    console.log(template);
    console.log(name);
    console.log(age)
}
var myname = 'programmer';
var getAge = function () {
    return 18;
}
test`hello my name is ${myname}, I am ${getAge() }`

在其转译并运行后,我们会发现模板被拆分为三个参数打印出来
对应temlpate参数的是一个数组

Array[3]
0:"hello my name is "
1:", I am "
2:""

对应name参数的是"programmer",对应age参数的是 18
也就相当于模板被两段${}分割的每个部分构成了第一个参数,而两个${}是另外两个参数

class Programmer {
    skill: string;
    age: number;
}
var me: Programmer = new Programmer();
me.age = 18;
me.skill = 'ts';
function test(a:string,b:string,c:string = 'ccc'){
console.log(a);
console.log(b);
console.log(c);
}
test('aaa','bbb');
//打印aaa,bbb,ccc
function test(a:string,b?:string,c:string = 'ccc'){
console.log(a);
console.log(b);
console.log(c);
}
test('aaa');
//打印aaa,undefined,ccc
function test(...args) {
    args.forEach(function (arg) {
        console.log(arg)
    })
};
test(1, 2, 3, 4);
//打印1,2,3,4

转译为js后如下

function test() {
    var args = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
    }
    args.forEach(function (arg) {
        console.log(arg);
    });
}
;
test(1, 2, 3, 4);

也可以对声明了固定数量参数的函数进行参数数量不等的调用

function test(a,b,c) {
    console.log(a);
    console.log(b);
    console.log(c);
};
var arr = [1, 2];
test(...arr);//打印1,2,undefined
var arr2 = [1, 2, 3, 4, 5, 6];
test(...arr2);//只识别前三个参数,打印1,2,3
function* test(){
console.log("start");
yield;
console.log("finish");
}
var test1 = test();
//须将函数声明为变量使用此方法
test1.next();
test1.next();

每个.next()执行yield分割的一段代码,第一个test1.next();执行至yield之前停止打印出"start",第二个test1.next()执行之后的代码,打印出"finish"

function programmer(){
  return {
    name:"Tom",
    skill:{
      skill1:"TypeScript",
      skill2:"AngularJs"
    },
    age:18
  }
}
var {name,age} = programmer();
//此简写方法须满足变量名与函数中的属性名对应相同
console.log(name,age);
//打印出Tom 18
var {name:name1,skill:{skill1:skills}} = programmer();
//此为变量名与函数中属性名不相同的写法
console.log(name1,skills);
//打印出Tom TypeScript

转译后的js代码如下

"use strict";
function programmer() {
  return {
    name: "Tom",
    skill: {
      skill1: "TypeScript",
      skill2: "AngularJs"
    },
    age: 18
  };
}
var _programmer = programmer();
var name = _programmer.name;
var age = _programmer.age;
console.log(name, age);
var _programmer2 = programmer();
var name1 = _programmer2.name;
var skills = _programmer2.skill.skill1;
console.log(name1, skills);

另外还可以在其中使用之前提过的Rest and Spread操作符拆分对象

var obj = {a:1,b:2,c:3,d:4};
var {a,b,...others}=obj;
//打印出1 2 {"c":3,"d":4} *将后两个属性打包进others中

转译后js代码

"use strict";
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var obj = { a: 1, b: 2, c: 3, d: 4 };
var a = obj.a;
var b = obj.b;
var others = _objectWithoutProperties(obj, ["a", "b"]);
console.log(a, b, others);
var arr = [1,2,3,4];
var [num1,num2]=arr;
//注意此处是中括号
console.log(num1,num2);
//打印1 2
var [,,num3,num4]=arr;
console.log(num3,num4);
//打印3 4 用逗号空出对应位置即可跳跃取值
var [num5,,num6]=arr;
console.log(num5,num6);
//打印1 3
var [num7,...others]=arr;
console.log(num7,others);
//打印1 [2,3,4] *此时将剩余的三个数打包放进others中

转译后的js代码如下

"use strict";
var arr = [1, 2, 3, 4];
var num1 = arr[0];
var num2 = arr[1];
console.log(num1, num2);
var num3 = arr[2];
var num4 = arr[3];
console.log(num3, num4);
var num5 = arr[0];
var num6 = arr[2];
console.log(num5, num6);
var num7 = arr[0];
var others = arr.slice(1);
console.log(num7, others);
var test = arg1=>arg1;
//单个参数可以不加小括号
var sum = (arg1,arg2) =>arg1+arg2;
//单行声明可以不加大括号
var sum2 = (arg1,arg2)=>{
  return arg1+arg2
}
//多行写法

转译后js代码

"use strict";
var test = function test(arg1) {
  return arg1;
};
var sum = function sum(arg1, arg2) {
  return arg1 + arg2;
};
var sum2 = function sum2(arg1, arg2) {
  return arg1 + arg2;
};

这样看其实箭头表达式只是比原来的声明方法简便些而已,其实不然。箭头表达式真正强大的是可以很轻松的化解之前一直困扰我们得this的作用域问题
下面一段代码在js中是打印不出东西的

function Person(x){
  this.name=x;
  setInterval(function(){
    console.log(this.name)
  },1000)
}
var man = new Person('zhangsan');
//setInterval中的this指向的是全局对象window而不是我们所创建的对象

而应用箭头表达式就可以轻松解决作用域的问题

function Person(x){
  this.name=x;
  setInterval(()=>{
    console.log(this.name)
  },1000)
}
var man = new Person('zhangsan');
//每一秒打印出一个zhangsan

转译后js代码中可以看出实际上是ts帮我们写了纠正this这段代码

function Person(x) {
    var _this = this;
    this.name = x;
    setInterval(function () {
        console.log(_this.name);
    }, 1000);
}
var man = new Person('zhangsan');
var arr = [1, 2, 3, 4];
arr.attr = 'number 5';
//这里在ts会报错 但其实可以运行
arr.forEach((value,key) => console.log(value,key));
//打印出1,0|2,1|3,2|4,3
for (var i in arr) {
    console.log(i);
};
//打印出1,2,3,4,attr
for (var n of arr) {
    console.log(n);
}
//打印出1,2,3,4
for (var n of arr) {
    if(n>2)break
    console.log(n);
}
//打印出1,2

for of与另两者最大的区别就在于for of会忽视掉循环中的非数字元素(for in不会),且可以打断(forEach不可打断)

class Programmer{
  name;
  skill;
  work(){
    console.log("coding....")
}
};
var pro1 = new Programmer();
pro1.name = 'zhang san';
pro1.skill = 'TS';
pro1.work();
//转译后js代码
var Programmer = (function () {
    function Programmer() {
    }
    Programmer.prototype.work = function () {
        console.log("coding....");
    };
    ;
    return Programmer;
}());
var pro1 = new Programmer();
pro1.name = 'zhang san';
pro1.skill = 'TS';
pro1.work();

以上的代码中,声明了一个Programmer类,其中含有name,skill两个属性和work方法,而其调用方式与js中一样。我们可以看到转译后,work()实际上是定义在原型上的方法。
* 类的访问权限
在类的声明同时,可以用public private protected对其中属性和方法的访问权限进行声明

类访问权限
图中分别用三个访问操作符声明了3个属性和一个方法,声明private和protected的属性和方法在外部,也就是class代码的外面调用是会报错的,而在内部work()方法可以任意调用属性。
总结来说public操作符声明的可以在任意地方使用,也是不声明时的默认操作符;private操作符会使其只可在class内部被调用;而protected操作符则在class内部和该类的后代继承元素上可以使用
public protected private
外部 × ×
后代 ×
内部

需要注意的是:这段代码只是在书写阶段会引发报错,而使用时不会有任何问题,因为转译成js后并不存在这些功能

class Programmer {
    constructor() {
        console.log('coding');
    }
//构造器函数
};
var pro1 = new Programmer();
//打印一次coding

构造器函数的一个重要用途就是规定一个类里的某些属性必须在实例化时被传入值,如下

class Programmer {
    name;
    constructor(name:string) {
        console.log('coding by '+name);
    }
};
//也可以像下面这么写
class Programmer {
    constructor(public name:string) {
        console.log('coding by '+name);
    }
};
var pro1 = new Programmer();
//这行代码会引发编辑器报错
var pro2 = new Programmer('zhangsan');
//打印 coding by zhangsan
class Programmer{
    name;
    skill;
    work(){
        console.log("coding with "+this.skill)
    }
};
class WebProgrammer extends Programmer{
    no;
    learn() { 
        console.log(this.no+' is learning TS')
    }
}
var Wp1 = new WebProgrammer();
Wp1.skill = 'js';
Wp1.no = 1;
Wp1.work();
//打印 coding with js
Wp1.learn();
//打印 1 is learning Ts
class Programmer{
    name;
    skill;
    constructor(name) {};
    work(){
        console.log("coding with "+this.skill)
    }
};
class WebProgrammer extends Programmer{
    no;
    constructor(name:string,no:number) {
        super(name);
        this.no = no;
//在这里用super继承了父类构造函数的name属性,将name和no作为子类的两个实例化时必须赋值的属性
    }
    learn() { 
        console.log(this.no+' is learning TS')
    }
}
var Wp1 = new WebProgrammer('zhangsan',1);
Wp1.skill = 'js';
Wp1.work();
Wp1.learn();

上面介绍的是用super继承父类的属性,同样父类的方法也可以继承,下面是一个实例

class Programmer{
    name;
    skill;
    constructor(name) { };
    work(){
        console.log("coding with "+this.skill)
    }
};
class WebProgrammer extends Programmer{
    no;
    constructor(name:string,no:number) {
        super(name);
        this.no = no;
    }
    learn() { 
        super.work();
//继承父类的work方法
        this.learnAfterWork;
    };
    private learnAfterWork() {
//将该方法声明为私有
        console.log('learning sth')
    };
}
var Wp1 = new WebProgrammer('zhangsan',1);
Wp1.skill = 'js';
Wp1.work();
//打印coding with js
Wp1.learn();
//打印coding with js,learning sth
Wp1.learnAfterWork();
//无法打印 因为方法learnAfterWork是私有的,外部访问到
class Programmer{
name;
skill;
work(){
    console.log("coding with "+this.skill)
}
};
class WebProgrammer extends Programmer{
no;
learn() { 
    console.log(this.no+' is learning TS')
}
};
var Wp: Array<Programmer> = [];
//声明Wp数组里只能放入Programmer类的元素
Wp[0] = new Programmer();
//成功
Wp[1] = new WebProgrammer();
//成功 WebProgrammer也属于Programmer 
Wp[2] = 1;
//在ts中会报错
interface IProgrammer{
    name: string;
    age: number;
};
class Programmer{
    constructor(public config: IProgrammer) {}
//构造函数中限制属性要应用IProgrammer接口约束
};
var p1 = new Programmer();
//报错
var p2 = new Programmer('zhangsan',18);
//报错
var p3 = new Programmer({
    name: 'zhangsan',
    age:18
});
//正确调用方法:传入一个带有规定属性的对象
interface IProgrammer{
    useTool();
};
class JsProgrammer implements IProgrammer{
    useTool(){
        console.log('use JS')
    }
};
class TsProgrammer implements IProgrammer{
    useTool(){
        console.log('use TS')
    }
}

例子中若在声明的方法中没有实现useTool方法则会引发报错

export class klass1 { };
class klass2 { };
export var x1;
var x2;
export function func1() { };
function func(){};
//没有加export的就是没有输出

import.ts代码

import {klass1,x1,func1} from "export.ts";
x1=1;
var k1=new klass1;
func1();
//这里是取不到export.ts中没有导出的klass2,x2,func2的

ts注解是提供给指定的工具和框架使用的,为程序元素(类,方法,变量)加上更直观的的说明,而与程序业务逻辑无关

例如angular2里的@component (待研究.....)

"那么在ts文件怎么应用js中的那些库和框架呢?"

这时候就要用到类型定义文件xxx.d.ts,如将jquery的类型定义文件index.d.ts放到ts同目录下,这时jquery的接口就已经全部导出了,可以直接在ts文件中调用
而寻找各种各样工具的类型定义文件就要用到

上一篇下一篇

猜你喜欢

热点阅读