zoukankan      html  css  js  c++  java
  • 迈向angularjs2系列(1):typescript指南

    目录

    1.typescript和javascript的区别

    2.安装和helloworld程序

    3.从ES6、7引入的新特性

    4.卓越的静态类型

    5.编写泛型代码

    6.使用typings定义外部类型

    源代码下载

    https://pan.baidu.com/s/1eSeqWeQ,提取密码qygu。

    前言

    typescript是angularjs2推荐使用的脚本语言。它由微软2012年首次发布。 

    一.    typescript和javascript的区别

    1.从遵循的规范上的角度:

      Javascript遵循ECMA5的规范,TypeScript是语法上对ECMA6的实现。

    2.从功能上说:

    TypeScript提供了类、模块和接口来帮助构建组件,更方便写面向对象的程序,所以被称为更好的typescript。

    3.从支持上说:

    所有的浏览器都支持ES5及之前的javascript,而没有浏览器支持typescript,所以需要编译。另外typescript支持所有javascript的语法。

    照片显示了ES5、ES2015、ES2016以及typescript的关系。

    二. typescript的安装和helloworld程序

    (1)npm安装

     npm install -g typescript@1.8 全局安装了1.8版本的typescript编译器和tsc可执行程序(稍后就能看到使用tsc命令转换ts文件了),并且添加到环境变量中。为了确认正常,打印-v命令。
    $ npm -v   //首先看一下npm是不是新版本
    $ npm install -g typescript@1.8. //全局安装typescript
    $ tsc -v

    显示typescript的版本是:

    (2)运行第一个ts程序。

    第一种方式。

    首先在1.hello.ts程序中输入:

    console.log("hello world!")

    然后执行命令:

    $ tsc 1.hello.ts  //把ts文件转换为等价的hello.js

    最后运行node来执行js文件。

    $ node 1.hello.js //打印出hello world。成功!

    第二种方式。

    好处是运行一个命令就把转换和执行的任务完成了。

    先装好typescript和ts-node.

    $ npm install typescript@1.8  -g    //装好ts
    $ npm install  -g ts-node //装ts-node模块,选项是-g哦。

    再运行命令执行文件。

    $ ts-node 1.hello.ts //打印hello world,成功!

    三. 从ES6、7引入的新特性

    ES2015和ES2016有比较大的变化。

    (1)ES2015的箭头函数

    由来:javascript有一级函数的特性,也就是说,函数能够当做参数进行传参,很好的用法不过太啰嗦,所以ES2015提供了箭头函数,更加简洁。

    ts例子1:

    var even=[3,1,56,7].filter(el=>!(el%2));
    //!(el%2)偶数的取模为0,取反为true,那么filter函数就说,恭喜,通过了!没被通过的,被无情抛弃
    console.log(even);
    //打印56咯。

    通过运行ts-node命令,屏幕显示结果为:

    ts例子2:

    先预热一下,reduce函数是ES5的数组方法,第一个参数是回调callback,第二个参数是初始值。这里的代码第一个参数是一个箭头函数,和我们平常写的function没有本质区别。第二个参数是初始值0.

    var result=[1,2,3]
        .reduce((total,current)=>total+current,0);
    //reduce遍历数组,返回值是total+current。
     //回调函数的参数是total和current,total是上一次的返回值,第一次就为初始值0. 
    //
    current是当前数组元素的值。

    //那么就清晰了,第一个total为0,当前元素是1,返回的0+1.
    console.log(result);

    通过运行ts-node命令,屏幕显示结果为:

    ts例子3:

    sort是经典的排序函数。基本思路是先对价格排序,如果价格相等,再对数量排序。

    var data=[
        {price:10,total:70},
        {price:94,total:340},
        {price:14,total:34},
        {price:14,total:12}
    ];
    //
    var sorted=data.sort((a,b)=>{
        //a、b为依次遍历的数组元素
        var diff=a.price-b.price;
        if(diff!==0){
            return diff;
         }
        return a.total-b.total;
        //返回值小于0,a比b小,排在前端。返回值大于0,a比b大,排在后面
    })
    console.log(sorted);

    通过运行ts-node命令(编译),屏幕显示正确的排序结果为:

    ts例子4:

    function MyComponent(){
        this.age=42;
        console.log(this);
        setTimeout(()=>{
            //箭头函数的特性,this对象依然是外部this指向,并不会被改变。
            this.age+=1;
            console.log(this.age);
        },100);
        //等待100ms,age加1等于43,并打印出来
    }
    new MyComponent();

    通过编译,屏幕显示:

    (2)ES2015和ES2016中的类

    首先说明一下,ES6就是ES2015。ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。

    然后ECMAScript 2016就是ES7。

    ES6的类依然是使用构造函数和基于原型的继承,但是语法更加方便和简洁。

    下面是ES2016定义类的语法:

    class Human{
        static totalPeople=0;
        _name;//ES2016的属性定义语法
        constructor(name){
            this._name=name;
            Human.totalPeople+=1;
        }
        get name(){
            return this._name;
        }
        set name(val){
            this._name=val;
        }
        //name属性的get和set方法
        talk(){
            return "Hi,I'm"+this.name;
        }
    }
    class Developer extends Human{
        _languages;//ES2016的属性语法
        constructor(name,languages){
            super(name);
            this._languages=languages;
        }
        get languages(){
            return this._languages;
        }
        talk(){
            return super.talk()+" And I Know "+this.languages.join('.');
    
        }
    }
    var human=new Human("foobar");
    var dev=new Developer("bar",["javascript"]);
    console.log(dev.talk());

    喜欢!除了类的定义体是对象外,属性和方法的书写和java很像,我喜欢的方式。

     通过编译,屏幕显示结果为:

    (3)定义块级作用域中可见的变量

    java和c++是块级作用域。

    只列举代码,表现为2点,第一个是特定代码块的变量只能代码块内部可见,第二,嵌套在内部的代码块中也可见。

    public class Demo{
        // 属性块,在类初始化属性时候运行
        {
            int j = 2;// 块级变量
        }
    
        public void test1() {
            int j = 3;  // 方法级变量
            if(j == 3) {
                //j变量在代码嵌套的内部if语句中可见
                int k = 5;  // 块级变量
            }
    }

    javascript是函数作用域。

    var fns=[];
    for(var i=0;i<5;i+=1){
        fns.push(function(){
            console.log(i);
        })
    }
    fns.forEach(fn=>fn());

    打印结果,很奇怪。

    ES6代码:

    var fns=[];
    for(let i=0;i<5;i+=1){
        fns.push(function(){
            console.log(i);
        })
    }
    fns.forEach(fn=>fn());

    打印结果:

    (4)使用ES2016装饰器进行元编程。

    装饰器是ES2016的一个提案,它的依据是“在设计阶段可以对类和属性进行注释和修改”。在angular2很常用,可用来定义组件、指令以及管道,并且还能配合依赖注入机制来使用。

    首先,装饰器能干吗。

    装饰器典型的用法是把方法和属性标记为过期,另一个应用场景是声明式语法,从而实现面向切面的编程。其实呢,装饰器只是一个语法糖而已。装饰器目前并没有得到真正的使用

    Experimental support for decorators is a feature that is subject to change in a future release.
    //编写了Person类,它只有一个getter,名字为kidCount
    class Person{
        //kidCount有一个装饰器nonenumerable
        @nonenumerable
        get kidCount(){
            return 42;
        }
    }
    //装饰器函数接收3个参数,最后返回descriptor属性。
    function nonenumerable(target,name,descriptor){
        descriptor.enumerable=false;
        //可枚举性
        return descriptor;
    }
    var person=new Person();
    for(let prop in person){
        console.log(prop);
    }

    这个代码只是做演示,因为typescript并不能编译它。

    对应的ES5语法类似于:

    descriptor=nonenumerable(Person.prototype,'kidCount',descriptor);
    //descriptor
    Object.defineProperty(Person.prototype,'kidCount',descriptor);

    接下来,介绍angular 2装饰器的用法。

    @Component({
       selector:"app",
       providers:[NameList],
       tempalteUrl:"./app.html",
       directives:[RouterOutlet,RouterLink]
       });
    //这个定义好拽呀,直接写好@+Component+选项对象
    @RouterConfig({
       {path:"/",component:Home,name:'home'},
       {path:"/",component:About,name:'about'}
        });
    //写好@+RouterConfig+选项对象
    export class App{}

    这个代码现在还不能运行,等到使用angular2的时候就清楚明了了。

     (5)使用ES2015编写模块化的代码

    angular 1.x引入了一套模块系统,不过并不支持懒加载特性。angular 2种充分利用了ES2015提供的模块系统。ES2015提供了声明式API,以及使用模块加载器的命令式API。

    语法分为export和import两个方面。

    第一,看一个简单的DEMO:

    math.ts:

    export function square(x){
        return Math.pow(x,2);
    }
    export function log(x){
        return Math.log(x);
    }
    export const PI=Math.PI;

    math2.ts,更简洁的写法而已:

    function square(x){
        return Math.pow(x,2);
    }
    function log(x){
        return Math.log(x);
    }
    const PI=Math.PI;
    export {square,log,PI}

    app.ts调用,要编译的是app.ts哦:

    import {square,log} from "./math";
    console.log(square(2));
    console.log(log(100));

    屏幕显示效果:

    第二,ES2015模块化语法带有隐式的异步行为。

    比如说,

    A模块依赖于B、C模块。当用户请求A模块,JS模块加载器会先加载B和C模块,才能调用A模块。这里B和C模块都是异步加载的。

    第三,典型的应用场景会给导出的内容起一个名字。

    使用别名导入整个模块的DEMO:

    import * as math from "./math";
    //as语法咯
    console.log(math.square(2));
    console.log(math.log(100));

    第四,默认导出。

    模块导出使用了export default语法,是一种带名字的导出。

    基本的默认导出DEMO:

    math3.ts:

    export default function cube(x){
        return Math.pow(x,3);
        //默认导出的名字是cube
    }
    export function square(x){
        return Math.pow(x,2);
    }

    app3.ts:

    import cube from "./math3";
    //等同于import {default as cube} from "./math3
    console.log(cube(3));

    显示结果正常:

    默认导出混合其他导出的DEMO:

    math3.ts:

    export default function cube(x){
        return Math.pow(x,3);
        //默认导出的名字是cube
    }
    export function square(x){
        return Math.pow(x,2);
    }
    app4.ts:
    import cube,{square} from "./math3";
    console.log(square(2));
    console.log(cube(3));

    显示结果OK:

     (6)ES2015的模块加载器

    通过编程的方式加载app模块执行main函数,使用System对象的import方法就好了。现在代码因为缺乏配置项,所以还运行不起来。

    app.ts:

    export function main(){
        console.log(2);
    }

    init.js

    System.import("./app")
    .then(app=>{
        app.main();
    })
    .catch(error=>{
       console.log("致命的错误"); 
    });

    四: 卓越的静态类型

    有了静态类型,那么IDE开发环境除了避免输入错误的语法高亮,还提供精确静态分析的建议。很棒。

    typescript的所有类型包含几类:

    ● 原生类型

    ● any类型

    ● Object类型

    ●类

    ●接口

    ● 泛型

    (1)原生类型

    就是javascript比较熟悉的Number、String、Boolean、Null、以及Undefined。

    除了webstorm报类型(type)错误,运行编译命令,typescript  也报错 Type 'string' is not assignable to type 'number' 。那么就是说,一旦foo设置了类型,就不能赋值为其他类型了。

    而Enum是用户自定义的原生类型,它是Number的子类。含义是枚举用户自定义的类型,由一系列有名称的值也就是元素构成。

    定义enum如下:

    enum STATES{
        CONNECTING,
        WAITING,
        CONNECTED
    }
    //定义枚举类型
    if(this.state==STATES.CONNECTING){
        //通过点语法引入
    }

    (2)any类型

    any类型是所有其他类型的父类,代表可以拥有任何类型的值,类似于动态类型,一方面不会报错,另一方面则放弃了typescript的优点了。

    let foo:any;
    foo={};
    foo="bar";
    foo+=24;
    console.log(foo);//结果为"bar 24"。

    (3)Object类型

    第一,普通的对象都是Object类型。

    第二,Object类型的子类是Array类型。

    Typescript的数组,要求元素类型相同。都可以使用js的各种数组方法,比如push、join、splice等,也可以使用方括号运算符对数组元素进行访问。

    数值型数组DEMO:

    let primes:number[]=[];
    primes.push(2);
    primes.push(3);
    console.log(primes);

    any型数组:

    let randomItems:any[]=[];
    randomItems.push(1);
    randomItems.push("foo");
    randomItems.push("{}");
    console.log(randomItems);

    屏幕结果为

    第三,Object类型的子类是Function类型。

    javascript有两种方式创建新函数:

    //函数表达式
    var isPrime=function(n){
    
    }
    //函数声明
    function isPrime(n){
       
    }
    //或者,使用箭头函数
    var isPrime=n=>{
       //函数体
    }

    Typescript增加的是参数和返回值的类型。

    函数表达式:

    let isPrime:(n:number)=>boolean=n=>{
        //整个函数赋给变量isPrime,参数是number类型的n,返回值是boolean类型的n,函数体在{}里面
    }

    函数声明:

    function isPrime(n:number):boolean{
        //参数为number类型,返回值为boolean类型
    }

    对象字面量的定义写法:

    let person={
         _name:null,
         setName(name:string):void{
           //参数是string类型,返回值是void
           this._name=name;
         }
    }

    (4)类

    typescript定义类,属性的声明式强类型的。

     class-basic.ts:

    class Human {
        static totalPeople=0;
        _name:string;//强类型的哦
        constructor(name){
          this._name=name;
          Human.totalPeople+=1;
        };
        get name(){
            return this.name;
        }
        set name(val){
            this._name=val;
        }
        talk(){
            return "HI,I'am"+this._name;
        }
    }
    let human=new Human("foo");
    console.log(human._name);

    打印结果为

    成功

    访问修饰符有3个。更好的实现封装和更优雅的接口。

    ●public。public的属性和方法在任何地方可以访问。

    ●private。private的属性和方法只能在类定义内部进行访问。

    ●protected。protected的属性和方法可以类定义内部访问,也可以从子类访问。

    //typescript实现
    class Human{
        static totalPeople=0;
        constructor(protected name:string,private age:number){
            //定義了一個protected型的屬性,名為name,類型為string
            //age屬性。好處是避免显示式的赋值操作
            Human.totalPeople+=1;
        }
        talk(){
            return "Hi,I'm"+this.name;
        }
    }
    class Developer extends Human{
        constructor(name:string,private languages:string[],age:number){
            //显式使用访问修饰符,或者定义接收的参数类型,都可以混合使用的。
            super(name,age);
        }
        talk(){
            return super.talk()+" And I Know "+this.languages.join('.');
    
        }
    }
    //创建developer类的一个新实例
    let Dev=new Developer("foo",["javascript","Go"],42);
    //dev.languages=["java"];这行代码会报一个私有属性的错误
    let human=new Human("foo",42);
    //human.age=42;由于私有属性,所以报错
    //human.name="bar",会报一个protected错误。它只能在类类内部或者子类中访问

    (5)接口

    接口定义了一组对象共同的属性和方法,称作接口签名。要实现接口,那么实现定义接口规定的所有属性和方法。

    关键字就2个,interface和implements。

    interface Accountable1{
        goIncome():number;
        accountNumber:string
    }
    class Value implements Accountable1{
        constructor( public accountNumber:string){
            this.accountNumber=accountNumber;
        }
        goIncome():number{
            return 100;
        }
    }
    var extra=new Value("余额");
    console.log(extra.accountNumber);

    打印为

    接口实现,成功

    接口继承

    接口之间可以互相继承。接口继承另外一个接口,使用extends关键字。

    interface-extends.ts:

    interface Accountable2{
        accountNumber:string;
        getIncome():number;
    }
    interface Individual extends Accountable2{
        ssn:string;
    }

    实现多个接口:

    interface People{
        age:number;
        name:string;
    }
    interface Accountable{
        accountNumber:string;
        goIncome():number
    }
    class Person implements People,Accountable{
      //实现多个接口,用逗号分隔
        age:number;
        name:string;
        accountNumber:string;
        constructor(age:number,name:string,accountNumber:string){
    
        }
        goIncome():number{
            return 10;
        }
    }
    var person=new Person(10,"100","1000");

    五:编写泛型代码

     可以写类似java的泛型代码,好精彩!使用“<T>”。能写出更简洁的代码来。

    (1)使用泛型类

    定义一组类型的class。

    class Nodes<T> {
        value:T;
        left:Nodes<T>;
        right:Nodes<T>;
    }
    let numberNode=new Nodes<number>();
    
    let stringNode=new Nodes<string>();
    
    numberNode.right=new Nodes<number>();//类型匹配
    numberNode.value=42;//类型匹配
    //numberNode.value="42";报错,类型不匹配
    //numberNode.left=stringNode;报错,类型不匹配

    (2)使用泛型函数

    定义一组类型的函数。

    function identify<T>(arg:T){
        return arg;
    }
    interface Comparable{
        compare(a:Comparable):number;
    }
    function sort<I extends Comparable>(arr:Comparable[]){
        //
    }

    (3)多重泛型

    class Pair<K,V>{
        key:K;
        value:V;
    }
    let pair=new Pair<string,number>()
    pair.key="foo";
    pair.value=42;

    精彩

    五: 使用typescript的类型推断机制简化代码

    typescript可以猜测代码中的静态类型。好智能哦,所以可以用来省略一些代码。

    let answer=42;
    answer="42";
    //会提示错误,因为一开始的赋值,typescript已经把它当做number类型了。
    let answer;
    answer=42;
    answer="42";
    //这个时候不会报错,因为第一次声明时,typescript给到的静态类型是any。

    (1)最常见的类型

    let x=["42",42];
    //x的类型推断为any[]数组。
    let x=[42.null,32];
    //typescript的类型推断为number[]。

    (2)与上下文有关的类型推断

     

    这里可以看到e并没有规定类型,可是typescript就根据上下文推断它为MouseEvent鼠标事件。

    六: 额外的类型定义

    尽管静态类型很酷,但是我们使用的大部分前端类库都是基于javascript构建的,都是动态类型。而typescript提供了额外的类型定义,来给编译器提供提示。

    (1)使用预定义的外部类型定义

     step1:安装typings工具

    npm install  -g  typings

    于是就安装好了typings目录,tsconfig.json、typings.json中,内容为:

    //tsconfig.json
    {
      "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "sourceMap": true
      },
      "exclude": [
        "node_modules"
      ]
    }
    //typings.json
    {
      "dependencies": {}
    }

    step2:创建基础配置

    typings init 

    step3:搜索

    $  typings search module

    step4:安装is-builtin-module

    $  typings install is-builtin-module  --save

    这个时候,typings.json的内容变为

    {
      "dependencies": {
        "is-builtin-module": "registry:npm/is-builtin-module#1.0.0+20161031191623"
      }
    }

    typings目录变为

    (2)自定义外部类型

    step1:定义好类库的接口。

    define-external-type.ts:

    interface LibraryInterface{
        selectElements(selector:string):HTMLElement[];
        hide(element:HTMLElement):void;
        show(element:HTMLElement):void;
    }

    step2:定义ts.d文件。

    interface DOMLibraryInterface{
        selectElements(selector:string):HTMLElement[];
        hide(element:HTMLElement):void;
        show(element:HTMLElement):void;
    }
    declare var DOM:DOMLibraryInterface;

     step3:DOM通过reference引入,编译器就会找到对应的外部类型定义了。

    ///<reference path="dom.d.ts" />
    var DOM={
        selectElements:function(selector:string):HTMLElement[]{
            return [];
        },
        hide:function(element:HTMLElement):void {
            element.hidden=true;
        }
    };

    这个时候,会报错。直到完全实现定义的接口为止,如下:

    ///<reference path="dom.d.ts" />
    var DOM={
        selectElements:function(selector:string):HTMLElement[]{
            return [];
        },
        hide:function(element:HTMLElement):void {
            element.hidden=true;
        },
        show:function(element:HTMLElement):void{
            element.hidden=false;
        }
    };

    这样分离出来,就可以把全部的外部类型定义放在同一个文件里,方便管理了。

  • 相关阅读:
    Demo:刮刮卡橡皮擦效果
    养成良好的代码编写习惯
    我的百科
    专业英语词汇
    加载资源的类
    循环滚动翻页+居中项缩放
    学习笔记—Node中模块化规范
    学习笔记—Node中的EventLoop
    学习笔记—Node的全局对象
    学习笔记—Node的基本概念
  • 原文地址:https://www.cnblogs.com/chenmeng2062/p/7086816.html
Copyright © 2011-2022 走看看